diff --git a/src/arch/x86_64/interrupt/handler.rs b/src/arch/x86_64/interrupt/handler.rs index ddbef8285da68823d6489ba08af62425e45b3a99..acdf4a519dfe7d41068d7f94f2efffdb699697b7 100644 --- a/src/arch/x86_64/interrupt/handler.rs +++ b/src/arch/x86_64/interrupt/handler.rs @@ -407,6 +407,9 @@ macro_rules! interrupt_stack { } } core::arch::asm!(concat!( + // Clear direction flag, required by ABI when running any Rust code in the kernel. + "cld;", + // Backup all userspace registers to stack $save1!(), "push rax\n", @@ -463,6 +466,9 @@ macro_rules! interrupt { } core::arch::asm!(concat!( + // Clear direction flag, required by ABI when running any Rust code in the kernel. + "cld;", + // Backup all userspace registers to stack swapgs_iff_ring3_fast!(), "push rax\n", @@ -518,6 +524,9 @@ macro_rules! interrupt_error { } core::arch::asm!(concat!( + // Clear direction flag, required by ABI when running any Rust code in the kernel. + "cld;", + swapgs_iff_ring3_fast_errorcode!(), // Move rax into code's place, put code in last instead (to be // compatible with InterruptStack) diff --git a/src/arch/x86_64/interrupt/syscall.rs b/src/arch/x86_64/interrupt/syscall.rs index 3fd565db36b1d7456bd8acff26cc79040358aafe..4378aa8894fa0067c66cbeec7883aa08388edfe5 100644 --- a/src/arch/x86_64/interrupt/syscall.rs +++ b/src/arch/x86_64/interrupt/syscall.rs @@ -6,7 +6,7 @@ use crate::{ syscall::flag::{PTRACE_FLAG_IGNORE, PTRACE_STOP_PRE_SYSCALL, PTRACE_STOP_POST_SYSCALL}, }; use memoffset::offset_of; -use x86::{bits64::task::TaskStateSegment, msr, segmentation::SegmentSelector}; +use x86::{bits64::{rflags::RFlags, task::TaskStateSegment}, msr, segmentation::SegmentSelector}; pub unsafe fn init() { // IA32_STAR[31:0] are reserved. @@ -22,7 +22,32 @@ pub unsafe fn init() { msr::wrmsr(msr::IA32_STAR, u64::from(star_high) << 32); msr::wrmsr(msr::IA32_LSTAR, syscall_instruction as u64); - msr::wrmsr(msr::IA32_FMASK, 0x0300); // Clear trap flag and interrupt enable + + // DF needs to be cleared, required by the compiler ABI. If DF were not part of FMASK, + // userspace would be able to reverse the direction of in-kernel REP MOVS/STOS/(CMPS/SCAS), and + // cause all sorts of memory corruption. + // + // IF needs to be cleared, as the kernel currently assumes interrupts are disabled except in + // usermode and in kmain. + // + // TF needs to be cleared, as enabling userspace-rflags-controlled singlestep in the kernel + // would be a bad idea. + // + // AC is not currently used, but when SMAP is enabled, it should always be cleared when + // entering the kernel (and never be set except in usercopy functions), if for some reason AC + // was set before entering userspace (AC can only be modified by kernel code). + // + // The other flags could indeed be preserved and excluded from FMASK, but since they are not + // used to pass data to the kernel, they might as well be masked with *marginal* security + // benefits. + // + // Flags not included here are IOPL (not relevant to the kernel at all), "CPUID flag" (not used + // at all in 64-bit mode), RF (not used yet, but DR breakpoints would remain enabled both in + // user and kernel mode), VM8086 (not used at all), and VIF/VIP (system-level status flags?). + + let mask_critical = RFlags::FLAGS_DF | RFlags::FLAGS_IF | RFlags::FLAGS_TF | RFlags::FLAGS_AC; + let mask_other = RFlags::FLAGS_CF | RFlags::FLAGS_PF | RFlags::FLAGS_AF | RFlags::FLAGS_ZF | RFlags::FLAGS_SF | RFlags::FLAGS_OF; + msr::wrmsr(msr::IA32_FMASK, (mask_critical | mask_other).bits()); let efer = msr::rdmsr(msr::IA32_EFER); msr::wrmsr(msr::IA32_EFER, efer | 1);