Serenity Operating System
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}