Backtrace is finally cheap by abusing x86/Linux's shadow stack
a year ago
- #performance
- #debugging
- #shadow-stack
- Backtrace is a useful but expensive debugging tool in native programming due to the costly process of reading DWARF sections and chasing frame pointers on the stack.
- Shadow stack is a security feature that stores return addresses separately, preventing stack buffer overflow attacks and improving performance by avoiding linked list traversal.
- Recent hardware support for shadow stacks has been announced by Intel and AMD, with Linux kernel support added in version 6.4 (requires `CONFIG_X86_USER_SHADOW_STACK=y`).
- GCC can enable shadow stack support via `-fcf-protection=return` or `=full`, which adds the `SHSTK` feature to `.note.gnu.property`.
- Glibc (version 2.28+) dynamically enables shadow stack based on hardware detection and tunables (`glibc.cpu.x86_shstk`), with options `on`, `off`, and `permissive`.
- Current limitations include incomplete hardware detection (requiring manual tunables like `GLIBC_TUNABLES='glibc.cpu.hwcaps=SHSTK'`) and dependency on all linked libraries being compiled with `-fcf-protection=full`.
- Shadow stack allows faster backtrace capture by directly reading return addresses, bypassing slow frame pointer chasing, and can be combined with `/proc/self/map` for debugging.
- Developers can manually enable/disable shadow stack using syscalls (`arch_prctl`), but must handle context-switching libraries (e.g., glibc's `makecontext`, `swapcontext`, `longjmp`) carefully.
- Shadow stack usage for backtracing is still immature and requires workarounds, but it has potential to improve debugging performance if upstream support improves.