DF-5761 / verdict.json
{ "finding_id": "DF-5761", "status": "reproduced", "reproduced": true, "impact": "panic", "confidence": "certain", "verdict": "Reproduced deterministically (first shot) with a KLD rig compiling the verbatim production hci_link.c/sco_upper.c (plus hci_unit/hci_event/bt_input/hci_misc/l2cap_lower) against stubbed socket layers: hci_sco_newconn()s LIST_FOREACH over the global sco_pcb list holds a raw pcb pointer across the ->newconn callback (sonewconn window), while the syscall-context sco_detach() (close(2) of a listening SCO socket) LIST_REMOVEs + kfrees the pcbs with no lock anywhere; the walk then dereferences freed memory -> Fatal trap 12, Stopped at hci_sco_newconn+0x99: movq 0x18(%rax),%rax, current process Idle (netisr-context thread). This is a distinct list and sink from the DF-5753 scodone dispatch (DF-5753s authored fix does not cover the con_req-side walk).", "exploit_chain": "Primitive: UAF read through sp_next/sp_flags continuing the listener walk, and an indirect call (*pcb->sp_proto->newconn)(pcb->sp_upper, ...) through freed memory whenever the chunk is reused with attacker-shaped content (same M_BLUETOOTH zone, objcache LIFO reuse demonstrated in the rig with 0xA5-filled reallocations). Realistic ceiling today: kernel panic DoS - triggering requires a controller driver on netbt (incoming SCO Connection Request racing socket close), so the attacker is a local unprivileged socket owner racing close(2) or a paired peer timing connection requests. uid0 escalation not developed: the trigger surface (Bluetooth adjacency) constrains practical exploitation; the rig proves the primitive exists on production code.", "evidence": "run.log + panic.txt: Fatal trap 12: page fault ... fault virtual address = 0x18 ... current process = Idle ... Stopped at hci_sco_newconn+0x99: movq 0x18(%rax),%rax right after the CLOSE thread detached the listening pcbs under the sleeping newconn callback; fix_run.log: SURVIVED: 519149322 walks, 12274611 stress rounds, no hit with the fixed verbatim sources, guest up (uptime in transcript).", "kernel_refs": [ "sys/netbt/sco_upper.c:245", "sys/netbt/sco_upper.c:246", "sys/netbt/sco_upper.c:70", "sys/netbt/hci_link.c:782", "sys/netbt/hci_link.c:783", "sys/netbt/hci_link.c:796" ], "poc_changes": "Adapted the DF-5753 rig (fake hci_if device + hci_attach glue + hci_stub) into a two-lwkt-thread harness: WALK thread loops production hci_sco_newconn(unit, &peer) (exactly the hci_event_con_req netisr context); CLOSE thread runs production sco_attach/sco_listen/sco_detach; the rig newconn callback sleeps 300ms once to model sonewconn cost; after each detach an immediate same-size kmalloc is 0xA5-filled and kept (objcache LIFO reuse model); stress phase hammers both sides after the deterministic round. Two CLOSE-thread progress markers were added during fix debugging.", "attempts": 1, "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": 3000, "guest_dirty": 0, "build_cmd": "cd /root/df5761 && sh build.sh (copies verbatim /usr/src/sys/netbt sources, KLD make)", "run_cmd": "kldload /root/df5761/sco_walk_harness.ko && sysctl hw.scow_setup=1 && sysctl hw.scow_fire=1 (EXPECT: panic in hci_sco_newconn in round 1)", "code_hash": "4382e0ad665a07dbc7714fc561e72acbf37494765641e82d1735acbee5af9995", "notes": "Fix validation took 3 iterations, each catching a real defect in the fix itself: v1 non-atomic --sp_refcnt + unlocked attach -> stress-phase double fault; v2 uninitialized *new on empty snapshot -> write through garbage pointer at hci_sco_newconn+0x228; v3 (shipped fix.diff: atomic refcount + spinlock + snapshot/pin walk + new=NULL init) survived 519M walks + 12.3M stress rounds. The panic output of the v1 stress crash was console-garbled (two cpus faulting); the baseline deterministic panic is clean.", "recommended_fix": "Reference-count sco_pcb (atomic ops) + guard list mutation with a spinlock; hci_sco_newconn snapshots+pins listening candidates under the lock and runs newconn() outside it (see fix.diff).", "fix_status": "fixed", "fix_baseline_reproduced": true, "fix_patched_reproduced": false, "fix_verdict": "fix.diff applied to the guests /usr/src/sys/netbt (sco.h + sco_upper.c + hci_link.c), rig rebuilt from the patched verbatim sources: the deterministic close-under-callback round completes cleanly (CLOSE markers show all four victims detached with the walks pins holding), then 519,149,322 hci_sco_newconn walks against 12,274,611 concurrent attach/listen/detach stress rounds complete with no fault; guest up.", "fix_evidence": "fix_run.log: [harness] CLOSE: starting detaches / detached victim 0..3 / CLOSE thread: 4 listening pcbs detached followed by [harness] SURVIVED: 519149322 walks, 12274611 stress rounds, no hit and uptime; build.log shows the patched sources compiled -Werror clean." } |