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

urndis(4) attach-failure paths call urndis_detach() on a never-attached usb_ether β†’ deterministic kernel panic (lockmgr of NULL, fault VA 0) from plugging a malicious RNDIS device

Summary

Fourth member of the DF-5334/5384/5395 uether class: every attach failure goto runs the full detach with ue_lock/ue_methods unassigned and uether_ifattach never run - uether_getifp returns the embedded never-NULL ifnet so UE_LOCK lockmgr(NULL)s: Fatal trap 12 VA 0. Reproduced 4x deterministically with the malformed-descriptor and rejected-MAC-query device variants (crash dump taken; boot-time variant bricks the boot); detach-only-what-was-set fix validated (attach returned 6, guest up, valid device still attaches).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5397 Β· 7 files
FileTypeDescriptionSize
README.md β€” 3.7 KB ↓ raw
VERDICT.md β€” 2.7 KB ↓ raw
fix.diff β€” 937 B view raw
panic.txt β€” 13.1 KB view raw
run.serial-baseline-panic-usbdebug.log β€” 17.4 KB view raw
run.serial-baseline-panic2.log β€” 20.8 KB view raw
run.serial-patched-clean.log β€” 29.5 KB view raw

DF-5397 β€” urndis(4) attach-failure paths call urndis_detach() on a

never-attached usb_ether -> deterministic kernel panic (lockmgr NULL)

What

sys/bus/u4b/net/if_urndis.c β€” urndis_attach() failure paths (if_urndis.c:258-299: "No valid alternate setting found", "Unable to get hardware address", "Invalid address length", "Unable to set data filters") jump to:

detach:
    urndis_detach(dev);          /* if_urndis.c:309-311 */
    return (ENXIO);

ue->ue_lock, ue->ue_methods etc. are assigned only at if_urndis.c:290-294 β€” after every failure goto β€” and uether_ifattach() never ran. urndis_detach() calls uether_ifdetach(ue); on DragonFly every guard there is dead:

  • usb_proc_drain() early-returns (zeroed up_lock) β€” safe;
  • uether_getifp(ue) returns the address of the embedded ue->ue_arpcom.ac_if β€” never NULL β€” so if (ifp != NULL) passes;
  • UE_LOCK(ue) β†’ lockmgr(ue->ue_lock == NULL) β†’ page fault VA 0.

