]>
git.proxmox.com Git - rustc.git/blob - vendor/compiler_builtins/src/probestack.rs
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.
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.
11 //! This module defines the `__rust_probestack` intrinsic which is used in the
12 //! implementation of "stack probes" on certain platforms.
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.
22 //! [The Stack Clash]: https://blog.qualys.com/securitylabs/2017/06/19/the-stack-clash
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
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
36 //! Note that `#[naked]` is typically used here for the stack probe because the
37 //! ABI corresponds to no actual ABI.
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!
44 #![cfg(not(feature = "mangled-names"))]
45 // Windows already has builtins to do this.
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"))]
53 pub fn __rust_probestack();
56 // A wrapper for our implementation of __rust_probestack, which allows us to
57 // keep the assembly inline while controlling all CFI directives in the assembly
58 // emitted for the function.
60 // This is the ELF version.
61 #[cfg(not(any(target_vendor = "apple", target_os = "uefi")))]
62 macro_rules
! define_rust_probestack
{
66 .pushsection .text.__rust_probestack
67 .globl __rust_probestack
68 .type __rust_probestack, @function
69 .hidden __rust_probestack
74 .size __rust_probestack, . - __rust_probestack
81 #[cfg(all(target_os = "uefi", target_arch = "x86_64"))]
82 macro_rules
! define_rust_probestack
{
86 .globl __rust_probestack
94 // Same as above, but for Mach-O. Note that the triple underscore
96 #[cfg(target_vendor = "apple")]
97 macro_rules
! define_rust_probestack
{
101 .globl ___rust_probestack
109 // In UEFI x86 arch, triple underscore is deliberate.
110 #[cfg(all(target_os = "uefi", target_arch = "x86"))]
111 macro_rules
! define_rust_probestack
{
115 .globl ___rust_probestack
123 // Our goal here is to touch each page between %rsp+8 and %rsp+8-%rax,
124 // ensuring that if any pages are unmapped we'll make a page fault.
126 // The ABI here is that the stack frame size is located in `%rax`. Upon
127 // return we're not supposed to modify `%rsp` or `%rax`.
129 // Any changes to this function should be replicated to the SGX version below.
131 target_arch
= "x86_64",
132 not(all(target_env
= "sgx", target_vendor
= "fortanix"))
134 global_asm
!(define_rust_probestack
!(
138 .cfi_adjust_cfa_offset 8
139 .cfi_offset %rbp, -16
141 .cfi_def_cfa_register %rbp
143 mov %rax,%r11 // duplicate %rax as we're clobbering %r11
145 // Main loop, taken in one page increments. We're decrementing rsp by
146 // a page each time until there's less than a page remaining. We're
147 // guaranteed that this function isn't called unless there's more than a
150 // Note that we're also testing against `8(%rsp)` to account for the 8
151 // bytes pushed on the stack orginally with our return address. Using
152 // `8(%rsp)` simulates us testing the stack pointer in the caller's
155 // It's usually called when %rax >= 0x1000, but that's not always true.
156 // Dynamic stack allocation, which is needed to implement unsized
157 // rvalues, triggers stackprobe even if %rax < 0x1000.
158 // Thus we have to check %r11 first to avoid segfault.
169 // Finish up the last remaining stack space requested, getting the last
174 // Restore the stack pointer to what it previously was when entering
175 // this function. The caller will readjust the stack pointer after we
180 .cfi_def_cfa_register %rsp
181 .cfi_adjust_cfa_offset -8
187 // This function is the same as above, except that some instructions are
188 // [manually patched for LVI].
190 // [manually patched for LVI]: https://software.intel.com/security-software-guidance/insights/deep-dive-load-value-injection#specialinstructions
192 target_arch
= "x86_64",
193 all(target_env
= "sgx", target_vendor
= "fortanix")
195 global_asm
!(define_rust_probestack
!(
199 .cfi_adjust_cfa_offset 8
200 .cfi_offset %rbp, -16
202 .cfi_def_cfa_register %rbp
204 mov %rax,%r11 // duplicate %rax as we're clobbering %r11
206 // Main loop, taken in one page increments. We're decrementing rsp by
207 // a page each time until there's less than a page remaining. We're
208 // guaranteed that this function isn't called unless there's more than a
211 // Note that we're also testing against `8(%rsp)` to account for the 8
212 // bytes pushed on the stack orginally with our return address. Using
213 // `8(%rsp)` simulates us testing the stack pointer in the caller's
216 // It's usually called when %rax >= 0x1000, but that's not always true.
217 // Dynamic stack allocation, which is needed to implement unsized
218 // rvalues, triggers stackprobe even if %rax < 0x1000.
219 // Thus we have to check %r11 first to avoid segfault.
230 // Finish up the last remaining stack space requested, getting the last
235 // Restore the stack pointer to what it previously was when entering
236 // this function. The caller will readjust the stack pointer after we
241 .cfi_def_cfa_register %rsp
242 .cfi_adjust_cfa_offset -8
250 #[cfg(all(target_arch = "x86", not(target_os = "uefi")))]
251 // This is the same as x86_64 above, only translated for 32-bit sizes. Note
252 // that on Unix we're expected to restore everything as it was, this
253 // function basically can't tamper with anything.
255 // The ABI here is the same as x86_64, except everything is 32-bits large.
256 global_asm
!(define_rust_probestack
!(
260 .cfi_adjust_cfa_offset 4
263 .cfi_def_cfa_register %ebp
283 .cfi_def_cfa_register %esp
284 .cfi_adjust_cfa_offset -4
290 #[cfg(all(target_arch = "x86", target_os = "uefi"))]
291 // UEFI target is windows like target. LLVM will do _chkstk things like windows.
292 // probestack function will also do things like _chkstk in MSVC.
293 // So we need to sub %ax %sp in probestack when arch is x86.
295 // REF: Rust commit(74e80468347)
296 // rust\src\llvm-project\llvm\lib\Target\X86\X86FrameLowering.cpp: 805
298 // MSVC x32's _chkstk and cygwin/mingw's _alloca adjust %esp themselves.
299 // MSVC x64's __chkstk and cygwin/mingw's ___chkstk_ms do not adjust %rsp
301 global_asm
!(define_rust_probestack
!(
305 .cfi_adjust_cfa_offset 4
308 .cfi_def_cfa_register %ebp
333 .cfi_def_cfa_register %esp
334 .cfi_adjust_cfa_offset -4