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)
PoC verification
Evidence pack
findings/poc/DF-2347 Β· 7 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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_testableConfirmed 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.
No comments yet.