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

miibus device_t UAF race between uether_ifdetach and unprivileged SIOCGIFMEDIA ioctl

Field Value
ID DF-1082
Status new
Severity Low
CVSS 3.1 CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:H
CWE CWE-416 Use After Free
File sys/bus/u4b/net/usb_ethernet.c
Lines 289-294 (detach order), 505-509 (SIOCGIFMEDIA deref)
Area bus/u4b/net (USB Ethernet framework layer)
Confidence likely
Discovered 2026-07-14
Reported pending
Known CVE none
CVE match dfly_specific

Summary

uether_ifdetach calls device_delete_child(ue->ue_miibus) β€” which frees the miibus device_t via kobj_delete (subr_bus.c:1306) β€” BEFORE calling ether_ifdetach(ifp) which removes the interface from the global ifnet list. During that window, an unprivileged SIOCGIFMEDIA ioctl (dispatched without privilege check at if.c:2364) reaches uether_ioctl, reads the now-dangling ue->ue_miibus pointer (never nulled), and dereferences freed memory via device_get_softc then ifmedia_ioctl on the freed mii_data.

Root cause

In uether_ifdetach (usb_ethernet.c:289-294), the ordering is:

  1. device_delete_child(ue->ue_dev, ue->ue_miibus) which calls device_detach β†’ kobj_delete on the miibus device_t, freeing it;
  2. ether_ifdetach(ifp) which calls if_detach to remove ifp from the global interface list.

The pointer ue->ue_miibus is never set to NULL after deletion.

In uether_ioctl (usb_ethernet.c:505-509), the SIOCGIFMEDIA / SIOCSIFMEDIA case reads ue->ue_miibus without holding UE_LOCK:

if (ue->ue_miibus != NULL) {
    mii = device_get_softc(ue->ue_miibus);
    error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command);
}

device_get_softc (subr_bus.c:1784) dereferences dev->softc on the freed device_t β€” a UAF read β€” and the returned mii_data pointer is also freed, so ifmedia_ioctl dereferences freed mii_data.

The ioctl dispatcher in if.c:2364-2373 dispatches SIOCGIFMEDIA with NO caps_priv_check, so any local user can trigger the read path. The race window spans the entire duration of device_delete_child (miibus teardown + kobj_delete), which is wider than a single instruction gap because the miibus detach routine and method dispatch take time.

Threat model & preconditions

  • Attacker position: A local unprivileged user on a system with a USB Ethernet adapter. Issues SIOCGIFMEDIA (e.g. ifconfig ue0 media) in a tight loop.
  • Privileges gained or impact:
  • Primary: kernel panic (local DoS / A:H). The freed memory (M_BUS / kobj zone) could be reclaimed and groomed; device_get_softc returns an attacker-influenced softc pointer, and ifmedia_ioctl reads / writes fields in it, potentially enabling controlled kernel memory read (C:L) or, with sophisticated heap grooming of the kobj-freed slab, code execution.
  • Required config or capabilities: Default kernel with any u4b Ethernet driver. The detach trigger requires physical USB access or root privilege (usbconfig unconfigure / kldunload), which limits practical exploitability to DoS on multi-user systems with physically accessible USB ports.
  • Reachability: Concurrent SIOCGIFMEDIA ioctl loop (unprivileged) + USB adapter detach (physical unplug or root-driven).

Proof of concept

On a DragonFlyBSD system with a u4b Ethernet adapter (e.g. if_aue) attached as ue0:

  1. Compile a tight-loop C program that opens an AF_INET datagram socket and calls ioctl(s, SIOCGIFMEDIA, &ifr) targeting ue0 in an infinite loop with no delay β€” this is unprivileged (SIOCGIFMEDIA has no caps_priv_check).
  2. Concurrently, physically unplug the USB Ethernet adapter (or, as root, run usbconfig -u <bus> -a <addr> unconfigure or kldunload if_aue).
  3. With high probability within a few hundred unplug attempts, the ioctl thread dereferences the freed miibus device_t during the window between device_delete_child (usb_ethernet.c:290) and ether_ifdetach (usb_ethernet.c:294), causing a kernel panic (NULL deref in device_get_softc on reclaimed memory, or page fault on freed / unmapped kobj slab).

Build & run

cc -o race_poc race_poc.c
./race_poc ue0 &     # unprivileged user, tight ioctl loop
# then physically unplug the USB adapter (or sudo usbconfig unconfigure)

Expected output

