Implement a guard page for the kernel stack

This commit is contained in:
Chen Chengjun
2023-11-09 15:13:39 +08:00
committed by Tate, Hongliang Tian
parent c776954dfc
commit 5cc9f250dd
2 changed files with 57 additions and 10 deletions

View File

@ -1,7 +1,9 @@
use crate::arch::mm::PageTableFlags;
use crate::config::{KERNEL_STACK_SIZE, PAGE_SIZE}; use crate::config::{KERNEL_STACK_SIZE, PAGE_SIZE};
use crate::cpu::CpuSet; use crate::cpu::CpuSet;
use crate::prelude::*; use crate::prelude::*;
use crate::user::UserSpace; use crate::user::UserSpace;
use crate::vm::page_table::KERNEL_PAGE_TABLE;
use crate::vm::{VmAllocOptions, VmSegment}; use crate::vm::{VmAllocOptions, VmSegment};
use spin::{Mutex, MutexGuard}; use spin::{Mutex, MutexGuard};
@ -39,6 +41,7 @@ extern "C" {
pub struct KernelStack { pub struct KernelStack {
segment: VmSegment, segment: VmSegment,
old_guard_page_flag: Option<PageTableFlags>,
} }
impl KernelStack { impl KernelStack {
@ -47,12 +50,49 @@ impl KernelStack {
segment: VmAllocOptions::new(KERNEL_STACK_SIZE / PAGE_SIZE) segment: VmAllocOptions::new(KERNEL_STACK_SIZE / PAGE_SIZE)
.is_contiguous(true) .is_contiguous(true)
.alloc_contiguous()?, .alloc_contiguous()?,
old_guard_page_flag: None,
})
}
/// Generate a kernel stack with a guard page.
/// An additional page is allocated and be regarded as a guard page, which should not be accessed.
pub fn new_with_guard_page() -> Result<Self> {
let stack_segment = VmAllocOptions::new(KERNEL_STACK_SIZE / PAGE_SIZE + 1)
.is_contiguous(true)
.alloc_contiguous()?;
let unpresent_flag = PageTableFlags::empty();
let old_guard_page_flag = Self::protect_guard_page(&stack_segment, unpresent_flag);
Ok(Self {
segment: stack_segment,
old_guard_page_flag: Some(old_guard_page_flag),
}) })
} }
pub fn end_paddr(&self) -> Paddr { pub fn end_paddr(&self) -> Paddr {
self.segment.end_paddr() self.segment.end_paddr()
} }
pub fn has_guard_page(&self) -> bool {
self.old_guard_page_flag.is_some()
}
fn protect_guard_page(stack_segment: &VmSegment, flags: PageTableFlags) -> PageTableFlags {
let mut kernel_pt = KERNEL_PAGE_TABLE.get().unwrap().lock();
let guard_page_vaddr = {
let guard_page_paddr = stack_segment.start_paddr();
crate::vm::paddr_to_vaddr(guard_page_paddr)
};
// Safety: The protected address must be the address of guard page hence it should be safe and valid.
unsafe { kernel_pt.protect(guard_page_vaddr, flags).unwrap() }
}
}
impl Drop for KernelStack {
fn drop(&mut self) {
if self.has_guard_page() {
Self::protect_guard_page(&self.segment, self.old_guard_page_flag.unwrap());
}
}
} }
/// A task that executes a function to the end. /// A task that executes a function to the end.
@ -225,7 +265,7 @@ impl TaskOptions {
ctx: TaskContext::default(), ctx: TaskContext::default(),
}), }),
exit_code: 0, exit_code: 0,
kstack: KernelStack::new()?, kstack: KernelStack::new_with_guard_page()?,
link: LinkedListAtomicLink::new(), link: LinkedListAtomicLink::new(),
priority: self.priority, priority: self.priority,
cpu_affinity: self.cpu_affinity, cpu_affinity: self.cpu_affinity,
@ -262,7 +302,7 @@ impl TaskOptions {
ctx: TaskContext::default(), ctx: TaskContext::default(),
}), }),
exit_code: 0, exit_code: 0,
kstack: KernelStack::new()?, kstack: KernelStack::new_with_guard_page()?,
link: LinkedListAtomicLink::new(), link: LinkedListAtomicLink::new(),
priority: self.priority, priority: self.priority,
cpu_affinity: self.cpu_affinity, cpu_affinity: self.cpu_affinity,

