Serenity Operating System
at master 143 lines 7.6 kB view raw
1/* 2 * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <Kernel/Arch/CPU.h> 8#include <Kernel/Arch/PageFault.h> 9#include <Kernel/Arch/Processor.h> 10#include <Kernel/Arch/RegisterState.h> 11#include <Kernel/Arch/SafeMem.h> 12#include <Kernel/PerformanceManager.h> 13#include <Kernel/Thread.h> 14 15namespace Kernel { 16 17void PageFault::handle(RegisterState& regs) 18{ 19 auto fault_address = m_vaddr.get(); 20 bool faulted_in_kernel = regs.previous_mode() == ExecutionMode::Kernel; 21 22 if (faulted_in_kernel && Processor::current_in_irq()) { 23 // If we're faulting in an IRQ handler, first check if we failed 24 // due to safe_memcpy, safe_strnlen, or safe_memset. If we did, 25 // gracefully continue immediately. Because we're in an IRQ handler 26 // we can't really try to resolve the page fault in a meaningful 27 // way, so we need to do this before calling into 28 // MemoryManager::handle_page_fault, which would just bail and 29 // request a crash 30 if (handle_safe_access_fault(regs, fault_address)) 31 return; 32 } 33 34 auto current_thread = Thread::current(); 35 36 if (current_thread) { 37 current_thread->set_handling_page_fault(true); 38 PerformanceManager::add_page_fault_event(*current_thread, regs); 39 } 40 41 ScopeGuard guard = [current_thread] { 42 if (current_thread) 43 current_thread->set_handling_page_fault(false); 44 }; 45 46 if (!faulted_in_kernel) { 47 VirtualAddress userspace_sp = VirtualAddress { regs.userspace_sp() }; 48 bool has_valid_stack_pointer = current_thread->process().address_space().with([&](auto& space) { 49 return MM.validate_user_stack(*space, userspace_sp); 50 }); 51 if (!has_valid_stack_pointer) { 52 dbgln("Invalid stack pointer: {}", userspace_sp); 53 return handle_crash(regs, "Bad stack on page fault", SIGSEGV); 54 } 55 } 56 57 auto response = MM.handle_page_fault(*this); 58 59 if (response == PageFaultResponse::ShouldCrash || response == PageFaultResponse::OutOfMemory || response == PageFaultResponse::BusError) { 60 if (faulted_in_kernel && handle_safe_access_fault(regs, fault_address)) { 61 // If this would be a ring0 (kernel) fault and the fault was triggered by 62 // safe_memcpy, safe_strnlen, or safe_memset then we resume execution at 63 // the appropriate _fault label rather than crashing 64 return; 65 } 66 67 if (response == PageFaultResponse::BusError && current_thread->has_signal_handler(SIGBUS)) { 68 current_thread->send_urgent_signal_to_self(SIGBUS); 69 return; 70 } 71 72 if (response != PageFaultResponse::OutOfMemory && current_thread) { 73 if (current_thread->has_signal_handler(SIGSEGV)) { 74 current_thread->send_urgent_signal_to_self(SIGSEGV); 75 return; 76 } 77 } 78 79 dbgln("Unrecoverable page fault, {}{}{} address {}", 80 is_reserved_bit_violation() ? "reserved bit violation / " : "", 81 is_instruction_fetch() ? "instruction fetch / " : "", 82 is_write() ? "write to" : "read from", 83 VirtualAddress(fault_address)); 84 constexpr FlatPtr kmalloc_scrub_pattern = explode_byte(KMALLOC_SCRUB_BYTE); 85 constexpr FlatPtr kfree_scrub_pattern = explode_byte(KFREE_SCRUB_BYTE); 86 if (response == PageFaultResponse::BusError) { 87 dbgln("Note: Address {} is an access to an undefined memory range of an Inode-backed VMObject", VirtualAddress(fault_address)); 88 } else if ((fault_address & 0xffff0000) == (kmalloc_scrub_pattern & 0xffff0000)) { 89 dbgln("Note: Address {} looks like it may be uninitialized kmalloc() memory", VirtualAddress(fault_address)); 90 } else if ((fault_address & 0xffff0000) == (kfree_scrub_pattern & 0xffff0000)) { 91 dbgln("Note: Address {} looks like it may be recently kfree()'d memory", VirtualAddress(fault_address)); 92 } else if (fault_address < 4096) { 93 dbgln("Note: Address {} looks like a possible nullptr dereference", VirtualAddress(fault_address)); 94 } else if constexpr (SANITIZE_PTRS) { 95 constexpr FlatPtr refptr_scrub_pattern = explode_byte(REFPTR_SCRUB_BYTE); 96 constexpr FlatPtr nonnullrefptr_scrub_pattern = explode_byte(NONNULLREFPTR_SCRUB_BYTE); 97 constexpr FlatPtr ownptr_scrub_pattern = explode_byte(OWNPTR_SCRUB_BYTE); 98 constexpr FlatPtr nonnullownptr_scrub_pattern = explode_byte(NONNULLOWNPTR_SCRUB_BYTE); 99 constexpr FlatPtr lockrefptr_scrub_pattern = explode_byte(LOCKREFPTR_SCRUB_BYTE); 100 constexpr FlatPtr nonnulllockrefptr_scrub_pattern = explode_byte(NONNULLLOCKREFPTR_SCRUB_BYTE); 101 102 if ((fault_address & 0xffff0000) == (refptr_scrub_pattern & 0xffff0000)) { 103 dbgln("Note: Address {} looks like it may be a recently destroyed LockRefPtr", VirtualAddress(fault_address)); 104 } else if ((fault_address & 0xffff0000) == (nonnullrefptr_scrub_pattern & 0xffff0000)) { 105 dbgln("Note: Address {} looks like it may be a recently destroyed NonnullLockRefPtr", VirtualAddress(fault_address)); 106 } else if ((fault_address & 0xffff0000) == (ownptr_scrub_pattern & 0xffff0000)) { 107 dbgln("Note: Address {} looks like it may be a recently destroyed OwnPtr", VirtualAddress(fault_address)); 108 } else if ((fault_address & 0xffff0000) == (nonnullownptr_scrub_pattern & 0xffff0000)) { 109 dbgln("Note: Address {} looks like it may be a recently destroyed NonnullOwnPtr", VirtualAddress(fault_address)); 110 } else if ((fault_address & 0xffff0000) == (lockrefptr_scrub_pattern & 0xffff0000)) { 111 dbgln("Note: Address {} looks like it may be a recently destroyed LockRefPtr", VirtualAddress(fault_address)); 112 } else if ((fault_address & 0xffff0000) == (nonnulllockrefptr_scrub_pattern & 0xffff0000)) { 113 dbgln("Note: Address {} looks like it may be a recently destroyed NonnullLockRefPtr", VirtualAddress(fault_address)); 114 } 115 } 116 117 if (current_thread) { 118 auto& current_process = current_thread->process(); 119 if (current_process.is_user_process()) { 120 auto fault_address_string = KString::formatted("{:p}", fault_address); 121 auto fault_address_view = fault_address_string.is_error() ? ""sv : fault_address_string.value()->view(); 122 (void)current_process.try_set_coredump_property("fault_address"sv, fault_address_view); 123 (void)current_process.try_set_coredump_property("fault_type"sv, type() == PageFault::Type::PageNotPresent ? "NotPresent"sv : "ProtectionViolation"sv); 124 StringView fault_access; 125 if (is_instruction_fetch()) 126 fault_access = "Execute"sv; 127 else 128 fault_access = access() == PageFault::Access::Read ? "Read"sv : "Write"sv; 129 (void)current_process.try_set_coredump_property("fault_access"sv, fault_access); 130 } 131 } 132 133 if (response == PageFaultResponse::BusError) 134 return handle_crash(regs, "Page Fault (Bus Error)", SIGBUS, false); 135 return handle_crash(regs, "Page Fault", SIGSEGV, response == PageFaultResponse::OutOfMemory); 136 } else if (response == PageFaultResponse::Continue) { 137 dbgln_if(PAGE_FAULT_DEBUG, "Continuing after resolved page fault"); 138 } else { 139 VERIFY_NOT_REACHED(); 140 } 141} 142 143}