Skip to content

AD3006: EnableNonExecutableStack

Summary

Property Value
ID AD3006
Name EnableNonExecutableStack
Category Security
Severity Error
Applies to ELF (Linux/Unix)

Description

This check ensures that non-executable stack is enabled. A common type of exploit is the stack buffer overflow. An application receives, from an attacker, more data than it is prepared for and stores this information on its stack, writing beyond the space reserved for it.

This can be designed to cause execution of the data written on the stack. One mechanism to mitigate this vulnerability is for the system to not allow the execution of instructions in sections of memory identified as part of the stack.

How It Works

The rule examines the PT_GNU_STACK segment in the ELF program headers:

  • Checks for presence of PT_GNU_STACK
  • Verifies the PF_X (execute) flag is NOT set
  • Confirms the segment specifies non-executable permissions

Why This Matters

Non-executable stack protection is the single most impactful mitigation against classic buffer overflow exploits. It uses hardware features to make the stack incapable of executing code, blocking the most common exploitation technique.

The Classic Attack It Prevents

Stack buffer overflow exploitation traditionally works like this:

void vulnerable() {
    char buf[64];
    gets(buf);  // No bounds checking!
}

// Attacker sends:
// [NOP sled][shellcode][return addr pointing to buffer]

// Execution flow:
// 1. Function returns to attacker's address
// 2. Jump into NOP sled on stack
// 3. Execute shellcode
// 4. Attacker wins

With NX stack, step 3 causes a SIGSEGV instead of shellcode execution.

Hardware Protection

NX is enforced by the CPU's memory management unit:

CPU Feature Page Table Bit
AMD64 NX (No-Execute) Bit 63
Intel 64 XD (Execute Disable) Bit 63
ARM64 XN (Execute Never) Varies
ARM32 XN Domain-based

Why It's Effective

Property Benefit
Hardware enforcement Cannot be bypassed by software
Zero overhead No runtime performance cost
Every instruction check CPU checks on each fetch
Page granularity Entire stack protected

Attack Technique Evolution

1990s: Stack shellcode → Blocked by NX
2000s: Return-to-libc → Uses existing code
2010s: ROP chains → Uses code snippets
2020s: JOP, COOP → More sophisticated

NX forced attackers to develop much more complex techniques.

The PT_GNU_STACK Segment

ELF specifies stack permissions through PT_GNU_STACK:

Good: PT_GNU_STACK with RW (Read-Write only)
  Flags: 0x6 (PF_R | PF_W)

Bad: PT_GNU_STACK with RWX (Executable)
  Flags: 0x7 (PF_R | PF_W | PF_X)

Bad: No PT_GNU_STACK (legacy default = executable)

Common Causes of Executable Stack

Cause Solution
GCC nested functions Trampolines—avoid or use alternatives
Hand-written assembly Add .note.GNU-stack section
Old object files Recompile with modern toolchain
JIT compilers (vulnerable implementation) Use proper mprotect lifecycle

Nested Function Trampolines

GCC's nested functions can cause executable stack:

void outer() {
    void inner() { /* nested */ }
    // GCC may put trampoline on stack
}

Fix: Avoid nested functions or use -fno-trampolines.

  • NX protection: Hardware-enforced non-executable memory
  • Shellcode prevention: Stack-based shellcode won't execute
  • Classic exploit mitigation: Defeats traditional buffer overflow exploits
  • Standard protection: Expected on all modern systems

Resolution

GCC/Clang

# Mark stack as non-executable
gcc -Wl,-z,noexecstack source.c -o binary

# Combine with other security flags
gcc -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now source.c -o binary

CMake

add_link_options(-Wl,-z,noexecstack)

Makefile

LDFLAGS += -Wl,-z,noexecstack

Fix Assembly Files

If assembly files are making the stack executable, add:

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

When to Suppress

This rule should rarely be suppressed. Exceptions might include:

  • Nested functions: GCC trampolines may need executable stack
  • JIT compilers: Managing executable memory differently
  • Very legacy code: Specific compatibility requirements

Caveats

  • Same as AD3002 - this rule covers the same protection
  • Assembly files without .note.GNU-stack cause executable stack
  • All object files must be compiled correctly

Fixing Common Issues

Assembly Files

# Check which objects are causing the issue
for f in *.o; do
    if readelf -l "$f" 2>/dev/null | grep -q "GNU_STACK.*RWE"; then
        echo "Executable stack: $f"
    fi
done

Nested Functions (GCC)

Avoid nested functions that require trampolines:

// Bad - may require executable stack
void outer(void) {
    void inner(void) { /* ... */ }
    inner();
}

// Good - use regular function
static void inner(void) { /* ... */ }
void outer(void) {
    inner();
}

References