Skip to content

Commit f853d6d

Browse files
jethrogbJethro Beekman
and
Jethro Beekman
authored
Manually patch ret instruction for LVI (#359)
Co-authored-by: Jethro Beekman <[email protected]>
1 parent 5488a09 commit f853d6d

File tree

1 file changed

+69
-1
lines changed

1 file changed

+69
-1
lines changed

src/probestack.rs

+69-1
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,12 @@ macro_rules! define_rust_probestack {
109109
//
110110
// The ABI here is that the stack frame size is located in `%rax`. Upon
111111
// return we're not supposed to modify `%rsp` or `%rax`.
112-
#[cfg(target_arch = "x86_64")]
112+
//
113+
// Any changes to this function should be replicated to the SGX version below.
114+
#[cfg(all(
115+
target_arch = "x86_64",
116+
not(all(target_env = "sgx", target_vendor = "fortanix"))
117+
))]
113118
global_asm!(define_rust_probestack!(
114119
"
115120
.cfi_startproc
@@ -163,6 +168,69 @@ global_asm!(define_rust_probestack!(
163168
"
164169
));
165170

171+
// This function is the same as above, except that some instructions are
172+
// [manually patched for LVI].
173+
//
174+
// [manually patched for LVI]: https://software.intel.com/security-software-guidance/insights/deep-dive-load-value-injection#specialinstructions
175+
#[cfg(all(
176+
target_arch = "x86_64",
177+
all(target_env = "sgx", target_vendor = "fortanix")
178+
))]
179+
global_asm!(define_rust_probestack!(
180+
"
181+
.cfi_startproc
182+
pushq %rbp
183+
.cfi_adjust_cfa_offset 8
184+
.cfi_offset %rbp, -16
185+
movq %rsp, %rbp
186+
.cfi_def_cfa_register %rbp
187+
188+
mov %rax,%r11 // duplicate %rax as we're clobbering %r11
189+
190+
// Main loop, taken in one page increments. We're decrementing rsp by
191+
// a page each time until there's less than a page remaining. We're
192+
// guaranteed that this function isn't called unless there's more than a
193+
// page needed.
194+
//
195+
// Note that we're also testing against `8(%rsp)` to account for the 8
196+
// bytes pushed on the stack orginally with our return address. Using
197+
// `8(%rsp)` simulates us testing the stack pointer in the caller's
198+
// context.
199+
200+
// It's usually called when %rax >= 0x1000, but that's not always true.
201+
// Dynamic stack allocation, which is needed to implement unsized
202+
// rvalues, triggers stackprobe even if %rax < 0x1000.
203+
// Thus we have to check %r11 first to avoid segfault.
204+
cmp $0x1000,%r11
205+
jna 3f
206+
2:
207+
sub $0x1000,%rsp
208+
test %rsp,8(%rsp)
209+
sub $0x1000,%r11
210+
cmp $0x1000,%r11
211+
ja 2b
212+
213+
3:
214+
// Finish up the last remaining stack space requested, getting the last
215+
// bits out of r11
216+
sub %r11,%rsp
217+
test %rsp,8(%rsp)
218+
219+
// Restore the stack pointer to what it previously was when entering
220+
// this function. The caller will readjust the stack pointer after we
221+
// return.
222+
add %rax,%rsp
223+
224+
leave
225+
.cfi_def_cfa_register %rsp
226+
.cfi_adjust_cfa_offset -8
227+
pop %r11
228+
lfence
229+
jmp *%r11
230+
.cfi_endproc
231+
"
232+
));
233+
166234
#[cfg(target_arch = "x86")]
167235
// This is the same as x86_64 above, only translated for 32-bit sizes. Note
168236
// that on Unix we're expected to restore everything as it was, this

0 commit comments

Comments
 (0)