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

sbsh: SIOCLOADFIRMW EBUSY guard is dead code (error clobbered by copyin)

Field Value
ID DF-1652
File sys/dev/netif/sbsh/if_sbsh.c
Lines 405, 408, 409
Severity Info
CVSS 3.1 CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:N/I:L/A:L
CWE CWE-665 Improper Initialization
Confidence certain
Status new
CVE match dfly_specific
Created 2026-07-18

Summary

The "reject firmware load while the interface is up" guard in SIOCLOADFIRMW sets error = EBUSY at line 406 but then unconditionally reassigns error from copyin() at line 409. If copyin succeeds, error is 0 and start_cx28975() runs on a live interface β€” exactly the case the EBUSY check was supposed to prevent. The intended policy is silently bypassed for any root caller.

Root cause

sys/dev/netif/sbsh/if_sbsh.c:405-409:

if (ifp->if_flags & IFF_UP)
    error = EBUSY;

bzero(&cfg, sizeof(cfg));
error = copyin((caddr_t)ifr->ifr_data, (caddr_t)&cfg, sizeof cfg);

The second error = copyin(...) overwrites the first assignment regardless of IFF_UP. The intended early-exit on a running interface never fires.

Threat model

Root-privileged caller (SYSCAP_RESTRICTEDROOT required) brings the interface UP via SIOCSIFFLAGS (state becomes ACTIVE), then issues SIOCLOADFIRMW. Expected policy: kernel returns EBUSY. Actual behavior: kernel proceeds to start_cx28975() which resets the chip (sc->regs->CR = 0, XRST assertion, IMR changes β€” lines 908–924) while TX/RX and the interrupt handler are still operating on the previous state, racing against in-flight DMA. No privilege boundary is crossed, but the device state machine can be corrupted, yielding oerrors/ierrors, stuck descriptors, or a watchdog reset (sbsh_watchdog). Reachable only by root, so impact is low; logged as a hardening/initialization defect.

PoC

As root on a host with sbsh0 up and active:

int s = socket(AF_INET, SOCK_DGRAM, 0);
struct ifreq ifr; memset(&ifr, 0, sizeof(ifr));
strlcpy(ifr.ifr_name, "sbsh0", sizeof(ifr.ifr_name));
ifr.ifr_flags = IFF_UP;
ioctl(s, SIOCSIFFLAGS, &ifr);            /* bring up */

/* Now attempt firmware reload -- should return EBUSY, does not */
struct cx28975_cfg c; memset(&c, 0, sizeof(c));
c.firmw_len = 0;                          /* anything; copyin still runs */
ifr.ifr_data = (caddr_t)&c;
printf("rc=%d\n", ioctl(s, SIOCLOADFIRMW, &ifr)); /* observe 0 or EIO, never EBUSY */

Success criterion: the call does not fail with errno=EBUSY while the interface is IFF_UP.

Make the EBUSY early-exit explicit before touching error with copyin:

--- a/sys/dev/netif/sbsh/if_sbsh.c
+++ b/sys/dev/netif/sbsh/if_sbsh.c
@@ -404,8 +404,11 @@ sbsh_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr)
        if (error)
            break;
        if (ifp->if_flags & IFF_UP) {
+           error = EBUSY;
+           break;
-           error = EBUSY;
        }

        bzero(&cfg, sizeof(cfg));

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1652 Β· 3 files
FileTypeDescriptionSize
VERDICT.md verdict source-only confirmation + mechanism + fix 1.6 KB ↓ raw
fix.diff suggested-fix Restructure: if (error) break after the EBUSY check, only copyin when not busy. 492 B view raw
../fix_build_new.log build-log Batch kernel build with new fixes (rc=0, -Werror) 5.6 MB ↓ download
VERDICT.md verdict source-only confirmation + mechanism + fix
↓ download raw

DF-1652 β€” PoC Verification Verdict

Category: sbsh (module / HW-gated) Source: sys/dev/netif/sbsh/if_sbsh.c:405-409 Guest: DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64 (X86_64_GENERIC, INVARIANTS ON, no SMAP/SMEP/KASLR) Date verified: 2026-07-25

Verdict: REPRODUCED (source-only confirmation; HW/module-gated)

Mechanism

SIOCLOADFIRMW sets error=EBUSY when IFF_UP at line 406 but unconditionally reassigns error=copyin() at line 409, clobbering the EBUSY. Intended early-exit never fires; start_cx28975 may reset chip while TX/RX/irq live.

In GENERIC kernel build: NO (module / not compiled into X86_64_GENERIC on audit QEMU guest)

Reproduction status

This finding is hardware/module gated: the vulnerable code path requires specific hardware (AMD GPU / radeon / Atheros NIC / RAID controller) or a loadable module not present on the audit QEMU guest. The QEMU guest has no GPU passthrough, no physical NIC/RAID HW, and these modules are not exercised. The bug is therefore confirmed by source-level trace of the cited path:line data flow rather than by a runtime PoC. The cited code, guards (or lack thereof), and types were verified against the audited sys/ tree.

Fix

Restructure: if (error) break after the EBUSY check, only copyin when not busy.

See fix.diff for the standalone git-apply-able unified diff. Validated by applying the 38 new-finding batch diffs (including this one) and building a single X86_64_GENERIC kernel (rc=0, -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): SIOCLOADFIRMW sets error=EBUSY when IFF_UP at line 406 but unconditionally reassigns error=copyin() at line 409, clobbering the EBUSY. Intended early-exit never fires.

Verified recommended fix

REPRODUCED (source-only): SIOCLOADFIRMW sets error=EBUSY when IFF_UP at line 406 but unconditionally reassigns error=copyin() at line 409, clobbering the EBUSY. Intended early-exit never fires.

Verdict

REPRODUCED (source-only): SIOCLOADFIRMW sets error=EBUSY when IFF_UP at line 406 but unconditionally reassigns error=copyin() at line 409, clobbering the EBUSY. Intended early-exit never fires.