| ========= |
| SafeStack |
| ========= |
| |
| .. contents:: |
| :local: |
| |
| Introduction |
| ============ |
| |
| SafeStack is an instrumentation pass that protects programs against attacks |
| based on stack buffer overflows, without introducing any measurable performance |
| overhead. It works by separating the program stack into two distinct regions: |
| the safe stack and the unsafe stack. The safe stack stores return addresses, |
| register spills, and local variables that are always accessed in a safe way, |
| while the unsafe stack stores everything else. This separation ensures that |
| buffer overflows on the unsafe stack cannot be used to overwrite anything |
| on the safe stack. |
| |
| SafeStack is a part of the `Code-Pointer Integrity (CPI) Project |
| <https://dslab.epfl.ch/proj/cpi/>`_. |
| |
| Performance |
| ----------- |
| |
| The performance overhead of the SafeStack instrumentation is less than 0.1% on |
| average across a variety of benchmarks (see the `Code-Pointer Integrity |
| <https://dslab.epfl.ch/pubs/cpi.pdf>`__ paper for details). This is mainly |
| because most small functions do not have any variables that require the unsafe |
| stack and, hence, do not need unsafe stack frames to be created. The cost of |
| creating unsafe stack frames for large functions is amortized by the cost of |
| executing the function. |
| |
| In some cases, SafeStack actually improves the performance. Objects that end up |
| being moved to the unsafe stack are usually large arrays or variables that are |
| used through multiple stack frames. Moving such objects away from the safe |
| stack increases the locality of frequently accessed values on the stack, such |
| as register spills, return addresses, and small local variables. |
| |
| Compatibility |
| ------------- |
| |
| Most programs, static libraries, or individual files can be compiled |
| with SafeStack as is. SafeStack requires basic runtime support, which, on most |
| platforms, is implemented as a compiler-rt library that is automatically linked |
| in when the program is compiled with SafeStack. |
| |
| Linking a DSO with SafeStack is not currently supported. |
| |
| Known compatibility limitations |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| Certain code that relies on low-level stack manipulations requires adaption to |
| work with SafeStack. One example is mark-and-sweep garbage collection |
| implementations for C/C++ (e.g., Oilpan in chromium/blink), which must be |
| changed to look for the live pointers on both safe and unsafe stacks. |
| |
| SafeStack supports linking statically modules that are compiled with and |
| without SafeStack. An executable compiled with SafeStack can load dynamic |
| libraries that are not compiled with SafeStack. At the moment, compiling |
| dynamic libraries with SafeStack is not supported. |
| |
| Signal handlers that use ``sigaltstack()`` must not use the unsafe stack (see |
| ``__attribute__((no_sanitize("safe-stack")))`` below). |
| |
| Programs that use APIs from ``ucontext.h`` are not supported yet. |
| |
| Security |
| -------- |
| |
| SafeStack protects return addresses, spilled registers and local variables that |
| are always accessed in a safe way by separating them in a dedicated safe stack |
| region. The safe stack is automatically protected against stack-based buffer |
| overflows, since it is disjoint from the unsafe stack in memory, and it itself |
| is always accessed in a safe way. In the current implementation, the safe stack |
| is protected against arbitrary memory write vulnerabilities though |
| randomization and information hiding: the safe stack is allocated at a random |
| address and the instrumentation ensures that no pointers to the safe stack are |
| ever stored outside of the safe stack itself (see limitations below). |
| |
| Known security limitations |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| A complete protection against control-flow hijack attacks requires combining |
| SafeStack with another mechanism that enforces the integrity of code pointers |
| that are stored on the heap or the unsafe stack, such as `CPI |
| <https://dslab.epfl.ch/proj/cpi/>`_, or a forward-edge control flow integrity |
| mechanism that enforces correct calling conventions at indirect call sites, |
| such as `IFCC <https://research.google.com/pubs/archive/42808.pdf>`_ with arity |
| checks. Clang has control-flow integrity protection scheme for :doc:`C++ virtual |
| calls <ControlFlowIntegrity>`, but not non-virtual indirect calls. With |
| SafeStack alone, an attacker can overwrite a function pointer on the heap or |
| the unsafe stack and cause a program to call arbitrary location, which in turn |
| might enable stack pivoting and return-oriented programming. |
| |
| In its current implementation, SafeStack provides precise protection against |
| stack-based buffer overflows, but protection against arbitrary memory write |
| vulnerabilities is probabilistic and relies on randomization and information |
| hiding. The randomization is currently based on system-enforced ASLR and shares |
| its known security limitations. The safe stack pointer hiding is not perfect |
| yet either: system library functions such as ``swapcontext``, exception |
| handling mechanisms, intrinsics such as ``__builtin_frame_address``, or |
| low-level bugs in runtime support could leak the safe stack pointer. In the |
| future, such leaks could be detected by static or dynamic analysis tools and |
| prevented by adjusting such functions to either encrypt the stack pointer when |
| storing it in the heap (as already done e.g., by ``setjmp``/``longjmp`` |
| implementation in glibc), or store it in a safe region instead. |
| |
| The `CPI paper <https://dslab.epfl.ch/pubs/cpi.pdf>`_ describes two alternative, |
| stronger safe stack protection mechanisms, that rely on software fault |
| isolation, or hardware segmentation (as available on x86-32 and some x86-64 |
| CPUs). |
| |
| At the moment, SafeStack assumes that the compiler's implementation is correct. |
| This has not been verified except through manual code inspection, and could |
| always regress in the future. It's therefore desirable to have a separate |
| static or dynamic binary verification tool that would check the correctness of |
| the SafeStack instrumentation in final binaries. |
| |
| Usage |
| ===== |
| |
| To enable SafeStack, just pass ``-fsanitize=safe-stack`` flag to both compile |
| and link command lines. |
| |
| Supported Platforms |
| ------------------- |
| |
| SafeStack was tested on Linux, NetBSD, FreeBSD and MacOSX. |
| |
| Low-level API |
| ------------- |
| |
| ``__has_feature(safe_stack)`` |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| In some rare cases one may need to execute different code depending on |
| whether SafeStack is enabled. The macro ``__has_feature(safe_stack)`` can |
| be used for this purpose. |
| |
| .. code-block:: c |
| |
| #if __has_feature(safe_stack) |
| // code that builds only under SafeStack |
| #endif |
| |
| ``__attribute__((no_sanitize("safe-stack")))`` |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| Use ``__attribute__((no_sanitize("safe-stack")))`` on a function declaration |
| to specify that the safe stack instrumentation should not be applied to that |
| function, even if enabled globally (see ``-fsanitize=safe-stack`` flag). This |
| attribute may be required for functions that make assumptions about the |
| exact layout of their stack frames. |
| |
| All local variables in functions with this attribute will be stored on the safe |
| stack. The safe stack remains unprotected against memory errors when accessing |
| these variables, so extra care must be taken to manually ensure that all such |
| accesses are safe. Furthermore, the addresses of such local variables should |
| never be stored on the heap, as it would leak the location of the SafeStack. |
| |
| ``__builtin___get_unsafe_stack_ptr()`` |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| This builtin function returns current unsafe stack pointer of the current |
| thread. |
| |
| ``__builtin___get_unsafe_stack_bottom()`` |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| This builtin function returns a pointer to the bottom of the unsafe stack of the |
| current thread. |
| |
| ``__builtin___get_unsafe_stack_top()`` |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| This builtin function returns a pointer to the top of the unsafe stack of the |
| current thread. |
| |
| ``__builtin___get_unsafe_stack_start()`` |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| Deprecated: This builtin function is an alias for |
| ``__builtin___get_unsafe_stack_bottom()``. |
| |
| Design |
| ====== |
| |
| Please refer to the `Code-Pointer Integrity <https://dslab.epfl.ch/proj/cpi/>`__ |
| project page for more information about the design of the SafeStack and its |
| related technologies. |
| |
| setjmp and exception handling |
| ----------------------------- |
| |
| The `OSDI'14 paper <https://dslab.epfl.ch/pubs/cpi.pdf>`_ mentions that |
| on Linux the instrumentation pass finds calls to setjmp or functions that |
| may throw an exception, and inserts required instrumentation at their call |
| sites. Specifically, the instrumentation pass saves the shadow stack pointer |
| on the safe stack before the call site, and restores it either after the |
| call to setjmp or after an exception has been caught. This is implemented |
| in the function ``SafeStack::createStackRestorePoints``. |
| |
| Publications |
| ------------ |
| |
| `Code-Pointer Integrity <https://dslab.epfl.ch/pubs/cpi.pdf>`__. |
| Volodymyr Kuznetsov, Laszlo Szekeres, Mathias Payer, George Candea, R. Sekar, Dawn Song. |
| USENIX Symposium on Operating Systems Design and Implementation |
| (`OSDI <https://www.usenix.org/conference/osdi14>`_), Broomfield, CO, October 2014 |