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