Skip to content

AD3044: EnableShadowCallStack

Summary

Property Value
ID AD3044
Name EnableShadowCallStack
Category Security
Severity Note
Applies to ELF (Linux/Unix) - AArch64, RISC-V

Description

Shadow Call Stack is a security feature that protects return addresses by storing them in a separate "shadow" stack. When a function is called, the return address is saved to both the regular stack and the shadow stack. On function return, the return address is loaded exclusively from the shadow stack, ignoring any modifications to the regular stack.

This provides strong protection against: - Return-Oriented Programming (ROP) - Attackers cannot redirect execution by overwriting return addresses - Stack buffer overflows - Even if the stack is corrupted, return addresses are protected - Use-after-return vulnerabilities - Shadow stack is separate from the main stack

SCS uses a dedicated register (x18 on AArch64) to point to the shadow stack, making it very efficient with minimal runtime overhead.

How to Fix

Clang

Compile with the -fsanitize=shadow-call-stack flag:

# AArch64
clang --target=aarch64-linux-gnu -fsanitize=shadow-call-stack -o binary source.c

# RISC-V
clang --target=riscv64-linux-gnu -fsanitize=shadow-call-stack -o binary source.c

GCC

GCC 10+ supports Shadow Call Stack:

gcc -fsanitize=shadow-call-stack -o binary source.c

Platform Support

Architecture Register Used Status
AArch64 x18 Production ready
RISC-V x18 (gp) Supported
x86_64 N/A Not supported

Performance Considerations

Shadow Call Stack is designed for production use with minimal overhead:

Metric Impact
Runtime overhead <1% typical
Memory per thread 4-8KB shadow stack
Register reservation x18 dedicated
Code size increase <1%

Benchmark data (typical application):

Workload Overhead
Compute-bound 0.1-0.5%
I/O-bound Negligible
Call-heavy 0.5-1%

Why SCS is efficient: - Each function call adds only two instructions (save/restore on shadow stack) - x18 register is dedicated, avoiding memory indirection - Shadow stack has excellent cache locality

Trade-offs:

Aspect Impact
Register pressure One less general register (x18 reserved)
Thread creation Small additional memory allocation
ABI compatibility All code must respect x18 reservation

Considerations

  • ABI compatibility: The x18 register must be reserved across all code. On Android, x18 is reserved system-wide.
  • Thread-local storage: Each thread needs its own shadow stack.
  • setjmp/longjmp: Requires special handling to maintain shadow stack consistency.

Applicability

This rule applies to: - AArch64 (ARM64) ELF binaries - RISC-V ELF binaries

This rule does not apply to: - x86/x86_64 binaries (use Intel CET Shadow Stack instead - AD3016) - ARM32 binaries

References