| 1 | // Copyright 2017 The Rust Project Developers. See the COPYRIGHT | 
| 2 | // file at the top-level directory of this distribution and at | 
|---|
| 3 | // http://rust-lang.org/COPYRIGHT. | 
|---|
| 4 | // | 
|---|
| 5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | 
|---|
| 6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | 
|---|
| 7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | 
|---|
| 8 | // option. This file may not be copied, modified, or distributed | 
|---|
| 9 | // except according to those terms. | 
|---|
| 10 |  | 
|---|
| 11 | //! This module defines the `__rust_probestack` intrinsic which is used in the | 
|---|
| 12 | //! implementation of "stack probes" on certain platforms. | 
|---|
| 13 | //! | 
|---|
| 14 | //! The purpose of a stack probe is to provide a static guarantee that if a | 
|---|
| 15 | //! thread has a guard page then a stack overflow is guaranteed to hit that | 
|---|
| 16 | //! guard page. If a function did not have a stack probe then there's a risk of | 
|---|
| 17 | //! having a stack frame *larger* than the guard page, so a function call could | 
|---|
| 18 | //! skip over the guard page entirely and then later hit maybe the heap or | 
|---|
| 19 | //! another thread, possibly leading to security vulnerabilities such as [The | 
|---|
| 20 | //! Stack Clash], for example. | 
|---|
| 21 | //! | 
|---|
| 22 | //! [The Stack Clash]: https://blog.qualys.com/securitylabs/2017/06/19/the-stack-clash | 
|---|
| 23 | //! | 
|---|
| 24 | //! The `__rust_probestack` is called in the prologue of functions whose stack | 
|---|
| 25 | //! size is larger than the guard page, for example larger than 4096 bytes on | 
|---|
| 26 | //! x86. This function is then responsible for "touching" all pages relevant to | 
|---|
| 27 | //! the stack to ensure that that if any of them are the guard page we'll hit | 
|---|
| 28 | //! them guaranteed. | 
|---|
| 29 | //! | 
|---|
| 30 | //! The precise ABI for how this function operates is defined by LLVM. There's | 
|---|
| 31 | //! no real documentation as to what this is, so you'd basically need to read | 
|---|
| 32 | //! the LLVM source code for reference. Often though the test cases can be | 
|---|
| 33 | //! illuminating as to the ABI that's generated, or just looking at the output | 
|---|
| 34 | //! of `llc`. | 
|---|
| 35 | //! | 
|---|
| 36 | //! Note that `#[naked]` is typically used here for the stack probe because the | 
|---|
| 37 | //! ABI corresponds to no actual ABI. | 
|---|
| 38 | //! | 
|---|
| 39 | //! Finally it's worth noting that at the time of this writing LLVM only has | 
|---|
| 40 | //! support for stack probes on x86 and x86_64. There's no support for stack | 
|---|
| 41 | //! probes on any other architecture like ARM or PowerPC64. LLVM I'm sure would | 
|---|
| 42 | //! be more than welcome to accept such a change! | 
|---|
| 43 |  | 
|---|
| 44 | #![ cfg(not(feature = "mangled-names"))] | 
|---|
| 45 | // Windows and Cygwin already has builtins to do this. | 
|---|
| 46 | #![ cfg(not(any(windows, target_os = "cygwin")))] | 
|---|
| 47 | // All these builtins require assembly | 
|---|
| 48 | #![ cfg(not(feature = "no-asm"))] | 
|---|
| 49 | // We only define stack probing for these architectures today. | 
|---|
| 50 | #![ cfg(any(target_arch = "x86_64", target_arch = "x86"))] | 
|---|
| 51 |  | 
|---|
| 52 | // Our goal here is to touch each page between %rsp+8 and %rsp+8-%rax, | 
|---|
| 53 | // ensuring that if any pages are unmapped we'll make a page fault. | 
|---|
| 54 | // | 
|---|
| 55 | // FIXME(abi_custom): This function is unsafe because it uses a custom ABI, | 
|---|
| 56 | // it does not actually match `extern "C"`. | 
|---|
| 57 | // | 
|---|
| 58 | // The ABI here is that the stack frame size is located in `%rax`. Upon | 
|---|
| 59 | // return we're not supposed to modify `%rsp` or `%rax`. | 
|---|
| 60 | #[ cfg(target_arch = "x86_64")] | 
|---|
| 61 | #[unsafe( naked)] | 
|---|
| 62 | #[ rustc_std_internal_symbol] | 
|---|
| 63 | pub unsafe extern "C"fn __rust_probestack() { | 
|---|
| 64 | #[ cfg(not(all(target_env = "sgx", target_vendor = "fortanix")))] | 
|---|
| 65 | macro_rules! ret { | 
|---|
| 66 | () => { | 
|---|
| 67 | "ret" | 
|---|
| 68 | }; | 
|---|
| 69 | } | 
|---|
| 70 |  | 
|---|
| 71 | #[ cfg(all(target_env = "sgx", target_vendor = "fortanix"))] | 
|---|
| 72 | macro_rules! ret { | 
|---|
| 73 | // for this target, [manually patch for LVI]. | 
|---|
| 74 | // | 
|---|
| 75 | // [manually patch for LVI]: https://software.intel.com/security-software-guidance/insights/deep-dive-load-value-injection#specialinstructions | 
|---|
| 76 | () => { | 
|---|
| 77 | " | 
|---|
| 78 |             pop %r11 | 
|---|
| 79 |             lfence | 
|---|
| 80 |             jmp *%r11 | 
|---|
| 81 |             " | 
|---|
| 82 | }; | 
|---|
| 83 | } | 
|---|
| 84 |  | 
|---|
| 85 | core::arch::naked_asm!( | 
|---|
| 86 | " | 
|---|
| 87 |             .cfi_startproc | 
|---|
| 88 |             pushq  %rbp | 
|---|
| 89 |             .cfi_adjust_cfa_offset 8 | 
|---|
| 90 |             .cfi_offset %rbp, -16 | 
|---|
| 91 |             movq   %rsp, %rbp | 
|---|
| 92 |             .cfi_def_cfa_register %rbp | 
|---|
| 93 |  | 
|---|
| 94 |             mov    %rax,%r11        // duplicate %rax as we're clobbering %r11 | 
|---|
| 95 |  | 
|---|
| 96 |             // Main loop, taken in one page increments. We're decrementing rsp by | 
|---|
| 97 |             // a page each time until there's less than a page remaining. We're | 
|---|
| 98 |             // guaranteed that this function isn't called unless there's more than a | 
|---|
| 99 |             // page needed. | 
|---|
| 100 |             // | 
|---|
| 101 |             // Note that we're also testing against `8(%rsp)` to account for the 8 | 
|---|
| 102 |             // bytes pushed on the stack orginally with our return address. Using | 
|---|
| 103 |             // `8(%rsp)` simulates us testing the stack pointer in the caller's | 
|---|
| 104 |             // context. | 
|---|
| 105 |  | 
|---|
| 106 |             // It's usually called when %rax >= 0x1000, but that's not always true. | 
|---|
| 107 |             // Dynamic stack allocation, which is needed to implement unsized | 
|---|
| 108 |             // rvalues, triggers stackprobe even if %rax < 0x1000. | 
|---|
| 109 |             // Thus we have to check %r11 first to avoid segfault. | 
|---|
| 110 |             cmp    $0x1000,%r11 | 
|---|
| 111 |             jna    3f | 
|---|
| 112 |         2: | 
|---|
| 113 |             sub    $0x1000,%rsp | 
|---|
| 114 |             test   %rsp,8(%rsp) | 
|---|
| 115 |             sub    $0x1000,%r11 | 
|---|
| 116 |             cmp    $0x1000,%r11 | 
|---|
| 117 |             ja     2b | 
|---|
| 118 |  | 
|---|
| 119 |         3: | 
|---|
| 120 |             // Finish up the last remaining stack space requested, getting the last | 
|---|
| 121 |             // bits out of r11 | 
|---|
| 122 |             sub    %r11,%rsp | 
|---|
| 123 |             test   %rsp,8(%rsp) | 
|---|
| 124 |  | 
|---|
| 125 |             // Restore the stack pointer to what it previously was when entering | 
|---|
| 126 |             // this function. The caller will readjust the stack pointer after we | 
|---|
| 127 |             // return. | 
|---|
| 128 |             add    %rax,%rsp | 
|---|
| 129 |  | 
|---|
| 130 |             leave | 
|---|
| 131 |             .cfi_def_cfa_register %rsp | 
|---|
| 132 |             .cfi_adjust_cfa_offset -8 | 
|---|
| 133 |     ", | 
|---|
| 134 | ret!(), | 
|---|
| 135 | " | 
|---|
| 136 |             .cfi_endproc | 
|---|
| 137 |     ", | 
|---|
| 138 | options(att_syntax) | 
|---|
| 139 | ) | 
|---|
| 140 | } | 
|---|
| 141 |  | 
|---|
| 142 | #[ cfg(all(target_arch = "x86", not(target_os = "uefi")))] | 
|---|
| 143 | // This is the same as x86_64 above, only translated for 32-bit sizes. Note | 
|---|
| 144 | // that on Unix we're expected to restore everything as it was, this | 
|---|
| 145 | // function basically can't tamper with anything. | 
|---|
| 146 | // | 
|---|
| 147 | // FIXME(abi_custom): This function is unsafe because it uses a custom ABI, | 
|---|
| 148 | // it does not actually match `extern "C"`. | 
|---|
| 149 | // | 
|---|
| 150 | // The ABI here is the same as x86_64, except everything is 32-bits large. | 
|---|
| 151 | #[unsafe( naked)] | 
|---|
| 152 | #[ rustc_std_internal_symbol] | 
|---|
| 153 | pub unsafe extern "C"fn __rust_probestack() { | 
|---|
| 154 | core::arch::naked_asm!( | 
|---|
| 155 | " | 
|---|
| 156 |             .cfi_startproc | 
|---|
| 157 |             push   %ebp | 
|---|
| 158 |             .cfi_adjust_cfa_offset 4 | 
|---|
| 159 |             .cfi_offset %ebp, -8 | 
|---|
| 160 |             mov    %esp, %ebp | 
|---|
| 161 |             .cfi_def_cfa_register %ebp | 
|---|
| 162 |             push   %ecx | 
|---|
| 163 |             mov    %eax,%ecx | 
|---|
| 164 |  | 
|---|
| 165 |             cmp    $0x1000,%ecx | 
|---|
| 166 |             jna    3f | 
|---|
| 167 |         2: | 
|---|
| 168 |             sub    $0x1000,%esp | 
|---|
| 169 |             test   %esp,8(%esp) | 
|---|
| 170 |             sub    $0x1000,%ecx | 
|---|
| 171 |             cmp    $0x1000,%ecx | 
|---|
| 172 |             ja     2b | 
|---|
| 173 |  | 
|---|
| 174 |         3: | 
|---|
| 175 |             sub    %ecx,%esp | 
|---|
| 176 |             test   %esp,8(%esp) | 
|---|
| 177 |  | 
|---|
| 178 |             add    %eax,%esp | 
|---|
| 179 |             pop    %ecx | 
|---|
| 180 |             leave | 
|---|
| 181 |             .cfi_def_cfa_register %esp | 
|---|
| 182 |             .cfi_adjust_cfa_offset -4 | 
|---|
| 183 |             ret | 
|---|
| 184 |             .cfi_endproc | 
|---|
| 185 |     ", | 
|---|
| 186 | options(att_syntax) | 
|---|
| 187 | ) | 
|---|
| 188 | } | 
|---|
| 189 |  | 
|---|
| 190 | #[ cfg(all(target_arch = "x86", target_os = "uefi"))] | 
|---|
| 191 | // UEFI target is windows like target. LLVM will do _chkstk things like windows. | 
|---|
| 192 | // probestack function will also do things like _chkstk in MSVC. | 
|---|
| 193 | // So we need to sub %ax %sp in probestack when arch is x86. | 
|---|
| 194 | // | 
|---|
| 195 | // FIXME(abi_custom): This function is unsafe because it uses a custom ABI, | 
|---|
| 196 | // it does not actually match `extern "C"`. | 
|---|
| 197 | // | 
|---|
| 198 | // REF: Rust commit(74e80468347) | 
|---|
| 199 | // rust\src\llvm-project\llvm\lib\Target\X86\X86FrameLowering.cpp: 805 | 
|---|
| 200 | // Comments in LLVM: | 
|---|
| 201 | //   MSVC x32's _chkstk and cygwin/mingw's _alloca adjust %esp themselves. | 
|---|
| 202 | //   MSVC x64's __chkstk and cygwin/mingw's ___chkstk_ms do not adjust %rsp | 
|---|
| 203 | //   themselves. | 
|---|
| 204 | #[unsafe( naked)] | 
|---|
| 205 | #[ rustc_std_internal_symbol] | 
|---|
| 206 | pub unsafe extern "C"fn __rust_probestack() { | 
|---|
| 207 | core::arch::naked_asm!( | 
|---|
| 208 | " | 
|---|
| 209 |             .cfi_startproc | 
|---|
| 210 |             push   %ebp | 
|---|
| 211 |             .cfi_adjust_cfa_offset 4 | 
|---|
| 212 |             .cfi_offset %ebp, -8 | 
|---|
| 213 |             mov    %esp, %ebp | 
|---|
| 214 |             .cfi_def_cfa_register %ebp | 
|---|
| 215 |             push   %ecx | 
|---|
| 216 |             push   %edx | 
|---|
| 217 |             mov    %eax,%ecx | 
|---|
| 218 |  | 
|---|
| 219 |             cmp    $0x1000,%ecx | 
|---|
| 220 |             jna    3f | 
|---|
| 221 |         2: | 
|---|
| 222 |             sub    $0x1000,%esp | 
|---|
| 223 |             test   %esp,8(%esp) | 
|---|
| 224 |             sub    $0x1000,%ecx | 
|---|
| 225 |             cmp    $0x1000,%ecx | 
|---|
| 226 |             ja     2b | 
|---|
| 227 |  | 
|---|
| 228 |         3: | 
|---|
| 229 |             sub    %ecx,%esp | 
|---|
| 230 |             test   %esp,8(%esp) | 
|---|
| 231 |             mov    4(%ebp),%edx | 
|---|
| 232 |             mov    %edx, 12(%esp) | 
|---|
| 233 |             add    %eax,%esp | 
|---|
| 234 |             pop    %edx | 
|---|
| 235 |             pop    %ecx | 
|---|
| 236 |             leave | 
|---|
| 237 |  | 
|---|
| 238 |             sub   %eax, %esp | 
|---|
| 239 |             .cfi_def_cfa_register %esp | 
|---|
| 240 |             .cfi_adjust_cfa_offset -4 | 
|---|
| 241 |             ret | 
|---|
| 242 |             .cfi_endproc | 
|---|
| 243 |     ", | 
|---|
| 244 | options(att_syntax) | 
|---|
| 245 | ) | 
|---|
| 246 | } | 
|---|
| 247 |  | 
|---|