{
  "finding_id": "DF-5561",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "dos",
  "confidence": "certain",
  "verdict": "kdb_trap()s db_global_jmpbuf re-entry path re-executes db_active++ (db_interface.c:170-172) while the abandoned outer invocations db_active-- (line 180) never runs, permanently leaking db_active >= 1; trap.c:420 then routes every trap (incl. ordinary user-mode page faults/ASTs) into trap_fatal()+kdb_trap(). Reproduced deterministically on the stock snapshot kernel: counter read 2 in-kernel at the nested prompt after ddb `call 0x10()` faulted, post-continue trap storm (kernel: type 7 trap from a user-mode AST), panic: stop_cpus() failed with a double-kdb_trap backtrace, permanent all-CPU db> wedge, ssh dead. With the one-line fix (guard the increment), the same sequence keeps db_active at 1 and produces no storm and no panic \u2014 fix validated on a rebuilt guest kernel.",
  "exploit_chain": "root/NMI/panic-induced ddb session -> ddb command faults outside db_nofault (e.g. `call <badaddr>`, db_fncall has no fault guard) -> nested kdb_trap default case longjmps to db_global_jmpbuf -> setjmp returns nonzero, db_active++ runs again -> `c` exits only the re-entered session (single db_active--) -> db_active stuck at 1 -> trap.c:420 diverts every subsequent trap to trap_fatal/kdb_trap -> all-CPU stop_cpus on each trap -> stop_cpus mask mismatch -> panic: stop_cpus() failed -> eternal db> wedge (no dump, no reboot, no remote access).",
  "evidence": "serial.baseline.txt lines 210-268 (print *db_active: 1 -> call 0x10() -> kernel: type 12 trap -> print *db_active: 2 -> c -> Fatal trap 7 user-mode AST -> panic: stop_cpus() failed -> nested kdb_trap backtrace); panic.txt (kdb_trap() at kdb_trap+0x38f twice); serial.patched.txt (same trigger, counter stays 1, CPU restart, silence \u2014 no storm); VERDICT.md caveat section for the orthogonal post-ddb-exit freeze control (heartbeat test, stock==patched).",
  "kernel_refs": [
    "sys/platform/pc64/x86_64/db_interface.c:152",
    "sys/platform/pc64/x86_64/db_interface.c:170",
    "sys/platform/pc64/x86_64/db_interface.c:172",
    "sys/platform/pc64/x86_64/db_interface.c:180",
    "sys/platform/pc64/x86_64/db_interface.c:195",
    "sys/platform/pc64/x86_64/trap.c:420",
    "sys/platform/pc64/x86_64/trap.c:1096",
    "sys/ddb/db_command.c:515",
    "sys/ddb/db_sysctl.c:73"
  ],
  "poc_changes": "Trigger is a ddb console sequence (sysctl debug.enter_debugger=ddb vehicle + `call 0x10()` + `print *db_active` + `c`), not a compiled C PoC. Rig had to be built: vm.shs -serial file: backend is output-only, so the guest was relaunched with a socket chardev + logfile (serial_vm.sh) and a persistent paced FIFO->socket writer that must DRAIN the socket or QEMU backpressures the emulated 16550 and freezes poll-mode cnputc (serial_writer.py). ddb prints symbols as addresses: use `print *db_active`, not `print db_active`.",
  "attempts": 3,
  "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": 5400,
  "guest_dirty": 0,
  "build_cmd": "cd /usr/src && patch -N -p1 < /root/df5561_fix.diff && make -j6 nativekernel KERNCONF=X86_64_GENERIC && make installkernel KERNCONF=X86_64_GENERIC",
  "run_cmd": "sh serial_vm.sh up; python3 serial_writer.py &; ./dfbsd-qemu/vm.sh run_root sysctl debug.enter_debugger=ddb; then at db> via /tmp/opencode/df-5561-ser.in: print *db_active; call 0x10(); print *db_active; c  (see README.md)",
  "code_hash": "0c58ac0ac32c81fb0692087f6147bfdcf30ea79f3ddd4251e59b693c36913c73",
  "notes": "Pass-2 re-audit of sys/platform/pc64/x86_64/db_interface.c. Adjacent-class observations recorded for the orchestrator, not filed here (wrong file): (1) trap.c:422-424 gd_trap_nesting_level is leaked by the same longjmp abandonment; (2) post-ddb-exit userland timer freeze on this rig reproduces on BOTH stock and patched with zero triggers (control-tested with a /dev/console heartbeat) \u2014 separate pre-existing exit-path defect, needs its own file audit of the cpustop/systimer rearm path. The vkernel64 twin (sys/platform/vkernel64/x86_64/db_interface.c:169-171) carries the identical db_active++ pattern and should receive the same guard. Cross-checks done: trap_msg[] table bounds are correct (31 entries, MAX_TRAP_MSG=30, type<=255, trap.c:106-139); gdb_tab NULL class is DF-5475 (not re-reported); P-packet stack overflow class is DF-1080/DF-2361 (not re-reported); gdb stub arms db_jmpbuf itself per-access (x86_64-gdbstub.c:288,301) so nofault faults in gdb mode are handled.",
  "recommended_fix": "Guard the re-entry increment: `if (db_active == 0) db_active++;` after the setjmp in kdb_trap() (and the same guard in the vkernel64 twin).",
  "fix_status": "fixed",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "Rebuilt kernel with the guarded increment inside the guest (nativekernel); identical trigger sequence leaves db_active=1 at the nested prompt (stock: 2) and produces no post-continue trap storm and no stop_cpus() panic \u2014 the leak signature is eliminated. Residual post-ddb-exit timer freeze is the pre-existing orthogonal exit-path issue (control-verified identical on stock with zero triggers).",
  "fix_evidence": "serial.patched.txt (counter 1 at nested prompt, clean restarting CPUs / restarted, no kernel: type storm, no panic); build.log; serial.baseline.txt for the contrast."
}