# DF-2340 VERDICT — reproduced (deterministic panic in the production kue_setmulti), fix validated (fixed)

## The claim, verified line by line

DF-2340 (filed 2026-07-28, pass 1) claims: "kue_attach() allocates
sc->sc_mcfilters using KUE_MCFILTCNT(sc) which reads sc->sc_desc.kue_mcastfilt.
But descriptor only fetched later asynchronously in kue_attach_post() (queued
by uether_ifattach()). At allocation time newbus-zero-initialized softc yields
KUE_MCFILTCNT(sc)==0 so kmalloc(0) returns ZERO_LENGTH_PTR=((void*)-8).
Non-NULL check at :490 does NOT catch. Later kue_setmulti() executes
memcpy(KUE_MCFILT(sc,i) LLADDR ETHER_ADDR_LEN) ... first write targets
0xFFFFFFFFFFFFFFF8 unmapped top-of-space guard word -> faults. Malicious
KL5KUSB101B adapter controlling 16-byte GET_ETHER_DESCRIPTOR reply sets
kue_mcastfilt!=0."

Every element checks out against the source:

| # | Claim | Source proof |
|---|-------|--------------|
| 1 | allocation reads the not-yet-fetched descriptor | if_kue.c:488-489 uses `KUE_MCFILTCNT(sc)`; `sc_desc` is filled only at if_kue.c:439-440 inside `kue_attach_post()`, which `uether_ifattach()` (if_kue.c:501) queues as an async task (usb_ethernet.c:182, executed at :192-203 via `ue_methods->ue_attach_post` at :202). The kmalloc at :488 therefore always runs against a zeroed `sc_desc`. |
| 2 | `KUE_MCFILTCNT` == 0 there | if_kuereg.h:69 `UGETW(sc_desc.kue_mcastfilt) & 0x7FFF`; newbus softcs are zero-allocated. |
| 3 | `kmalloc(0)` → `(void*)-8`, guard dead | kern_slaballoc.c:193 `#define ZERO_LENGTH_PTR ((void *)-8)`; :885-890 size==0 returns it regardless of M_WAITOK; :490's `== NULL` can never fire. **Observed live**: harness printed `sc_mcfilters=0xfffffffffffffff8 (ZERO_LENGTH_PTR=0xfffffffffffffff8, NULL-guard at :490 DEAD)`. |
| 4 | descriptor is device-controlled | if_kue.c:439-440: `kue_ctl(KUE_CTL_READ, KUE_CMD_GET_ETHER_DESCRIPTOR, 0, &sc->sc_desc, sizeof(sc->sc_desc))` — 16 bytes from the adapter's firmware; if_kue.c:50-52 acknowledges the filter count is firmware-dependent. |
| 5 | kue_setmulti re-bounds with the late value and writes through the sentinel | if_kue.c:373-387: loop bound `i == KUE_MCFILTCNT(sc)` (now the device value), `memcpy(KUE_MCFILT(sc, i), LLADDR(...), ETHER_ADDR_LEN)` with `KUE_MCFILT` = `&sc->sc_mcfilters[i*6]` (if_kuereg.h:70-71). Reachable via SIOCADDMULTI/SIOCDELMULTI → ue_setmulti_task (usb_ethernet.c:497-502, :398-405) — and IPv6 auto-joins multicast groups at interface-up, so "evil adapter + ifconfig ue0 up" suffices. |
| 6 | `0xfffffffffffffff8` unmapped → fault | **Observed**: `Fatal trap 12 ... fault virtual address = 0xfffffffffffffff8, fault code = supervisor write data, page not present, Stopped at kue_setmulti+0x89: movl %edi,(%rax)`. The faulting instruction is the driver's own inlined memcpy store. |
| 7 | kfree of the sentinel is a no-op (latent, not caught at detach) | kern_slaballoc.c:1253 `if (ptr == ZERO_LENGTH_PTR) ...` handled specially; if_kue.c:522 detach stays quiet. |

