Skip to content

Commit 8107d09

Browse files
committed
bugfix for race demand-page access.
1 parent 3a57221 commit 8107d09

File tree

8 files changed

+148
-22
lines changed

8 files changed

+148
-22
lines changed

crate/memory/src/memory_set/handler/delay.rs

+12-2
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,20 @@ impl<T: FrameAllocator> MemoryHandler for Delay<T> {
4949
}
5050
}
5151

52-
fn handle_page_fault(&self, pt: &mut dyn PageTable, addr: VirtAddr) -> bool {
52+
fn handle_page_fault_ext(
53+
&self,
54+
pt: &mut dyn PageTable,
55+
addr: VirtAddr,
56+
access: super::AccessType,
57+
) -> bool {
5358
let entry = pt.get_entry(addr).expect("failed to get entry");
5459
if entry.present() {
55-
// not a delay case
60+
// permission check.
61+
if access.check_access(entry) {
62+
return true;
63+
}
64+
// permisison check failed.
65+
error!("Permission check failed at 0x{:x}.", addr);
5666
return false;
5767
}
5868
let frame = self.allocator.alloc().expect("failed to alloc frame");

crate/memory/src/memory_set/handler/file.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,24 @@ impl<F: Read, T: FrameAllocator> MemoryHandler for File<F, T> {
5858
}
5959
}
6060

61-
fn handle_page_fault(&self, pt: &mut dyn PageTable, addr: usize) -> bool {
61+
fn handle_page_fault_ext(
62+
&self,
63+
pt: &mut dyn PageTable,
64+
addr: usize,
65+
access: super::AccessType,
66+
) -> bool {
6267
let addr = addr & !(PAGE_SIZE - 1);
6368
let entry = pt.get_entry(addr).expect("failed to get entry");
6469
if entry.present() {
70+
// permission check.
71+
if access.check_access(entry) {
72+
return true;
73+
}
74+
// permisison check failed.
75+
error!(
76+
"Permission check failed at 0x{:x}, access = {:?}.",
77+
addr, access
78+
);
6579
return false;
6680
}
6781
let execute = entry.execute();

crate/memory/src/memory_set/handler/mod.rs

+55-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,45 @@
11
use super::*;
2-
2+
#[derive(Copy, Clone, Debug)]
3+
pub struct AccessType {
4+
pub write: bool,
5+
pub execute: bool,
6+
pub user: bool,
7+
}
8+
impl AccessType {
9+
pub fn unknown() -> Self {
10+
AccessType {
11+
write: true,
12+
execute: true,
13+
user: true,
14+
}
15+
}
16+
pub fn read(user: bool) -> Self {
17+
AccessType {
18+
write: false,
19+
execute: false,
20+
user,
21+
}
22+
}
23+
pub fn write(user: bool) -> Self {
24+
AccessType {
25+
write: true,
26+
execute: false,
27+
user,
28+
}
29+
}
30+
pub fn execute(user: bool) -> Self {
31+
AccessType {
32+
write: false,
33+
execute: true,
34+
user,
35+
}
36+
}
37+
pub fn check_access(self, entry: &dyn paging::Entry) -> bool {
38+
((!self.write) || entry.writable())
39+
&& ((!self.execute) || entry.execute())
40+
&& ((!self.user) || entry.user())
41+
}
42+
}
343
// here may be a interesting part for lab
444
pub trait MemoryHandler: Debug + Send + Sync + 'static {
545
fn box_clone(&self) -> Box<dyn MemoryHandler>;
@@ -22,7 +62,20 @@ pub trait MemoryHandler: Debug + Send + Sync + 'static {
2262

2363
/// Handle page fault on `addr`
2464
/// Return true if success, false if error
25-
fn handle_page_fault(&self, pt: &mut dyn PageTable, addr: VirtAddr) -> bool;
65+
fn handle_page_fault(&self, pt: &mut dyn PageTable, addr: VirtAddr) -> bool {
66+
self.handle_page_fault_ext(pt, addr, AccessType::unknown())
67+
}
68+
69+
/// Handle page fault on `addr` and access type `access`
70+
/// Return true if success (or should-retry), false if error
71+
fn handle_page_fault_ext(
72+
&self,
73+
pt: &mut dyn PageTable,
74+
addr: VirtAddr,
75+
_access: AccessType,
76+
) -> bool {
77+
self.handle_page_fault(pt, addr)
78+
}
2679
}
2780

2881
impl Clone for Box<dyn MemoryHandler> {

crate/memory/src/memory_set/handler/shared.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ impl<T: FrameAllocator> SharedGuard<T> {
3535
self.target.insert(virt_addr, phys_addr);
3636
Some(phys_addr)
3737
}
38-
38+
3939
pub fn dealloc(&mut self, virt_addr: usize) {
4040
let phys_addr = self.target.get(&virt_addr).unwrap().clone();
4141
self.allocator.dealloc(phys_addr);

crate/memory/src/memory_set/mod.rs

+9
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,15 @@ impl<T: PageTableExt> MemorySet<T> {
376376
&mut self.page_table
377377
}
378378

379+
pub fn handle_page_fault_ext(&mut self, addr: VirtAddr, access: handler::AccessType) -> bool {
380+
let area = self.areas.iter().find(|area| area.contains(addr));
381+
match area {
382+
Some(area) => area
383+
.handler
384+
.handle_page_fault_ext(&mut self.page_table, addr, access),
385+
None => false,
386+
}
387+
}
379388
pub fn handle_page_fault(&mut self, addr: VirtAddr) -> bool {
380389
let area = self.areas.iter().find(|area| area.contains(addr));
381390
match area {

kernel/src/arch/riscv/interrupt/consts.rs

+9
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,15 @@ pub fn is_page_fault(trap: usize) -> bool {
1414
trap == InstructionPageFault || trap == LoadPageFault || trap == StorePageFault
1515
}
1616

17+
pub fn is_execute_page_fault(trap: usize) -> bool {
18+
trap == InstructionPageFault
19+
}
20+
pub fn is_read_page_fault(trap: usize) -> bool {
21+
trap == LoadPageFault
22+
}
23+
pub fn is_write_page_fault(trap: usize) -> bool {
24+
trap == StorePageFault
25+
}
1726
pub fn is_syscall(trap: usize) -> bool {
1827
trap == Syscall
1928
}

kernel/src/arch/riscv/interrupt/mod.rs

+16-9
Original file line numberDiff line numberDiff line change
@@ -39,20 +39,27 @@ pub extern "C" fn trap_handler(tf: &mut TrapFrame) {
3939
trap_handler_no_frame(&mut tf.sepc);
4040
}
4141

42+
use crate::memory::AccessType;
4243
#[inline]
4344
pub fn trap_handler_no_frame(sepc: &mut usize) {
4445
use self::scause::{Exception as E, Interrupt as I, Trap};
4546
let scause = scause::read();
4647
let stval = stval::read();
48+
let is_user = false;
4749
trace!("Interrupt @ CPU{}: {:?} ", super::cpu::id(), scause.cause());
4850
match scause.cause() {
4951
Trap::Interrupt(I::SupervisorExternal) => external(),
5052
Trap::Interrupt(I::SupervisorSoft) => ipi(),
5153
Trap::Interrupt(I::SupervisorTimer) => timer(),
52-
Trap::Exception(E::LoadPageFault) => page_fault(stval, sepc),
53-
Trap::Exception(E::StorePageFault) => page_fault(stval, sepc),
54-
Trap::Exception(E::InstructionPageFault) => page_fault(stval, sepc),
55-
_ => panic!("unhandled trap {:?}", scause.cause()),
54+
Trap::Exception(E::LoadPageFault) => page_fault(stval, sepc, AccessType::read(is_user)),
55+
Trap::Exception(E::StorePageFault) => page_fault(stval, sepc, AccessType::write(is_user)),
56+
Trap::Exception(E::InstructionPageFault) => {
57+
page_fault(stval, sepc, AccessType::execute(is_user))
58+
}
59+
_ => {
60+
let bits = scause.bits();
61+
panic!("unhandled trap {:?} ({})", scause.cause(), bits);
62+
}
5663
}
5764
trace!("Interrupt end");
5865
}
@@ -62,7 +69,6 @@ fn external() {
6269
unsafe {
6370
super::board::handle_external_interrupt();
6471
}
65-
6672
IRQ_MANAGER
6773
.read()
6874
.try_handle_interrupt(Some(SupervisorExternal));
@@ -78,11 +84,11 @@ pub fn timer() {
7884
crate::trap::timer();
7985
}
8086

81-
fn page_fault(stval: usize, sepc: &mut usize) {
87+
fn page_fault(stval: usize, sepc: &mut usize, access: AccessType) {
8288
let addr = stval;
8389
info!("\nEXCEPTION: Page Fault @ {:#x}", addr);
8490

85-
if crate::memory::handle_page_fault(addr) {
91+
if crate::memory::handle_page_fault_ext(addr, access) {
8692
return;
8793
}
8894
extern "C" {
@@ -94,6 +100,7 @@ fn page_fault(stval: usize, sepc: &mut usize) {
94100
*sepc = crate::memory::read_user_fixup as usize;
95101
return;
96102
}
103+
error!("unhandled page fault {:#x} from {:#x}", addr, sepc);
97104
panic!("unhandled page fault");
98105
}
99106

@@ -121,8 +128,8 @@ pub fn wait_for_interrupt() {
121128
}
122129
}
123130

124-
pub fn handle_user_page_fault(thread: &Arc<Thread>, addr: usize) -> bool {
125-
thread.vm.lock().handle_page_fault(addr)
131+
pub fn handle_user_page_fault_ext(thread: &Arc<Thread>, addr: usize, access: AccessType) -> bool {
132+
thread.vm.lock().handle_page_fault_ext(addr, access)
126133
}
127134

128135
pub fn handle_reserved_inst(tf: &mut UserContext) -> bool {

kernel/src/process/thread.rs

+31-7
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use super::{
55
use crate::arch::interrupt::consts::{
66
is_intr, is_page_fault, is_reserved_inst, is_syscall, is_timer_intr,
77
};
8-
use crate::arch::interrupt::{get_trap_num, handle_reserved_inst, handle_user_page_fault};
8+
use crate::arch::interrupt::{get_trap_num, handle_reserved_inst};
99
use crate::arch::{
1010
cpu,
1111
fp::FpState,
@@ -505,10 +505,8 @@ pub fn spawn(thread: Arc<Thread>) {
505505
thread_context.fp.restore();
506506
cx.run();
507507
thread_context.fp.save();
508-
509508
let trap_num = get_trap_num(&cx);
510509
trace!("back from user: {:#x?} trap_num {:#x}", cx, trap_num);
511-
512510
let mut exit = false;
513511
let mut do_yield = false;
514512
match trap_num {
@@ -517,10 +515,36 @@ pub fn spawn(thread: Arc<Thread>) {
517515
// page fault
518516
let addr = get_page_fault_addr();
519517
info!("page fault from user @ {:#x}", addr);
520-
521-
if !handle_user_page_fault(&thread, addr) {
522-
// TODO: SIGSEGV
523-
panic!("page fault handle failed");
518+
#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
519+
{
520+
use crate::arch::interrupt::consts::{
521+
is_execute_page_fault, is_read_page_fault, is_write_page_fault,
522+
};
523+
use crate::arch::interrupt::handle_user_page_fault_ext;
524+
let access_type = match trap_num {
525+
_ if is_execute_page_fault(trap_num) => {
526+
crate::memory::AccessType::execute(true)
527+
}
528+
_ if is_read_page_fault(trap_num) => {
529+
crate::memory::AccessType::read(true)
530+
}
531+
_ if is_write_page_fault(trap_num) => {
532+
crate::memory::AccessType::write(true)
533+
}
534+
_ => unreachable!(),
535+
};
536+
if !handle_user_page_fault_ext(&thread, addr, access_type) {
537+
// TODO: SIGSEGV
538+
panic!("page fault handle failed");
539+
}
540+
}
541+
#[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))]
542+
{
543+
use crate::arch::interrupt::handle_user_page_fault;
544+
if !handle_user_page_fault(&thread, addr) {
545+
// TODO: SIGSEGV
546+
panic!("page fault handle failed");
547+
}
524548
}
525549
}
526550
_ if is_syscall(trap_num) => exit = handle_syscall(&thread, cx).await,

0 commit comments

Comments
 (0)