# DF-5530 — VERDICT

**Status: reproduced (panic, both sinks) — production-shape race + deterministic sink.**

## Environment

DragonFly 6.5-DEVELOPMENT `#0: Thu Jul 2 06:02:54 UTC 2026`,
X86_64_GENERIC, `pseudo-device bpf` compiled in (kernel, not KLD),
xhci.ko/ehci.ko loaded, QEMU `-device qemu-xhci` so `usbus0` exists.
Rigs are KLDs built against the guest's `/usr/src/sys`.

## How it was reproduced

### Sink 1 — usb_pf.c:191 (`ubus->ifp = NULL`, ubus from `ifp->if_softc == NULL`)

`usbpf_race.c` drives the two *production* code paths against each other
on the live bus:

* creator thread: `if_clone_create("usbus0", …)` /
  `if_clone_destroy("usbus0")` — exactly the SIOCIFCREATE path
  (net/if.c:2005-2013 → if_clone.c:74-95 → `usbpf_clone_create`);
* detacher thread: unlocked read of `ubus->ifp` (the same read
  `usbpf_detach()` performs at usb_pf.c:209) and, whenever the published
  ifnet has not set `IFF_UP` yet (usb_pf.c:170 — strictly inside the
  usb_pf.c:157→177 window), calls the production `usbpf_detach(ubus)`
  (usb_controller.c:240's entry point, usb_pf.c:207).

The detacher's filter keeps it out of the known DF-2347/DF-2607
double-destroy interleaving (striking a *completed* clone); with the
filter, every strike lands on a half-built ifnet. Result — twice, on two
independent boots, < 45 s each (`panic.txt`, `panic_race.txt`):

```
Fatal trap 12: page fault while in kernel mode
fault virtual address  = 0x5c8
fault code             = supervisor write data, page not present
instruction pointer    = 0x8:0xffffffff80a50a9f
Stopped at usbpf_clone_destroy+0xf:  movq $0,0x5c8(%rax)
```

`0x5c8` is `offsetof(struct usb_bus, ifp)`; the faulting store is
`ubus->ifp = NULL` (usb_pf.c:191) with `ubus == ifp->if_softc == NULL`
— i.e. the strike landed between the publish at usb_pf.c:157 and the
`ifp->if_softc = ubus` assignment at usb_pf.c:164.

### Sink 2 — net/bpf.c:1574 (`bp->bif_ifp` with `bp == NULL`)

`bpfdetach_null.c` constructs exactly the state `usbpf_clone_destroy()`
sees when the strike lands between usb_pf.c:164 and :177 (ifnet named,
`if_softc` set, **not yet bpf-attached**): `if_alloc(IFT_USB)` +
`ksnprintf(if_xname)` + production `bpfdetach(ifp)`. Result
(`panic_sink.txt`):

```
DF5530: calling bpfdetach() on not-bpf-attached usbus0 ...
Fatal user address access from kernel mode from kldload ...
Fatal trap 12: page fault while in kernel mode
fault virtual address  = 0x20
instruction pointer    = 0x8:0xffffffff802aac53
Stopped at bpfdetach.cold.7:  movq 0x20,%rax
```

`0x20` is `offsetof(struct bpf_if, bif_ifp)`; `bp` is NULL because the
search loop (bpf.c:1567-1571) legitimately found no match and the
"Interface wasn't attached" guard tests `bp->bif_ifp == NULL` instead of
`bp == NULL`.

## Why this is not the known findings

DF-1100, DF-2347 and DF-2607 all race `usbpf_xfertap` (the tap hooks)
against clone teardown. DF-5530's race is **create vs destroy**
(`usbpf_clone_create` vs `usbpf_detach`/`usbpf_clone_destroy`), no tap
involved, with two distinct NULL-deref sinks, one of which
(the `bpfdetach` typo) is a latent defect in net/bpf.c reachable by this
file's destroy path.

## Threat model

Both race endpoints are privileged: clone creation is SIOCIFCREATE
(`caps_priv_check(SYSCAP_RESTRICTEDROOT)`, net/if.c:2005-2013); the
detach side is controller detach (kldunload / devd) or SYSUNINIT
shutdown. A root user racing `ifconfig usbus0 create` against
`kldunload xhci` (or unplug-driven detach on hotplug-capable hardware)
gets a deterministic kernel panic — local DoS, no memory-safety impact
demonstrated (both faults are NULL derefs of small offsets). Severity
Medium per the rubric ("local DoS … requires unusual privilege").

## Fix validation

`fix.diff` (usb_pf.c: publish-after-init + NULL-softc guard; bpf.c:
`bp == NULL` typo) applied to a clean-source guest, kernel rebuilt with
`make nativekernel`, installed, rebooted:

* `kldload bpfdetach_null.ko` → loads cleanly, prints
  `bpfdetach: usbus0 was not attached`, **no panic**;
* `kldload df5530_race.ko` → runs its full 240 s window with the same
  strike filter, **no panic** (`RACE_RESULT=NOHIT`).

See `fix_build.log`, `fix_run.log`.

## Attempts

1. race v1 (no strike filter) — guest died <30 s at
   `usbpf_clone_destroy+0xf` (same sink 1 signature; coarse detacher).
2. race v2 (ifunit-based filter) — harness artifact: `ifunit()` panics
   without ifnet_lock ("panic: ifnet is not locked"); filter replaced
   with the IFF_UP check, no production code involved in this artifact.
3. race v3 (IFF_UP filter) — **sink 1 reproduced** (<45 s), `panic.txt`.
4. sink module — **sink 2 reproduced** deterministically,
   `panic_sink.txt`.

## 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.
