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// We only define stack probing for these architectures today.
48#![cfg(any(target_arch = "x86_64", target_arch = "x86"))]
49
50// Our goal here is to touch each page between %rsp+8 and %rsp+8-%rax,
51// ensuring that if any pages are unmapped we'll make a page fault.
52//
53// The ABI here is that the stack frame size is located in `%rax`. Upon
54// return we're not supposed to modify `%rsp` or `%rax`.
55#[cfg(target_arch = "x86_64")]
56#[unsafe(naked)]
57#[rustc_std_internal_symbol]
58pub unsafe extern "custom" fn __rust_probestack() {
59 core::arch::naked_asm!(
60 "
61 .cfi_startproc
62 pushq %rbp
63 .cfi_adjust_cfa_offset 8
64 .cfi_offset %rbp, -16
65 movq %rsp, %rbp
66 .cfi_def_cfa_register %rbp
67
68 mov %rax,%r11 // duplicate %rax as we're clobbering %r11
69
70 // Main loop, taken in one page increments. We're decrementing rsp by
71 // a page each time until there's less than a page remaining. We're
72 // guaranteed that this function isn't called unless there's more than a
73 // page needed.
74 //
75 // Note that we're also testing against `8(%rsp)` to account for the 8
76 // bytes pushed on the stack originally with our return address. Using
77 // `8(%rsp)` simulates us testing the stack pointer in the caller's
78 // context.
79
80 // It's usually called when %rax >= 0x1000, but that's not always true.
81 // Dynamic stack allocation, which is needed to implement unsized
82 // rvalues, triggers stackprobe even if %rax < 0x1000.
83 // Thus we have to check %r11 first to avoid segfault.
84 cmp $0x1000,%r11
85 jna 3f
86 2:
87 sub $0x1000,%rsp
88 test %rsp,8(%rsp)
89 sub $0x1000,%r11
90 cmp $0x1000,%r11
91 ja 2b
92
93 3:
94 // Finish up the last remaining stack space requested, getting the last
95 // bits out of r11
96 sub %r11,%rsp
97 test %rsp,8(%rsp)
98
99 // Restore the stack pointer to what it previously was when entering
100 // this function. The caller will readjust the stack pointer after we
101 // return.
102 add %rax,%rsp
103
104 leave
105 .cfi_def_cfa_register %rsp
106 .cfi_adjust_cfa_offset -8
107 ",
108 #[cfg(not(all(target_env = "sgx", target_vendor = "fortanix")))]
109 " ret",
110 #[cfg(all(target_env = "sgx", target_vendor = "fortanix"))]
111 "
112 // for this target, [manually patch for LVI].
113 //
114 // [manually patch for LVI]: https://software.intel.com/security-software-guidance/insights/deep-dive-load-value-injection#specialinstructions
115 pop %r11
116 lfence
117 jmp *%r11
118 ",
119 "
120 .cfi_endproc
121 ",
122 options(att_syntax)
123 )
124}
125
126#[cfg(all(target_arch = "x86", not(target_os = "uefi")))]
127// This is the same as x86_64 above, only translated for 32-bit sizes. Note
128// that on Unix we're expected to restore everything as it was, this
129// function basically can't tamper with anything.
130//
131// The ABI here is the same as x86_64, except everything is 32-bits large.
132#[unsafe(naked)]
133#[rustc_std_internal_symbol]
134pub unsafe extern "custom" fn __rust_probestack() {
135 core::arch::naked_asm!(
136 "
137 .cfi_startproc
138 push %ebp
139 .cfi_adjust_cfa_offset 4
140 .cfi_offset %ebp, -8
141 mov %esp, %ebp
142 .cfi_def_cfa_register %ebp
143 push %ecx
144 mov %eax,%ecx
145
146 cmp $0x1000,%ecx
147 jna 3f
148 2:
149 sub $0x1000,%esp
150 test %esp,8(%esp)
151 sub $0x1000,%ecx
152 cmp $0x1000,%ecx
153 ja 2b
154
155 3:
156 sub %ecx,%esp
157 test %esp,8(%esp)
158
159 add %eax,%esp
160 pop %ecx
161 leave
162 .cfi_def_cfa_register %esp
163 .cfi_adjust_cfa_offset -4
164 ret
165 .cfi_endproc
166 ",
167 options(att_syntax)
168 )
169}
170
171#[cfg(all(target_arch = "x86", target_os = "uefi"))]
172// UEFI target is windows like target. LLVM will do _chkstk things like windows.
173// probestack function will also do things like _chkstk in MSVC.
174// So we need to sub %ax %sp in probestack when arch is x86.
175//
176// REF: Rust commit(74e80468347)
177// rust\src\llvm-project\llvm\lib\Target\X86\X86FrameLowering.cpp: 805
178// Comments in LLVM:
179// MSVC x32's _chkstk and cygwin/mingw's _alloca adjust %esp themselves.
180// MSVC x64's __chkstk and cygwin/mingw's ___chkstk_ms do not adjust %rsp
181// themselves.
182#[unsafe(naked)]
183#[rustc_std_internal_symbol]
184pub unsafe extern "custom" fn __rust_probestack() {
185 core::arch::naked_asm!(
186 "
187 .cfi_startproc
188 push %ebp
189 .cfi_adjust_cfa_offset 4
190 .cfi_offset %ebp, -8
191 mov %esp, %ebp
192 .cfi_def_cfa_register %ebp
193 push %ecx
194 push %edx
195 mov %eax,%ecx
196
197 cmp $0x1000,%ecx
198 jna 3f
199 2:
200 sub $0x1000,%esp
201 test %esp,8(%esp)
202 sub $0x1000,%ecx
203 cmp $0x1000,%ecx
204 ja 2b
205
206 3:
207 sub %ecx,%esp
208 test %esp,8(%esp)
209 mov 4(%ebp),%edx
210 mov %edx, 12(%esp)
211 add %eax,%esp
212 pop %edx
213 pop %ecx
214 leave
215
216 sub %eax, %esp
217 .cfi_def_cfa_register %esp
218 .cfi_adjust_cfa_offset -4
219 ret
220 .cfi_endproc
221 ",
222 options(att_syntax)
223 )
224}
225