← New search

Other meanings of Shadow stack

Computer security

Shadow stack

Shadow stack is a computer security mechanism that stores protected copies of function return addresses so an attacker cannot easily redirect execution by corrupting the ordinary call stack. It primarily defends the backward edge of control flow against stack-based control-flow hijacking, including return-oriented programming attacks.

Backward edge
Protected control-flow direction
Security role
Return address
Value copied and checked
Protected datum
Hardware or software
Common implementation forms
Deployment models
1

Purpose and operation

A shadow stack protects function returns by keeping a second, integrity-protected record of return addresses. When a function call creates a return address on the ordinary stack, the mechanism records the same address in storage that normal program instructions cannot freely modify; on return, the two values are compared, and execution is stopped or diverted if they differ.1 This targets the backward edge of a program’s control-flow graph: transfers from a called function back to its caller. The ordinary stack remains necessary for local variables, arguments, saved registers, and other data, but its return-address copy is no longer trusted on its own.

Shadow stacks complement, rather than replace, defenses such as stack canaries, address-space layout randomization, and forward-edge control-flow integrity. A canary can reveal some stack corruption, whereas a shadow stack directly checks the control-flow datum most useful for return-oriented programming.

2

Implementations and platform support

Shadow stacks can be implemented in software, by compiler-inserted instrumentation, or in hardware with protected memory and dedicated control-flow instructions. LLVM’s ShadowCallStack instrumentation maintains a separate call stack, while hardware-assisted designs reduce the amount of checking required in ordinary code.2 Intel Control-flow Enforcement Technology, commonly abbreviated CET, defines a hardware shadow stack for supported processors; its operating-system support includes mechanisms for enabling protection and handling violations. The Linux kernel documents user-mode shadow stacks on x86 under CET, including the write restrictions that distinguish shadow-stack memory from ordinary writable memory.3

Compatibility is a central engineering issue. Calls, returns, exceptions, signals, context switches, setjmp/longjmp paths, hand-written assembly, dynamic loaders, and foreign-language interfaces must preserve the protected stack’s logical state.

3

Security properties and limits

A shadow stack makes return-address substitution substantially harder, but it does not establish complete control-flow integrity by itself. It generally prevents an attacker from changing where a legitimate return instruction goes; it does not necessarily constrain indirect calls, indirect jumps, function pointers, or virtual dispatch. Those forward-edge transfers require separate controls, such as compiler-based CFI or hardware branch-target checks.1

The mechanism also depends on trustworthy privileged software and correct handling of exceptional control flow. An attacker who gains arbitrary code execution may still abuse legitimate call-and-return sequences, corrupt non-control data, exploit an unchecked forward edge, or attack an unprotected component. Shadow-stack violations therefore indicate a serious integrity failure, but their absence is not proof that a program is memory-safe. Hardware protection, privilege separation, and careful ABI design determine how strong the practical guarantee becomes.

4

Lesser-known aspects

The difficult cases are often outside the ordinary call-and-return path. Operating systems must coordinate shadow stacks with asynchronous signals, interrupt or exception entry, thread creation, debugging, process inheritance, and non-local transfers such as longjmp. Compiler documentation also warns that software shadow-call-stack schemes can impose calling-convention constraints and may require a reserved register, which can make them unsuitable for unrestricted interoperability.2

Protected storage need not be physically separate silicon: the defining property is that ordinary compromised code cannot rewrite the authoritative return-address records. Conversely, merely duplicating return addresses in another writable buffer is not a shadow stack in the security sense. Research on control-flow integrity placed shadow stacks within a broader family of policies that protect execution paths, while modern implementations combine them with memory permissions and operating-system support rather than treating them as a complete standalone defense.13

Glossary

Backward edge
A control-flow transfer from a called function back to its caller, usually implemented by a return instruction.
Forward edge
A control-flow transfer to a function or code location selected by a call, jump, function pointer, or dispatch mechanism.
Return-oriented programming
An exploitation technique that chains existing instruction sequences by manipulating saved return addresses.
Control-flow integrity
A class of defenses that restricts execution to control-flow transfers permitted by a program’s intended policy.
Shadow call stack
A closely related term for a protected stack containing call return addresses, often used for compiler-based implementations.

Terminology varies by platform: “shadow stack” commonly describes hardware-enforced protection, while “shadow call stack” often denotes compiler instrumentation.