2026-07-09 07:16:15 -08:00
|
|
|
# ShadowCallStack
|
2018-04-03 14:33:53 -08:00
|
|
|
|
2026-07-09 07:16:15 -08:00
|
|
|
```{contents}
|
|
|
|
|
:local:
|
|
|
|
|
```
|
2018-04-03 14:33:53 -08:00
|
|
|
|
2026-07-09 07:16:15 -08:00
|
|
|
## Introduction
|
2018-04-03 14:33:53 -08:00
|
|
|
|
2019-02-12 13:45:23 -09:00
|
|
|
ShadowCallStack is an instrumentation pass, currently only implemented for
|
2024-01-24 11:06:58 -09:00
|
|
|
aarch64 and RISC-V, that protects programs against return address overwrites
|
2019-02-12 13:45:23 -09:00
|
|
|
(e.g. stack buffer overflows.) It works by saving a function's return address
|
|
|
|
|
to a separately allocated 'shadow call stack' in the function prolog in
|
|
|
|
|
non-leaf functions and loading the return address from the shadow call stack
|
|
|
|
|
in the function epilog. The return address is also stored on the regular stack
|
|
|
|
|
for compatibility with unwinders, but is otherwise unused.
|
|
|
|
|
|
|
|
|
|
The aarch64 implementation is considered production ready, and
|
2026-07-09 07:16:15 -08:00
|
|
|
an [implementation of the runtime][scs-runtime] has been added to Android's libc
|
2019-03-07 09:56:36 -09:00
|
|
|
(bionic). An x86_64 implementation was evaluated using Chromium and was found
|
|
|
|
|
to have critical performance and security deficiencies--it was removed in
|
|
|
|
|
LLVM 9.0. Details on the x86_64 implementation can be found in the
|
2026-07-09 07:16:15 -08:00
|
|
|
[Clang 7.0.1 documentation][clang-701-doc].
|
2019-02-12 13:45:23 -09:00
|
|
|
|
2026-07-09 07:16:15 -08:00
|
|
|
### Comparison
|
2018-04-03 14:33:53 -08:00
|
|
|
|
2019-02-12 13:45:23 -09:00
|
|
|
To optimize for memory consumption and cache locality, the shadow call
|
|
|
|
|
stack stores only an array of return addresses. This is in contrast to other
|
2026-07-09 07:16:15 -08:00
|
|
|
schemes, like {doc}`SafeStack`, that mirror the entire stack and trade-off
|
2019-02-12 13:45:23 -09:00
|
|
|
consuming more memory for shorter function prologs and epilogs with fewer
|
|
|
|
|
memory accesses.
|
|
|
|
|
|
2026-07-09 07:16:15 -08:00
|
|
|
[Return Flow Guard][rfg] is a pure software implementation of shadow call stacks
|
2019-03-07 09:56:36 -09:00
|
|
|
on x86_64. Like the previous implementation of ShadowCallStack on x86_64, it is
|
|
|
|
|
inherently racy due to the architecture's use of the stack for calls and
|
|
|
|
|
returns.
|
2019-02-12 13:45:23 -09:00
|
|
|
|
2026-07-09 07:16:15 -08:00
|
|
|
Intel [Control-flow Enforcement Technology][cet] (CET) is a proposed hardware
|
2019-02-12 13:45:23 -09:00
|
|
|
extension that would add native support to use a shadow stack to store/check
|
|
|
|
|
return addresses at call/return time. Being a hardware implementation, it
|
|
|
|
|
would not suffer from race conditions and would not incur the overhead of
|
|
|
|
|
function instrumentation, but it does require operating system support.
|
2018-04-03 14:33:53 -08:00
|
|
|
|
2026-07-09 07:16:15 -08:00
|
|
|
### Compatibility
|
2018-04-03 14:33:53 -08:00
|
|
|
|
2019-02-12 13:45:23 -09:00
|
|
|
A runtime is not provided in compiler-rt so one must be provided by the
|
|
|
|
|
compiled application or the operating system. Integrating the runtime into
|
|
|
|
|
the operating system should be preferred since otherwise all thread creation
|
|
|
|
|
and destruction would need to be intercepted by the application.
|
|
|
|
|
|
2026-07-09 07:16:15 -08:00
|
|
|
The instrumentation makes use of the platform register `x18` on AArch64,
|
|
|
|
|
`x3` (`gp`) on RISC-V with software shadow stack and `ssp` on RISC-V with
|
|
|
|
|
hardware shadow stack, which needs [Zicfiss][zicfiss] and `-fcf-protection=return`.
|
2024-11-07 20:46:27 -09:00
|
|
|
Users can choose between the software and hardware based shadow stack
|
2026-07-09 07:16:15 -08:00
|
|
|
implementation on RISC-V backend by passing `-fsanitize=shadow-call-stack`
|
|
|
|
|
or `Zicfiss` with `-fcf-protection=return`.
|
|
|
|
|
For simplicity we will refer to this as the `SCSReg`. On some platforms,
|
|
|
|
|
`SCSReg` is reserved, and on others, it is designated as a scratch register.
|
2024-02-10 05:18:46 -09:00
|
|
|
This generally means that any code that may run on the same thread as code
|
|
|
|
|
compiled with ShadowCallStack must either target one of the platforms whose ABI
|
2026-07-09 07:16:15 -08:00
|
|
|
reserves `SCSReg` (currently Android, Darwin, Fuchsia and Windows) or be
|
|
|
|
|
compiled with a flag to reserve that register (e.g., `-ffixed-x18`). If
|
2024-02-10 05:18:46 -09:00
|
|
|
absolutely necessary, code compiled without reserving the register may be run on
|
|
|
|
|
the same thread as code that uses ShadowCallStack by saving the register value
|
2026-07-09 07:16:15 -08:00
|
|
|
temporarily on the stack ([example in Android][android-example]) but this should be done with
|
2024-02-10 05:18:46 -09:00
|
|
|
care since it risks leaking the shadow call stack address.
|
|
|
|
|
|
[CodeGen][RISCV] Change Shadow Call Stack Register to X3
ShadowCallStack implementation uses s2 register on RISC-V, but that
choice is problematic for reasons described in:
https://lists.riscv.org/g/sig-toolchains/message/544,
https://github.com/riscv-non-isa/riscv-elf-psabi-doc/issues/370, and
https://github.com/google/android-riscv64/issues/72
The concern over the register choice was also brought up in
https://reviews.llvm.org/D84414.
https://reviews.llvm.org/D84414#2228666 said:
```
"If the register choice is the only concern about this work, then I think
we can probably land it as-is and fixup the register choice if we see
major drawbacks later. Yes, it's an ABI issue, but on the other hand the
shadow call stack is not a standard ABI anyway.""
```
Since we have now found a sufficient reason to fixup the register
choice, we should go ahead and update the implementation. We propose
using x3(gp) which is now the platform register in the RISC-V ABI.
Reviewed By: asb, hiraditya, mcgrathr, craig.topper
Differential Revision: https://reviews.llvm.org/D146463
2023-03-20 13:16:15 -08:00
|
|
|
Because it requires a dedicated register, the ShadowCallStack feature is
|
2026-07-09 07:16:15 -08:00
|
|
|
incompatible with any other feature that may use `SCSReg`. However, there is
|
[CodeGen][RISCV] Change Shadow Call Stack Register to X3
ShadowCallStack implementation uses s2 register on RISC-V, but that
choice is problematic for reasons described in:
https://lists.riscv.org/g/sig-toolchains/message/544,
https://github.com/riscv-non-isa/riscv-elf-psabi-doc/issues/370, and
https://github.com/google/android-riscv64/issues/72
The concern over the register choice was also brought up in
https://reviews.llvm.org/D84414.
https://reviews.llvm.org/D84414#2228666 said:
```
"If the register choice is the only concern about this work, then I think
we can probably land it as-is and fixup the register choice if we see
major drawbacks later. Yes, it's an ABI issue, but on the other hand the
shadow call stack is not a standard ABI anyway.""
```
Since we have now found a sufficient reason to fixup the register
choice, we should go ahead and update the implementation. We propose
using x3(gp) which is now the platform register in the RISC-V ABI.
Reviewed By: asb, hiraditya, mcgrathr, craig.topper
Differential Revision: https://reviews.llvm.org/D146463
2023-03-20 13:16:15 -08:00
|
|
|
no inherent reason why ShadowCallStack needs to use a specific register; in
|
|
|
|
|
principle, a platform could choose to reserve and use another register for
|
|
|
|
|
ShadowCallStack, but this would be incompatible with the ABI standards
|
|
|
|
|
published in AAPCS64 and the RISC-V psABI.
|
2019-02-12 13:45:23 -09:00
|
|
|
|
|
|
|
|
Special unwind information is required on functions that are compiled
|
|
|
|
|
with ShadowCallStack and that may be unwound, i.e. functions compiled with
|
2026-07-09 07:16:15 -08:00
|
|
|
`-fexceptions` (which is the default in C++). Some unwinders (such as the
|
2019-02-12 13:45:23 -09:00
|
|
|
libgcc 4.9 unwinder) do not understand this unwind info and will segfault
|
|
|
|
|
when encountering it. LLVM libunwind processes this unwind info correctly,
|
|
|
|
|
however. This means that if exceptions are used together with ShadowCallStack,
|
|
|
|
|
the program must use a compatible unwinder.
|
2018-04-03 14:33:53 -08:00
|
|
|
|
2026-07-09 07:16:15 -08:00
|
|
|
## Security
|
2018-04-03 14:33:53 -08:00
|
|
|
|
|
|
|
|
ShadowCallStack is intended to be a stronger alternative to
|
2026-07-09 07:16:15 -08:00
|
|
|
`-fstack-protector`. It protects from non-linear overflows and arbitrary
|
2019-02-12 13:45:23 -09:00
|
|
|
memory writes to the return address slot.
|
|
|
|
|
|
2026-07-09 07:16:15 -08:00
|
|
|
The instrumentation makes use of the `SCSReg` register to reference the shadow
|
2019-02-12 13:45:23 -09:00
|
|
|
call stack, meaning that references to the shadow call stack do not have
|
|
|
|
|
to be stored in memory. This makes it possible to implement a runtime that
|
|
|
|
|
avoids exposing the address of the shadow call stack to attackers that can
|
|
|
|
|
read arbitrary memory. However, attackers could still try to exploit side
|
2026-07-09 07:16:15 -08:00
|
|
|
channels exposed by the operating system [[1]][scs-cartography] [[2]][safestack-bypass]
|
|
|
|
|
or processor [[3]][anc]
|
2019-02-12 13:45:23 -09:00
|
|
|
to discover the address of the shadow call stack.
|
2018-04-03 14:33:53 -08:00
|
|
|
|
2019-02-12 13:45:23 -09:00
|
|
|
Unless care is taken when allocating the shadow call stack, it may be
|
|
|
|
|
possible for an attacker to guess its address using the addresses of
|
|
|
|
|
other allocations. Therefore, the address should be chosen to make this
|
|
|
|
|
difficult. One way to do this is to allocate a large guard region without
|
|
|
|
|
read/write permissions, randomly select a small region within it to be
|
|
|
|
|
used as the address of the shadow call stack and mark only that region as
|
|
|
|
|
read/write. This also mitigates somewhat against processor side channels.
|
2026-07-09 07:16:15 -08:00
|
|
|
The intent is that the Android runtime [will do this][android-will-do-this], but the platform will
|
|
|
|
|
first need to be [changed][android-changed] to avoid using `setrlimit(RLIMIT_AS)` to limit
|
2019-02-12 13:45:23 -09:00
|
|
|
memory allocations in certain processes, as this also limits the number of
|
|
|
|
|
guard regions that can be allocated.
|
|
|
|
|
|
|
|
|
|
The runtime will need the address of the shadow call stack in order to
|
|
|
|
|
deallocate it when destroying the thread. If the entire program is compiled
|
2026-07-09 07:16:15 -08:00
|
|
|
with `SCSReg` reserved, this is trivial: the address can be derived from the
|
|
|
|
|
value stored in `SCSReg` (e.g. by masking out the lower bits). If a guard
|
2019-02-12 13:45:23 -09:00
|
|
|
region is used, the address of the start of the guard region could then be
|
|
|
|
|
stored at the start of the shadow call stack itself. But if it is possible
|
2026-07-09 07:16:15 -08:00
|
|
|
for code compiled without reserving `SCSReg` to run on a thread managed by the
|
2019-02-12 13:45:23 -09:00
|
|
|
runtime, which is the case on Android for example, the address must be stored
|
|
|
|
|
somewhere else instead. On Android we store the address of the start of the
|
|
|
|
|
guard region in TLS and deallocate the entire guard region including the
|
|
|
|
|
shadow call stack at thread exit. This is considered acceptable given that
|
|
|
|
|
the address of the start of the guard region is already somewhat guessable.
|
|
|
|
|
|
|
|
|
|
One way in which the address of the shadow call stack could leak is in the
|
2026-07-09 07:16:15 -08:00
|
|
|
`jmp_buf` data structure used by `setjmp` and `longjmp`. The Android
|
|
|
|
|
runtime [avoids this][android-avoids-this] by only storing the low bits of `SCSReg` in the
|
|
|
|
|
`jmp_buf`, which requires the address of the shadow call stack to be
|
2019-02-12 13:45:23 -09:00
|
|
|
aligned to its size.
|
|
|
|
|
|
2026-07-09 07:16:15 -08:00
|
|
|
The architecture's call and return instructions (`bl` and `ret`) operate on
|
2019-02-12 13:45:23 -09:00
|
|
|
a register rather than the stack, which means that leaf functions are generally
|
|
|
|
|
protected from return address overwrites even without ShadowCallStack.
|
2018-04-03 14:33:53 -08:00
|
|
|
|
2026-07-09 07:16:15 -08:00
|
|
|
## Usage
|
2018-04-03 14:33:53 -08:00
|
|
|
|
2026-07-09 07:16:15 -08:00
|
|
|
To enable ShadowCallStack, just pass the `-fsanitize=shadow-call-stack` flag
|
[CodeGen][RISCV] Change Shadow Call Stack Register to X3
ShadowCallStack implementation uses s2 register on RISC-V, but that
choice is problematic for reasons described in:
https://lists.riscv.org/g/sig-toolchains/message/544,
https://github.com/riscv-non-isa/riscv-elf-psabi-doc/issues/370, and
https://github.com/google/android-riscv64/issues/72
The concern over the register choice was also brought up in
https://reviews.llvm.org/D84414.
https://reviews.llvm.org/D84414#2228666 said:
```
"If the register choice is the only concern about this work, then I think
we can probably land it as-is and fixup the register choice if we see
major drawbacks later. Yes, it's an ABI issue, but on the other hand the
shadow call stack is not a standard ABI anyway.""
```
Since we have now found a sufficient reason to fixup the register
choice, we should go ahead and update the implementation. We propose
using x3(gp) which is now the platform register in the RISC-V ABI.
Reviewed By: asb, hiraditya, mcgrathr, craig.topper
Differential Revision: https://reviews.llvm.org/D146463
2023-03-20 13:16:15 -08:00
|
|
|
to both compile and link command lines. On aarch64, you also need to pass
|
2026-07-09 07:16:15 -08:00
|
|
|
`-ffixed-x18` unless your target already reserves `x18`. No additional flags
|
2024-02-10 05:18:46 -09:00
|
|
|
need to be passed on RISC-V because the software based shadow stack uses
|
2026-07-09 07:16:15 -08:00
|
|
|
`x3` (`gp`), which is always reserved, and the hardware based shadow call
|
|
|
|
|
stack uses a dedicated register, `ssp`.
|
2024-02-10 05:18:46 -09:00
|
|
|
However, it is important to disable GP relaxation in the linker when using the
|
|
|
|
|
software based shadow call stack on RISC-V. This can be done with the
|
2026-07-09 07:16:15 -08:00
|
|
|
`--no-relax-gp` flag in GNU ld, and is off by default in LLD.
|
2018-04-03 14:33:53 -08:00
|
|
|
|
2026-07-09 07:16:15 -08:00
|
|
|
### Low-level API
|
2018-04-03 14:33:53 -08:00
|
|
|
|
2026-07-09 07:16:15 -08:00
|
|
|
#### `__has_feature(shadow_call_stack)`
|
2018-04-03 14:33:53 -08:00
|
|
|
|
|
|
|
|
In some cases one may need to execute different code depending on whether
|
2026-07-09 07:16:15 -08:00
|
|
|
ShadowCallStack is enabled. The macro `__has_feature(shadow_call_stack)` can
|
2018-04-03 14:33:53 -08:00
|
|
|
be used for this purpose.
|
|
|
|
|
|
2026-07-09 07:16:15 -08:00
|
|
|
```c
|
|
|
|
|
#if defined(__has_feature)
|
|
|
|
|
# if __has_feature(shadow_call_stack)
|
|
|
|
|
// code that builds only under ShadowCallStack
|
|
|
|
|
# endif
|
|
|
|
|
#endif
|
|
|
|
|
```
|
2018-04-03 14:33:53 -08:00
|
|
|
|
2026-07-09 07:16:15 -08:00
|
|
|
#### `__attribute__((no_sanitize("shadow-call-stack")))`
|
2018-04-03 14:33:53 -08:00
|
|
|
|
2026-07-09 07:16:15 -08:00
|
|
|
Use `__attribute__((no_sanitize("shadow-call-stack")))` on a function
|
2018-04-03 14:33:53 -08:00
|
|
|
declaration to specify that the shadow call stack instrumentation should not be
|
|
|
|
|
applied to that function, even if enabled globally.
|
|
|
|
|
|
2026-07-09 07:16:15 -08:00
|
|
|
## Example
|
2018-04-03 14:33:53 -08:00
|
|
|
|
|
|
|
|
The following example code:
|
|
|
|
|
|
2026-07-09 07:16:15 -08:00
|
|
|
```c++
|
|
|
|
|
int foo() {
|
|
|
|
|
return bar() + 1;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Generates the following aarch64 assembly when compiled with `-O2`:
|
|
|
|
|
|
|
|
|
|
```none
|
|
|
|
|
stp x29, x30, [sp, #-16]!
|
|
|
|
|
mov x29, sp
|
|
|
|
|
bl bar
|
|
|
|
|
add w0, w0, #1
|
|
|
|
|
ldp x29, x30, [sp], #16
|
|
|
|
|
ret
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Adding `-fsanitize=shadow-call-stack` would output the following assembly:
|
|
|
|
|
|
|
|
|
|
```none
|
|
|
|
|
str x30, [x18], #8
|
|
|
|
|
stp x29, x30, [sp, #-16]!
|
|
|
|
|
mov x29, sp
|
|
|
|
|
bl bar
|
|
|
|
|
add w0, w0, #1
|
|
|
|
|
ldp x29, x30, [sp], #16
|
|
|
|
|
ldr x30, [x18, #-8]!
|
|
|
|
|
ret
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
[android-avoids-this]: https://android.googlesource.com/platform/bionic/+/808d176e7e0dd727c7f929622ec017f6e065c582/libc/arch-arm64/bionic/setjmp.S#49
|
|
|
|
|
[android-changed]: https://android-review.googlesource.com/c/platform/frameworks/av/+/837745
|
|
|
|
|
[android-example]: https://android-review.googlesource.com/c/platform/frameworks/base/+/803717
|
|
|
|
|
[android-will-do-this]: https://android-review.googlesource.com/c/platform/bionic/+/891622
|
|
|
|
|
[anc]: https://www.vusec.net/projects/anc/
|
|
|
|
|
[cet]: https://software.intel.com/sites/default/files/managed/4d/2a/control-flow-enforcement-technology-preview.pdf
|
|
|
|
|
[clang-701-doc]: https://releases.llvm.org/7.0.1/tools/clang/docs/ShadowCallStack.html
|
|
|
|
|
[rfg]: https://xlab.tencent.com/en/2016/11/02/return-flow-guard/
|
|
|
|
|
[safestack-bypass]: https://www.blackhat.com/docs/eu-16/materials/eu-16-Goktas-Bypassing-Clangs-SafeStack.pdf
|
|
|
|
|
[scs-cartography]: https://eyalitkin.wordpress.com/2017/09/01/cartography-lighting-up-the-shadows/
|
|
|
|
|
[scs-runtime]: https://android.googlesource.com/platform/bionic/+/808d176e7e0dd727c7f929622ec017f6e065c582/libc/bionic/pthread_create.cpp#128
|
|
|
|
|
[zicfiss]: https://github.com/riscv/riscv-cfi/blob/main/cfi_backward.adoc
|