This patch adds the safe stack instrumentation pass to
LLVM, which separates the program stack into a safe stack, which stores return addresses, register spills, and local variables that are statically verified to be accessed in a safe way, and the unsafe stack, which stores everything else. Such separation makes it much harder for an attacker to corrupt objects on the safe stack, including function pointers stored in spilled registers and return addresses. You can find more information about the safe stack, as well as other parts of or control-flow hijack protection technique in our OSDI paper on code-pointer integrity (http://dslab.epfl.ch/pubs/cpi.pdf) and our project website (http://levee.epfl.ch).
I'm really surprised this wasn't done sooner.
There's nothing in the C Standard that mandates how the call stack must be implemented,
but it seems that for the past forty years or so,
the system stack
(the stack the CPU uses to store return addresses for subroutine calls or state information when handling interrupts)
has been the default place to store the call stack,
which is the prime reason why buffer overflows are so dangerous
(because with a buffer overrun, the attacker can embed machine code and cause the CPU to return to the embedded machine code to do
it might appear that dedicating another CPU register to point to this secondary stack might be wasteful,
but most C compilers on modern systems already use a second CPU register to point to the primary stack
(to make it easier to generate stack frames when debugging or analyzing a
the system stack wouldn't have to be so big—even an 8K stack on a 64-bit machine would easily allow a call-depth
B which calls
C which calls
D is a call-depth of 4)
of over 500
1,024 but this would disallow other uses of the stack,
such as temporarily saving registers,
or handling of interrupts) which should handle most programs
(the exception being badly written programs with unbounded recursion;
sidenote to Google:
good one, you got me).
This patch, however, seems to still save the call stack in the system stack with the exeception of arrays or items whose address is taken, and it stores the pointer to the “unsafe stack” in a thread-specific variable. It turns out the performance loss isn't that bad as most routines don't have such problematic variables to begin with.