⬢ DragonFlyBSD Kernel Audit
DF-5749 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-5749",
  "status": "reproduced",
  "reproduced": true,
  "impact": "panic",
  "confidence": "certain",
  "verdict": "REPRODUCED (4 baseline crashes, 3 rig generations). l2cap_detach() on the cpu0 netisr protocol port (close(2)) races the softclock l2cap_rtx() timeout with zero synchronization: both tear down the same l2cap_channel/l2cap_req. Observed: Fatal trap 12 supervisor WRITE at 0x70 (TAILQ_REMOVE through NULL/reused lr_link in l2cap_request_free), panic: zone: freeing free entry from zfree() <- l2cap_rtx() (request double-free), and hard freezes. Production-code KLD rig compiles l2cap_upper/lower/misc/signal.c verbatim (md5-verified) and drives only production entry points; the pure production-pair rig (single port-serialized worker + real 1-tick callout delivery on cpu0 softclock vs detach) also kills the unfixed kernel. fix.diff v6 (global l2cap_lock + exactly-once request free + single-execution close claim + L2CAP_CLOSING completion flag) survives the control and burst rigs but NOT the production-pair stress: a callout-delivered l2cap_rtx() still executes after l2cap_request_free()s synchronizing callout_stop() returned and zfree ran (fault VA 0x12342380 = ZENTRY_FREE+8, i.e. req->lr_chan of a freed zone entry) -> fix_status fix_failed; a complete fix needs deferred request reclamation or the upstream NetBSD global-lock + callout_halt-with-lock model.",
  "exploit_chain": "unprivileged local user (system with a Bluetooth controller; netbt KLD) -> connect(2) arms the 30s l2cap rtx (no peer response needed) -> close(2) timed against the timeout -> double l2cap_close()/l2cap_request_free -> kernel page fault / zone double-free / heap corruption (panic class; memory-corruption primitive demonstrated via the double free and freed-slot writes, no uid0 chain developed -- local DoS with corruption potential)",
  "evidence": "panic.txt (both trap signatures); run.log / console_baseline_final.txt (pristine-reset baseline: zone: freeing free entry zfree<-l2cap_rtx); console_panic.txt (Fatal trap 12 write at 0x70 in l2cap_request_free+0x70); fix_run.log (v6-patched kernel still panicking at l2cap_rtx+0x9, fault 0x12342380=ZENTRY_FREE+8); setup.log (verbatim-source md5s); VERDICT.md (full interleaving analysis + fix iteration record)",
  "kernel_refs": [
    "sys/netbt/l2cap_upper.c:259",
    "sys/netbt/l2cap_upper.c:267",
    "sys/netbt/l2cap_upper.c:282",
    "sys/netbt/l2cap_misc.c:184",
    "sys/netbt/l2cap_misc.c:163",
    "sys/netbt/l2cap_lower.c:69",
    "sys/netbt/l2cap_lower.c:78",
    "sys/netbt/l2cap_lower.c:97",
    "sys/netbt/l2cap_socket.c:248",
    "sys/kern/kern_timeout.c:1091",
    "sys/vm/vm_zone.c:76"
  ],
  "poc_changes": "rig written fresh (no seed): KLD compiles production l2cap_upper/lower/misc/signal.c verbatim with faithful hci stubs (hci_acl_close mirrors hci_link.c:153-164); three race drivers developed (race3 direct-callback-fire, race4 real-callout churn, race5 pure production pair) after the single-threaded burst mode proved unable to interleave mid-body on one cpu (100k clean rounds documented); fix.diff iterated v1->v6 during validation",
  "attempts": 12,
  "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": 14400,
  "guest_dirty": 0,
  "build_cmd": "sh /root/l2uh/build.sh   # copies verbatim /usr/src/sys/netbt sources, make -m /usr/src/share/mk SYSDIR=/usr/src/sys",
  "run_cmd": "kldload /root/l2uh/l2uh_harness.ko; sysctl hw.l2uh_once=1; sysctl hw.l2uh_race3=2000   # expect panic",
  "code_hash": "bc87c90bed6698842e0d102f1a2d9a4272767297b1e8e1f324365244cb23b51f",
  "notes": "netbt is KLD-only in GENERIC (kldload netbt); harness self-contains the production files so netbt.ko is not needed. Baseline crashes: race3 x3 (trap-12 write @0x70, hard freeze, zone double-free) + race4 hard wedge. Race4s armer also races active-list ops that production serializes on the protocol port (rig artifact, documented); race5 is the faithful pair and also kills the unfixed kernel. Same-cpu burst mode cannot collide (LWKT phase alignment) - 100k clean rounds, documented in VERDICT.md.",
  "recommended_fix": "Serialize the L2CAP channel/request teardown end-to-end: upstream-model global l2cap lock held across rtx/close/request lifecycle plus a callout-halt-with-released-lock primitive (or defer l2cap_req zone reclamation until its callback generation is provably dead). v6 fix.diff attached as partial stepping stone only.",
  "fix_status": "fix_failed",
  "fix_baseline_reproduced": true,
  "fix_patched_reproduced": true,
  "fix_verdict": "v6 fix.diff applied to guest /usr/src, module rebuilt from patched verbatim sources (md5-verified), guest rebooted: control round and burst rig clean, but the pure production-pair race5 still panics after ~500-5000 rounds (l2cap_rtx on a post-zfree request: fault VA 0x12342380 = ZENTRY_FREE+8). The synchronizing callout_stop() in l2cap_request_free does not close the stop-vs-dispatch window on the foreign cpus softclock queue; complete fix requires deferred reclamation or the upstream global-lock + callout_halt-with-lock model.",
  "fix_evidence": "fix_build.log; fix_run.log (patched-kernel panic at l2cap_rtx+0x9); VERDICT.md Fix validation section (v1->v6 iteration record)"
}