DF-5471 / fix.diff
--- a/sys/platform/pc64/x86_64/db_trace.c +++ b/sys/platform/pc64/x86_64/db_trace.c @@ -224,7 +224,14 @@ db_expr_t offset; const char *sym, *name; - if ((unsigned long)*fp < PAGE_SIZE) { + /* + * Never walk a frame outside kernel space. Stack content is + * untrusted once a stack is corrupted; without this check the + * walk can be steered into user address space or arbitrary kernel + * addresses (and into unmapped pages, faulting inside db_read_bytes). + */ + if ((unsigned long)*fp < PAGE_SIZE || + !INKERNEL((vm_offset_t)*fp)) { *fp = NULL; return; } @@ -255,6 +262,17 @@ */ if (frame_type == NORMAL) { *ip = (db_addr_t) rip; + /* + * The adopted frame must be in kernel space and must move + * strictly up the stack. This kills wild pointers, cycles, + * and (with INKERNEL()) user-space frame chains sprayed by + * an unprivileged process. + */ + if (!INKERNEL((vm_offset_t)rbp) || + (struct x86_64_frame *)rbp <= *fp) { + *fp = NULL; + return; + } *fp = (struct x86_64_frame *) rbp; return; } @@ -306,7 +324,10 @@ } *ip = (db_addr_t) rip; - *fp = (struct x86_64_frame *) rbp; + if (!INKERNEL((vm_offset_t)rbp)) + *fp = NULL; + else + *fp = (struct x86_64_frame *) rbp; } void @@ -333,6 +354,10 @@ * a convenience. */ frame = (struct x86_64_frame *)addr; + if (!INKERNEL((vm_offset_t)frame)) { + db_printf("%p is not in kernel space\n", (void *)addr); + return; + } for (i = 0; i < 4096; i += 8) { struct x86_64_frame *check; |