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:
MH_ALLOW_STACK_EXECUTIONflagMH_NO_HEAP_EXECUTIONflag 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:
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:
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 |