# DF-5722 — VERDICT

**Status: reproduced** (deterministic, 3/3 boots, stock X86_64_GENERIC
snapshot kernel on a 1-vCPU QEMU/KVM guest with socket serial console).
**Impact: panic** (root-console-triggerable kernel panic; debugger
robustness defect, not privilege escalation). **Confidence: certain** for
the trigger and the layer attribution; the exact leak site inside the
session window is narrowed to db_interface.c:160-201 + interrupt
entry/exit bookkeeping and would need dump analysis to pin to a single
instruction (three vmcores are preserved on the guest /var/crash).

## Narrative

During the pass-2 audit of `sys/ddb/db_run.c` I live-verified the whole
step/continue engine on a UP guest (thread-context sessions all clean:
TF set/clear symmetry — `rfl`=0x246 TF-clear at every stop after `s`;
breakpoint dispatch and count handling — "Breakpoint at" banners, count
reset, STEP_INVISIBLE invisible-step roundtrips; continue paths). While
verifying the breakpoint dispatch in interrupt context (`b hardclock`),
every resume panicked:

  `panic: td_critcount is/would-go negative! <td> -1`
  crit_panic ← crit_panic ← crit_exit_wrapper
  (sys/kern/lwkt_thread.c:1772-1781, called from crit_exit() at
  sys/platform/pc64/x86_64/db_interface.c:201 — the C wrapper, the only
  crit_exit_wrapper call site on this path)

Bisect (full matrix in README):
- thread-context sessions: sysctl-entry, single-step, breakpoint-hit in
  `sys_kill` resumed with `c` — ALL alive.
- interrupt-context session (breakpoint in `hardclock`, the clock path):
  PANIC on resume — minimal recipe = `b hardclock`, `c`, `d hardclock`,
  `c` (run4-minimal-panic.serial.log).
- The DF-5721 software-watchpoint was exonerated by run #3 (panic without
  any watchpoint).
- The SMP-guest starvation (DF-5720 lead) is a separate failure: on the
  SMP guest the *second* kdb_trap entry's stop_cpus hangs before any
  resume completes; on UP (stop_cpus no-op) the resume completes and the
  interrupt-context panic is what surfaces. Both live-captured; neither
  is db_run.c (see below).

## Why db_run.c is exonerated (this audit's file)

- The engine is context-agnostic: db_stop_at_pc/db_restart_at_pc ran
  identical code in the surviving thread-context controls and the
  panicking interrupt-context runs; captured state identical
  (`db_run_mode` 0/4/5 transitions per code, TF clear at stops, DRs
  idle, breakpoint list operations symmetric).
- The panic fires in the machine layer's critical-section accounting
  around/inside kdb_trap (db_interface.c:160-201) in interaction with
  interrupt entry/exit TD_CRITCOUNT bookkeeping (ipl.s doreti/doreti_soft
  temporary decls at ipl.s:281-283/304-306, interrupt stub guarded
  inc/dec msi_vector.s:56-79): across an interrupt-context session one
  td_critcount decrement goes unmatched, and the session-exit
  crit_exit() underflows (0 → -1 → crit_panic).

## Impact

Root console only (all unprivileged entry routes closed — probes in the
DF-5721 pack: user int3 → SIGTRAP; PT_STEP artifact filtered;
PT_SETDBREGS kernel-addr EINVAL; sysctl door EPERM). Consequence for the
privileged operator: any breakpoint in interrupt-context code (clock,
ITHREAD, driver interrupt paths — the bread and butter of kernel
debugging) converts the first `c` into a panic + dump + reboot. Local
DoS requires root, so: Low severity, panic/dos impact class.

## Fix

`fix.diff` (defensive, authored against the read-only sys/ tree):
kdb_trap saves `curthread->td_critcount` after its crit_enter() and
force-restores it before returning (replacing the bare crit_exit() at
db_interface.c:201). This makes the debugger session transparent to the
interrupted context's crit accounting regardless of where inside the
session the count leaked, and directly prevents the observed
`would-go negative` exit. NOT validated by rebuild+rerun: the panic
recipe requires a UP guest (SMP guests wedge in stop_cpus first, a
different defect) and a kernel rebuild on the 1-vCPU guest was not
feasible in-run. fix_status=not_testable. The upstream-quality fix is to
find the unmatched decrement (dump analysis of the preserved vmcores);
the save/restore is the safe belt that stays correct even then.

## Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest `make -j6 nativekernel KERNCONF=X86_64_GENERIC` completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
