diff --git a/aero.py b/aero.py index 4fef1ad123c..de2b7637483 100755 --- a/aero.py +++ b/aero.py @@ -470,11 +470,11 @@ def run_in_emulator(args, iso_path): print("Running with KVM acceleration enabled") if platform.system() == 'Darwin': - qemu_args += ['-accel', 'hvf', '-cpu', 'qemu64,+la57' if args.la57 else 'qemu64'] + qemu_args += ['-accel', 'hvf', '-cpu', 'qemu64,+la57,+smap,+smep,+umip' if args.la57 else 'qemu64,+smap,+smep,+umip'] else: - qemu_args += ['-enable-kvm', '-cpu', 'host,+la57' if args.la57 else 'host'] + qemu_args += ['-enable-kvm', '-cpu', 'host,+la57,+smap,+smep,+umip' if args.la57 else 'host,+smap,+smep,+umip'] else: - qemu_args += ["-cpu", "qemu64,+la57" if args.la57 else "qemu64"] + qemu_args += ["-cpu", "qemu64,+la57,+smap,+smep,+umip" if args.la57 else "qemu64,+smap,+smep,+umip"] run_command(['qemu-system-x86_64', *qemu_args]) diff --git a/bootstrap.yml b/bootstrap.yml index 1aacef0a1ba..3aba7989d61 100644 --- a/bootstrap.yml +++ b/bootstrap.yml @@ -236,9 +236,9 @@ tools: # -g blows up the binary size. - 'CFLAGS=-pipe' compile: - - args: ['make', '-j@PARALLELISM@'] + - args: ['make', '-j@PARALLELISM@', 'all-binutils', 'all-gas', 'all-ld'] install: - - args: ['make', 'install'] + - args: ['make', 'install-binutils', 'install-gas', 'install-ld'] - name: host-gcc from_source: gcc diff --git a/src/aero_kernel/src/arch/x86_64/controlregs.rs b/src/aero_kernel/src/arch/x86_64/controlregs.rs index d8663b1bd86..5c3911f638d 100644 --- a/src/aero_kernel/src/arch/x86_64/controlregs.rs +++ b/src/aero_kernel/src/arch/x86_64/controlregs.rs @@ -17,6 +17,8 @@ * along with Aero. If not, see . */ +use core::sync::atomic::{AtomicUsize, Ordering}; + use crate::mem::paging::{PhysAddr, PhysFrame, VirtAddr}; bitflags::bitflags! { @@ -273,3 +275,34 @@ pub fn read_cr2() -> VirtAddr { VirtAddr::new(value) } } + +/// Returns true if Supervisor Mode Access Prevention (SMAP) is supported by the CPU and is enabled in Cr4. +#[inline] +pub fn smap_enabled() -> bool { + read_cr4().contains(Cr4Flags::SUPERVISOR_MODE_ACCESS_PREVENTION) +} + +#[inline] +pub fn with_userspace_access(f: F) -> R +where + F: FnOnce() -> R, +{ + static REF_COUNT: AtomicUsize = AtomicUsize::new(0); + if smap_enabled() { + unsafe { + asm!("stac"); + } + REF_COUNT.fetch_add(1, Ordering::Acquire); + }; + let r = f(); + if smap_enabled() { + REF_COUNT.fetch_sub(1, Ordering::Release); + + if REF_COUNT.load(Ordering::Relaxed) == 0 { + unsafe { + asm!("clac"); + } + } + }; + r +} diff --git a/src/aero_kernel/src/arch/x86_64/interrupts/exceptions.rs b/src/aero_kernel/src/arch/x86_64/interrupts/exceptions.rs index 0b8c16ce424..4eea3c55553 100644 --- a/src/aero_kernel/src/arch/x86_64/interrupts/exceptions.rs +++ b/src/aero_kernel/src/arch/x86_64/interrupts/exceptions.rs @@ -97,6 +97,23 @@ pub(super) fn page_fault(stack: &mut InterruptErrorStack) { log::error!("stack: {:#x?}", stack); }; + if reason.contains(PageFaultErrorCode::PROTECTION_VIOLATION) && !reason.contains(PageFaultErrorCode::USER_MODE) { + if controlregs::smap_enabled() { + unwind::prepare_panic(); + + log::error!("SMAP violation page fault"); + print_info(); + + controlregs::with_userspace_access(||unwind::unwind_stack_trace()); + + unsafe { + loop { + super::halt(); + } + } + } + } + if accessed_address < userland_last_address && scheduler::is_initialized() || stack.stack.iret.is_user() { @@ -130,7 +147,7 @@ pub(super) fn page_fault(stack: &mut InterruptErrorStack) { scheduler::get_scheduler().log_ptable(); } - unwind::unwind_stack_trace(); + controlregs::with_userspace_access(||unwind::unwind_stack_trace()); let task = scheduler::get_scheduler().current_task(); task.signal(aero_syscall::signal::SIGSEGV); @@ -147,7 +164,7 @@ pub(super) fn page_fault(stack: &mut InterruptErrorStack) { log::error!("Page fault"); print_info(); - unwind::unwind_stack_trace(); + controlregs::with_userspace_access(||unwind::unwind_stack_trace()); unsafe { loop { diff --git a/src/aero_kernel/src/arch/x86_64/mod.rs b/src/aero_kernel/src/arch/x86_64/mod.rs index d26b478b0d9..2fb8293ed3b 100644 --- a/src/aero_kernel/src/arch/x86_64/mod.rs +++ b/src/aero_kernel/src/arch/x86_64/mod.rs @@ -146,9 +146,6 @@ extern "C" fn x86_64_aero_main(boot_info: &'static StivaleStruct) -> ! { .unwrap() }); - // Initialize the CPU specific features. - init_cpu(); - // We initialize the COM ports before doing anything else. // // This will help printing panics and logs before or when the debug renderer @@ -156,6 +153,9 @@ extern "C" fn x86_64_aero_main(boot_info: &'static StivaleStruct) -> ! { drivers::uart_16550::init(); logger::init(); + // Initialize the CPU specific features. + init_cpu(); + // Parse the kernel command line. let command_line: &'static _ = boot_info.command_line().map_or("", |cmd| unsafe { let cmdline = PhysAddr::new(cmd.command_line).as_hhdm_virt(); @@ -251,13 +251,50 @@ pub fn init_cpu() { cr0.remove(controlregs::Cr0Flags::EMULATE_COPROCESSOR); cr0.insert(controlregs::Cr0Flags::MONITOR_COPROCESSOR); + cr0.insert(controlregs::Cr0Flags::WRITE_PROTECT); controlregs::write_cr0(cr0); } { + // Check if SMAP is supported. + let has_smap = CpuId::new() + .get_extended_feature_info() + .map_or(false, |i| i.has_smap()); + + // Check if SMEP is supported. + let has_smep = CpuId::new() + .get_extended_feature_info() + .map_or(false, |i| i.has_smep()); + + // Check if UMIP is supported. + let has_umip = CpuId::new() + .get_extended_feature_info() + .map_or(false, |i| i.has_umip()); + let mut cr4 = controlregs::read_cr4(); + if has_smap { + log::info!("SMAP is supported, enabling SMAP."); + cr4.insert(controlregs::Cr4Flags::SUPERVISOR_MODE_ACCESS_PREVENTION); + } else { + log::info!("SMAP is not supported."); + } + + if has_smep { + log::info!("SMEP is supported, enabling SMEP."); + cr4.insert(controlregs::Cr4Flags::SUPERVISOR_MODE_EXECUTION_PROTECTION); + } else { + log::info!("SMEP is not supported."); + } + + if has_umip { + log::info!("UMIP is supported, enabling UMIP."); + cr4.insert(controlregs::Cr4Flags::USER_MODE_INSTRUCTION_PREVENTION); + } else { + log::info!("UMIP is not supported."); + } + cr4.insert(controlregs::Cr4Flags::OSFXSR); cr4.insert(controlregs::Cr4Flags::OSXMMEXCPT_ENABLE); diff --git a/src/aero_kernel/src/arch/x86_64/signals.rs b/src/aero_kernel/src/arch/x86_64/signals.rs index 21ad2ccd4f4..620892da028 100644 --- a/src/aero_kernel/src/arch/x86_64/signals.rs +++ b/src/aero_kernel/src/arch/x86_64/signals.rs @@ -20,6 +20,7 @@ use crate::userland; use crate::userland::scheduler; use crate::utils::StackHelper; +use crate::arch::controlregs; use super::interrupts::InterruptStack; @@ -107,11 +108,11 @@ pub fn sigreturn(stack: &mut InterruptStack) -> usize { let current_task = scheduler::get_scheduler().current_task(); - current_task.signals().set_mask( + controlregs::with_userspace_access(||current_task.signals().set_mask( aero_syscall::signal::SigProcMask::Set, signal_frame.sigmask, None, - ); + )); writer.get_by(REDZONE_SIZE); diff --git a/src/aero_kernel/src/drivers/tty.rs b/src/aero_kernel/src/drivers/tty.rs index 77822adc1c3..bef794c5e6e 100644 --- a/src/aero_kernel/src/drivers/tty.rs +++ b/src/aero_kernel/src/drivers/tty.rs @@ -28,6 +28,7 @@ use crate::fs::inode; use crate::fs::inode::INodeInterface; use crate::mem::paging::VirtAddr; use crate::utils::sync::{BlockQueue, Mutex}; +use crate::arch::controlregs; use super::keyboard::KeyCode; use super::keyboard::KeyboardListener; @@ -234,11 +235,11 @@ impl INodeInterface for Tty { if buffer.len() > stdin.front_buffer.len() { for (i, c) in stdin.front_buffer.drain(..).enumerate() { - buffer[i] = c; + controlregs::with_userspace_access(||buffer[i] = c); } } else { for (i, c) in stdin.front_buffer.drain(..buffer.len()).enumerate() { - buffer[i] = c; + controlregs::with_userspace_access(||buffer[i] = c); } } @@ -264,13 +265,13 @@ impl INodeInterface for Tty { let (rows, cols) = crate::rendy::get_rows_cols(); - winsize.ws_row = rows as u16; - winsize.ws_col = cols as u16; + controlregs::with_userspace_access(||winsize.ws_row = rows as u16); + controlregs::with_userspace_access(||winsize.ws_col = cols as u16); let (xpixel, ypixel) = crate::rendy::get_resolution(); - winsize.ws_xpixel = xpixel as u16; - winsize.ws_ypixel = ypixel as u16; + controlregs::with_userspace_access(||winsize.ws_xpixel = xpixel as u16); + controlregs::with_userspace_access(||winsize.ws_ypixel = ypixel as u16); Ok(0x00) } @@ -282,7 +283,7 @@ impl INodeInterface for Tty { let lock = TERMIOS.lock_irq(); let this = &*lock; - *termios = this.clone(); + controlregs::with_userspace_access(||*termios = this.clone()); Ok(0x00) } @@ -299,7 +300,7 @@ impl INodeInterface for Tty { let mut lock = TERMIOS.lock_irq(); let this = &mut *lock; - *this = termios.clone(); + controlregs::with_userspace_access(||*this = termios.clone()); Ok(0x00) } diff --git a/src/aero_kernel/src/syscall/fs.rs b/src/aero_kernel/src/syscall/fs.rs index 229958ef6fe..28f77d07192 100644 --- a/src/aero_kernel/src/syscall/fs.rs +++ b/src/aero_kernel/src/syscall/fs.rs @@ -20,6 +20,8 @@ use aero_syscall::prelude::FdFlags; use aero_syscall::{AeroSyscallError, OpenFlags}; +use crate::arch::controlregs; + use crate::fs::inode::DirEntry; use crate::fs::pipe::Pipe; use crate::fs::{self, lookup_path, LookupMode}; @@ -39,8 +41,8 @@ pub fn write(fd: usize, buffer: usize, size: usize) -> Result Result Result Result Result Result { @@ -136,7 +138,7 @@ pub fn close(fd: usize) -> Result { } pub fn chdir(path: usize, size: usize) -> Result { - let buffer = validate_str(path as *mut u8, size).ok_or(AeroSyscallError::EINVAL)?; + let buffer = controlregs::with_userspace_access(||validate_str(path as *mut u8, size).ok_or(AeroSyscallError::EINVAL))?; let inode = fs::lookup_path(Path::new(buffer))?; if !inode.inode().metadata()?.is_directory() { @@ -149,7 +151,7 @@ pub fn chdir(path: usize, size: usize) -> Result { } pub fn mkdirat(dfd: usize, path: usize, size: usize) -> Result { - let path_str = validate_str(path as *mut u8, size).ok_or(AeroSyscallError::EINVAL)?; + let path_str = controlregs::with_userspace_access(||validate_str(path as *mut u8, size).ok_or(AeroSyscallError::EINVAL))?; let path = Path::new(path_str); // NOTE: If the pathname given in pathname is relative, then it is interpreted @@ -197,7 +199,7 @@ pub fn mkdir(path: usize, size: usize) -> Result { } pub fn rmdir(path: usize, size: usize) -> Result { - let path_str = validate_str(path as *mut u8, size).ok_or(AeroSyscallError::EINVAL)?; + let path_str = controlregs::with_userspace_access(||validate_str(path as *mut u8, size).ok_or(AeroSyscallError::EINVAL))?; let path = Path::new(path_str); let (_, child) = path.parent_and_basename(); @@ -224,7 +226,7 @@ pub fn getcwd(buffer: usize, size: usize) -> Result { let buffer = validate_slice_mut(buffer as *mut u8, size).ok_or(AeroSyscallError::EINVAL)?; let cwd = scheduler::get_scheduler().current_task().get_cwd(); - buffer[..cwd.len()].copy_from_slice(cwd.as_bytes()); + controlregs::with_userspace_access(||buffer[..cwd.len()].copy_from_slice(cwd.as_bytes())); Ok(cwd.len()) } @@ -275,8 +277,8 @@ pub fn pipe(fds: usize, flags: usize) -> Result { Ok(fd2) => fd2, }; - fds[0] = fd1; - fds[1] = fd2; + controlregs::with_userspace_access(||fds[0] = fd1); + controlregs::with_userspace_access(||fds[1] = fd2); Ok(0x00) } @@ -287,7 +289,7 @@ pub fn unlink( path_size: usize, flags: usize, ) -> Result { - let path_str = validate_str(path as *mut u8, path_size).ok_or(AeroSyscallError::EINVAL)?; + let path_str = controlregs::with_userspace_access(||validate_str(path as *mut u8, path_size).ok_or(AeroSyscallError::EINVAL))?; let path = Path::new(path_str); // TODO: Make use of the open flags. @@ -319,7 +321,7 @@ pub fn access( _mode: usize, _flags: usize, ) -> Result { - let path_str = validate_str(path as *mut u8, path_size).ok_or(AeroSyscallError::EINVAL)?; + let path_str = controlregs::with_userspace_access(||validate_str(path as *mut u8, path_size).ok_or(AeroSyscallError::EINVAL))?; let path = Path::new(path_str); if fd as isize == aero_syscall::AT_FDCWD { diff --git a/src/aero_kernel/src/syscall/ipc.rs b/src/aero_kernel/src/syscall/ipc.rs index ea0f76f98cc..860a61f8e4a 100644 --- a/src/aero_kernel/src/syscall/ipc.rs +++ b/src/aero_kernel/src/syscall/ipc.rs @@ -17,6 +17,7 @@ * along with Aero. If not, see . */ +use crate::arch::controlregs; use crate::userland::scheduler::get_scheduler; use crate::userland::task::TaskId; @@ -60,11 +61,10 @@ fn handle_recieve( let output = validate_slice_mut(message_ptr as *mut u8, message_size).ok_or(AeroSyscallError::EINVAL)?; - output[0..msg.data.len()].copy_from_slice(&msg.data); - - unsafe { + controlregs::with_userspace_access(||unsafe { + output[0..msg.data.len()].copy_from_slice(&msg.data); pid_ptr.write(msg.from); - } + }); Ok(msg.data.len()) } @@ -82,9 +82,11 @@ pub fn send(pid: usize, message: usize, message_size: usize) -> Result Result { let init_bytes = init.as_bytes(); let len = init.len(); - fixed[..len].copy_from_slice(init_bytes) + controlregs::with_userspace_access(||fixed[..len].copy_from_slice(init_bytes)) } // TODO: Safety checks! @@ -104,10 +106,10 @@ pub fn exec( envs: usize, envc: usize, ) -> Result { - let path = validate_str(path as *const u8, path_size).ok_or(AeroSyscallError::EINVAL)?; + let path = controlregs::with_userspace_access(||validate_str(path as *const u8, path_size).ok_or(AeroSyscallError::EINVAL))?; let path = Path::new(path); - let executable = fs::lookup_path(path)?; + let executable = controlregs::with_userspace_access(||fs::lookup_path(path))?; if executable.inode().metadata()?.is_directory() { return Err(AeroSyscallError::EISDIR); @@ -135,7 +137,7 @@ pub fn exec( } pub fn log(msg_start: usize, msg_size: usize) -> Result { - let message = validate_str(msg_start as *const u8, msg_size).ok_or(AeroSyscallError::EINVAL)?; + let message = controlregs::with_userspace_access(||validate_str(msg_start as *const u8, msg_size).ok_or(AeroSyscallError::EINVAL))?; log::debug!("{}", message); Ok(0x00) @@ -145,7 +147,7 @@ pub fn waitpid(pid: usize, status: usize, _flags: usize) -> Result Result if bytes.len() > slice.len() { Err(AeroSyscallError::ENAMETOOLONG) } else { - slice[0..bytes.len()].copy_from_slice(bytes); + controlregs::with_userspace_access(||slice[0..bytes.len()].copy_from_slice(bytes)); Ok(bytes.len()) } @@ -225,7 +227,7 @@ pub fn info(struc: usize) -> Result { let struc = unsafe { &mut *(struc as *mut aero_syscall::SysInfo) }; // TODO: Fill in the rest of the struct. - struc.uptime = crate::time::get_uptime_ticks() as i64; + controlregs::with_userspace_access(||struc.uptime = crate::time::get_uptime_ticks() as i64); Ok(0x00) } @@ -235,7 +237,7 @@ pub fn sethostname(ptr: usize, length: usize) -> Result match core::str::from_utf8(slice) { Ok(new_hostname) => { - *hostname().lock() = String::from(new_hostname); + controlregs::with_userspace_access(||*hostname().lock() = String::from(new_hostname)); Ok(0) } Err(_) => Err(AeroSyscallError::EINVAL), @@ -259,7 +261,7 @@ pub fn sigaction( }; let entry = if let Some(new) = new { - Some(SignalEntry::from_sigaction(*new, sigreturn)?) + Some(controlregs::with_userspace_access(||SignalEntry::from_sigaction(*new, sigreturn))?) } else { None }; @@ -278,7 +280,7 @@ pub fn sigaction( let task = scheduler.current_task(); let signals = task.signals(); - signals.set_signal(sig, entry, old); + controlregs::with_userspace_access(||signals.set_signal(sig, entry, old)); Ok(0) } diff --git a/src/aero_kernel/src/syscall/time.rs b/src/aero_kernel/src/syscall/time.rs index ab38eccc441..34707528fdc 100644 --- a/src/aero_kernel/src/syscall/time.rs +++ b/src/aero_kernel/src/syscall/time.rs @@ -19,6 +19,7 @@ use aero_syscall::AeroSyscallError; +use crate::arch::controlregs; use crate::utils::CeilDiv; use crate::{mem::paging::VirtAddr, userland::scheduler}; @@ -28,7 +29,7 @@ const CLOCK_TYPE_MONOTONIC: usize = 1; pub fn sleep(timespec: usize) -> Result { let timespec = VirtAddr::new(timespec as u64); let timespec = unsafe { &*(timespec.as_mut_ptr::()) }; - let duration = (timespec.tv_nsec as usize).ceil_div(1000000000) + timespec.tv_sec as usize; + let duration = controlregs::with_userspace_access(||(timespec.tv_nsec as usize).ceil_div(1000000000) + timespec.tv_sec as usize); scheduler::get_scheduler().inner.sleep(Some(duration))?; @@ -43,8 +44,8 @@ pub fn gettime(clock: usize, timespec: usize) -> Result CLOCK_TYPE_REALTIME => { let clock = crate::time::get_realtime_clock(); - timespec.tv_sec = clock.tv_sec; - timespec.tv_nsec = clock.tv_nsec; + controlregs::with_userspace_access(||timespec.tv_sec = clock.tv_sec); + controlregs::with_userspace_access(||timespec.tv_nsec = clock.tv_nsec); Ok(0x00) } diff --git a/src/aero_kernel/src/unwind.rs b/src/aero_kernel/src/unwind.rs index 10a7b13205a..531c1df9858 100644 --- a/src/aero_kernel/src/unwind.rs +++ b/src/aero_kernel/src/unwind.rs @@ -32,6 +32,7 @@ use crate::logger; use crate::rendy; use crate::arch::interrupts; +use crate::arch::controlregs; static PANIC_HOOK_READY: AtomicBool = AtomicBool::new(false); @@ -186,7 +187,7 @@ extern "C" fn rust_begin_unwind(info: &PanicInfo) -> ! { // Add a new line to make the stack trace more readable. log::error!(""); - unwind_stack_trace(); + controlregs::with_userspace_access(||unwind_stack_trace()); #[cfg(feature = "ci")] emu::exit_qemu(emu::ExitStatus::Success); diff --git a/src/aero_kernel/src/userland/signals.rs b/src/aero_kernel/src/userland/signals.rs index 7cefd53d4c7..b97684411c7 100644 --- a/src/aero_kernel/src/userland/signals.rs +++ b/src/aero_kernel/src/userland/signals.rs @@ -29,6 +29,7 @@ use bit_field::BitField; use aero_syscall::AeroSyscallError; use super::scheduler; +use crate::arch::controlregs; use crate::fs::FileSystemError; use crate::utils::sync::{Mutex, MutexGuard}; @@ -391,7 +392,7 @@ impl Signals { *old = self.blocked_mask.load(Ordering::SeqCst); } - let set = set & !IMMUTABLE_MASK; + let set = controlregs::with_userspace_access(||set & !IMMUTABLE_MASK); match how { SigProcMask::Block => { diff --git a/src/aero_kernel/src/userland/task.rs b/src/aero_kernel/src/userland/task.rs index 255d823968b..0c0826eaa35 100644 --- a/src/aero_kernel/src/userland/task.rs +++ b/src/aero_kernel/src/userland/task.rs @@ -30,6 +30,7 @@ use crate::fs::{self, FileSystem}; use crate::mem::paging::*; use crate::arch::task::ArchTask; +use crate::arch::controlregs; use crate::fs::file_table::FileTable; use crate::syscall::{ExecArgs, MessageQueue}; use crate::utils::sync::{BlockQueue, Mutex}; @@ -154,9 +155,9 @@ impl Zombies { // WIFEXITED: The child process has been terminated normally by // either calling sys_exit() or returning from the main function. - *status = 0x200; + controlregs::with_userspace_access(||*status = 0x200); // The lower 8-bits are used to store the exit status. - *status |= st as u32 & 0xff; + controlregs::with_userspace_access(||*status |= st as u32 & 0xff); Ok(tid.as_usize()) } @@ -399,7 +400,7 @@ impl Task { // Clear the signals that are pending for this task on exec. self.signals().clear(); - self.arch_task_mut().exec(vm, executable, argv, envv) + controlregs::with_userspace_access(||self.arch_task_mut().exec(vm, executable, argv, envv)) } pub fn vm(&self) -> &Arc { diff --git a/src/aero_kernel/src/userland/vm.rs b/src/aero_kernel/src/userland/vm.rs index e97a78180bc..bb72b916c1a 100644 --- a/src/aero_kernel/src/userland/vm.rs +++ b/src/aero_kernel/src/userland/vm.rs @@ -27,6 +27,7 @@ use xmas_elf::header::*; use xmas_elf::program::*; use xmas_elf::*; +use crate::arch::controlregs; use crate::arch::task::userland_last_address; use crate::fs; use crate::fs::cache::DirCacheItem; @@ -385,7 +386,7 @@ impl Mapping { let new_slice = new_frame.as_slice_mut::(); // Copy the contents from the old frame to the newly allocated frame. - new_slice.copy_from_slice(old_slice); + controlregs::with_userspace_access(||new_slice.copy_from_slice(old_slice)); // Re-map the page to the newly allocated frame and with the provided // protection flags. diff --git a/userland/apps/aero_shell/src/main.rs b/userland/apps/aero_shell/src/main.rs index 75905eece31..fb24259aa81 100644 --- a/userland/apps/aero_shell/src/main.rs +++ b/userland/apps/aero_shell/src/main.rs @@ -144,7 +144,7 @@ fn repl(history: &mut Vec) -> Result<(), AeroSyscallError> { } "uptime" => { - print!("{}", get_uptime()?); + println!("{}", get_uptime()?); } "sleep" => {