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

NULL pointer dereference in PLX attach path when local I/O resource allocation fails

Field Value
ID DF-2093
Status new
Severity Low
CVSS 3.1 CVSS:3.1/AV:P/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
CWE CWE-476 NULL Pointer Dereference
File sys/dev/netif/wi/if_wi_pci.c
Lines 177-180
Area net/wi
Confidence likely
Discovered 2026-07-25
Reported pending
Known CVE none
CVE match novel

Summary

In wi_pci_attach(), the PLX-bus (WI_BUS_PCI_PLX) branch allocates the local I/O resource with bus_alloc_resource_any() but never checks the return value for NULL before immediately passing it to rman_get_bustag()/rman_get_bushandle(). Both are bare pointer-dereference macros (rman.h:151,153), so a failed allocation dereferences NULL, traps in supervisor mode on page 0, and panics the kernel during device attach.

Root cause

if_wi_pci.c:177-180:

sc->local_rid = WI_PCI_LOCALRES;
sc->local = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
    &sc->local_rid, RF_ACTIVE);
sc->wi_localtag = rman_get_bustag(sc->local);     /* NULL deref if alloc failed */
sc->wi_localhandle = rman_get_bushandle(sc->local); /* NULL deref if alloc failed */

rman_get_bustag/rman_get_bushandle are defined in sys/sys/rman.h:151,153 as #define rman_get_bustag(r) ((r)->r_bustag) and #define rman_get_bushandle(r) ((r)->r_bushandle) β€” pure dereferences with no NULL guard.

Contrast the mem allocation two blocks down (if_wi_pci.c:191-199), which IS correctly NULL-checked before its rman_get_bustag() call, proving this is an oversight rather than intent. bus_alloc_resource_any() returns NULL on failure (standard Newbus contract; same pattern observed at if_wi.c:2075,2087).

Threat model & preconditions

  • Attacker position: physical access (PCI/PCIe/CardBus/Thunderbolt hot-plug) or a malicious paravirtualized PCI device presented to a virtualization guest (VM operator with PCI-passthrough control, or QEMU with a crafted PCI device descriptor).
  • Privileges gained or impact: kernel panic at attach time β€” denial of service (system reboot, or unbootable if the device is present at boot). No privilege escalation, no info leak.
  • Required config or capabilities: a PCI device advertising one of the PLX-bridge Prism2 vendor/device IDs in pci_ids[] (if_wi_pci.c:119-129, e.g. 0x16ab/0x1101 'GLPRISM2 WaveLAN', 0x10b7/0x7770 '3Com Airconnect') but lacking a valid BAR at PCI rid 0x14 (WI_PCI_LOCALRES, if_wireg.h:46). Could be a crafted card, buggy firmware/BIOS, or a malicious paravirtual device.
  • Reachability: Newbus device enumeration at boot or hot-plug. The wi(4) driver auto-probes on PCI vendor/device match.

Proof of concept

PoC source: findings/poc/DF-2093/

Build & run

# QEMU-based reproducer (no physical hardware needed):
# 1. Build a DragonFlyBSD guest with wi(4) loaded (kldload if_wi).
# 2. From the QEMU monitor, hot-add a crafted PCI device:
device_add ...,vendor=0x16ab,device=0x1101,bars=[none-at-0x14]
# 3. Observe panic in the guest.

Expected output

Fatal trap 12: page fault while in kernel mode
fault virtual address = 0x0
cpuid = 0
apic = ...
kernel: type 12 trap, code=0
...
wi_pci_attach+0x...   # frame in backtrace

Impact

  • Default config: not triggered unless matching PCI hardware is present.
  • Reliability: deterministic β€” bus_alloc_resource_any returns NULL exactly when the BAR is unassigned, and the deref is unconditional.
  • Blast radius: local DoS only. Requires physical/VM PCI device control.

Mirror the existing mem NULL-check (if_wi_pci.c:193-197) for the local resource. Add an immediate failure path that releases already-allocated iobase/irq via wi_free(dev) and returns ENXIO.

--- a/sys/dev/netif/wi/if_wi_pci.c
+++ b/sys/dev/netif/wi/if_wi_pci.c
@@ -176,6 +176,11 @@ wi_pci_attach(device_t dev)
        sc->local_rid = WI_PCI_LOCALRES;
        sc->local = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
            &sc->local_rid, RF_ACTIVE);
+       if (sc->local == NULL) {
+           device_printf(dev, "couldn't allocate PLX local I/O\n");
+           wi_free(dev);
+           return (ENXIO);
+       }
        sc->wi_localtag = rman_get_bustag(sc->local);
        sc->wi_localhandle = rman_get_bushandle(sc->local);

This brings the local allocation in line with the mem allocation just below and with the iobase/mem checks inside wi_alloc() (if_wi.c:2075,2087), eliminating the NULL-deref panic path. After the fix, attach fails cleanly with ENXIO and Newbus proceeds normally.

References

Timeline

  • 2026-07-25 Discovered during automated audit.
  • 2026-07-25 Reported to DragonFlyBSD security contact.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2093 Β· 2 files
FileTypeDescriptionSize
fix.diff suggested-fix git-apply-able fix 508 B view raw
VERDICT.md verdict source-trace confirmation 584 B ↓ raw
VERDICT.md verdict source-trace confirmation
↓ download raw

DF-2093 β€” wi_pci PLX branch NULL deref on resource alloc failure

Verdict

REPRODUCED (source-only confirmation). Bug confirmed by source tracing.

Mechanism

wi_pci_attach PLX-bus branch (if_wi_pci.c:177-180): sc->local=bus_alloc_resource_any() return not NULL-checked before rman_get_bustag/sc->local and rman_get_bushandle/sc->local. If allocation fails, immediate NULL deref.

Fix

Add NULL check: if (sc->local == NULL) return ENXIO before rman_get_* calls.

Batch-build status

Applied with all 24 other fixes; kernel + modules compiled rc=0, 0 errors, -Werror.

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

Added NULL check; batch build rc=0.

Added NULL check; batch build rc=0.
↓ fix.diffcombined build rc=0

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

wi_pci PLX sc->local not NULL-checked before rman deref.

Verified recommended fix

wi_pci PLX sc->local not NULL-checked before rman deref.

Verdict

wi_pci PLX sc->local not NULL-checked before rman deref.