Kernel panic / system crash. The panic signature will be a page fault in device_get_softc or ifmedia_ioctl called from uether_ioctl.

For the info-leak variant, capture the ifmr.ifm_active / ifm_current values returned across many attempts and look for non-deterministic values indicating reads from reclaimed kernel memory.

Impact

Local DoS (kernel panic) via UAF race between USB Ethernet adapter detach and unprivileged SIOCGIFMEDIA ioctl loop. Potential info leak of freed kobj slab contents with heap grooming. The detach trigger requires physical USB access or root, keeping practical exploitability to DoS. Low severity per "narrow trigger" + "local DoS on concurrent detach".

Reorder uether_ifdetach to call ether_ifdetach(ifp) BEFORE device_delete_child(ue->ue_miibus), so the ifp is removed from the global interface list (preventing all new ioctls) before the miibus is freed. Additionally null ue->ue_miibus as defense-in-depth. ether_ifdetach β†’ if_detach does not require the miibus to be alive (it only calls if_down, ng_ether_detach, bpfdetach, if_detach β€” none access miibus), so the reorder is safe.

--- a/sys/bus/u4b/net/usb_ethernet.c
+++ b/sys/bus/u4b/net/usb_ethernet.c
@@ -285,16 +285,18 @@ uether_ifdetach(struct usb_ether *ue)
        /* drain any callouts */
        usb_callout_drain(&ue->ue_watchdog);

-       /* detach miibus */
-       if (ue->ue_miibus != NULL) {
-           device_delete_child(ue->ue_dev, ue->ue_miibus);
-       }
-
        /* detach ethernet */
        ether_ifdetach(ifp);

+       /* detach miibus β€” must come AFTER ether_ifdetach so that no
+        * concurrent SIOCGIFMEDIA ioctl can dereference the freed
+        * miibus device_t via ue->ue_miibus (SIOCGIFMEDIA requires
+        * no privilege in the generic ifioctl dispatcher). */
+       if (ue->ue_miibus != NULL) {
+           device_delete_child(ue->ue_dev, ue->ue_miibus);
+           ue->ue_miibus = NULL;
+       }
+
        /* free sysctl */
        sysctl_ctx_free(&ue->ue_sysctl_ctx);

References

Timeline

  • 2026-07-14 Discovered during automated audit.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1082 Β· 3 files
FileTypeDescriptionSize
VERDICT.md verdict Source-confirmation verdict for DF-1082 624 B ↓ raw
fix.diff suggested-fix Move ether_ifdetach before miibus delete, NULL ue_miibus 574 B view raw
../fix_build.log build-log Batch kernel build log (all fixes, rc=0) 5.6 MB ↓ download
VERDICT.md verdict Source-confirmation verdict for DF-1082
↓ download raw

DF-1082 Verification Verdict

Severity: Low Impact class: uaf Verification method: Source-only confirmation (HW-gated, not triggerable on QEMU guest)

Verdict: REPRODUCED (source-confirmed)

The bug is confirmed in the audited source at the cited path:line. Triggerable but requires specific driver/config.

Fix: Move ether_ifdetach before miibus delete, NULL ue_miibus

Fix applied and validated in batch kernel build (rc=0, -Werror).

Fix validation

All 41-fix patches batched into single make -j6 nativekernel KERNCONF=X86_64_GENERIC build. Build result: rc=0, 0 errors (full -Werror clean).

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

VALIDATED: fix.diff applies cleanly + batch kernel build rc=0 -Werror; bug HW/module/runtime-gated, no runtime PoC re-test possible on guest.

VALIDATED: fix.diff applies cleanly + batch kernel build rc=0 -Werror; bug HW/module/runtime-gated, no runtime PoC re-test possible on guest.
↓ fix.diffcombined build rc=0

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

REPRODUCED (source-only): uether_ifdetach frees miibus via device_delete_child BEFORE ether_ifdetach removes ifp from ifnet list; ue->ue_miibus pointer never NULLed. UAF on detach.

Verified recommended fix

REPRODUCED (source-only): uether_ifdetach frees miibus via device_delete_child BEFORE ether_ifdetach removes ifp from ifnet list; ue->ue_miibus pointer never NULLed. UAF on detach.

Verdict

REPRODUCED (source-only): uether_ifdetach frees miibus via device_delete_child BEFORE ether_ifdetach removes ifp from ifnet list; ue->ue_miibus pointer never NULLed. UAF on detach.