Home | History | Annotate | Line # | Download | only in docs
      1  1.1  joerg =========
      2  1.1  joerg SafeStack
      3  1.1  joerg =========
      4  1.1  joerg 
      5  1.1  joerg .. contents::
      6  1.1  joerg    :local:
      7  1.1  joerg 
      8  1.1  joerg Introduction
      9  1.1  joerg ============
     10  1.1  joerg 
     11  1.1  joerg SafeStack is an instrumentation pass that protects programs against attacks
     12  1.1  joerg based on stack buffer overflows, without introducing any measurable performance
     13  1.1  joerg overhead. It works by separating the program stack into two distinct regions:
     14  1.1  joerg the safe stack and the unsafe stack. The safe stack stores return addresses,
     15  1.1  joerg register spills, and local variables that are always accessed in a safe way,
     16  1.1  joerg while the unsafe stack stores everything else. This separation ensures that
     17  1.1  joerg buffer overflows on the unsafe stack cannot be used to overwrite anything
     18  1.1  joerg on the safe stack.
     19  1.1  joerg 
     20  1.1  joerg SafeStack is a part of the `Code-Pointer Integrity (CPI) Project
     21  1.1  joerg <https://dslab.epfl.ch/proj/cpi/>`_.
     22  1.1  joerg 
     23  1.1  joerg Performance
     24  1.1  joerg -----------
     25  1.1  joerg 
     26  1.1  joerg The performance overhead of the SafeStack instrumentation is less than 0.1% on
     27  1.1  joerg average across a variety of benchmarks (see the `Code-Pointer Integrity
     28  1.1  joerg <https://dslab.epfl.ch/pubs/cpi.pdf>`__ paper for details). This is mainly
     29  1.1  joerg because most small functions do not have any variables that require the unsafe
     30  1.1  joerg stack and, hence, do not need unsafe stack frames to be created. The cost of
     31  1.1  joerg creating unsafe stack frames for large functions is amortized by the cost of
     32  1.1  joerg executing the function.
     33  1.1  joerg 
     34  1.1  joerg In some cases, SafeStack actually improves the performance. Objects that end up
     35  1.1  joerg being moved to the unsafe stack are usually large arrays or variables that are
     36  1.1  joerg used through multiple stack frames. Moving such objects away from the safe
     37  1.1  joerg stack increases the locality of frequently accessed values on the stack, such
     38  1.1  joerg as register spills, return addresses, and small local variables.
     39  1.1  joerg 
     40  1.1  joerg Compatibility
     41  1.1  joerg -------------
     42  1.1  joerg 
     43  1.1  joerg Most programs, static libraries, or individual files can be compiled
     44  1.1  joerg with SafeStack as is. SafeStack requires basic runtime support, which, on most
     45  1.1  joerg platforms, is implemented as a compiler-rt library that is automatically linked
     46  1.1  joerg in when the program is compiled with SafeStack.
     47  1.1  joerg 
     48  1.1  joerg Linking a DSO with SafeStack is not currently supported.
     49  1.1  joerg 
     50  1.1  joerg Known compatibility limitations
     51  1.1  joerg ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     52  1.1  joerg 
     53  1.1  joerg Certain code that relies on low-level stack manipulations requires adaption to
     54  1.1  joerg work with SafeStack. One example is mark-and-sweep garbage collection
     55  1.1  joerg implementations for C/C++ (e.g., Oilpan in chromium/blink), which must be
     56  1.1  joerg changed to look for the live pointers on both safe and unsafe stacks.
     57  1.1  joerg 
     58  1.1  joerg SafeStack supports linking statically modules that are compiled with and
     59  1.1  joerg without SafeStack. An executable compiled with SafeStack can load dynamic
     60  1.1  joerg libraries that are not compiled with SafeStack. At the moment, compiling
     61  1.1  joerg dynamic libraries with SafeStack is not supported.
     62  1.1  joerg 
     63  1.1  joerg Signal handlers that use ``sigaltstack()`` must not use the unsafe stack (see
     64  1.1  joerg ``__attribute__((no_sanitize("safe-stack")))`` below).
     65  1.1  joerg 
     66  1.1  joerg Programs that use APIs from ``ucontext.h`` are not supported yet.
     67  1.1  joerg 
     68  1.1  joerg Security
     69  1.1  joerg --------
     70  1.1  joerg 
     71  1.1  joerg SafeStack protects return addresses, spilled registers and local variables that
     72  1.1  joerg are always accessed in a safe way by separating them in a dedicated safe stack
     73  1.1  joerg region. The safe stack is automatically protected against stack-based buffer
     74  1.1  joerg overflows, since it is disjoint from the unsafe stack in memory, and it itself
     75  1.1  joerg is always accessed in a safe way. In the current implementation, the safe stack
     76  1.1  joerg is protected against arbitrary memory write vulnerabilities though
     77  1.1  joerg randomization and information hiding: the safe stack is allocated at a random
     78  1.1  joerg address and the instrumentation ensures that no pointers to the safe stack are
     79  1.1  joerg ever stored outside of the safe stack itself (see limitations below).
     80  1.1  joerg 
     81  1.1  joerg Known security limitations
     82  1.1  joerg ~~~~~~~~~~~~~~~~~~~~~~~~~~
     83  1.1  joerg 
     84  1.1  joerg A complete protection against control-flow hijack attacks requires combining
     85  1.1  joerg SafeStack with another mechanism that enforces the integrity of code pointers
     86  1.1  joerg that are stored on the heap or the unsafe stack, such as `CPI
     87  1.1  joerg <https://dslab.epfl.ch/proj/cpi/>`_, or a forward-edge control flow integrity
     88  1.1  joerg mechanism that enforces correct calling conventions at indirect call sites,
     89  1.1  joerg such as `IFCC <https://research.google.com/pubs/archive/42808.pdf>`_ with arity
     90  1.1  joerg checks. Clang has control-flow integrity protection scheme for :doc:`C++ virtual
     91  1.1  joerg calls <ControlFlowIntegrity>`, but not non-virtual indirect calls. With
     92  1.1  joerg SafeStack alone, an attacker can overwrite a function pointer on the heap or
     93  1.1  joerg the unsafe stack and cause a program to call arbitrary location, which in turn
     94  1.1  joerg might enable stack pivoting and return-oriented programming.
     95  1.1  joerg 
     96  1.1  joerg In its current implementation, SafeStack provides precise protection against
     97  1.1  joerg stack-based buffer overflows, but protection against arbitrary memory write
     98  1.1  joerg vulnerabilities is probabilistic and relies on randomization and information
     99  1.1  joerg hiding. The randomization is currently based on system-enforced ASLR and shares
    100  1.1  joerg its known security limitations. The safe stack pointer hiding is not perfect
    101  1.1  joerg yet either: system library functions such as ``swapcontext``, exception
    102  1.1  joerg handling mechanisms, intrinsics such as ``__builtin_frame_address``, or
    103  1.1  joerg low-level bugs in runtime support could leak the safe stack pointer. In the
    104  1.1  joerg future, such leaks could be detected by static or dynamic analysis tools and
    105  1.1  joerg prevented by adjusting such functions to either encrypt the stack pointer when
    106  1.1  joerg storing it in the heap (as already done e.g., by ``setjmp``/``longjmp``
    107  1.1  joerg implementation in glibc), or store it in a safe region instead.
    108  1.1  joerg 
    109  1.1  joerg The `CPI paper <https://dslab.epfl.ch/pubs/cpi.pdf>`_ describes two alternative,
    110  1.1  joerg stronger safe stack protection mechanisms, that rely on software fault
    111  1.1  joerg isolation, or hardware segmentation (as available on x86-32 and some x86-64
    112  1.1  joerg CPUs).
    113  1.1  joerg 
    114  1.1  joerg At the moment, SafeStack assumes that the compiler's implementation is correct.
    115  1.1  joerg This has not been verified except through manual code inspection, and could
    116  1.1  joerg always regress in the future. It's therefore desirable to have a separate
    117  1.1  joerg static or dynamic binary verification tool that would check the correctness of
    118  1.1  joerg the SafeStack instrumentation in final binaries.
    119  1.1  joerg 
    120  1.1  joerg Usage
    121  1.1  joerg =====
    122  1.1  joerg 
    123  1.1  joerg To enable SafeStack, just pass ``-fsanitize=safe-stack`` flag to both compile
    124  1.1  joerg and link command lines.
    125  1.1  joerg 
    126  1.1  joerg Supported Platforms
    127  1.1  joerg -------------------
    128  1.1  joerg 
    129  1.1  joerg SafeStack was tested on Linux, NetBSD, FreeBSD and macOS.
    130  1.1  joerg 
    131  1.1  joerg Low-level API
    132  1.1  joerg -------------
    133  1.1  joerg 
    134  1.1  joerg ``__has_feature(safe_stack)``
    135  1.1  joerg ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    136  1.1  joerg 
    137  1.1  joerg In some rare cases one may need to execute different code depending on
    138  1.1  joerg whether SafeStack is enabled. The macro ``__has_feature(safe_stack)`` can
    139  1.1  joerg be used for this purpose.
    140  1.1  joerg 
    141  1.1  joerg .. code-block:: c
    142  1.1  joerg 
    143  1.1  joerg     #if __has_feature(safe_stack)
    144  1.1  joerg     // code that builds only under SafeStack
    145  1.1  joerg     #endif
    146  1.1  joerg 
    147  1.1  joerg ``__attribute__((no_sanitize("safe-stack")))``
    148  1.1  joerg ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    149  1.1  joerg 
    150  1.1  joerg Use ``__attribute__((no_sanitize("safe-stack")))`` on a function declaration
    151  1.1  joerg to specify that the safe stack instrumentation should not be applied to that
    152  1.1  joerg function, even if enabled globally (see ``-fsanitize=safe-stack`` flag). This
    153  1.1  joerg attribute may be required for functions that make assumptions about the
    154  1.1  joerg exact layout of their stack frames.
    155  1.1  joerg 
    156  1.1  joerg All local variables in functions with this attribute will be stored on the safe
    157  1.1  joerg stack. The safe stack remains unprotected against memory errors when accessing
    158  1.1  joerg these variables, so extra care must be taken to manually ensure that all such
    159  1.1  joerg accesses are safe. Furthermore, the addresses of such local variables should
    160  1.1  joerg never be stored on the heap, as it would leak the location of the SafeStack.
    161  1.1  joerg 
    162  1.1  joerg ``__builtin___get_unsafe_stack_ptr()``
    163  1.1  joerg ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    164  1.1  joerg 
    165  1.1  joerg This builtin function returns current unsafe stack pointer of the current
    166  1.1  joerg thread.
    167  1.1  joerg 
    168  1.1  joerg ``__builtin___get_unsafe_stack_bottom()``
    169  1.1  joerg ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    170  1.1  joerg 
    171  1.1  joerg This builtin function returns a pointer to the bottom of the unsafe stack of the
    172  1.1  joerg current thread.
    173  1.1  joerg 
    174  1.1  joerg ``__builtin___get_unsafe_stack_top()``
    175  1.1  joerg ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    176  1.1  joerg 
    177  1.1  joerg This builtin function returns a pointer to the top of the unsafe stack of the
    178  1.1  joerg current thread.
    179  1.1  joerg 
    180  1.1  joerg ``__builtin___get_unsafe_stack_start()``
    181  1.1  joerg ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    182  1.1  joerg 
    183  1.1  joerg Deprecated: This builtin function is an alias for
    184  1.1  joerg ``__builtin___get_unsafe_stack_bottom()``.
    185  1.1  joerg 
    186  1.1  joerg Design
    187  1.1  joerg ======
    188  1.1  joerg 
    189  1.1  joerg Please refer to the `Code-Pointer Integrity <https://dslab.epfl.ch/proj/cpi/>`__
    190  1.1  joerg project page for more information about the design of the SafeStack and its
    191  1.1  joerg related technologies.
    192  1.1  joerg 
    193  1.1  joerg setjmp and exception handling
    194  1.1  joerg -----------------------------
    195  1.1  joerg 
    196  1.1  joerg The `OSDI'14 paper <https://dslab.epfl.ch/pubs/cpi.pdf>`_ mentions that
    197  1.1  joerg on Linux the instrumentation pass finds calls to setjmp or functions that
    198  1.1  joerg may throw an exception, and inserts required instrumentation at their call
    199  1.1  joerg sites. Specifically, the instrumentation pass saves the shadow stack pointer
    200  1.1  joerg on the safe stack before the call site, and restores it either after the
    201  1.1  joerg call to setjmp or after an exception has been caught. This is implemented
    202  1.1  joerg in the function ``SafeStack::createStackRestorePoints``.
    203  1.1  joerg 
    204  1.1  joerg Publications
    205  1.1  joerg ------------
    206  1.1  joerg 
    207  1.1  joerg `Code-Pointer Integrity <https://dslab.epfl.ch/pubs/cpi.pdf>`__.
    208  1.1  joerg Volodymyr Kuznetsov, Laszlo Szekeres, Mathias Payer, George Candea, R. Sekar, Dawn Song.
    209  1.1  joerg USENIX Symposium on Operating Systems Design and Implementation
    210  1.1  joerg (`OSDI <https://www.usenix.org/conference/osdi14>`_), Broomfield, CO, October 2014
    211