Skip to content

Case 136: Executable Stack Removed (the fix direction)

Field Value
Verdict 🟢 COMPATIBLE
Category Quality (Compatible)
Classification Rule
Platforms Linux
Flags —
Detected ChangeKinds executable_stack_removed
Source files catalog/cases/case136_executable_stack_removed/
Rule family executable-stack-flag-changed — Duplicate of case49_executable_stack
Subject ELF security hardening and deployment risk

Category: Quality | Verdict: 🟢 COMPATIBLE

Related rule: same executable-stack-flag-changed rule as case49_executable_stack (the canonical case for this rule) — identical GNU_STACK RWE→RW transition and library source, framed as the fix direction. See examples/CLAUDE.md's "Taxonomy" section.

Verdict and consumer impact

This is the fix counterpart to case49 (which goes the bad direction). v1 has an executable stack — PT_GNU_STACK with flags RWE (-Wl,-z,execstack). v2 corrects it to RW (-Wl,-z,noexecstack), restoring NX (No-eXecute) protection on the process stack. The exported ABI surface is byte-identical between versions, so every existing binary keeps working unmodified — this change only tightens a security property, it never breaks a caller.

Old/new diff

old/lib.c new/lib.c
linked with -Wl,-z,execstack (PT_GNU_STACK = RWE) linked with -Wl,-z,noexecstack (PT_GNU_STACK = RW)
(library source is identical — see old/lib.c / new/lib.c)

abicheck command

gcc -shared -fPIC -g old/lib.c -o libfoo_v1.so -Wl,-z,execstack
gcc -shared -fPIC -g new/lib.c -o libfoo_v2.so -Wl,-z,noexecstack
abicheck compare libfoo_v1.so libfoo_v2.so

Expected abicheck finding

Verdict: COMPATIBLE (exit 0)

- executable_stack_removed: Executable stack removed: library now uses a
  non-executable stack - NX protection restored (good practice)

Minimum evidence

min_evidence: L0 — PT_GNU_STACK's flags are read directly from the ELF program headers; no debug info or headers are needed to see the change.

Why abicheck catches it

abicheck reads each library's PT_GNU_STACK segment flags from the ELF program header table and diffs the executable bit between versions — a pure L0 ELF fact, tracked as the symmetric complement of the executable-stack regression case49 demonstrates, so a hardening improvement is reported as compatible rather than silently ignored or mis-flagged as a regression.

Runtime failure demonstration

No observable effect on existing binaries — both versions run identically; v2 only restores NX protection on the process stack.

readelf -lW libfoo_v1.so | grep GNU_STACK
# → GNU_STACK ... RWE 0x10

readelf -lW libfoo_v2.so | grep GNU_STACK
# → GNU_STACK ... RW  0x10

gcc -shared -fPIC -g old/lib.c -o libfoo.so -Wl,-z,execstack
gcc -g app.c -L. -lfoo -Wl,-rpath,. -o app
./app
# → compute(7) = 50
# → transform(3, 4) = 11

gcc -shared -fPIC -g new/lib.c -o libfoo.so -Wl,-z,noexecstack
./app
# → compute(7) = 50
# → transform(3, 4) = 11   ← identical

Safe redesign

This case is the safe redesign — the fix is exactly -Wl,-z,noexecstack (or, in CMake, add_link_options(-Wl,-z,noexecstack)), and ensuring any hand-written assembly inputs carry a .section .note.GNU-stack,"",@progbits marker so the linker never unions in an executable-stack requirement from an unmarked object file.

References


Source files

  • new/
  • old/
  • CMakeLists.txt
  • app.c

See also: Compatibility Catalog · All COMPATIBLE cases · Category: Quality (Compatible) · Rule: Executable-stack marking changed · Subject: ELF security hardening and deployment risk.