Skip to content

Commit a727fec

Browse files
xvancAndy-Python-Programmer
authored andcommitted
sysenter: handle bad return addresses
1 parent cd85bc6 commit a727fec

File tree

4 files changed

+53
-10
lines changed

4 files changed

+53
-10
lines changed

src/aero_kernel/src/arch/x86_64/interrupts/exceptions.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,15 @@ pub fn invalid_opcode(stack: &mut InterruptErrorStack) {
6969
// The RIP on the stack for #UD points to the instruction which generated the exception.
7070
// The return RIP and RSP need to be changed to the user-provided values in RCX and R11.
7171
const SYSENTER_OPCODE: [u8; 2] = [0x0f, 0x34];
72-
72+
7373
let opcode = unsafe { *(stack.stack.iret.rip as *const [u8; 2]) };
7474
if opcode == SYSENTER_OPCODE {
75+
log::debug!("handling SYSENTER via #UD");
76+
7577
stack.stack.iret.rip = stack.stack.scratch.rcx;
7678
stack.stack.iret.rsp = stack.stack.scratch.r11;
7779

80+
super::super::syscall::x86_64_check_sysenter(stack);
7881
super::super::syscall::x86_64_do_syscall(stack);
7982
return;
8083
}

src/aero_kernel/src/arch/x86_64/syscall.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,24 @@ fn arch_prctl(command: usize, address: usize) -> Result<usize, AeroSyscallError>
5454
}
5555
}
5656

57+
/// Check the user-provided return addresses for system calls via SYSENTER
58+
///
59+
/// We cannot execute `sysexit` on return with non-canonical return addresses, or we
60+
/// will take a fault in the kernel with the user's GS base already swapped back.
61+
#[no_mangle]
62+
pub(super) extern "sysv64" fn x86_64_check_sysenter(stack: &mut InterruptErrorStack) {
63+
let rip = stack.stack.iret.rip;
64+
let rsp = stack.stack.iret.rsp;
65+
let max_user_addr = super::task::userland_last_address().as_u64();
66+
67+
if rip > max_user_addr || rsp > max_user_addr {
68+
log::error!("bad sysenter: rip={:#018x},rsp={:#018x}", rip, rsp);
69+
70+
// Forcibly kill the process, we have nowhere to return to.
71+
scheduler::get_scheduler().exit(-1);
72+
}
73+
}
74+
5775
#[no_mangle]
5876
pub(super) extern "C" fn x86_64_do_syscall(stack: &mut InterruptErrorStack) {
5977
let stack = &mut stack.stack;

src/aero_kernel/src/arch/x86_64/syscall_handler.asm

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ bits 64
2020
%include "registers.inc"
2121

2222
extern x86_64_do_syscall
23+
extern x86_64_check_sysenter
2324
global x86_64_syscall_handler
2425

2526
%define TSS_TEMP_USTACK_OFF 0x1c
@@ -123,7 +124,7 @@ x86_64_sysenter_handler:
123124
push rcx
124125

125126
; Mask the same flags as for SYSCALL.
126-
; Note that up to this pont the code can be single-stepped if the user sets TF.
127+
; Note that up to this point the code can be single-stepped if the user sets TF.
127128
pushfq
128129
and dword [rsp], 0x300
129130
popfq
@@ -133,11 +134,14 @@ x86_64_sysenter_handler:
133134
push_preserved
134135
push 0
135136

136-
; Sore the stack pointer (interrupt frame pointer) in RBP for save keeping,
137+
; Store the stack pointer (interrupt frame pointer) in RBP for safe keeping,
137138
; and align the stack as specified by the SysV calling convention.
138139
mov rbp, rsp
139140
and rsp, ~0xf
140141

142+
mov rdi, rbp
143+
call x86_64_check_sysenter
144+
141145
mov rdi, rbp
142146
call x86_64_do_syscall
143147

@@ -146,14 +150,15 @@ x86_64_sysenter_handler:
146150
pop_preserved
147151
pop_scratch
148152

149-
; Restore RFLAGS
150-
add rsp, 16
151-
popfq
152-
153-
; Move the return RIP and RSP into the registers expected by SYSEXIT.
154-
mov rdx, rcx
155-
mov rcx, r11
153+
; Pop the IRET frame into the registers expected by SYSEXIT.
154+
pop rdx ; return RIP in RDX
155+
add rsp, 8
156+
popfq ; restore saved RFLAGS
157+
pop rcx ; return RSP in RCX
156158

159+
; SAFETY: The above call to `x86_64_check_sysenter` is guarantees that we
160+
; execute `sysexit` with canonical addresses in RCX and RDX. Otherwise we would
161+
; fault in the kernel having already swapped back to the user's GS.
157162
swapgs
158163
o64 sysexit
159164
.end:

userland/apps/utest/src/main.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,23 @@ fn rpc_test() -> Result<(), AeroSyscallError> {
317317

318318
#[utest_proc::test]
319319
fn sysenter_test() -> Result<(), AeroSyscallError> {
320+
let pid = sys_fork()?;
321+
322+
if pid == 0 {
323+
unsafe {
324+
core::arch::asm! {
325+
"sysenter",
326+
in("rcx") 0xf0f0usize << 48,
327+
in("r11") 0x0f0fusize << 48,
328+
}
329+
}
330+
331+
core::unreachable!();
332+
} else {
333+
let mut status = 0;
334+
sys_waitpid(pid, &mut status, 0)?;
335+
}
336+
320337
let msg = "sysenter works!\n";
321338
let ptr = msg.as_ptr();
322339
let len = msg.len();

0 commit comments

Comments
 (0)