diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 0b8fe0bf..1b7c4152 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -25,6 +25,9 @@ driver_ps2_mouse = [] kprobe_test = [] static_keys_test = [] +# kstack_protect 开启该功能后,会开启内核栈保护功能。用于辅助检测栈溢出。(内核栈占用会*2) +kstack_protect = [] + # 运行时依赖项 [dependencies] acpi = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/acpi-rs.git", rev = "282df2af7b" } @@ -79,7 +82,9 @@ static-keys = { version = "=0.7" } defer = "0.2.1" cfg-if = { version = "1.0.0" } -derive_builder = { version = "0.20.2", default-features = false, features = ["alloc"] } +derive_builder = { version = "0.20.2", default-features = false, features = [ + "alloc", +] } # target为x86_64时,使用下面的依赖 diff --git a/kernel/src/arch/loongarch64/mm/mod.rs b/kernel/src/arch/loongarch64/mm/mod.rs index 48b687ea..fbc53ab0 100644 --- a/kernel/src/arch/loongarch64/mm/mod.rs +++ b/kernel/src/arch/loongarch64/mm/mod.rs @@ -137,6 +137,10 @@ impl MemoryManagementArch for LoongArch64MMArch { fn make_entry(paddr: crate::mm::PhysAddr, page_flags: usize) -> usize { todo!() } + + fn enable_kernel_wp() {} + + fn disable_kernel_wp() {} } /// 获取内核地址默认的页面标志 diff --git a/kernel/src/arch/riscv64/mm/mod.rs b/kernel/src/arch/riscv64/mm/mod.rs index 1970d3d6..28ecaca1 100644 --- a/kernel/src/arch/riscv64/mm/mod.rs +++ b/kernel/src/arch/riscv64/mm/mod.rs @@ -284,6 +284,10 @@ impl MemoryManagementArch for RiscV64MMArch { const PAGE_READONLY_EXEC: usize = 0; const PROTECTION_MAP: [EntryFlags; 16] = protection_map(); + + fn enable_kernel_wp() {} + + fn disable_kernel_wp() {} } const fn protection_map() -> [EntryFlags; 16] { diff --git a/kernel/src/arch/x86_64/mm/fault.rs b/kernel/src/arch/x86_64/mm/fault.rs index 3122508a..27d54796 100644 --- a/kernel/src/arch/x86_64/mm/fault.rs +++ b/kernel/src/arch/x86_64/mm/fault.rs @@ -149,18 +149,37 @@ impl X86_64MMArch { /// - `error_code`: 错误标志 /// - `address`: 发生缺页异常的虚拟地址 pub fn do_kern_addr_fault( - _regs: &'static TrapFrame, + regs: &'static TrapFrame, error_code: X86PfErrorCode, address: VirtAddr, ) { + unsafe { crate::debug::traceback::lookup_kallsyms(regs.rip, 0xff) }; + let pcb = crate::process::ProcessManager::current_pcb(); + let kstack_guard_addr = pcb.kernel_stack().guard_page_address(); + if let Some(guard_page) = kstack_guard_addr { + let guard_page_size = pcb.kernel_stack().guard_page_size().unwrap(); + if address.data() >= guard_page.data() + && address.data() < guard_page.data() + guard_page_size + { + // 发生在内核栈保护页上 + error!( + "kernel stack guard page fault at {:#x}, guard page range: {:#x} - {:#x}", + address.data(), + guard_page.data(), + guard_page.data() + guard_page_size + ); + } + } panic!( "do_kern_addr_fault has not yet been implemented, - fault address: {:#x}, + fault address: {:#x}, + rip: {:#x}, error_code: {:#b}, pid: {}\n", address.data(), + regs.rip, error_code, - crate::process::ProcessManager::current_pid().data() + pcb.pid().data() ); //TODO https://code.dragonos.org.cn/xref/linux-6.6.21/arch/x86/mm/fault.c#do_kern_addr_fault } diff --git a/kernel/src/arch/x86_64/mm/mod.rs b/kernel/src/arch/x86_64/mm/mod.rs index dc1ee339..d5b9e8d5 100644 --- a/kernel/src/arch/x86_64/mm/mod.rs +++ b/kernel/src/arch/x86_64/mm/mod.rs @@ -42,12 +42,14 @@ static mut INITIAL_CR3_VALUE: PhysAddr = PhysAddr::new(0); static INNER_ALLOCATOR: SpinLock>> = SpinLock::new(None); +#[allow(dead_code)] #[derive(Clone, Copy, Debug)] pub struct X86_64MMBootstrapInfo { kernel_load_base_paddr: usize, kernel_code_start: usize, kernel_code_end: usize, kernel_data_end: usize, + kernel_rodata_start: usize, kernel_rodata_end: usize, start_brk: usize, } @@ -134,6 +136,7 @@ impl MemoryManagementArch for X86_64MMArch { fn _text(); fn _etext(); fn _edata(); + fn _rodata(); fn _erodata(); fn _end(); fn _default_kernel_load_base(); @@ -146,6 +149,7 @@ impl MemoryManagementArch for X86_64MMArch { kernel_code_start: _text as usize, kernel_code_end: _etext as usize, kernel_data_end: _edata as usize, + kernel_rodata_start: _rodata as usize, kernel_rodata_end: _erodata as usize, start_brk: _end as usize, }; @@ -158,15 +162,14 @@ impl MemoryManagementArch for X86_64MMArch { boot_callbacks() .early_init_memory_blocks() .expect("init memory area failed"); - - debug!("bootstrap info: {:?}", unsafe { BOOTSTRAP_MM_INFO }); + debug!("bootstrap info: {:#x?}", unsafe { BOOTSTRAP_MM_INFO }); debug!("phys[0]=virt[0x{:x}]", unsafe { MMArch::phys_2_virt(PhysAddr::new(0)).unwrap().data() }); // 初始化内存管理器 unsafe { allocator_init() }; - + Self::enable_kernel_wp(); send_to_default_serial8250_port("x86 64 mm init done\n\0".as_bytes()); } @@ -366,10 +369,35 @@ impl MemoryManagementArch for X86_64MMArch { const PAGE_WRITE: usize = 0; const PAGE_WRITE_EXEC: usize = 0; const PAGE_EXEC: usize = 0; + + /// 启用 内核态的 Write Protect + /// 这样即使在内核态,CPU也会检查页面的写保护位 + /// 防止内核错误地写入只读页面 + fn enable_kernel_wp() { + unsafe { + use x86::controlregs::{cr0, cr0_write, Cr0}; + let mut cr0_val = cr0(); + cr0_val.insert(Cr0::CR0_WRITE_PROTECT); + cr0_write(cr0_val); + // log::debug!("CR0.WP bit enabled for kernel write protection"); + } + } + + /// 禁用 内核态的 Write Protect + fn disable_kernel_wp() { + unsafe { + use x86::controlregs::{cr0, cr0_write, Cr0}; + let mut cr0_val = cr0(); + cr0_val.remove(Cr0::CR0_WRITE_PROTECT); + cr0_write(cr0_val); + // log::debug!("CR0.WP bit disabled for kernel write protection"); + } + } } /// 获取保护标志的映射表 /// +/// 参考: https://code.dragonos.org.cn/xref/linux-6.6.21/arch/x86/mm/pgprot.c#8 /// /// ## 返回值 /// - `[usize; 16]`: 长度为16的映射表 @@ -681,7 +709,7 @@ pub unsafe fn kernel_page_flags(virt: VirtAddr) -> Entr if virt.data() >= info.kernel_code_start && virt.data() < info.kernel_code_end { // Remap kernel code execute return EntryFlags::new().set_execute(true).set_write(true); - } else if virt.data() >= info.kernel_data_end && virt.data() < info.kernel_rodata_end { + } else if virt.data() >= info.kernel_rodata_start && virt.data() < info.kernel_rodata_end { // Remap kernel rodata read only return EntryFlags::new().set_execute(true); } else { diff --git a/kernel/src/init/cmdline.rs b/kernel/src/init/cmdline.rs index 0676e1df..87807eba 100644 --- a/kernel/src/init/cmdline.rs +++ b/kernel/src/init/cmdline.rs @@ -347,6 +347,7 @@ impl KernelCmdlineManager { log::warn!("cmdline: parameter {} is set twice", p.name); continue; } + p.value = Some(CString::new(value.unwrap()).unwrap()); p.initialized = true; } diff --git a/kernel/src/libs/elf.rs b/kernel/src/libs/elf.rs index 7ccbe6a5..045bb3a6 100644 --- a/kernel/src/libs/elf.rs +++ b/kernel/src/libs/elf.rs @@ -220,7 +220,10 @@ impl ElfLoader { map_flags: &MapFlags, total_size: usize, ) -> Result<(VirtAddr, bool), SystemError> { - // debug!("load_elf_segment: addr_to_map={:?}", addr_to_map); + // log::debug!("load_elf_segment: addr_to_map={:?}", addr_to_map); + // defer!({ + // log::debug!("load_elf_segment done"); + // }); // 映射位置的偏移量(页内偏移) let beginning_page_offset = Self::elf_page_offset(addr_to_map); @@ -343,6 +346,9 @@ impl ElfLoader { load_bias: usize, ) -> Result { // log::debug!("loading elf interp"); + // defer!({ + // log::debug!("load_elf_interp done"); + // }); let mut head_buf = [0u8; 512]; interp_elf_ex .file_mut() diff --git a/kernel/src/mm/mmio_buddy.rs b/kernel/src/mm/mmio_buddy.rs index ad0cd727..c954b5c1 100644 --- a/kernel/src/mm/mmio_buddy.rs +++ b/kernel/src/mm/mmio_buddy.rs @@ -653,50 +653,8 @@ impl MMIOSpaceGuard { /// 传入的物理地址【一定要是设备的物理地址】。 /// 如果物理地址是从内存分配器中分配的,那么会造成内存泄露。因为mmio_release的时候,只取消映射,不会释放内存。 pub unsafe fn map_phys(&self, paddr: PhysAddr, length: usize) -> Result<(), SystemError> { - if length > self.size { - return Err(SystemError::EINVAL); - } - - let check = self - .mapped - .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst); - if check.is_err() { - return Err(SystemError::EINVAL); - } - let flags = EntryFlags::mmio_flags(); - - let mut kernel_mapper = KernelMapper::lock(); - let r = kernel_mapper.map_phys_with_size(self.vaddr, paddr, length, flags, true); - return r; - } - - /// 将物理地址填写到虚拟地址空间中 - /// - /// ## Safety - /// - /// 传入的物理地址【一定要是设备的物理地址】。 - /// 如果物理地址是从内存分配器中分配的,那么会造成内存泄露。因为mmio_release的时候,只取消映射,不会释放内存。 - pub unsafe fn map_phys_with_flags( - &self, - paddr: PhysAddr, - length: usize, - flags: EntryFlags, - ) -> Result<(), SystemError> { - if length > self.size { - return Err(SystemError::EINVAL); - } - - let check = self - .mapped - .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst); - if check.is_err() { - return Err(SystemError::EINVAL); - } - - let mut kernel_mapper = KernelMapper::lock(); - let r = kernel_mapper.map_phys_with_size(self.vaddr, paddr, length, flags, true); - return r; + self.map_phys_with_flags(paddr, length, flags) } /// # map_any_phys - 将任意物理地址映射到虚拟地址 @@ -734,6 +692,33 @@ impl MMIOSpaceGuard { return Ok(vaddr); } + /// 将物理地址填写到虚拟地址空间中 + /// + /// ## Safety + /// + /// 传入的物理地址【一定要是设备的物理地址】。 + /// 如果物理地址是从内存分配器中分配的,那么会造成内存泄露。因为mmio_release的时候,只取消映射,不会释放内存。 + pub unsafe fn map_phys_with_flags( + &self, + paddr: PhysAddr, + length: usize, + flags: EntryFlags, + ) -> Result<(), SystemError> { + if length > self.size { + return Err(SystemError::EINVAL); + } + + let check = self + .mapped + .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst); + if check.is_err() { + return Err(SystemError::EINVAL); + } + + let mut kernel_mapper = KernelMapper::lock(); + kernel_mapper.map_phys_with_size(self.vaddr, paddr, length, flags, true) + } + /// 泄露一个MMIO space guard,不会释放映射的空间 pub unsafe fn leak(self) { core::mem::forget(self); diff --git a/kernel/src/mm/mod.rs b/kernel/src/mm/mod.rs index a088ed13..4df64ee0 100644 --- a/kernel/src/mm/mod.rs +++ b/kernel/src/mm/mod.rs @@ -694,6 +694,14 @@ pub trait MemoryManagementArch: Clone + Copy + Debug { } ret } + + /// 启用 内核态的 Write Protect + /// 这样即使在内核态,CPU也会检查页面的写保护位 + /// 防止内核错误地写入只读页面 + fn enable_kernel_wp(); + + /// 禁用 内核态的 Write Protect + fn disable_kernel_wp(); } /// @brief 虚拟地址范围 diff --git a/kernel/src/process/execve.rs b/kernel/src/process/execve.rs index 7d2e4ac2..eb0e6809 100644 --- a/kernel/src/process/execve.rs +++ b/kernel/src/process/execve.rs @@ -26,7 +26,7 @@ pub fn do_execve( } })?; - // debug!("load binary file done"); + // log::debug!("load binary file done"); // debug!("argv: {:?}, envp: {:?}", argv, envp); param.init_info_mut().args = argv; param.init_info_mut().envs = envp; diff --git a/kernel/src/process/mod.rs b/kernel/src/process/mod.rs index 88535663..5aa10643 100644 --- a/kernel/src/process/mod.rs +++ b/kernel/src/process/mod.rs @@ -54,7 +54,7 @@ use crate::{ percpu::{PerCpu, PerCpuVar}, set_IDLE_PROCESS_ADDRESS_SPACE, ucontext::AddressSpace, - VirtAddr, + PhysAddr, VirtAddr, }, namespaces::{mnt_namespace::FsStruct, pid_namespace::PidStrcut, NsProxy}, net::socket::SocketInode, @@ -1596,24 +1596,143 @@ impl ProcessSchedulerInfo { } } -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct KernelStack { stack: Option>, /// 标记该内核栈是否可以被释放 - can_be_freed: bool, + ty: KernelStackType, +} + +#[derive(Debug)] +pub enum KernelStackType { + KernelSpace(VirtAddr, PhysAddr), + Static, + Dynamic, +} + +// 为什么需要这个锁? +// alloc_from_kernel_space 使用该函数分配内核栈时,如果该函数被中断打断, +// 而切换的任务使用dealloc_from_kernel_space回收内核栈,对 +// KernelMapper的可变引用获取将会失败造成错误 +static KSTACK_LOCK: SpinLock<()> = SpinLock::new(()); + +unsafe fn alloc_from_kernel_space() -> (VirtAddr, PhysAddr) { + use crate::arch::MMArch; + use crate::mm::allocator::page_frame::{allocate_page_frames, PageFrameCount}; + use crate::mm::kernel_mapper::KernelMapper; + use crate::mm::page::EntryFlags; + use crate::mm::MemoryManagementArch; + + // Layout + // --------------- + // | KernelStack | + // | guard page | size == KernelStack::SIZE + // | KernelStack | + // | guard page | + // | .......... | + // --------------- + + let _guard = KSTACK_LOCK.try_lock_irqsave().unwrap(); + let need_size = KernelStack::SIZE * 2; + let page_num = PageFrameCount::new(need_size.div_ceil(MMArch::PAGE_SIZE).next_power_of_two()); + + let (paddr, _count) = allocate_page_frames(page_num).expect("kernel stack alloc failed"); + + let guard_vaddr = MMArch::phys_2_virt(paddr).unwrap(); + let _kstack_paddr = paddr + KernelStack::SIZE; + let kstack_vaddr = guard_vaddr + KernelStack::SIZE; + + core::ptr::write_bytes(kstack_vaddr.data() as *mut u8, 0, KernelStack::SIZE); + + let guard_flags = EntryFlags::new(); + + let mut kernel_mapper = KernelMapper::lock(); + let kernel_mapper = kernel_mapper.as_mut().unwrap(); + + for i in 0..KernelStack::SIZE / MMArch::PAGE_SIZE { + let guard_page_vaddr = guard_vaddr + i * MMArch::PAGE_SIZE; + // Map the guard page + let flusher = kernel_mapper.remap(guard_page_vaddr, guard_flags).unwrap(); + flusher.flush(); + } + + // unsafe { + // log::debug!( + // "trigger kernel stack guard page :{:#x}", + // (kstack_vaddr.data() - 8) + // ); + // let guard_ptr = (kstack_vaddr.data() - 8) as *mut usize; + // guard_ptr.write(0xfff); // Invalid + // } + + // log::info!( + // "[kernel stack alloc]: virt: {:#x}, phy: {:#x}", + // kstack_vaddr.data(), + // _kstack_paddr.data() + // ); + (guard_vaddr, paddr) +} + +unsafe fn dealloc_from_kernel_space(vaddr: VirtAddr, paddr: PhysAddr) { + use crate::arch::mm::kernel_page_flags; + use crate::arch::MMArch; + use crate::mm::allocator::page_frame::{deallocate_page_frames, PageFrameCount, PhysPageFrame}; + use crate::mm::kernel_mapper::KernelMapper; + use crate::mm::MemoryManagementArch; + + let _guard = KSTACK_LOCK.try_lock_irqsave().unwrap(); + + let need_size = KernelStack::SIZE * 2; + let page_num = PageFrameCount::new(need_size.div_ceil(MMArch::PAGE_SIZE).next_power_of_two()); + + // log::info!( + // "[kernel stack dealloc]: virt: {:#x}, phy: {:#x}", + // vaddr.data(), + // paddr.data() + // ); + + let mut kernel_mapper = KernelMapper::lock(); + let kernel_mapper = kernel_mapper.as_mut().unwrap(); + + // restore the guard page flags + for i in 0..KernelStack::SIZE / MMArch::PAGE_SIZE { + let guard_page_vaddr = vaddr + i * MMArch::PAGE_SIZE; + let flusher = kernel_mapper + .remap(guard_page_vaddr, kernel_page_flags(vaddr)) + .unwrap(); + flusher.flush(); + } + + // release the physical page + unsafe { deallocate_page_frames(PhysPageFrame::new(paddr), page_num) }; } impl KernelStack { - pub const SIZE: usize = 0x8000; - pub const ALIGN: usize = 0x8000; + pub const SIZE: usize = 0x4000; + pub const ALIGN: usize = 0x4000; pub fn new() -> Result { - return Ok(Self { - stack: Some( - AlignedBox::<[u8; KernelStack::SIZE], { KernelStack::ALIGN }>::new_zeroed()?, - ), - can_be_freed: true, - }); + if cfg!(feature = "kstack_protect") { + unsafe { + let (kstack_vaddr, kstack_paddr) = alloc_from_kernel_space(); + let real_kstack_vaddr = kstack_vaddr + KernelStack::SIZE; + Ok(Self { + stack: Some( + AlignedBox::<[u8; KernelStack::SIZE], { KernelStack::ALIGN }>::new_unchecked( + real_kstack_vaddr.data() as *mut [u8; KernelStack::SIZE], + ), + ), + ty: KernelStackType::KernelSpace(kstack_vaddr, kstack_paddr), + }) + } + } else { + Ok(Self { + stack: Some( + AlignedBox::<[u8; KernelStack::SIZE], { KernelStack::ALIGN }>::new_zeroed()?, + ), + ty: KernelStackType::Dynamic, + }) + } } /// 根据已有的空间,构造一个内核栈结构体 @@ -1624,14 +1743,38 @@ impl KernelStack { return Err(SystemError::EFAULT); } - return Ok(Self { + Ok(Self { stack: Some( AlignedBox::<[u8; KernelStack::SIZE], { KernelStack::ALIGN }>::new_unchecked( base.data() as *mut [u8; KernelStack::SIZE], ), ), - can_be_freed: false, - }); + ty: KernelStackType::Static, + }) + } + + pub fn guard_page_address(&self) -> Option { + match self.ty { + KernelStackType::KernelSpace(kstack_virt_addr, _) => { + return Some(kstack_virt_addr); + } + _ => { + // 静态内核栈和动态内核栈没有guard page + return None; + } + } + } + + pub fn guard_page_size(&self) -> Option { + match self.ty { + KernelStackType::KernelSpace(_, _) => { + return Some(KernelStack::SIZE); + } + _ => { + // 静态内核栈和动态内核栈没有guard page + return None; + } + } } /// 返回内核栈的起始虚拟地址(低地址) @@ -1708,10 +1851,20 @@ impl Drop for KernelStack { drop(pcb_ptr); } } - // 如果该内核栈不可以被释放,那么,这里就forget,不调用AlignedBox的drop函数 - if !self.can_be_freed { - let bx = self.stack.take(); - core::mem::forget(bx); + match self.ty { + KernelStackType::KernelSpace(kstack_virt_addr, kstack_phy_addr) => { + // 释放内核栈 + unsafe { + dealloc_from_kernel_space(kstack_virt_addr, kstack_phy_addr); + } + let bx = self.stack.take(); + core::mem::forget(bx); + } + KernelStackType::Static => { + let bx = self.stack.take(); + core::mem::forget(bx); + } + KernelStackType::Dynamic => {} } } } diff --git a/kernel/src/syscall/user_access.rs b/kernel/src/syscall/user_access.rs index 2850dfdc..36c3fe09 100644 --- a/kernel/src/syscall/user_access.rs +++ b/kernel/src/syscall/user_access.rs @@ -7,8 +7,12 @@ use core::{ }; use alloc::{ffi::CString, vec::Vec}; +use defer::defer; -use crate::mm::{verify_area, VirtAddr}; +use crate::{ + arch::MMArch, + mm::{verify_area, MemoryManagementArch, VirtAddr}, +}; use super::SystemError; @@ -37,6 +41,10 @@ pub unsafe fn clear_user(dest: VirtAddr, len: usize) -> Result Result { verify_area(dest, src.len()).map_err(|_| SystemError::EFAULT)?; + MMArch::disable_kernel_wp(); + defer!({ + MMArch::enable_kernel_wp(); + }); let p = dest.data() as *mut u8; // 拷贝数据