mirror of
https://github.com/SerenityOS/serenity
synced 2024-09-06 17:06:31 +00:00
Kernel: Send SIGBUS to threads that use after valid Inode mmaped range
According to Dr. POSIX, we should allow to call mmap on inodes even on ranges that currently don't map to any actual data. Trying to read or write to those ranges should result in SIGBUS being sent to the thread that did violating memory access. To implement this restriction, we simply check if the result of read_bytes on an Inode returns 0, which means we have nothing valid to map to the program, hence it should receive a SIGBUS in that case.
This commit is contained in:
parent
69f7a59a34
commit
60b088b89a
|
@ -317,7 +317,7 @@ void page_fault_handler(TrapFrame* trap)
|
||||||
PageFault fault { regs.exception_code, VirtualAddress { fault_address } };
|
PageFault fault { regs.exception_code, VirtualAddress { fault_address } };
|
||||||
auto response = MM.handle_page_fault(fault);
|
auto response = MM.handle_page_fault(fault);
|
||||||
|
|
||||||
if (response == PageFaultResponse::ShouldCrash || response == PageFaultResponse::OutOfMemory) {
|
if (response == PageFaultResponse::ShouldCrash || response == PageFaultResponse::OutOfMemory || response == PageFaultResponse::BusError) {
|
||||||
if (faulted_in_kernel && handle_safe_access_fault(regs, fault_address)) {
|
if (faulted_in_kernel && handle_safe_access_fault(regs, fault_address)) {
|
||||||
// If this would be a ring0 (kernel) fault and the fault was triggered by
|
// If this would be a ring0 (kernel) fault and the fault was triggered by
|
||||||
// safe_memcpy, safe_strnlen, or safe_memset then we resume execution at
|
// safe_memcpy, safe_strnlen, or safe_memset then we resume execution at
|
||||||
|
@ -325,6 +325,11 @@ void page_fault_handler(TrapFrame* trap)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (response == PageFaultResponse::BusError && current_thread->has_signal_handler(SIGBUS)) {
|
||||||
|
current_thread->send_urgent_signal_to_self(SIGBUS);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (response != PageFaultResponse::OutOfMemory && current_thread) {
|
if (response != PageFaultResponse::OutOfMemory && current_thread) {
|
||||||
if (current_thread->has_signal_handler(SIGSEGV)) {
|
if (current_thread->has_signal_handler(SIGSEGV)) {
|
||||||
current_thread->send_urgent_signal_to_self(SIGSEGV);
|
current_thread->send_urgent_signal_to_self(SIGSEGV);
|
||||||
|
@ -341,7 +346,9 @@ void page_fault_handler(TrapFrame* trap)
|
||||||
constexpr FlatPtr free_scrub_pattern = explode_byte(FREE_SCRUB_BYTE);
|
constexpr FlatPtr free_scrub_pattern = explode_byte(FREE_SCRUB_BYTE);
|
||||||
constexpr FlatPtr kmalloc_scrub_pattern = explode_byte(KMALLOC_SCRUB_BYTE);
|
constexpr FlatPtr kmalloc_scrub_pattern = explode_byte(KMALLOC_SCRUB_BYTE);
|
||||||
constexpr FlatPtr kfree_scrub_pattern = explode_byte(KFREE_SCRUB_BYTE);
|
constexpr FlatPtr kfree_scrub_pattern = explode_byte(KFREE_SCRUB_BYTE);
|
||||||
if ((fault_address & 0xffff0000) == (malloc_scrub_pattern & 0xffff0000)) {
|
if (response == PageFaultResponse::BusError) {
|
||||||
|
dbgln("Note: Address {} is an access to an undefined memory range of an Inode-backed VMObject", VirtualAddress(fault_address));
|
||||||
|
} else if ((fault_address & 0xffff0000) == (malloc_scrub_pattern & 0xffff0000)) {
|
||||||
dbgln("Note: Address {} looks like it may be uninitialized malloc() memory", VirtualAddress(fault_address));
|
dbgln("Note: Address {} looks like it may be uninitialized malloc() memory", VirtualAddress(fault_address));
|
||||||
} else if ((fault_address & 0xffff0000) == (free_scrub_pattern & 0xffff0000)) {
|
} else if ((fault_address & 0xffff0000) == (free_scrub_pattern & 0xffff0000)) {
|
||||||
dbgln("Note: Address {} looks like it may be recently free()'d memory", VirtualAddress(fault_address));
|
dbgln("Note: Address {} looks like it may be recently free()'d memory", VirtualAddress(fault_address));
|
||||||
|
@ -390,6 +397,8 @@ void page_fault_handler(TrapFrame* trap)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (response == PageFaultResponse::BusError)
|
||||||
|
return handle_crash(regs, "Page Fault (Bus Error)", SIGBUS, false);
|
||||||
return handle_crash(regs, "Page Fault", SIGSEGV, response == PageFaultResponse::OutOfMemory);
|
return handle_crash(regs, "Page Fault", SIGSEGV, response == PageFaultResponse::OutOfMemory);
|
||||||
} else if (response == PageFaultResponse::Continue) {
|
} else if (response == PageFaultResponse::Continue) {
|
||||||
dbgln_if(PAGE_FAULT_DEBUG, "Continuing after resolved page fault");
|
dbgln_if(PAGE_FAULT_DEBUG, "Continuing after resolved page fault");
|
||||||
|
|
|
@ -10,6 +10,7 @@ namespace Kernel {
|
||||||
|
|
||||||
enum class PageFaultResponse {
|
enum class PageFaultResponse {
|
||||||
ShouldCrash,
|
ShouldCrash,
|
||||||
|
BusError,
|
||||||
OutOfMemory,
|
OutOfMemory,
|
||||||
Continue,
|
Continue,
|
||||||
};
|
};
|
||||||
|
|
|
@ -501,6 +501,11 @@ PageFaultResponse Region::handle_inode_fault(size_t page_index_in_region)
|
||||||
}
|
}
|
||||||
|
|
||||||
auto nread = result.value();
|
auto nread = result.value();
|
||||||
|
// Note: If we received 0, it means we are at the end of file or after it,
|
||||||
|
// which means we should return bus error.
|
||||||
|
if (nread == 0)
|
||||||
|
return PageFaultResponse::BusError;
|
||||||
|
|
||||||
if (nread < PAGE_SIZE) {
|
if (nread < PAGE_SIZE) {
|
||||||
// If we read less than a page, zero out the rest to avoid leaking uninitialized data.
|
// If we read less than a page, zero out the rest to avoid leaking uninitialized data.
|
||||||
memset(page_buffer + nread, 0, PAGE_SIZE - nread);
|
memset(page_buffer + nread, 0, PAGE_SIZE - nread);
|
||||||
|
|
Loading…
Reference in a new issue