Disabled bpf_peers_present early-out: tap kmallocs and copies every USB transfer payload even with no bpf listener (36-44x per-transfer overhead)
Summary
DFly #if 0'd FreeBSD's early-out while its bpf lazily assigns if_bpf: with a root-created usbus clone and NO listener, every USB transfer still pays precompute + kmalloc + full payload copy + kfree - measured 1094-1256 cycles/call vs 25-34 (36.2-43.5x) on the SUBMIT paths under the USB BUS lock, for as long as the unwatched tap exists. Restore-the-early-out fix (ifp->if_bpf==NULL return).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5531 Β· 10 files| File | Type | Description | Size | |
|---|---|---|---|---|
| tap_cost.c | β | 5.6 KB | view raw | |
| Makefile | β | 103 B | β download | |
| build.sh | β | 134 B | view raw | |
| run.sh | β | 172 B | view raw | |
| build.log | β | 490 B | view raw | |
| run.log | β | 524 B | view raw | |
| run.2.log | β | 526 B | view raw | |
| fix.diff | β | 309 B | view raw | |
| VERDICT.md | β | 2.7 KB | β raw | |
| verdict.json | β | 3.9 KB | view raw |
DF-5531 β VERDICT
Status: reproduced (behavioral/performance; no memory-safety impact).
Environment
Same guest as DF-5530 (DragonFly 6.5-DEVELOPMENT, bpf compiled in,
qemu-xhci). tap_cost.c KLD calls the production usbpf_xfertap()
(global symbol, sys/bus/u4b/usb_pf.c:345) with a synthetic 8x512 BULK-OUT
xfer whose xroot->bus points at the real usbus0 softc, after
ifconfig usbus0 create with no bpf listener attached.
Result (two runs, two independent boots)
run 1: (A) ifp set, if_bpf==NULL : 1094 cycles/call (B) ifp NULL: 25 -> 43.5x run 2: (A) ifp set, if_bpf==NULL : 1256 cycles/call (B) ifp NULL: 34 -> 36.2x
run.log prints bus ifp=0xfffff8008fd1e480 if_bpf=0 β live proof that
DragonFly's bpf leaves ifp->if_bpf NULL until a listener attaches
(net/bpf.c:297/324). Case (A) therefore measures exactly the
created-but-unwatched state: full precompute + kmalloc(totlen)
(usb_pf.c:381) + usbd_copy_out of all 4096 payload bytes
(usb_pf.c:468) + bpf_tap(NULL,β¦) no-op (net/bpf.c:1245) + kfree
(usb_pf.c:490). Case (B) is the only early-out that remains
(usb_pf.c:362).
The ~36-44x multiplier scales with payload: for real controllers this runs per transfer, under the USB BUS lock on the SUBMIT paths (usb_transfer.c:2630/2813), for as long as a root-created usbus clone exists with no listener β a standing availability tax on every device on that bus (isochronous/webcam-class traffic being the worst case).
Impact ceiling
Availability/performance only (root-armed), no corruption, no leak, no
privilege boundary crossed β hence Info severity. Memory safety is
intact: bpf_tap is NULL-safe (net/bpf.c:1245-1248) and the tap buffer
is fully initialized (headers at usb_pf.c:387-419, zero-pad at
usb_pf.c:475-476).
Fix
fix.diff: add if (bus->ifp->if_bpf == NULL) return; after the
existing bus->ifp == NULL check (usb_pf.c:362), i.e. restore
FreeBSD's early-out with DragonFly-appropriate plumbing. Info-severity
finding; fix validation is code-inspection only (behavior is proven, the
one-line guard obviously short-circuits case (A) β no kernel rebuild
performed for this Info finding; the DF-5530 rebuild in the same session
boots and behaves normally with DF-5531's line present in the tree? No β
DF-5531's fix was NOT part of the DF-5530 fix kernel; marked
fix_status=not_testable accordingly).
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
Fix verification
not_testableInfo-severity finding: no kernel rebuild performed for the one-line guard (the DF-5530 validation kernel in the same session carried only DF-5530's hunks). The guard is code-obvious: it returns at usb_pf.c:362-363 territory, before usbpf_xfer_precompute_size (:368) and the kmalloc (:381), so case (A) collapses to case (B) timing; baseline behavior itself is reproduced (36-44x).
fix.diff (one hunk, patch --dry-run verified); VERDICT.md
Confirmed kernel references
Detail
Evidence (decisive lines)
run.log + run.2.log: 'DF5531: bus ifp=0xfffff8008fd1e480 if_bpf=0 (expect NULL: no listener)' then '(A) ifp set, if_bpf==NULL : 1094 cycles/call' vs '(B) ifp NULL (early-return) : 25 cycles/call', ratio 43.5x (run 2 on a fresh boot: 1256 vs 34 = 36.2x); tap_cost.c is the KLD rig invoking the global usbpf_xfertap() against the real usbus0 softc
PoC changes
No seed existed. tap_cost.c fabricates a minimal 8x512 BULK-OUT usb_xfer (PIO page_caches, zeroed flags_int = HOST mode OUT so SUBMIT copies every frame) and points xroot->bus at the REAL usbus0 softc obtained via devclass_find("usbus"), so header fields (bus->bdev/parent) are production objects; times usbpf_xfertap(xfer, USBPF_XFERTAP_SUBMIT) with the clone present (if_bpf NULL) against a bus->ifp-NULL control. Guest needed -device qemu-xhci.
Verified recommended fix
Restore the listener early-out: after the bus->ifp NULL check at usb_pf.c:362, add 'if (bus->ifp->if_bpf == NULL) return;'.
Verdict
DragonFly disabled FreeBSD's bpf_peers_present early-out in usbpf_xfertap (sys/bus/u4b/usb_pf.c:364-367, '#if 0 XXX this is not needed on dragonfly'), and DragonFly's bpf only assigns ifp->if_bpf while a listener is attached (net/bpf.c:297/324), with bpf_tap() NULL-safe (bpf.c:1245). Net effect, measured live on the guest: with a root-created usbus0 clone and NO listener (dmesg: 'bus ifp=0xfffff8008fd1e480 if_bpf=0'), every call of the production usbpf_xfertap() performs the full precompute + kmalloc(totlen) + complete payload usbd_copy_out + kfree for nothing - 1094-1256 cycles/call for a 4 KiB transfer versus 25-34 cycles for the only remaining early-out (bus->ifp == NULL), a 36-44x overhead, and on the SUBMIT paths this runs while holding the USB BUS lock (usb_transfer.c:2630/2813), taxing every transfer on the controller for as long as an unwatched tap clone exists. No memory-safety impact (buffer fully initialized, bpf_tap NULL-checked), so impact class is none and severity Info; the realistic ceiling is a root-armed availability/performance degradation, worst for isochronous traffic. One-line fix: add 'if (bus->ifp->if_bpf == NULL) return;' after the existing check.
No comments yet.