Kernel+LibC: Make page fault crashes a bit more readable.

We'll now try to detect crashes that were due to dereferencing nullptr,
uninitialized malloc() memory, or recently free()'d memory.
It's not perfect but I think it's pretty good. :^)

Also added some color to the most important parts of the crash log,
and added some more modes to /bin/crash for exercising this code.

Fixes #243.
This commit is contained in:
Andreas Kling 2019-06-19 20:52:12 +02:00
parent 15bea7153a
commit 8c0ae711d8
Notes: sideshowbarker 2024-07-19 13:32:49 +09:00
5 changed files with 76 additions and 11 deletions

View file

@ -53,3 +53,13 @@ constexpr unsigned GB = KB * KB * KB;
namespace std { namespace std {
typedef decltype(nullptr) nullptr_t; typedef decltype(nullptr) nullptr_t;
} }
static constexpr dword explode_byte(byte b)
{
return b << 24 | b << 16 | b << 8 | b;
}
static_assert(explode_byte(0xff) == 0xffffffff);
static_assert(explode_byte(0x80) == 0x80808080);
static_assert(explode_byte(0x7f) == 0x7f7f7f7f);
static_assert(explode_byte(0) == 0);

View file

@ -7,6 +7,7 @@
#include <Kernel/Arch/i386/CPU.h> #include <Kernel/Arch/i386/CPU.h>
#include <Kernel/KSyms.h> #include <Kernel/KSyms.h>
#include <Kernel/VM/MemoryManager.h> #include <Kernel/VM/MemoryManager.h>
#include <LibC/mallocdefs.h>
//#define PAGE_FAULT_DEBUG //#define PAGE_FAULT_DEBUG
@ -254,9 +255,9 @@ void exception_14_handler(RegisterDumpWithExceptionCode& regs)
{ {
ASSERT(current); ASSERT(current);
dword faultAddress; dword fault_address;
asm("movl %%cr2, %%eax" asm("movl %%cr2, %%eax"
: "=a"(faultAddress)); : "=a"(fault_address));
dword fault_page_directory; dword fault_page_directory;
asm("movl %%cr3, %%eax" asm("movl %%cr3, %%eax"
@ -270,22 +271,33 @@ void exception_14_handler(RegisterDumpWithExceptionCode& regs)
regs.exception_code & 1 ? "PV" : "NP", regs.exception_code & 1 ? "PV" : "NP",
fault_page_directory, fault_page_directory,
regs.exception_code & 2 ? "write" : "read", regs.exception_code & 2 ? "write" : "read",
faultAddress); fault_address);
#endif #endif
#ifdef PAGE_FAULT_DEBUG #ifdef PAGE_FAULT_DEBUG
dump(regs); dump(regs);
#endif #endif
auto response = MM.handle_page_fault(PageFault(regs.exception_code, VirtualAddress(faultAddress))); auto response = MM.handle_page_fault(PageFault(regs.exception_code, VirtualAddress(fault_address)));
if (response == PageFaultResponse::ShouldCrash) { if (response == PageFaultResponse::ShouldCrash) {
kprintf("%s(%u:%u) unrecoverable page fault, %s vaddr=%p\n", dbgprintf("\033[31;1m%s(%u:%u) Unrecoverable page fault, %s address %p\033[0m\n",
current->process().name().characters(), current->process().name().characters(),
current->pid(), current->pid(),
current->tid(), current->tid(),
regs.exception_code & 2 ? "write" : "read", regs.exception_code & 2 ? "write to" : "read from",
faultAddress); fault_address);
dword malloc_scrub_pattern = explode_byte(MALLOC_SCRUB_BYTE);
dword free_scrub_pattern = explode_byte(FREE_SCRUB_BYTE);
if ((fault_address & 0xffff0000) == (malloc_scrub_pattern & 0xffff0000)) {
dbgprintf("\033[33;1mNote: Address %p looks like it may be uninitialized malloc() memory\033[0m\n", fault_address);
} else if ((fault_address & 0xffff0000) == (free_scrub_pattern & 0xffff0000)) {
dbgprintf("\033[33;1mNote: Address %p looks like it may be recently free()'d memory\033[0m\n", fault_address);
} else if (fault_address < 4096) {
dbgprintf("\033[33;1mNote: Address %p looks like a possible nullptr dereference\033[0m\n", fault_address);
}
dump(regs); dump(regs);
current->process().crash(SIGSEGV, regs.eip); current->process().crash(SIGSEGV, regs.eip);
} else if (response == PageFaultResponse::Continue) { } else if (response == PageFaultResponse::Continue) {

View file

@ -2,6 +2,7 @@
#include <AK/InlineLinkedList.h> #include <AK/InlineLinkedList.h>
#include <AK/Vector.h> #include <AK/Vector.h>
#include <assert.h> #include <assert.h>
#include <mallocdefs.h>
#include <serenity.h> #include <serenity.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -12,8 +13,6 @@
//#define MALLOC_DEBUG //#define MALLOC_DEBUG
#define RECYCLE_BIG_ALLOCATIONS #define RECYCLE_BIG_ALLOCATIONS
#define MALLOC_SCRUB_BYTE 0x85
#define FREE_SCRUB_BYTE 0x82
#define MAGIC_PAGE_HEADER 0x42657274 #define MAGIC_PAGE_HEADER 0x42657274
#define MAGIC_BIGALLOC_HEADER 0x42697267 #define MAGIC_BIGALLOC_HEADER 0x42697267
#define PAGE_ROUND_UP(x) ((((size_t)(x)) + PAGE_SIZE - 1) & (~(PAGE_SIZE - 1))) #define PAGE_ROUND_UP(x) ((((size_t)(x)) + PAGE_SIZE - 1) & (~(PAGE_SIZE - 1)))

5
LibC/mallocdefs.h Normal file
View file

@ -0,0 +1,5 @@
#pragma once
#define MALLOC_SCRUB_BYTE 0xdc
#define FREE_SCRUB_BYTE 0xed

View file

@ -4,17 +4,22 @@
static void print_usage_and_exit() static void print_usage_and_exit()
{ {
printf("usage: crash -[sdia]\n"); printf("usage: crash -[sdiamfMF]\n");
exit(0); exit(0);
} }
#pragma GCC optimize("O0")
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
enum Mode { enum Mode {
SegmentationViolation, SegmentationViolation,
DivisionByZero, DivisionByZero,
IllegalInstruction, IllegalInstruction,
Abort Abort,
WriteToUninitializedMallocMemory,
WriteToFreedMemory,
ReadFromUninitializedMallocMemory,
ReadFromFreedMemory,
}; };
Mode mode = SegmentationViolation; Mode mode = SegmentationViolation;
@ -29,6 +34,14 @@ int main(int argc, char** argv)
mode = IllegalInstruction; mode = IllegalInstruction;
else if (String(argv[1]) == "-a") else if (String(argv[1]) == "-a")
mode = Abort; mode = Abort;
else if (String(argv[1]) == "-m")
mode = ReadFromUninitializedMallocMemory;
else if (String(argv[1]) == "-f")
mode = ReadFromFreedMemory;
else if (String(argv[1]) == "-M")
mode = WriteToUninitializedMallocMemory;
else if (String(argv[1]) == "-F")
mode = WriteToFreedMemory;
else else
print_usage_and_exit(); print_usage_and_exit();
@ -55,6 +68,32 @@ int main(int argc, char** argv)
ASSERT_NOT_REACHED(); ASSERT_NOT_REACHED();
} }
if (mode == ReadFromUninitializedMallocMemory) {
auto* uninitialized_memory = (volatile dword**)malloc(1024);
volatile auto x = uninitialized_memory[0][0];
ASSERT_NOT_REACHED();
}
if (mode == ReadFromFreedMemory) {
auto* uninitialized_memory = (volatile dword**)malloc(1024);
free(uninitialized_memory);
volatile auto x = uninitialized_memory[4][0];
ASSERT_NOT_REACHED();
}
if (mode == WriteToUninitializedMallocMemory) {
auto* uninitialized_memory = (volatile dword**)malloc(1024);
uninitialized_memory[4][0] = 1;
ASSERT_NOT_REACHED();
}
if (mode == WriteToFreedMemory) {
auto* uninitialized_memory = (volatile dword**)malloc(1024);
free(uninitialized_memory);
uninitialized_memory[4][0] = 1;
ASSERT_NOT_REACHED();
}
ASSERT_NOT_REACHED(); ASSERT_NOT_REACHED();
return 0; return 0;
} }