⬢ DragonFlyBSD Kernel Audit
DF-5732 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-5732",
  "status": "reproduced",
  "reproduced": true,
  "impact": "dos",
  "confidence": "certain",
  "verdict": "Reproduced live on the stock snapshot kernel: db_show_regs (sys/ddb/db_print.c:46-68) ends with db_print_loc_and_inst(PC_REGS(DDB_REGS)) which disassembles ddb_regs.tf_rip; tf_rip is DDB-writable via `set $rip` (db_trace.c:107 + db_variables.c:120-129). After `set $rip = 0xdead000000000000`, `show registers` prints the register dump then hits Fatal trap 9: general protection fault - trap()s db_active funnel (trap.c:414-427) routes every trap taken under the db_nofault guard (db_interface.c:242) into trap_fatal() (full crash banner + register disclosure) before kdb_traps db_nofault longjmp (db_interface.c:140-144) quietly recovers to db>. The longjmp also skips the funnels --gd_trap_nesting_level (leak, trap.c:422-424) and the abandoned nested dispatch corrupts session-resume state (DF-5727s pack independently captured panic: trap: critical section count mismatch! on cont after this exact funnel). Same funnel hit via `x/i 0xdead000000000000` (trap 9 at the identical RIP 0xffffffff80bccb70 = db_read_bytes read loop). Rated Low: trigger requires the already-privileged DDB console; the defect is a debugger-robustness/information-noise bug in the kernels primary crash-response tool, not an escalation.",
  "exploit_chain": "root/serial-console DDB session -> set $rip = 0xdead000000000000 (raw db_expr_t write, no validation) -> show registers -> db_print.c:68 db_print_loc_and_inst -> db_disasm dereferences noncanonical rip under db_nofault -> #GP -> trap.c:420 funnel -> trap_fatal banner + register disclosure + leaked gd_trap_nesting_level -> kdb_trap nofault longjmp recovers db> with corrupted resume state (crit-count panic on cont per DF-5727 capture). No unprivileged path; console access is root-equivalent.",
  "evidence": "run4-decisive-setrip-showregisters.log (register dump then Fatal trap 9 banner, recovered db>); panic.txt (banner extract); session0-sweep.log (x/i noncanonical sibling trigger at same RIP + full modifier/format sweep incl. harmless /x /99 modifiers, $maxoff=-1 cosmetic attribution, radix 8/10 formatting); run1-control-valid-xi.log + run3-control-held-connection.log + run3-sshpoll.log (honest controls: clean DDB enter/cont also fails to resume on this snapshot, so the post-cont wedge is not claimed as fault-attributed); serial-full.log (complete console capture); VERDICT.md (full narrative + killed candidates); fix.diff (funnel fix, never applied to sys/)",
  "kernel_refs": [
    "sys/ddb/db_print.c:68",
    "sys/ddb/db_print.c:46",
    "sys/platform/pc64/x86_64/db_trace.c:107",
    "sys/ddb/db_variables.c:120",
    "sys/platform/pc64/x86_64/db_interface.c:242",
    "sys/platform/pc64/x86_64/db_interface.c:140",
    "sys/platform/pc64/x86_64/trap.c:414",
    "sys/platform/pc64/x86_64/trap.c:422",
    "sys/ddb/db_examine.c:178"
  ],
  "poc_changes": "No seed PoC existed (pass-2 discovery). Built the rig fresh: socket-serial VM (serial_vm.sh, adapted from proven DF-5728 rig) + paced serial driver (drive.py); DDB entry via backgrounded sysctl debug.enter_debugger=ddb.",
  "attempts": 4,
  "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul  2 06:02:54 UTC 2026     root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC  x86_64",
  "runtime_sec": 520,
  "guest_dirty": 1,
  "build_cmd": "none (DDB console repro; no compile)",
  "run_cmd": "findings/poc/DF-5732/serial_vm.sh up; ssh root@dfbsd sysctl debug.enter_debugger=ddb & sleep 6; DF5732_SOCK=/tmp/opencode/df-5732-ser.sock python3 findings/poc/DF-5732/drive.py set $rip = 0xdead000000000000 3 show registers 6",
  "code_hash": "92bfc87840082b15e018327ea6d7fc9e9b99e10345c4a1f49a260be3af98d15b",
  "notes": "Pass-2 context: file is 69 LOC = db_show_regs only (prompts db_print_cmd/db_hex_value map was stale - they live in db_examine.c/db_output.c/db_lex.c). Negative results with citations in VERDICT.md: modifier dispatch unreachable here (dummy4) and verified harmless live; db_num_to_str buf[25] fits worst case; db_format_radix/_hex ksnprintf-bounded and offset provably in [0,2^32) via db_sym.c:210/221/225 unsigned-int diff; name/offset never uninitialized (db_sym.c:225,237-239); register table type-safe (tf_* are register_t, frame.h:79-83); $maxoff cast interplay is DF-5730s radius (cosmetic, verified live); stale-modifier strcpy OOB unreachable (fresh tIDENT always precedes, db_command.c:308,318; String too long longjmps first, db_command.c:481-487). Post-cont guest death occurs even on clean DDB sessions on this snapshot (controls) - claimed impact limited to the deterministic banner/disclosure/nesting-leak/resume-corruption, dos-class. Related-but-distinct: DF-5561 (db_global_jmpbuf re-entry/db_active double-increment - different longjmp, loud stop_cpus panic), DF-5727 (watch size bug; its pack incidentally captured the crit-count resume panic used here as corroboration).",
  "recommended_fix": "In trap()s DDB funnel, honor db_nofault BEFORE trap_fatal(): export db_nofault_pending()/db_nofault_take() from db_interface.c and longjmp quietly for guarded probes instead of running the crash banner and abandoning the nesting bookkeeping (fix.diff).",
  "fix_status": "not_testable",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": null,
  "fix_verdict": "fix.diff authored post-verification against the read-only sys/ tree (trap.c funnel pre-check + db_interface.c accessors + ddb.h decls, pc64 and vkernel64). Not kernel-build-validated: Low-severity dos/robustness finding, and fix-validation kernel builds are contract-reserved for memory-corruption findings. Baseline reproduction is deterministic (banner on both triggers).",
  "fix_evidence": "fix.diff; baseline = run4-decisive-setrip-showregisters.log / panic.txt"
}