LLVM's shadow-call-stack feature is a compiler mode intended to harden the generated code against stack-smashing attacks such as exploits of buffer overrun bugs.
The Clang/LLVM documentation page linked above describes the scheme. The capsule summary is that the function return address is never reloaded from the normal stack but only from a separate "shadow call stack". This is an additional stack, but rather than containing whole stack frames of whatever size each function needs, it contains only a single address word for each call frame it records: just the return address. Since the shadow call stack is allocated independently of other stacks or heap blocks with its own randomized address to which pointers are rare, it is much less likely that some sort of buffer overrun or use-after-free exploit will overwrite a return address in memory so that it can cause the program to return to an instruction by the attacker.
The shadow-call-stack and safe-stack instrumentation schemes and ABIs are related and similar but also orthogonal. Each can be enabled or disabled independently for any function. Fuchsia's compiler ABI and libc always interoperate with code built with or without either kind of instrumentation, regardless of what instrumentation was or wasn't used in the particular libc build.
Interoperation and ABI Effects
In general, shadow-call-stack does not affect the ABI. The machine-specific
calling conventions are unchanged. It works fine to have some functions in a
program built with shadow-call-stack and some not. It doesn't matter if
combining the two comes from directly compiled
.o files, from archive
.a files), or from shared libraries (
.so files), in any
While there is some additional per-thread state (the shadow call stack
pointer, see below), code not using
shadow-call-stack does not need to do anything about this state to keep it
correct when calling, or being called by, code that does use safe-stack. The
only potential exceptions to this are for code that is implementing its own
kinds of non-local exits or context-switching (e.g. coroutines). The Zircon C
longjmp code saves and restores this additional state
automatically, so anything that is based on
longjmp already handles everything
correctly even if the code calling
longjmp doesn't know about
For AArch64 (ARM64), the
x18 register is already reserved as "fixed" in the
ABI generally. Code unaware of the shadow-call-stack extension to the ABI is
interoperable with the shadow-call-stack ABI by default if it simply never
The feature is not yet supported on any other architecture.
Use in Zircon & Fuchsia
This is enabled in the Clang compiler by the
command-line option. TODO(TC-616): When the support is fully tested and
the ABI finalized for each machine, this will become the default mode of the
*-fuchsia targets. To disable it for a specific compilation,
Currently Zircon supports shadow-call-stack only for user-mode code. TODO(ZX-4677): In future, it will be supported in the kernel too. TODO(BLD-584): Currently, shadow-call-stack is not enabled in the Fuchsia build (except for some special test cases).
There is no facility for specifying the size of the shadow call stack. Currently it's fixed at one page (4KB), which allows for a call depth of 512. There are guard pages around the shadow call stack so that overflow or underflow will fault immediately.
The essential addition to support shadow-call-stack code is the shadow call stack pointer. This is a register with a global use, like the traditional stack pointer. But each call frame pushes and pops a single return address word rather than arbitrary data as in the normal stack frame.
For AArch64 (ARM64), the
x18 register holds the shadow call stack pointer at
function entry. The shadow call stack grows upwards with post-increment
x18 always points to the next free slot. The compiler never
touches the register except to spill and reload the return address register
x30, aka LR). The Fuchsia ABI requires that
x18 contain a valid shadow
stack pointer at all times. That is, it must always be valid to push a
new address onto the shadow call stack at
x18 (modulo stack overflow).
Notes for low-level and assembly code
Most code, even in assembly, does not need to think about shadow-call-stack
issues at all. The calling conventions are not changed. All use of the stack
(and/or the unsafe stack) is the same with or without
shadow-call-stack; when frame pointers are enabled, the return address will
be stored on the machine stack next to the frame pointer as expected. For
AArch64 (ARM64), function calls still use
x30 for the return address as
normal, though functions that clobber
x30 can choose to spill and reload it
using different memory. Non-leaf functions written in assembly should ideally
make use of the shadow-call-stack ABI by spilling and reloading the return
address register there instead of on the machine stack.
The main exception is code that is implementing something like a non-local
exit or context switch. Such code may need to save or restore the shadow call
stack pointer. Both the
longjmp function and C++
throw already handle
this directly, so C or C++ code using those constructs does not need to do
New code implementing some new kind of non-local exit or context switch will
need to handle the shadow call stack pointer similarly to how it handles the
traditional machine stack pointer register and the unsafe stack
pointer. Any such code should use
#if __has_feature(shadow_call_stack) to
test at compile time whether shadow-call-stack is being used in the particular
build. That preprocessor construct can be used in C, C++, or assembly (
Except as otherwise noted, the content of this page is licensed under the Creative Commons Attribution 4.0 License, and code samples are licensed under the Apache 2.0 License. For details, see the Google Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.