Identical class to DF-5334 (cdce). An attacker-supplied USB device (evil RNDIS NIC / tethering dongle β€” precisely this driver's threat model) triggers it by making the attach fail once: e.g. malformed class-specific descriptors so usbd_transfer_setup() finds no usable alternate setting on the data interface (the exact gadget used here), or by failing/short-answering the RNDIS MAC query.

Reproduction (baseline, stock if_urndis.ko)

Malformed RNDIS gadget over usb-redir (RNDIS_MALFORMED=1 boot_rig_cold.sh, usbredir_rndis.py with malformed on):

urndis0: <vendor 0x0525 product 0xa4a2, class 2/0, rev 2.00/0.00, addr 2> on usbus0
urndis0: No valid alternate setting found
Fatal trap 12: page fault while in kernel mode
fault virtual address   = 0x0
Stopped at      lockmgr_exclusive+0x5d: orl     (%r14),%esi

lockmgr_exclusive() at lockmgr_exclusive+0x5d
uether_ifdetach() at uether_ifdetach+0x2f
urndis_detach() at urndis_detach+0x2a
urndis_attach() at urndis_attach+0x11d
device_doattach() at device_doattach+0x3af
usb_probe_and_attach() at usb_probe_and_attach+0x176
uhub_explore() at uhub_explore+0x231
usb_bus_explore() at usb_bus_explore+0xd0
usb_process() at usb_process+0x76

(4 independent reproductions; crash dump taken via call dumpsys. A second variant β€” "Unable to get hardware address" when the RNDIS query response is rejected β€” panics at urndis_attach+0x13a with the same teardown, captured in run.serial-baseline-panic-usbdebug.log.)

Fix validation (patched if_urndis.ko, fix.diff)

fix.diff replaces the error-path urndis_detach(dev) with a teardown of only what was actually initialised (USB transfers + the softc lock). Same malformed device, patched module:

urndis0: <vendor 0x0525 product 0xa4a2, class 2/0, rev 2.00/0.00, addr 2> on usbus0
urndis0: No valid alternate setting found
device_probe_and_attach: urndis0 attach returned 6

Guest stays up (ssh alive, zero Fatal trap in dmesg.boot), attach retried cleanly, and the unpatched flow of DF-5396 (valid device) still attaches (ue0: MAC address: 02:00:de:ad:be:ef) β€” no regression.

Run it

  1. guest: mv /boot/kernel/if_cdce.ko /root/ (cdce's VPI match on 0525:a4a2 would win probe priority and panic via its own DF-5334 bug first), kldload if_urndis
  2. host: RNDIS_MALFORMED=1 sh boot_rig_cold.sh (device present at boot, malformed CS descriptors) β€” or toggle malformed on on the control port and replug.
  3. baseline: panic (above); patched: clean ENXIO, guest survives.

Kernel references

VERDICT.md
↓ download raw

DF-5397 VERDICT

Status: reproduced (deterministic kernel panic / local-device DoS via attach-failure path). Fix: validated on the guest.

How reproduced

The malicious usbredir RNDIS device (usbredir_rndis.py from the DF-5396 pack, RNDIS_MALFORMED=1) presents CDC class-specific descriptors whose walked lengths are one byte too long each, so DF's config parse never finds the data interface and urndis_attach()\'s alternate-setting loop fails on every iteration -> "No valid alternate setting found" -> goto detach -> urndis_detach(dev) -> uether_ifdetach() over the never-initialised usb_ether -> lockmgr(NULL) -> Fatal trap 12, VA 0.

Four independent baseline reproductions (two descriptor-failure variants: alt-setting failure and MAC-query rejection), each with the DDB backtrace naming uether_ifdetach+0x2f <- urndis_detach+0x2a <- urndis_attach, and a crash dump via call dumpsys. Evidence: panic.txt, run.serial-baseline-panic-usbdebug.log (USB_DEBUG kernel, annotated), run.serial-baseline-panic2.log, run.serial-patched-clean.log.

Why not worse than a panic

The faulting pointer is NULL (ue_lock), so the primitive is a denial-of-service at attach time (device plug-in / boot with the evil gadget present). Because the panic happens with preemption state of the usb explore thread and a fully attacker-chosen timing window, it is also a reliability/UX kill: a boot-time evil device bricks the boot (serial DDB, ssh never starts). No memory-corruption primitive was identified in this path (the NULL is dereferenced, not written through).

Fix validation

fix.diff (tear down only usbd transfers + lock, never uether_ifdetach on the un-attached ue) applied on the guest, module rebuilt + installed:

  • malformed device replug: urndis0: No valid alternate setting found + device_probe_and_attach: urndis0 attach returned 6, guest alive, zero Fatal traps.
  • valid device (regression): full attach + DF-5396 differential ran on the same patched module build (ue0 up, RNDIS data path live).

Notes

  • cdce (DF-5334) shares the bug and must be kept from probing first for this driver's PoC (VPI 0525:a4a2 beats urndis's class match).
  • FreeBSD's urndis_attach has the same shape; whether FreeBSD's uether_ifdetach is equally unguarded was not verified in this run.

Kernel references

sys/bus/u4b/net/if_urndis.c:290-294, :309-311; sys/bus/u4b/net/usb_ethernet.c:268-314; DF-5334 (cdce sibling).

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

fixed
baseline reproduced→ patch + rebuild →patched clean

fix.diff applied on guest, module rebuilt+installed: same malformed-device replug prints 'No valid alternate setting found' + 'device_probe_and_attach: urndis0 attach returned 6', guest stays up (ssh alive, zero Fatal traps); regression check with the valid device still attaches (ue0 up, DF-5396 differential ran on the same build).

run.serial-patched-clean.log tail (urndis0 lines + attach returned 6) and live ssh checks recorded in VERDICT.md
↓ fix.diffper-fix-DF-5397

Confirmed kernel references

Detail

Exploit chain

DoS only: the faulting pointer is NULL (read-deref in lockmgr), no write primitive identified. A plugged evil gadget or an evil device present at boot kills the kernel/requires hard reset.

Evidence (decisive lines)

panic.txt (Fatal trap 12 VA=0x0 + full DDB trace), run.serial-baseline-panic-usbdebug.log (USB_DEBUG-annotated, urndis0 messages + trace), run.serial-baseline-panic2.log (second variant at urndis_attach+0x13a 'Unable to get hardware address'), run.serial-patched-clean.log ('attach returned 6', guest alive)

PoC changes

No seed; reused the DF-5396 usbredir rig with its malformed-descriptor mode (RNDIS_MALFORMED=1 / 'malformed on' control command) which reproduces the exact alt-setting-loop failure. cdce must be prevented from probing first (its VPI match on 0525:a4a2 wins priority and panics via its own DF-5334 bug).

Verified recommended fix

On urndis_attach error paths, tear down only usbd_transfer_unsetup + lockuninit; never call urndis_detach()/uether_ifdetach() before uether_ifattach() succeeded

Verdict

Reproduced deterministically (4 independent runs, 2 variants). urndis_attach() failure paths goto a label that calls urndis_detach(), which drives uether_ifdetach() over a never-initialised usb_ether: ue_lock is assigned only after the last failure goto and uether_ifattach never ran, while uether_getifp() on DragonFly returns the embedded (never-NULL) ac_if - so lockmgr(NULL) faults at VA 0. Triggered by a malicious RNDIS gadget whose descriptors fail the alternate-setting loop (or whose MAC query response is rejected); DDB backtrace: lockmgr_exclusive <- uether_ifdetach+0x2f <- urndis_detach+0x2a <- urndis_attach+0x11d <- usb_probe_and_attach <- uhub_explore. Boot-time variant bricks the boot. fix.diff (teardown only transfers+lock on the error path) validated: attach returns ENXIO cleanly, guest survives, valid-device attach unaffected.