Skip to content

AD5002: DoNotAllowExecutableStack

Summary

Property Value
ID AD5002
Name DoNotAllowExecutableStack
Category Security
Severity Error
Applies to Mach-O (macOS, iOS)

Description

Mach-O binaries should not allow executable stack. An executable stack enables attackers to execute shellcode placed on the stack through buffer overflow attacks.

How It Works

The rule checks the Mach-O header for:

  1. MH_ALLOW_STACK_EXECUTION flag
  2. MH_NO_HEAP_EXECUTION flag presence

A secure binary should NOT have the MH_ALLOW_STACK_EXECUTION flag set.

Why This Matters

An executable stack is one of the most dangerous configurations possible. It allows attackers to place their code directly on the stack and execute itβ€”the simplest possible path from vulnerability to exploitation.

Historical Context

Stack execution prevention evolved on Apple platforms:

Classic Mac OS: No memory protection
macOS 10.4:    NX bit support added (Intel)
macOS 10.5+:   Non-executable stack default
iOS:           Always non-executable

The Classic Stack Attack

void vulnerable(char *input) {
    char buffer[64];
    strcpy(buffer, input);  // No bounds check!
}

// With executable stack:
// [SHELLCODE][PADDING][RET β†’ buffer]
// Jump to stack, run shellcode directly

// With non-executable stack:
// [SHELLCODE][PADDING][RET β†’ buffer]
// Jump to stack β†’ CPU faults β†’ Crash, no exploitation

Protection Layers

Attack Step NX Stack Effect
Buffer overflow Still possible
Shellcode on stack Still lands there
Jump to shellcode FAILS – CPU prevents
Code execution Blocked

Hardware Enforcement

Apple Hardware NX Support
Intel Macs XD bit in page tables
Apple Silicon Full NX support
iOS devices Hardware enforced

Cannot be bypassed without kernel compromise.

Why It Might Still Be Enabled

Reason Better Solution
GCC nested functions Avoid or use alternatives
Legacy code Refactor
JIT compilation Use proper mprotect

Mach-O Flags

Secure binary:
  Flags: MH_NO_HEAP_EXECUTION
  Missing: MH_ALLOW_STACK_EXECUTION

Insecure binary:
  Flags: MH_ALLOW_STACK_EXECUTION
  Trivially exploitable via stack overflow
  • Shellcode prevention: Stack-based payloads won't execute
  • NX protection: Hardware-enforced on modern Macs
  • Buffer overflow mitigation: Classic exploits fail
  • Defense in depth: Complements other protections

Performance Considerations

Non-executable stack has zero runtime overhead:

Aspect Impact
Runtime overhead None
Memory usage Same
Startup time None

Why there's no overhead: - NX (non-executable) is enforced by the CPU's memory management unit - Page table flags are set at allocation time, not checked continuously - No runtime checks or instrumentation required

This is a pure security improvement with zero performance cost. There is no valid performance reason to enable executable stack.

Resolution

Clang/Xcode

Do not use the -allow_stack_execute flag:

# Correct - no special flags needed
clang source.c -o binary

# WRONG - do not use
clang -Wl,-allow_stack_execute source.c -o binary

Xcode Build Settings

Ensure these settings: 1. Select target β†’ Build Settings 2. "Allow Stack Execution" should be No

CMake

Avoid setting allow_stack_execute:

# Don't add this!
# add_link_options(-Wl,-allow_stack_execute)

When to Suppress

This rule should rarely be suppressed:

  • JIT compilers: May manage own memory protection
  • Trampolines: Nested function support (avoid if possible)
  • Legacy code: Very old code patterns

Caveats

  • Non-executable stack is default on modern macOS
  • Some legacy code may require executable stack
  • iOS never allows executable stack

macOS Memory Protections

Protection macOS Status
NX (Non-executable) Default enabled
ASLR Default enabled
Stack Guard Default enabled
Hardened Runtime Recommended

Hardened Runtime

For additional protection, enable Hardened Runtime:

# Sign with hardened runtime
codesign --force --options runtime -s "Developer ID" binary

This provides: - Protection against code injection - Library validation - Debugging protection - DYLD environment variable protection

iOS vs macOS

Feature macOS iOS
Executable stack Can be enabled Never allowed
JIT allowed Yes App Store: No
Hardened runtime Optional Always on

References