AD3016: EnableIntelShadowStack¶
Summary¶
| Property | Value |
|---|---|
| ID | AD3016 |
| Name | EnableIntelShadowStack |
| Category | Security |
| Severity | Warning |
| Applies to | ELF (Linux/Unix) - x86_64 |
Description¶
This rule checks that x86_64 ELF binaries have Intel Shadow Stack (SHSTK) enabled as part of Control-flow Enforcement Technology (CET). Shadow Stack protects against Return-Oriented Programming (ROP) attacks by maintaining a hardware-protected copy of return addresses.
Why This Matters¶
Intel Shadow Stack provides the most robust defense against ROP attacks available today. By maintaining a hardware-protected copy of return addresses, it makes ROP exploitation extremely difficult even with full memory corruption.
The ROP Problem¶
Return-Oriented Programming has dominated exploitation for over a decade:
Classic ROP:
1. Overflow buffer, overwrite return address
2. Chain "gadgets" (code sequences ending in RET)
3. Each RET pops next gadget address from stack
4. Achieve arbitrary computation
5. Stack canaries? Bypassed if leak exists.
6. ASLR? Bypassed with info leak.
How Shadow Stack Defeats ROP¶
With Shadow Stack:
1. CALL pushes return address to BOTH stacks
2. Attacker overwrites return address on normal stack
3. RET pops from normal stack (corrupted)
4. CPU compares with shadow stack (clean)
5. MISMATCH! #CP exception
6. Attack defeated
Shadow Stack Protection Properties¶
| Property | Benefit |
|---|---|
| Separate memory region | Not adjacent to corruptible data |
| Hardware-protected | Normal instructions can't write |
| Per-thread | Each thread has own shadow stack |
| CPU-managed | No software overhead for checks |
What Attackers Would Need¶
To bypass Shadow Stack, attackers would need to:
1. Find ROP gadgets (still possible with IBT off)
2. Write exploit (harder - return addresses protected)
3. Either:
a. Corrupt shadow stack (requires hardware bug)
b. Use only non-RET gadgets (JOP - but IBT stops this)
c. Find oracle to leak shadow stack location
Performance Characteristics¶
| Operation | Overhead |
|---|---|
| CALL instruction | ~1 cycle (parallel push) |
| RET instruction | ~1 cycle (parallel compare) |
| Overall application | 0-2% measured |
Hardware implementation means near-zero overhead.
Combining IBT and Shadow Stack¶
| Protection | Attack Prevented |
|---|---|
| IBT only | JOP, COP |
| Shadow Stack only | ROP |
| Both (full CET) | ROP, JOP, COP |
Full CET (-fcf-protection=full) provides comprehensive control-flow protection.
OS Support Status¶
| OS | Shadow Stack Support |
|---|---|
| Linux 5.6+ | Kernel support |
| Linux 6.6+ | Improved userspace |
| Windows 10 20H1+ | Full support |
| Windows 11 | Enabled by default |
- ROP protection: Shadow Stack detects corrupted return addresses
- Hardware enforcement: The CPU maintains a separate, protected stack for return addresses
- Tamper-resistant: The shadow stack cannot be modified by normal instructions
- Low overhead: Hardware implementation has minimal performance impact
Performance Considerations¶
Intel Shadow Stack is hardware-accelerated with near-zero overhead:
| Metric | Impact |
|---|---|
| Per CALL overhead | ~1 cycle (parallel push) |
| Per RET overhead | ~1 cycle (parallel compare) |
| Overall application | 0-2% measured |
| Memory overhead | Small shadow stack per thread |
Why overhead is minimal: - Hardware maintains shadow stack in parallel with normal execution - No software instrumentation needed - Shadow stack operations happen during existing CALL/RET cycles
Measured overhead:
| Workload | Overhead |
|---|---|
| SPEC CPU 2017 | ~1% average |
| Server workloads | <1% |
| Call-heavy code | 1-2% |
Combined with IBT:
Full CET (-fcf-protection=full) enables both IBT and Shadow Stack with approximately 1-2% combined overhead.
How Shadow Stack Works¶
- On
CALL, the return address is pushed to both the regular stack and the shadow stack - On
RET, the CPU compares the return address from both stacks - If they differ, a control protection fault (#CP) is raised
- Attackers cannot overwrite shadow stack entries to perform ROP
How to Fix¶
Compile with CET enabled¶
# Enable both IBT and Shadow Stack
gcc -fcf-protection=full -o myapp myapp.c
# Enable Shadow Stack only
gcc -fcf-protection=return -o myapp myapp.c
Link with CET-aware linker¶
Verify the fix¶
Example¶
Fail: Binary does not have Shadow Stack enabled
Pass: Binary has Shadow Stack enabled
Requirements¶
- Compiler: GCC 8+ or Clang 7+
- CPU: Intel Tiger Lake+, AMD Zen 3+
- Kernel: Linux 6.6+ for full userspace shadow stack support
- glibc: 2.39+ for full support
See Also¶
- AD3015: EnableIntelCET - IBT protection
- AD3044: EnableShadowCallStack (AArch64, RISC-V)
- Kernel Shadow Stack docs