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)
PoC verification
Evidence pack
findings/poc/DF-0420 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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 |
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_bufferloaded into localmat :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); elseNG_SEND_DATA_ONLY->NG_SEND_DATA_FLAGS(netgraph.h:894-903, consumes mbuf + setsm=NULL). Either way the mbuf is GONE and the localmis NULL β butsc->sc_isoc_in_bufferstill holds the stale pointer. - Loop continues (
total>0),m==NULL,MGETHDR/MCLGETfail ->return -1(:1123/:1130). Line :1178 (sc_isoc_in_buffer = m) is NOT executed. sc_isoc_in_buffernow 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 viausbd_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.
Recommended fix (compile-validated)
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_testablecompile 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.
No comments yet.