⬢ DragonFlyBSD Kernel Audit
DF-5725 / fix.diff
← back to finding ↓ download raw
--- a/sys/ddb/db_break.c	2026-06-29 12:51:18.000000000 +0000
+++ b/sys/ddb/db_break.c	2026-10-10 18:39:43.915899474 +0000
@@ -88,12 +88,26 @@
 db_set_breakpoint(vm_map_t map, db_addr_t addr, int count)
 {
 	db_breakpoint_t	bkpt;
+	int		inst;
 
 	if (db_find_breakpoint(map, addr)) {
 	    db_printf("Already set.\n");
 	    return;
 	}
 
+	/*
+	 * Save the original instruction byte NOW, at breakpoint-set time,
+	 * while the console can still react to a bad address.  If we
+	 * defer the read to db_set_breakpoints() (resume time), a fault
+	 * on one bad breakpoint aborts the whole insert pass mid-list
+	 * via the DDB trap path: breakpoints earlier in the list stay
+	 * patched with INT3 while db_breakpoints_inserted remains FALSE,
+	 * and the next insert pass re-reads the patched INT3 byte as the
+	 * "original" instruction, permanently corrupting kernel text
+	 * when the breakpoint is later cleared.
+	 */
+	inst = db_get_value(addr, BKPT_SIZE, FALSE);
+
 	bkpt = db_breakpoint_alloc();
 	if (bkpt == 0) {
 	    db_printf("Too many breakpoints.\n");
@@ -105,6 +119,7 @@
 	bkpt->flags = 0;
 	bkpt->init_count = count;
 	bkpt->count = count;
+	bkpt->bkpt_inst = inst;
 
 	bkpt->link = db_breakpoint_list;
 	db_breakpoint_list = bkpt;
@@ -168,9 +183,6 @@
 	         bkpt != 0;
 	         bkpt = bkpt->link)
 		if (db_map_current(bkpt->map)) {
-		    bkpt->bkpt_inst = db_get_value(bkpt->address,
-						   BKPT_SIZE,
-						   FALSE);
 		    db_put_value(bkpt->address,
 				 BKPT_SIZE,
 				 BKPT_SET(bkpt->bkpt_inst));