|
| 1 | +.. SPDX-License-Identifier: GPL-2.0 |
| 2 | +
|
| 3 | +:Author: Deepak Gupta <debug@rivosinc.com> |
| 4 | +:Date: 12 January 2024 |
| 5 | + |
| 6 | +========================================================= |
| 7 | +Shadow stack to protect function returns on RISC-V Linux |
| 8 | +========================================================= |
| 9 | + |
| 10 | +This document briefly describes the interface provided to userspace by Linux |
| 11 | +to enable shadow stacks for user mode applications on RISC-V. |
| 12 | + |
| 13 | +1. Feature Overview |
| 14 | +-------------------- |
| 15 | + |
| 16 | +Memory corruption issues usually result in crashes. However, in the |
| 17 | +hands of a creative adversary, these issues can result in a variety of |
| 18 | +security problems. |
| 19 | + |
| 20 | +Some of those security issues can be code re-use attacks on programs |
| 21 | +where an adversary can use corrupt return addresses present on the |
| 22 | +stack. chaining them together to perform return oriented programming |
| 23 | +(ROP) and thus compromising the control flow integrity (CFI) of the |
| 24 | +program. |
| 25 | + |
| 26 | +Return addresses live on the stack in read-write memory. Therefore |
| 27 | +they are susceptible to corruption, which allows an adversary to |
| 28 | +control the program counter. On RISC-V, the ``zicfiss`` extension |
| 29 | +provides an alternate stack (the "shadow stack") on which return |
| 30 | +addresses can be safely placed in the prologue of the function and |
| 31 | +retrieved in the epilogue. The ``zicfiss`` extension makes the |
| 32 | +following changes: |
| 33 | + |
| 34 | +- PTE encodings for shadow stack virtual memory |
| 35 | + An earlier reserved encoding in first stage translation i.e. |
| 36 | + PTE.R=0, PTE.W=1, PTE.X=0 becomes the PTE encoding for shadow stack pages. |
| 37 | + |
| 38 | +- The ``sspush x1/x5`` instruction pushes (stores) ``x1/x5`` to shadow stack. |
| 39 | + |
| 40 | +- The ``sspopchk x1/x5`` instruction pops (loads) from shadow stack and compares |
| 41 | + with ``x1/x5`` and if not equal, the CPU raises a ``software check exception`` |
| 42 | + with ``*tval = 3`` |
| 43 | + |
| 44 | +The compiler toolchain ensures that function prologues have ``sspush |
| 45 | +x1/x5`` to save the return address on shadow stack in addition to the |
| 46 | +regular stack. Similarly, function epilogues have ``ld x5, |
| 47 | +offset(x2)`` followed by ``sspopchk x5`` to ensure that a popped value |
| 48 | +from the regular stack matches with the popped value from the shadow |
| 49 | +stack. |
| 50 | + |
| 51 | +2. Shadow stack protections and linux memory manager |
| 52 | +----------------------------------------------------- |
| 53 | + |
| 54 | +As mentioned earlier, shadow stacks get new page table encodings that |
| 55 | +have some special properties assigned to them, along with instructions |
| 56 | +that operate on the shadow stacks: |
| 57 | + |
| 58 | +- Regular stores to shadow stack memory raise store access faults. This |
| 59 | + protects shadow stack memory from stray writes. |
| 60 | + |
| 61 | +- Regular loads from shadow stack memory are allowed. This allows |
| 62 | + stack trace utilities or backtrace functions to read the true call |
| 63 | + stack and ensure that it has not been tampered with. |
| 64 | + |
| 65 | +- Only shadow stack instructions can generate shadow stack loads or |
| 66 | + shadow stack stores. |
| 67 | + |
| 68 | +- Shadow stack loads and stores on read-only memory raise AMO/store |
| 69 | + page faults. Thus both ``sspush x1/x5`` and ``sspopchk x1/x5`` will |
| 70 | + raise AMO/store page fault. This simplies COW handling in kernel |
| 71 | + during fork(). The kernel can convert shadow stack pages into |
| 72 | + read-only memory (as it does for regular read-write memory). As |
| 73 | + soon as subsequent ``sspush`` or ``sspopchk`` instructions in |
| 74 | + userspace are encountered, the kernel can perform COW. |
| 75 | + |
| 76 | +- Shadow stack loads and stores on read-write or read-write-execute |
| 77 | + memory raise an access fault. This is a fatal condition because |
| 78 | + shadow stack loads and stores should never be operating on |
| 79 | + read-write or read-write-execute memory. |
| 80 | + |
| 81 | +3. ELF and psABI |
| 82 | +----------------- |
| 83 | + |
| 84 | +The toolchain sets up :c:macro:`GNU_PROPERTY_RISCV_FEATURE_1_BCFI` for |
| 85 | +property :c:macro:`GNU_PROPERTY_RISCV_FEATURE_1_AND` in the notes |
| 86 | +section of the object file. |
| 87 | + |
| 88 | +4. Linux enabling |
| 89 | +------------------ |
| 90 | + |
| 91 | +User space programs can have multiple shared objects loaded in their |
| 92 | +address space. It's a difficult task to make sure all the |
| 93 | +dependencies have been compiled with shadow stack support. Thus |
| 94 | +it's left to the dynamic loader to enable shadow stacks for the |
| 95 | +program. |
| 96 | + |
| 97 | +5. prctl() enabling |
| 98 | +-------------------- |
| 99 | + |
| 100 | +:c:macro:`PR_SET_SHADOW_STACK_STATUS` / :c:macro:`PR_GET_SHADOW_STACK_STATUS` / |
| 101 | +:c:macro:`PR_LOCK_SHADOW_STACK_STATUS` are three prctls added to manage shadow |
| 102 | +stack enabling for tasks. These prctls are architecture-agnostic and return |
| 103 | +-EINVAL if not implemented. |
| 104 | + |
| 105 | +* prctl(PR_SET_SHADOW_STACK_STATUS, unsigned long arg) |
| 106 | + |
| 107 | +If arg = :c:macro:`PR_SHADOW_STACK_ENABLE` and if CPU supports |
| 108 | +``zicfiss`` then the kernel will enable shadow stacks for the task. |
| 109 | +The dynamic loader can issue this :c:macro:`prctl` once it has |
| 110 | +determined that all the objects loaded in address space have support |
| 111 | +for shadow stacks. Additionally, if there is a :c:macro:`dlopen` to |
| 112 | +an object which wasn't compiled with ``zicfiss``, the dynamic loader |
| 113 | +can issue this prctl with arg set to 0 (i.e. |
| 114 | +:c:macro:`PR_SHADOW_STACK_ENABLE` being clear) |
| 115 | + |
| 116 | +* prctl(PR_GET_SHADOW_STACK_STATUS, unsigned long * arg) |
| 117 | + |
| 118 | +Returns the current status of indirect branch tracking. If enabled |
| 119 | +it'll return :c:macro:`PR_SHADOW_STACK_ENABLE`. |
| 120 | + |
| 121 | +* prctl(PR_LOCK_SHADOW_STACK_STATUS, unsigned long arg) |
| 122 | + |
| 123 | +Locks the current status of shadow stack enabling on the |
| 124 | +task. Userspace may want to run with a strict security posture and |
| 125 | +wouldn't want loading of objects without ``zicfiss`` support. In this |
| 126 | +case userspace can use this prctl to disallow disabling of shadow |
| 127 | +stacks on the current task. |
| 128 | + |
| 129 | +5. violations related to returns with shadow stack enabled |
| 130 | +----------------------------------------------------------- |
| 131 | + |
| 132 | +Pertaining to shadow stacks, the CPU raises a ``software check |
| 133 | +exception`` upon executing ``sspopchk x1/x5`` if ``x1/x5`` doesn't |
| 134 | +match the top of shadow stack. If a mismatch happens, then the CPU |
| 135 | +sets ``*tval = 3`` and raises the exception. |
| 136 | + |
| 137 | +The Linux kernel will treat this as a :c:macro:`SIGSEGV` with code = |
| 138 | +:c:macro:`SEGV_CPERR` and follow the normal course of signal delivery. |
| 139 | + |
| 140 | +6. Shadow stack tokens |
| 141 | +----------------------- |
| 142 | + |
| 143 | +Regular stores on shadow stacks are not allowed and thus can't be |
| 144 | +tampered with via arbitrary stray writes. However, one method of |
| 145 | +pivoting / switching to a shadow stack is simply writing to the CSR |
| 146 | +``CSR_SSP``. This will change the active shadow stack for the |
| 147 | +program. Writes to ``CSR_SSP`` in the program should be mostly |
| 148 | +limited to context switches, stack unwinds, or longjmp or similar |
| 149 | +mechanisms (like context switching of Green Threads) in languages like |
| 150 | +Go and Rust. CSR_SSP writes can be problematic because an attacker can |
| 151 | +use memory corruption bugs and leverage context switching routines to |
| 152 | +pivot to any shadow stack. Shadow stack tokens can help mitigate this |
| 153 | +problem by making sure that: |
| 154 | + |
| 155 | +- When software is switching away from a shadow stack, the shadow |
| 156 | + stack pointer should be saved on the shadow stack itself (this is |
| 157 | + called the ``shadow stack token``). |
| 158 | + |
| 159 | +- When software is switching to a shadow stack, it should read the |
| 160 | + ``shadow stack token`` from the shadow stack pointer and verify that |
| 161 | + the ``shadow stack token`` itself is a pointer to the shadow stack |
| 162 | + itself. |
| 163 | + |
| 164 | +- Once the token verification is done, software can perform the write |
| 165 | + to ``CSR_SSP`` to switch shadow stacks. |
| 166 | + |
| 167 | +Here "software" could refer to the user mode task runtime itself, |
| 168 | +managing various contexts as part of a single thread. Or "software" |
| 169 | +could refer to the kernel, when the kernel has to deliver a signal to |
| 170 | +a user task and must save the shadow stack pointer. The kernel can |
| 171 | +perform similar procedure itself by saving a token on the user mode |
| 172 | +task's shadow stack. This way, whenever :c:macro:`sigreturn` happens, |
| 173 | +the kernel can read and verify the token and then switch to the shadow |
| 174 | +stack. Using this mechanism, the kernel helps the user task so that |
| 175 | +any corruption issue in the user task is not exploited by adversaries |
| 176 | +arbitrarily using :c:macro:`sigreturn`. Adversaries will have to make |
| 177 | +sure that there is a valid ``shadow stack token`` in addition to |
| 178 | +invoking :c:macro:`sigreturn`. |
| 179 | + |
| 180 | +7. Signal shadow stack |
| 181 | +----------------------- |
| 182 | +The following structure has been added to sigcontext for RISC-V:: |
| 183 | + |
| 184 | + struct __sc_riscv_cfi_state { |
| 185 | + unsigned long ss_ptr; |
| 186 | + }; |
| 187 | + |
| 188 | +As part of signal delivery, the shadow stack token is saved on the |
| 189 | +current shadow stack itself. The updated pointer is saved away in the |
| 190 | +:c:macro:`ss_ptr` field in :c:macro:`__sc_riscv_cfi_state` under |
| 191 | +:c:macro:`sigcontext`. The existing shadow stack allocation is used |
| 192 | +for signal delivery. During :c:macro:`sigreturn`, kernel will obtain |
| 193 | +:c:macro:`ss_ptr` from :c:macro:`sigcontext`, verify the saved |
| 194 | +token on the shadow stack, and switch the shadow stack. |
0 commit comments