Check whether RCX is canonical in sysretq.

This commit is contained in:
4lDO2
2021-02-15 16:48:34 +01:00
parent a183953ee8
commit ff33090fd0

View File

@@ -58,16 +58,29 @@ pub unsafe extern "C" fn __inner_syscall_instruction(stack: *mut InterruptStack)
});
}
macro_rules! fast_sysretq {
() => { "
pop rcx // Pop userspace return pointer
sub rsp, 8 // Pop fake userspace CS
pop r11 // Pop rflags
pop QWORD PTR gs:[0x70] // Pop userspace stack pointer
mov rsp, gs:[0x70] // Restore userspace stack pointer
swapgs // Restore gs from TSS to user data
ud2
sysretq // Return into userspace; RCX=>RIP,R11=>RFLAGS
" }
}
function!(syscall_instruction => {
// Yes, this is magic. No, you don't need to understand
"
swapgs // Set gs segment to TSS
mov gs:[0x70], rsp // Save userspace stack pointer
mov rsp, gs:[4] // Load kernel stack pointer
push QWORD PTR 5 * 8 + 3 // Push fake userspace SS (resembling iret frame)
push QWORD PTR 5 * 8 + 3 // Push fake SS (resembling iret stack frame)
push QWORD PTR gs:[0x70] // Push userspace rsp
push r11 // Push rflags
push QWORD PTR 6 * 8 + 3 // Push fake userspace CS (resembling iret frame)
push QWORD PTR 6 * 8 + 3 // Push fake CS (resembling iret stack frame)
push rcx // Push userspace return pointer
",
@@ -94,14 +107,54 @@ function!(syscall_instruction => {
// Return
"
pop rcx // Pop userspace return pointer
add rsp, 8 // Pop CS
pop r11 // Pop rflags
pop QWORD PTR gs:[0x70] // Pop userspace stack pointer
add rsp, 8 // Pop SS
mov rsp, gs:[0x70] // Restore userspace stack pointer
swapgs // Restore gs from TSS to user data
sysretq // Return into userspace; RCX=>RIP,R11=>RFLAGS
// Test whether RCX is canonical. This is not strictly necessary, but could be fatal if
// some kernel bug would allow RCX to be modified by user code.
// Peek at the preserved RCX, to double-check that it is canonical.
// Set ZF iff bit 47 (i.e. the bit that must be sign extended) is set.
// TODO: Any hacky one-instruction canonicalness check?
bt QWORD PTR [rsp], 47
// Jump to the canonicalness check.
jnz 2f
// Otherwise, continue with the fast return.
1:
// Fast sysretq
",
fast_sysretq!(),
"
// Jumps to aligned addresses are faster, and it is not always the case the userspace
// doesn't call from higher half (though unlikely).
.align 8, 0x90
2:
// Load userspace return RIP.
mov rcx, [rsp]
// Shift it to the right by 47, only getting the bits which must all be zero or one.
shr rcx, 47
// Since we have already checked that RCX[47] == 1, RCX[63:47] must all be one.
cmp rcx, 0x1FFFF
// If the address is canonical, go to the slower iretq.
jne 3f
",
fast_sysretq!(),
"
3:
// Slow iretq
xor rcx, rcx
xor r11, r11
swapgs
iretq
",
});