β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-0420

Use-after-free in SCO reassembly buffer: sc_isoc_in_buffer retains dangling pointer after mbuf forwarded and realloc fails

Summary

ubt_isoc_read_one_frame(:1103-1181): loads m=sc->sc_isoc_in_buffer(:1113). When SCO frame complete, ubt_fwd_mbuf_up(:1173) forwards m, sets m=NULL. If loop continues and MGETHDR/MCLGET fails(M_NOWAIT memory pressure), returns -1 at :1123/:1130 WITHOUT clearing sc_isoc_in_buffer(:1178 not reached). Next callback loads dangling pointer, reads m->m_pkthdr.len from freed memory, writes USB SCO data into freed mbuf via usbd_copy_out(:1154-1155). Comment "XXX out of sync!" acknowledges. Malicious USB BT dongle or USB-passthrough crafts isoc sizes + mbuf exhaustion. UAF read (info leak) + UAF write (controlled heap corruption).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-0420 Β· 6 files
FileTypeDescriptionSize
fix.diff suggested-fix NULL sc_isoc_in_buffer after ubt_fwd_mbuf_up handoff (ng_ubt.c:1173) 818 B view raw
module_build.log build-log ng_ubt.ko built with fix applied, RC=0 (compile-validated) 1.1 KB view raw
VERDICT.md verdict full UAF trace + reachability analysis + fix 4.3 KB ↓ raw
env.txt environment uname (guest has no BT hardware) 150 B view raw
../fix_build_combined.log build-log Combined 41-finding kernel build (rc=0, -Werror clean) 5.6 MB ↓ download
../fix_build_summary.txt build-summary Summary of the combined 41-finding kernel build 826 B view raw
VERDICT.md verdict full UAF trace + reachability analysis + fix
↓ download raw

DF-0420 β€” Use-after-free in SCO reassembly buffer (ng_ubt)

Verdict: LATENT UAF (code-confirmed real, NOT triggerable on this guest)

Summary

ubt_isoc_read_one_frame() (sys/netgraph7/bluetooth/drivers/ubt/ng_ubt.c:1103) manages a per-softc SCO reassembly mbuf in sc->sc_isoc_in_buffer. When a complete SCO frame is assembled it is handed off with ubt_fwd_mbuf_up(&m) (:1173), which frees/queues the mbuf and NULLs the local m (via NG_FREE_M/NG_SEND_DATA_FLAGS, both set the macro arg to NULL). The field sc_isoc_in_buffer is only re-assigned at :1178 (sc_isoc_in_buffer = m), AFTER the while-loop. If the loop continues and a new buffer allocation fails (MGETHDR/MCLGET with M_NOWAIT under memory pressure, :1120/:1126), the function returns -1 at :1123/:1130 without reaching :1178, so sc_isoc_in_buffer retains the pointer to the just-freed mbuf. The next USB isoc callback then does:

m = sc_isoc_in_buffer; // dangling (:1113) got = m->m_pkthdr.len; // UAF READ (:1142) usbd_copy_out(... mtod(m,...) + len); // UAF WRITE (:1154-1155)

The in-tree comment "XXX out of sync!" at :1123/:1130 acknowledges the lost synchronization but not the dangling-pointer consequence.

Why this is a genuine UAF (trace)

  • sc_isoc_in_buffer loaded into local m at :1113 (start of callback).
  • Frame completes -> ubt_fwd_mbuf_up(sc, &m) at :1173. Inside (ng_ubt.c:1279): if hook NULL -> NG_FREE_M(*m) (netgraph.h:919-925, frees
  • sets *m=NULL); else NG_SEND_DATA_ONLY -> NG_SEND_DATA_FLAGS (netgraph.h:894-903, consumes mbuf + sets m=NULL). Either way the mbuf is GONE and the local m is NULL β€” but sc->sc_isoc_in_buffer still holds the stale pointer.
  • Loop continues (total>0), m==NULL, MGETHDR/MCLGET fail -> return -1 (:1123/:1130). Line :1178 (sc_isoc_in_buffer = m) is NOT executed.
  • sc_isoc_in_buffer now points at freed memory. Next callback derefs it. This is a UAF with attacker-influenced content (the SCO audio bytes delivered by the USB controller land in the freed mbuf via usbd_copy_out).

Reachability β€” NOT triggerable on this guest (valid hard blocker)

The ubt driver (sys/conf/files:1671, optional netgraph7_bluetooth_ubt) attaches to a real USB Bluetooth controller (USB VID/PID match in usbdevs), and ubt_isoc_read_one_frame is an isochronous USB transfer callback that fires only when the host controller delivers SCO frames from such a device. On this audit guest: - there is no Bluetooth hardware (no USB BT controller; QEMU does not emulate one that attaches to ubt); - the driver is not compiled into the default X86_64_GENERIC kernel (it is optional netgraph7_bluetooth_ubt, absent from sys/config/X86_64_GENERIC); - /boot/kernel/ng_ubt.ko is not present. So the code path is dead on this guest and cannot be exercised by any harness without emulated USB-Bluetooth hardware producing SCO isochronous traffic.

The realistic trigger conditions that WOULD make it live: a host with a USB BT dongle + an active SCO (voice) link + transient M_NOWAIT mbuf pressure (achievable by an attacker who controls USB traffic via a malicious device, per the CVSS AV:P physical vector). The CVSS AV:P/AC:H reflects this.

Exploit chain

Memory-corruption class (UAF write into a freed mbuf via usbd_copy_out), but requires physical USB-BT access, so the realistic impact ceiling is a privileged-to-kernel escalation for a malicious USB device, not an unprivileged local privesc. No chain developed on this guest (path is dead without HW); the primitive is characterized by code trace above. No uid=0 claim.

fix.diff NULLs sc->sc_isoc_in_buffer immediately after the ubt_fwd_mbuf_up handoff (:1173), so the field never retains a pointer to a forwarded/freed mbuf. The existing line :1178 then either keeps it NULL (loop ended cleanly) or sets it to a fresh partial buffer. Validated to apply cleanly + compile (built ng_ubt.ko with make in sys/netgraph7/bluetooth/drivers/ubt, RC=0). Not live-tested (no BT HW).

Files

  • fix.diff β€” NULL sc_isoc_in_buffer after forward (ng_ubt.c:1173)
  • module_build.log β€” ng_ubt.ko build with fix applied (RC=0)
  • env.txt β€” guest environment (no BT HW)

Fix verification

not_testable

compile validated

see evidence pack

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

Source-confirmed. ubt SCO reassembly sc_isoc_in_buffer dangling on MGET fail. No USB BT HW. Module not in GENERIC.