Skip to content

Case 49: Executable Stack (GNU_STACK RWX)

Field Value
Verdict ๐ŸŸข COMPATIBLE
Category Quality (Compatible)
Platforms Linux
Flags Bad practice
Detected ChangeKinds โ€”
Source files examples/case49_executable_stack/

Category: ELF / Security | Verdict: BAD PRACTICE

What this case is about

Both libraries export identical symbols with identical signatures. The difference is in the PT_GNU_STACK program header: v1 has flags RWE (read-write-execute), while v2 has RW (read-write, non-executable).

An executable stack is a security bad practice: it disables NX (No-eXecute) protection for the entire process, making stack-based buffer overflow exploits trivially exploitable.

How executable stacks sneak in

The most common cause is linking a single .o file compiled from assembly that has .section .note.GNU-stack,"x",@progbits. The GNU linker takes the union of all stack permissions โ€” if any input object requests an executable stack, the entire shared library (and any process loading it) gets one.

This happens frequently with: - Hand-written assembly without the proper .note.GNU-stack annotation - Old assembly files generated by compilers that defaulted to executable stacks - Third-party object files or static archives

What abicheck detects

  • EXECUTABLE_STACK: The library has PT_GNU_STACK with execute permission. This is classified as a security metadata issue. Because the actual symbols and types are identical, the functional ABI is compatible.

Overall verdict: COMPATIBLE (same ABI surface; executable stack is security concern).

How to reproduce

# Build bad version (with executable stack via linker flag)
gcc -shared -fPIC -g bad.c -o libbad.so -Wl,-z,execstack

# Build good version (non-executable stack)
gcc -shared -fPIC -g good.c -o libgood.so -Wl,-z,noexecstack

# Verify GNU_STACK flags
readelf -l libbad.so  | grep -A1 GNU_STACK
# โ†’ GNU_STACK  0x000000 ... RWE  โ† executable!
readelf -l libgood.so | grep -A1 GNU_STACK
# โ†’ GNU_STACK  0x000000 ... RW   โ† correct

# Run abicheck
python3 -m abicheck.cli dump libbad.so  -o /tmp/v1.json
python3 -m abicheck.cli dump libgood.so -o /tmp/v2.json
python3 -m abicheck.cli compare /tmp/v1.json /tmp/v2.json
# โ†’ COMPATIBLE + EXECUTABLE_STACK warning

How to fix

Ensure all assembly files include a non-executable stack annotation:

.section .note.GNU-stack,"",@progbits

Or use the linker flag to force a non-executable stack:

gcc -shared -fPIC lib.c asm.S -o libfoo.so -Wl,-z,noexecstack

In CMake:

target_link_options(foo PRIVATE -Wl,-z,noexecstack)

Real-world example

The Linux kernel, glibc, and all major distributions flag executable stack as a security defect. Fedora and Debian both have policies requiring -Wl,-z,noexecstack and lintian/rpmlint checks that reject packages with executable stacks.

References

Real Failure Demo

Severity: SECURITY / BAD PRACTICE

The program still runs, but the v1 artifact requests an executable process stack. Hardened loaders and distro policy can reject this even when symbol ABI looks otherwise compatible.

cmake -S examples -B /tmp/abicheck-examples-build -DCMAKE_BUILD_TYPE=Debug
cmake --build /tmp/abicheck-examples-build --target case50_executable_stack_v1 case50_executable_stack_v2

readelf -W -l /tmp/abicheck-examples-build/case50_executable_stack/libv1.so | grep GNU_STACK
# GNU_STACK ... RWE

readelf -W -l /tmp/abicheck-examples-build/case50_executable_stack/libv2.so | grep GNU_STACK
# GNU_STACK ... RW

Source files

  • CMakeLists.txt
  • app.c
  • bad.c
  • good.c

See also: Examples overview ยท All COMPATIBLE cases ยท Category: Quality (Compatible).