One refinement to the original text: the finding says "(kmem_alloc(ksize=0)
result)" in its title shorthand — the actual allocator is `kmalloc(0, M_USBDEV,
M_WAITOK)`; the sentinel constant is `ZERO_LENGTH_PTR`, value `(void*)-8`,
returned by `kmalloc()`'s size==0 branch (kern_slaballoc.c:885-890). Substance
unchanged.

## How it was proven (production-code KLD rig)

No KL5KUSB101B hardware exists on the guest; as with DF-5396/DF-5411/DF-5453,
a KLD harness compiles the **verbatim production driver** into the module
(`#include <bus/u4b/net/if_kue.c>` from `/usr/src/sys` — zero modifications;
the `kue_setmulti()` that faults is the driver's own static function) and
reconstructs the exact production state around it:

* zeroed softc (newbus M_ZERO equivalent) + `lockinit(&sc->sc_lock, ..., LK_CANRECURSE)` (if_kue.c:478) + `ue_sc = sc` (:495);
* `ue_tq` placed in the `usb_proc_drain()` state (`up_gone=1`, the same flag
  usb_process.c:422 sets when a device detaches) so `kue_setmulti()`'s
  trailing `kue_ctl`/`kue_setword` control requests return
  `USB_ERR_IOERROR` immediately via `usbd_do_request_proc()`'s
  `usb_proc_is_gone()` check (usb_request.c:768-771) — the harness never
  fakes a usb_device and never fakes request success; the requests fail
  exactly as they do on a detaching real device, and `kue_setmulti()`
  ignores them in production style;
* if_kue.c:488-489 replayed *at its true position in the attach sequence*
  (sc_desc still zero) — this is the actual allocation the driver performs;
* the 16-byte device descriptor reply lands in `sc_desc` (the effect of
  if_kue.c:439-440) with `kue_mcastfilt = 0x0002`;
* the ifnet is the embedded `ue_arpcom.ac_if` (uether_getifp,
  usb_ethernet.c:134-137) with ONE AF_LINK membership constructed exactly as
  `if_addmulti_serialized()` (sys/net/if.c:2688-2720) leaves it;
* `KUE_LOCK` + verbatim `kue_setmulti(&sc->sc_ue)`.

The only non-production code is this attach-frame plumbing (kue_attach /
kue_setmulti are static and only reachable through newbus with a physical
adapter). Every byte allocated, copied and faulted on follows the production
call chain.

## Results

**Baseline (stock /usr/src)** — reproduced twice (run.log, run.2.log; second
guest incarnation, identical fault):

```
kue_poc: if_kue.c:488 with sc_desc all-zero: kmalloc(0*6) -> sc_mcfilters=0xfffffffffffffff8 (ZERO_LENGTH_PTR=0xfffffffffffffff8, NULL-guard at :490 DEAD)
kue_poc: descriptor arrived: KUE_MCFILTCNT(sc)=2 (was 0 at allocation time in the stock driver)
kue_poc: entering VERBATIM kue_setmulti() (if_kue.c:355-398): memcpy(KUE_MCFILT(sc,0)=0xfffffffffffffff8, ..., 6)

Fatal trap 12: page fault while in kernel mode
fault virtual address	= 0xfffffffffffffff8
fault code		= supervisor write data, page not present
Stopped at      kue_setmulti+0x89:      movl    %edi,(%rax)
```

Guest dead in DDB (`vm.sh status` => down). Panic in the driver's own
function, fixed fault VA, deterministic across CPUs/boots.

**Patched (/usr/src + fix.diff)** — survived twice (fix_run.log), clean
loads/unloads of both the harness and the rebuilt production `if_kue.ko`
(`KUE_DRIVER_LOAD_OK`), guest up throughout:

