⬢ DragonFlyBSD Kernel Audit
DF-5470 / fix.diff
← back to finding ↓ download raw
--- 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;