View File

@ -156,7 +156,7 @@ impl<T: PageTableEntryTrait> PageTable<T, UserMode> {
unsafe { self.do_unmap(vaddr) } unsafe { self.do_unmap(vaddr) }
} }
pub fn protect(&mut self, vaddr: Vaddr, flags: T::F) -> Result<(), PageTableError> { pub fn protect(&mut self, vaddr: Vaddr, flags: T::F) -> Result<T::F, PageTableError> {
if is_kernel_vaddr(vaddr) { if is_kernel_vaddr(vaddr) {
return Err(PageTableError::InvalidVaddr); return Err(PageTableError::InvalidVaddr);
} }
@ -201,12 +201,13 @@ impl<T: PageTableEntryTrait> PageTable<T, KernelMode> {
/// Modify the flags mapped at `vaddr`. The `vaddr` should not be at the low address /// Modify the flags mapped at `vaddr`. The `vaddr` should not be at the low address
/// (memory belonging to the user mode program). /// (memory belonging to the user mode program).
/// If the modification succeeds, it will return the old flags of `vaddr`.
/// ///
/// # Safety /// # Safety
/// ///
/// Modifying kernel mappings is considered unsafe, and incorrect operation may cause crashes. /// Modifying kernel mappings is considered unsafe, and incorrect operation may cause crashes.
/// User must take care of the consequences when using this API. /// User must take care of the consequences when using this API.
pub unsafe fn protect(&mut self, vaddr: Vaddr, flags: T::F) -> Result<(), PageTableError> { pub unsafe fn protect(&mut self, vaddr: Vaddr, flags: T::F) -> Result<T::F, PageTableError> {
if is_user_vaddr(vaddr) { if is_user_vaddr(vaddr) {
return Err(PageTableError::InvalidVaddr); return Err(PageTableError::InvalidVaddr);
} }
@ -319,22 +320,28 @@ impl<T: PageTableEntryTrait, M> PageTable<T, M> {
Ok(()) Ok(())
} }
/// Modify the flags mapped at `vaddr` /// Modify the flags mapped at `vaddr`.
/// If the modification succeeds, it will return the old flags of `vaddr`.
/// ///
/// # Safety /// # Safety
/// ///
/// This function allows arbitrary modifications to the page table. /// This function allows arbitrary modifications to the page table.
/// Incorrect modifications may cause the kernel to crash /// Incorrect modifications may cause the kernel to crash
/// (e.g., make the linear mapping visible to the user mode applications.). /// (e.g., make the linear mapping visible to the user mode applications.).
unsafe fn do_protect(&mut self, vaddr: Vaddr, flags: T::F) -> Result<(), PageTableError> { unsafe fn do_protect(&mut self, vaddr: Vaddr, new_flags: T::F) -> Result<T::F, PageTableError> {
let last_entry = self.page_walk(vaddr, false).unwrap(); let last_entry = self.page_walk(vaddr, false).unwrap();
trace!("Page Table: Protect vaddr:{:x?}, flags:{:x?}", vaddr, flags); let old_flags = last_entry.flags();
if last_entry.is_unused() || !last_entry.flags().is_present() { trace!(
"Page Table: Protect vaddr:{:x?}, flags:{:x?}",
vaddr,
new_flags
);
if last_entry.is_unused() || !old_flags.is_present() {
return Err(PageTableError::InvalidModification); return Err(PageTableError::InvalidModification);
} }
last_entry.update(last_entry.paddr(), flags); last_entry.update(last_entry.paddr(), new_flags);
tlb_flush(vaddr); tlb_flush(vaddr);
Ok(()) Ok(old_flags)
} }
pub fn root_paddr(&self) -> Paddr { pub fn root_paddr(&self) -> Paddr {