瀏覽代碼

feat: debug kernel stack (#1176)

* feat: debug kernel stack

Signed-off-by: Godones <chenlinfeng25@outlook.com>

* fix: Create guard pages for the kstack through remap

Signed-off-by: Godones <chenlinfeng25@outlook.com>

* feat(arch/x86_64): 启用内核态写保护功能

添加enable_kernel_wp函数来设置CR0的WP位,防止内核错误写入只读页面

Signed-off-by: longjin <longjin@DragonOS.org>

* fix(x86_64/mm): 在内核地址错误处理中添加RIP寄存器信息

Signed-off-by: longjin <longjin@DragonOS.org>

* fix: Fixed the error introduced by enabling WP flag on x86

Restore accidentally deleted functions.

Signed-off-by: Godones <chenlinfeng25@outlook.com>

* refactor: 移除kstack_protect默认特性并优化内存管理

- 从default特性中移除kstack_protect
- 为X86_64MMBootstrapInfo添加kernel_rodata_start字段
- 调整内核页标志对rodata区域的判断逻辑

Signed-off-by: longjin <longjin@DragonOS.org>

* fix(mm): 解决加载二进制文件到用户空间的时候,忘记关闭wp的问题

Signed-off-by: longjin <longjin@DragonOS.org>

* fix

Signed-off-by: longjin <longjin@DragonOS.org>

---------

Signed-off-by: Godones <chenlinfeng25@outlook.com>
Signed-off-by: longjin <longjin@DragonOS.org>
Co-authored-by: longjin <longjin@DragonOS.org>
linfeng 4 天之前
父節點
當前提交
799e573259

+ 6 - 1
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时,使用下面的依赖

+ 4 - 0
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() {}
 }
 
 /// 获取内核地址默认的页面标志

+ 4 - 0
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<MMArch>; 16] = protection_map();
+
+    fn enable_kernel_wp() {}
+
+    fn disable_kernel_wp() {}
 }
 
 const fn protection_map() -> [EntryFlags<MMArch>; 16] {

+ 22 - 3
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
     }

+ 32 - 4
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<Option<BuddyAllocator<MMArch>>> = 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<A: MemoryManagementArch>(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 {

+ 1 - 0
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;
                     }

+ 7 - 1
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<BinaryLoaderResult, ExecError> {
         // log::debug!("loading elf interp");
+        // defer!({
+        //     log::debug!("load_elf_interp done");
+        // });
         let mut head_buf = [0u8; 512];
         interp_elf_ex
             .file_mut()

+ 28 - 43
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<MMArch>,
-    ) -> 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<MMArch>,
+    ) -> 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);

+ 8 - 0
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 虚拟地址范围

+ 1 - 1
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;

+ 171 - 18
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<AlignedBox<[u8; KernelStack::SIZE], { KernelStack::ALIGN }>>,
     /// 标记该内核栈是否可以被释放
-    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<Self, SystemError> {
-        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<VirtAddr> {
+        match self.ty {
+            KernelStackType::KernelSpace(kstack_virt_addr, _) => {
+                return Some(kstack_virt_addr);
+            }
+            _ => {
+                // 静态内核栈和动态内核栈没有guard page
+                return None;
+            }
+        }
+    }
+
+    pub fn guard_page_size(&self) -> Option<usize> {
+        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 => {}
         }
     }
 }

+ 9 - 1
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<usize, SystemErro
 
 pub unsafe fn copy_to_user(dest: VirtAddr, src: &[u8]) -> Result<usize, SystemError> {
     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;
     // 拷贝数据