kernel: Better fault reports

This commit is contained in:
K. Lange 2021-10-04 20:34:42 +09:00
parent bbf49fbc9d
commit 05e224dee7
7 changed files with 102 additions and 2 deletions

View File

@ -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);

View File

@ -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);

View File

@ -8,6 +8,9 @@
#include <kernel/misc.h>
#include <kernel/time.h>
#include <kernel/ptrace.h>
#include <kernel/hashmap.h>
#include <kernel/module.h>
#include <kernel/ksym.h>
#include <sys/time.h>
#include <sys/utsname.h>
@ -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();
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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)) {

View File

@ -3,6 +3,7 @@
static int init(int argc, char * argv[]) {
printf("Hello, modules.\n");
*(volatile int*)0x60000000 = 42;
return 0;
}