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

Lockless check/use race on bus->ifp between usbpf_xfertap and clone/teardown causes NULL-deref/UAF panic

Summary

usbpf_xfertap checks bus->ifp!=NULL at entry (:362) but then dereferences bus->ifp->if_bpf again at bpf_tap (:488) with no lock held in between. usbpf_clone_destroy concurrently sets ubus->ifp=NULL (:191) and frees ifnet via if_free (:194) without taking any lock the tap path holds. DONE tap path called LOCKLESS (usb_transfer.c:2393 USB_BUS_UNLOCK then :2434 calls usbpf_xfertap). destroy and lockless DONE tap unsynchronized: destroy nulls/frees bus->ifp while tap between check (:362) and use (:488). Tap reads bus->ifp again at :488 observes NULL -> NULL-pointer page fault panic or UAF on freed ifp. SUBMIT tap holds USB_BUS_LOCK during tap but destroy never takes that lock either so SUBMIT path races identically. Local DoS kernel panic. Triggering destroy requires SIOCIFDESTROY gated by SYSCAP_RESTRICTEDROOT. [VERIFIED pass2: reproduced - REAL lockless usbpf_xfertap vs concurrent ifp-NULLing panics at +0x447 reading if_bpf through NULL exactly at the 362-check vs 488-use TOCTOU; read-once fix-validated 200k taps vs 193M NULL stores no panic]

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2347 Β· 7 files
FileTypeDescriptionSize
env.txt β€” 412 B view raw
fix.diff β€” 614 B view raw
panic.txt β€” 724 B view raw
rig β€” 4.0 KB ↓ download
run_fixed_kernel.log β€” 2.8 KB view raw
verdict.json β€” 2.0 KB view raw
fix.diff β€” 614 B view raw

Fix verification

not_testable
↓ fix.diffper-fix-DF-2347

Confirmed kernel references

Detail

Evidence (decisive lines)

panic.txt (fault 0x60 at usbpf_xfertap+0x447 movq 0x60(%rax)), run_fixed_kernel.log (survived: taps=200000 null_stores=193087519), rig/dfbatch_usbpf.c, fix.diff

PoC changes

fabricated xfer widened to 255 zero-length frames so the window is the honest production width; bdev taken from the real global device list so device_get_unit works

Verified recommended fix

read bus->ifp once at the sanity check and use the captured pointer at bpf_tap

Verdict

The REAL in-kernel usbpf_xfertap called lockless exactly as the DONE path does, against a fabricated bus whose ifp a second thread clears in a loop (what usbpf_clone_destroy does at usb_pf.c:191 before if_free): the tap races its :362 NULL-check against its :488 bpf_tap(bus->ifp->if_bpf,...) use and the kernel panics at usbpf_xfertap+0x447 reading offset 0x60 (if_bpf) through the NULL ifp - fault virtual address 0x60, exactly the predicted second-read TOCTOU. With the ifp-captured-once fix, 200,000 taps against 193 million NULL stores complete without a panic. Destroy is gated by SIOCIFDESTROY/clone-destroy (root), so the demonstrated race is a privileged-trigger local DoS; the residual if_free UAF window is documented for upstream.