From f5023c9730a8ee950813ad8123fd482d50007386 Mon Sep 17 00:00:00 2001 From: Dominique Martinet Date: Wed, 6 Feb 2019 11:38:39 +0900 Subject: [PATCH] page fault handler: protect thread accesses current cpu's thread can be NULL during init, we don't want null derefs in the page fault handler Change-Id: I0a2c22b39cae2c258d211317cffc2408e19f3bbf --- arch/arm64/kernel/cpu.c | 8 +++++++- arch/x86_64/kernel/cpu.c | 8 +++++++- kernel/mem.c | 20 +++++++++++--------- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/arch/arm64/kernel/cpu.c b/arch/arm64/kernel/cpu.c index addd7ea6..59318655 100644 --- a/arch/arm64/kernel/cpu.c +++ b/arch/arm64/kernel/cpu.c @@ -1563,7 +1563,7 @@ void unhandled_page_fault(struct thread *thread, void *fault_addr, void *regs) { const uintptr_t address = (uintptr_t)fault_addr; - struct process_vm *vm = thread->vm; + struct process_vm *vm; struct vm_range *range; unsigned long irqflags; unsigned long error = 0; @@ -1578,6 +1578,11 @@ unhandled_page_fault(struct thread *thread, void *fault_addr, void *regs) (error & PF_RSVD ? "was" : "wasn't"), (error & PF_INSTR ? "was" : "wasn't")); + if (!thread) + goto skipvm; + + vm = thread->vm; + range = lookup_process_memory_range(vm, address, address+1); if (range) { __kprintf("address is in range, flag: 0x%lx\n", @@ -1587,6 +1592,7 @@ unhandled_page_fault(struct thread *thread, void *fault_addr, void *regs) __kprintf("address is out of range! \n"); } +skipvm: kprintf_unlock(irqflags); /* TODO */ diff --git a/arch/x86_64/kernel/cpu.c b/arch/x86_64/kernel/cpu.c index fb7d787c..dce674fb 100644 --- a/arch/x86_64/kernel/cpu.c +++ b/arch/x86_64/kernel/cpu.c @@ -1089,7 +1089,7 @@ void unhandled_page_fault(struct thread *thread, void *fault_addr, void *regs) { const uintptr_t address = (uintptr_t)fault_addr; - struct process_vm *vm = thread->vm; + struct process_vm *vm; struct vm_range *range; unsigned long irqflags; unsigned long error = ((struct x86_user_context *)regs)->gpr.error; @@ -1104,6 +1104,11 @@ unhandled_page_fault(struct thread *thread, void *fault_addr, void *regs) (error & PF_RSVD ? "was" : "wasn't"), (error & PF_INSTR ? "was" : "wasn't")); + if (!thread) + goto skipvm; + + vm = thread->vm; + range = lookup_process_memory_range(vm, address, address+1); if (range) { __kprintf("address is in range, flag: 0x%lx\n", @@ -1113,6 +1118,7 @@ unhandled_page_fault(struct thread *thread, void *fault_addr, void *regs) __kprintf("address is out of range! \n"); } +skipvm: kprintf_unlock(irqflags); /* TODO */ diff --git a/kernel/mem.c b/kernel/mem.c index c8a1328c..774e4eb0 100644 --- a/kernel/mem.c +++ b/kernel/mem.c @@ -1150,7 +1150,7 @@ static void page_fault_handler(void *fault_addr, uint64_t reason, void *regs) int error; #ifdef PROFILE_ENABLE uint64_t t_s = 0; - if (thread->profile) + if (thread && thread->profile) t_s = rdtsc(); #endif // PROFILE_ENABLE @@ -1163,7 +1163,7 @@ static void page_fault_handler(void *fault_addr, uint64_t reason, void *regs) cpu_enable_interrupt(); - if ((uintptr_t)fault_addr < PAGE_SIZE) { + if ((uintptr_t)fault_addr < PAGE_SIZE || !thread) { error = -EINVAL; } else { error = page_fault_process_vm(thread->vm, fault_addr, reason); @@ -1179,9 +1179,9 @@ static void page_fault_handler(void *fault_addr, uint64_t reason, void *regs) // no return } - kprintf("%s fault VM failed for TID: %d, addr: 0x%lx, " - "reason: %d, error: %d\n", __FUNCTION__, - thread->tid, fault_addr, reason, error); + kprintf("%s fault VM failed for TID: %d, addr: 0x%lx, reason: %d, error: %d\n", + __func__, thread ? thread->tid : -1, fault_addr, + reason, error); unhandled_page_fault(thread, fault_addr, regs); preempt_enable(); memset(&info, '\0', sizeof info); @@ -1192,12 +1192,14 @@ static void page_fault_handler(void *fault_addr, uint64_t reason, void *regs) set_signal(SIGBUS, regs, &info); } else { - struct process_vm *vm = thread->vm; - struct vm_range *range; + struct vm_range *range = NULL; info.si_signo = SIGSEGV; info.si_code = SEGV_MAPERR; - range = lookup_process_memory_range(vm, (uintptr_t)fault_addr, ((uintptr_t)fault_addr) + 1); + if (thread) + range = lookup_process_memory_range(thread->vm, + (uintptr_t)fault_addr, + ((uintptr_t)fault_addr) + 1); if (range) info.si_code = SEGV_ACCERR; info._sifields._sigfault.si_addr = fault_addr; @@ -1219,7 +1221,7 @@ out: set_cputime(interrupt_from_user(regs) ? CPUTIME_MODE_K2U : CPUTIME_MODE_K2K_OUT); #ifdef PROFILE_ENABLE - if (thread->profile) + if (thread && thread->profile) profile_event_add(PROFILE_page_fault, (rdtsc() - t_s)); #endif // PROFILE_ENABLE return;