⬢ DragonFlyBSD Kernel Audit
DF-5721 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-5721",
  "status": "reproduced",
  "reproduced": true,
  "impact": "none",
  "confidence": "certain",
  "verdict": "DDB software watchpoints (`watch`) are inert on x86_64: live-verified that a 4-byte watch on the every-tick-written kernel global `ticks` never stops the kernel (ticks advanced to 0xb342 with the watch listed as armed, machine alive), because the pmap RO protection (db_watch.c:226-229) faults as a plain page fault that vm_fault() silently rewires writable, while the watchpoint classifier the resume choreography needs is dead code (db_find_watchpoint #ifdef notused, db_watch.c:241-273) and IS_WATCHPOINT_TRAP is hardwired 0 (db_machdep.h:67), making the watchpt arm of db_restart_at_pc (db_run.c:199-206) unreachable. Root-console-only surface (unprivileged probes all negative), no memory unsafety: Low severity, debugger-correctness defect that leaves an operator debugging with a dead instrument.",
  "exploit_chain": "",
  "evidence": "watch-desync.serial.log (db> watch ticks / c / machine alive / second session: ticks: b342 advancing, show watches lists it, zero stops); README.md root-cause chain with path:line; unpriv-probes.log (all reachability probes negative); fix.diff refuses the feature on x86_64",
  "kernel_refs": [
    "sys/ddb/db_run.c:199",
    "sys/ddb/db_run.c:206",
    "sys/ddb/db_run.c:208",
    "sys/ddb/db_trap.c:54",
    "sys/ddb/db_watch.c:218",
    "sys/ddb/db_watch.c:241",
    "sys/cpu/x86_64/include/db_machdep.h:67",
    "sys/platform/pc64/x86_64/trap.c:629"
  ],
  "poc_changes": "No seed PoC existed. Built the rig from scratch: socket-serial QEMU guests (serial_vm.sh SMP / serial_vm1.sh UP), a paced serial-line DDB driver (drive_ddb.py), and three unprivileged reachability probes (probe_int3.c, probe_step.c, probe_dbregs.c) compiled on the guest.",
  "attempts": 3,
  "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #0 Thu Jul 2 06:02:54 UTC 2026 x86_64",
  "runtime_sec": 480,
  "guest_dirty": 0,
  "build_cmd": "cc -O0 -o probe_int3 probe_int3.c && cc -O0 -o probe_step probe_step.c && cc -O0 -o probe_dbregs probe_dbregs.c   (on guest; DDB sessions need no build)",
  "run_cmd": "./serial_vm1.sh up; ssh dfbsd sysctl debug.enter_debugger=ddb & ; ./drive_ddb.py watch ticks 4 c 6   # then re-enter and inspect x/x ticks + show watches",
  "code_hash": "d2ddfc831df07815aad62268d74dafffd091bac78ffa1e8ddcc391f1cdee803a",
  "notes": "Full implementation fix = resurrect db_find_watchpoint in the kernel page-fault path plus a real IS_WATCHPOINT_TRAP; fix.diff ships the safe refuse-on-x86_64 variant. The watch was initially suspected in the interrupt-context resume panic; bisect (DF-5722) proved it innocent.",
  "recommended_fix": "Refuse software watchpoints on x86_64 (define DB_NO_SOFTWARE_WATCHPOINTS in db_machdep.h, reject in db_watchpoint_cmd) until a real watchpoint trap classifier exists; point users at hwatch.",
  "fix_status": "not_testable",
  "fix_baseline_reproduced": true,
  "fix_patched_reproduced": false,
  "fix_verdict": "fix.diff authored post-verification against the read-only sys/ tree; not applied/rebuilt (kernel rebuild infeasible in-run on the 1-vCPU validation guest; Low-severity feature-disable, not memory corruption, so rebuild validation is not mandatory per contract).",
  "fix_evidence": "findings/poc/DF-5721/fix.diff"
}