```
kue_poc: PATCHED order: descriptor first (KUE_MCFILTCNT=2), then kmalloc(12) -> sc_mcfilters=0xfffff8008d5e16b0
kue_poc: entering VERBATIM kue_setmulti() (if_kue.c:355-398): memcpy(KUE_MCFILT(sc,0)=0xfffff8008d5e16b0, ..., 6)
kue_poc: SURVIVED kue_setmulti() — buffer was properly sized (fix in effect?)
KLDLOAD_RC=0 / UNLOAD_OK / KUE_DRIVER_LOAD_OK / KUE_DRIVER_UNLOAD_OK
```

Identical device data on both sides; the only delta is the fix's
allocation-ordering change (the harness models the attach-order position of
the single allocation; `kue_setmulti()` itself is the verbatim compiled
function in both builds). fix.diff is `git apply --check -p0`-clean against
the pristine read-only `sys/` tree and `patch(1)` Plan A in-guest.

Rig-detour note (kept honest in run.2.log): the first "fixed" build silently
omitted `-DDF2340_FIXED` because `sys/conf/kmod.mk:98` rebuilds `CFLAGS` from
`COPTS`; the resulting binary was a *baseline* replay and panicked on the
patched-source guest — recorded as baseline datapoint #2 (it proves the panic
tracks the replayed ordering, not the tree state). Moving the define to
`COPTS+=` fixed the build; verified `-DDF2340_FIXED` present in the cc line.

## Impact (primary objective: can an unprivileged user get uid=0?)

No. The corrupted "pointer" is a constant sentinel: every write lands at
`(char *)-8 + i*6`, i.e. the top of the kernel VA space (`i` is capped by the
device's 15-bit count AND by the number of joined multicast memberships), a
region with no kernel mapping — the primitive is a **deterministic fatal
page fault**, observed as such. There is no attacker-chosen address, no
attacker-chosen length beyond 6*i, and the copied bytes are multicast MAC
addresses. No escalation chain exists from this bug alone; the deliverable
here is the DoS proof, consistent with the finding's own AV:P/A:H rating.

Threat model: physical evil-USB (or VM USB-passthrough/usbredir) — one of the
33 kue_devs[] VID:PID pairs (if_kue.c:104-140) whose firmware answers
GET_ETHER_DESCRIPTOR with a non-zero filter count (all real kue firmware
does; the count is firmware-defined per if_kue.c:50-52), followed by any
multicast-list change (IPv6 auto-join at interface-up is enough). This is the
same evil-USB-net family as DF-1075 (cue), DF-5396 (urndis), DF-5411 (udav),
DF-5453 (kue attach).

Non-exploitability notes:
* If the descriptor read *fails* (zeros), `KUE_MCFILTCNT == 0`: the
  `i == KUE_MCFILTCNT` break fires before any memcpy and the driver falls
  back to ALLMULTI — no write. The bug strictly requires the device to
  report a non-zero count, which real firmware always does — i.e. on real
  kue adapters this path is expected to be live at first multicast join.
* The `M_WAITOK` NULL check at if_kue.c:490-493 is dead code on DragonFly
  (M_WAITOK cannot return NULL; kmalloc(0) returns the sentinel) — removed
  by the fix along with the moved allocation.
* FreeBSD parent-code note: the same pre-descriptor allocation ordering
  exists upstream, but FreeBSD `malloc(0)` returns a valid minimal
  allocation rather than a sentinel — the upstream manifestation would be a
  heap overflow sized by the device-reported count, worth a separate
  upstream report; not verified in this DragonFly audit.

## Fix

fix.diff (authored after verification, never applied to the read-only sys/
tree in the repo): move the allocation from `kue_attach()` to
`kue_attach_post()` immediately after the GET_ETHER_DESCRIPTOR read, add
`M_ZERO`, and skip allocation for a zero count (kue_setmulti then takes the
allmulti path; kfree(NULL) is a no-op). Validated as above: baseline panic
→ patched survival on identical input; patched driver builds `-Werror`
clean as a standalone module and loads/unloads clean.

## Guest hygiene

Guest was left in a good state: final `vm.sh reset with-src` after all
captures (clean-source snapshot, rebooted, ssh-verified), so
`guest_dirty = 0`.

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