From 05e224dee735f91c87c7f4a0104bbfbdf37d0318 Mon Sep 17 00:00:00 2001 From: "K. Lange" Date: Mon, 4 Oct 2021 20:34:42 +0900 Subject: [PATCH] kernel: Better fault reports --- base/usr/include/kernel/arch/x86_64/mmu.h | 1 + base/usr/include/kernel/ksym.h | 1 + kernel/arch/x86_64/idt.c | 95 +++++++++++++++++++++++ kernel/misc/ksym.c | 4 + kernel/sys/ptrace.c | 1 - kernel/sys/syscall.c | 1 - modules/test.c | 1 + 7 files changed, 102 insertions(+), 2 deletions(-) diff --git a/base/usr/include/kernel/arch/x86_64/mmu.h b/base/usr/include/kernel/arch/x86_64/mmu.h index 63cfd402..e815601d 100644 --- a/base/usr/include/kernel/arch/x86_64/mmu.h +++ b/base/usr/include/kernel/arch/x86_64/mmu.h @@ -44,4 +44,5 @@ size_t mmu_used_memory(void); void * sbrk(size_t); +union PML * mmu_get_page_other(union PML * root, uintptr_t virtAddr); int mmu_validate_user_pointer(void * addr, size_t size, int flags); diff --git a/base/usr/include/kernel/ksym.h b/base/usr/include/kernel/ksym.h index 208d0797..6db53c99 100644 --- a/base/usr/include/kernel/ksym.h +++ b/base/usr/include/kernel/ksym.h @@ -6,3 +6,4 @@ extern void ksym_install(void); extern void ksym_bind(const char * symname, void * value); extern void * ksym_lookup(const char * symname); extern list_t * ksym_list(void); +extern hashmap_t * ksym_get_map(void); diff --git a/kernel/arch/x86_64/idt.c b/kernel/arch/x86_64/idt.c index ad4d4ad3..d1904cd9 100644 --- a/kernel/arch/x86_64/idt.c +++ b/kernel/arch/x86_64/idt.c @@ -8,6 +8,9 @@ #include #include #include +#include +#include +#include #include #include @@ -157,6 +160,97 @@ void irq_uninstall_handler(size_t irq) { irq_routines[i * IRQ_CHAIN_SIZE + irq] = NULL; } +static struct LoadedModule * find_module(uintptr_t addr, char ** name) { + hashmap_t * modules = modules_get_list(); + + for (size_t i = 0; i < modules->size; ++i) { + hashmap_entry_t * x = modules->entries[i]; + while (x) { + struct LoadedModule * info = x->value; + if (info->baseAddress <= addr && addr <= info->baseAddress + info->loadedSize) { + *name = (char*)x->key; + return info; + } + x = x->next; + } + } + + return NULL; +} + +static int validate_pointer(uintptr_t base, size_t size) { + uintptr_t end = size ? (base + (size - 1)) : base; + uintptr_t page_base = base >> 12; + uintptr_t page_end = end >> 12; + for (uintptr_t page = page_base; page <= page_end; ++page) { + if ((page & 0xffff800000000) != 0 && (page & 0xffff800000000) != 0xffff800000000) return 0; + union PML * page_entry = mmu_get_page_other(this_core->current_process->thread.page_directory->directory, page << 12); + if (!page_entry) return 0; + if (!page_entry->bits.present) return 0; + } + return 1; +} + +extern char end[]; + +static uintptr_t matching_symbol(uintptr_t ip, char ** name) { + hashmap_t * symbols = ksym_get_map(); + uintptr_t best_match = 0; + for (size_t i = 0; i < symbols->size; ++i) { + hashmap_entry_t * x = symbols->entries[i]; + while (x) { + void* sym_addr = x->value; + char* sym_name = x->key; + if ((uintptr_t)sym_addr < ip && (uintptr_t)sym_addr > best_match) { + best_match = (uintptr_t)sym_addr; + *name = sym_name; + } + x = x->next; + } + } + return best_match; +} + +static void safe_dump_traceback(struct regs * r) { + uintptr_t ip = r->rip; + uintptr_t bp = r->rbp; + int depth = 0; + int max_depth = 20; + + while (bp && ip && depth < max_depth) { + printf(" 0x%016zx ", ip); + if (ip >= 0xffffffff80000000UL) { + char * name = NULL; + struct LoadedModule * mod = find_module(ip, &name); + if (mod) { + printf(" in module '%s', base address %#zx (offset %#zx)\n", + name, mod->baseAddress, r->rip - mod->baseAddress); + } else { + printf(" (unknown)\n"); + } + } else if (ip >= (uintptr_t)&end && ip <= 0x800000000000) { + printf(" in userspace\n"); + } else if (ip <= (uintptr_t)&end) { + /* Find symbol match */ + char * name; + uintptr_t addr = matching_symbol(ip, &name); + if (!addr) { + printf(" (no match)\n"); + } else { + printf(" %s+0x%zx\n", name, ip-addr); + } + } else { + printf(" (unknown)\n"); + } + if (!validate_pointer(bp, sizeof(uintptr_t) * 2)) { + break; + } + ip = *(uintptr_t*)(bp + sizeof(uintptr_t)); + bp = *(uintptr_t*)(bp); + depth++; + } +} + static void map_more_stack(uintptr_t fromAddr) { volatile process_t * volatile proc = this_core->current_process; if (proc->group != 0) { @@ -189,6 +283,7 @@ struct regs * isr_handler(struct regs * r) { printf("pid=%d (%s) ", (int)this_core->current_process->id, this_core->current_process->name); } printf("at %#zx\n", faulting_address); + safe_dump_traceback(r); dump_regs(r); arch_fatal(); } diff --git a/kernel/misc/ksym.c b/kernel/misc/ksym.c index 712949fc..74ecde4a 100644 --- a/kernel/misc/ksym.c +++ b/kernel/misc/ksym.c @@ -32,3 +32,7 @@ list_t * ksym_list(void) { assert(ksym_hash != NULL); return hashmap_keys(ksym_hash); } + +hashmap_t * ksym_get_map(void) { + return ksym_hash; +} diff --git a/kernel/sys/ptrace.c b/kernel/sys/ptrace.c index 9b40afb1..bc9e0ff8 100644 --- a/kernel/sys/ptrace.c +++ b/kernel/sys/ptrace.c @@ -91,7 +91,6 @@ long ptrace_getregs(pid_t pid, void * data) { return 0; } -extern union PML * mmu_get_page_other(union PML * root, uintptr_t virtAddr); long ptrace_peek(pid_t pid, void * addr, void * data) { if (!data || ptr_validate(data, "ptrace")) return -EFAULT; process_t * tracee = process_from_pid(pid); diff --git a/kernel/sys/syscall.c b/kernel/sys/syscall.c index 48a8bf48..5cff5dc9 100644 --- a/kernel/sys/syscall.c +++ b/kernel/sys/syscall.c @@ -25,7 +25,6 @@ static char hostname[256]; static size_t hostname_len = 0; -extern union PML * mmu_get_page_other(union PML * root, uintptr_t virtAddr); int ptr_validate(void * ptr, const char * syscall) { if (ptr) { if (!PTR_INRANGE(ptr)) { diff --git a/modules/test.c b/modules/test.c index 30e12f7c..2ef31031 100644 --- a/modules/test.c +++ b/modules/test.c @@ -3,6 +3,7 @@ static int init(int argc, char * argv[]) { printf("Hello, modules.\n"); + *(volatile int*)0x60000000 = 42; return 0; }