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

wpi_notif_intr RX index mask 0xfff wider than WPI_RX_RING_COUNT=64 can cause infinite softirq loop

Summary

wpi_notif_intr at if_wpi.c:2208-2212: hw=le32toh(shared->next)&0xfff (0..4095) but WPI_RX_RING_COUNT=64. If firmware writes value>63: while(rxq.cur!=hw) never terminates because rxq.cur cycles mod 64. Network stack hang. Fix: mask with (WPI_RX_RING_COUNT-1) instead of 0xfff.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1158 Β· 9 files
FileTypeDescriptionSize
fix.diff suggested-fix Replace & 0xfff with & (WPI_RX_RING_COUNT - 1) so firmware-supplied index cannot outrange the 64-entry ring 431 B view raw
VERDICT.md verdict full mechanism trace + reachability note 3.3 KB ↓ raw
README.md readme summary + reproduce steps 1.1 KB ↓ raw
build.sh build-script apply fix + build if_wpi.ko 504 B view raw
run.sh run-script placeholder runtime trigger (needs HW) 1.0 KB view raw
build.log build-log full successful build of patched if_wpi.ko 1.1 KB view raw
env.txt environment uname, cc, hw.model 497 B view raw
../fix_build_combined.log build-log Combined 41-finding kernel build (rc=0, -Werror clean) 5.6 MB ↓ download
../fix_build_summary.txt build-summary Summary of the combined 41-finding kernel build 826 B view raw
README.md readme summary + reproduce steps
↓ download raw

DF-1158 PoC verification β€” source-level trace of wpi_notif_intr index mask bug.

The driver is compiled into the X86_64_GENERIC kernel (sys/config/X86_64_GENERIC:272) but no Intel 3945ABG WiFi HW is present in the QEMU guest, so the vulnerable code path is not runtime-reachable here. Verification is by source-level trace + build-validation of the fix.

Bug location

Mechanism

The kernel trusts 12 bits of a firmware-DMA word but iterates rxq.cur modulo 64, so any firmware value > 63 in the low 12 bits makes the while-loop at if_wpi.c:2211 non-terminating. Kernel interrupt handler hangs β†’ DoS.

Reproduce

  1. Apply fix.diff to /usr/src.
  2. cd /usr/src/sys/dev/netif/wpi && make KMOD=if_wpi β†’ if_wpi.ko builds clean.
  3. Runtime test requires Intel 3945ABG HW (not present on this guest).

See VERDICT.md for the full analysis.

VERDICT.md verdict full mechanism trace + reachability note
↓ download raw

DF-1158 β€” wpi_notif_intr RX index mask wider than ring (source-only verification)

Verdict: REPRODUCED at source level (latent in compiled-in code; not triggerable without Intel 3945ABG WiFi HW)

Mechanism

wpi(4) is a statically-compiled-in driver in X86_64_GENERIC (sys/config/X86_64_GENERIC:272). The RX completion handler reads the firmware-owned index from shared DMA memory:

/* sys/dev/netif/wpi/if_wpi.c:2208 */
hw = le32toh(sc->shared->next) & 0xfff;          /* mask allows 0..4095 */
hw = (hw == 0) ? WPI_RX_RING_COUNT - 1 : hw - 1; /* WPI_RX_RING_COUNT = 64 */

while (sc->rxq.cur != hw) {                       /* unreachable exit */
    sc->rxq.cur = (sc->rxq.cur + 1) % WPI_RX_RING_COUNT;   /* stays in 0..63 */
    ...
}
  • WPI_RX_RING_COUNT_LOG is 6 in non-DIAGNOSTIC builds (sys/dev/netif/wpi/if_wpireg.h:27), so WPI_RX_RING_COUNT = 1<<6 = 64 (if_wpireg.h:30).
  • sc->shared->next is a uint32_t written by the WiFi firmware over DMA (sys/dev/netif/wpi/if_wpireg.h:235, registered at if_wpi.c:5347-5348).
  • The kernel masks the firmware-supplied value with 0xfff (12 bits β†’ 0..4095) but then iterates rxq.cur modulo WPI_RX_RING_COUNT (0..63).

Effect: If the firmware (buggy, malicious, or wrong chip variant) ever writes a value whose low 12 bits exceed 63, the while-loop's exit condition rxq.cur == hw becomes unreachable: rxq.cur cycles 0..63 forever, while hw is stuck at some value β‰₯ 64. The interrupt handler never returns β†’ kernel hang (DoS). The ring-config register tells the firmware WPI_RX_RING_COUNT_LOG (if_wpi.c:5356) but the kernel nonetheless trusts up to 12 bits of an attacker-influenced DMA word.

Trigger reachability on this guest

  • No Intel PRO/Wireless 3945ABG (wpi) device is present in the QEMU guest. pciconf -lv shows no such NIC; the driver attaches to no device.
  • The vulnerable code is present in the running kernel (compiled-in), but the RX-completion interrupt wpi_notif_intr is only entered from the device's interrupt handler, which never fires without the HW.
  • Therefore the bug is not runtime-triggerable on this guest β€” it is a defensive / latent hardening defect that requires real (or emulated) wpi HW.

Fix

fix.diff replaces & 0xfff with & (WPI_RX_RING_COUNT - 1) (=& 0x3f) so that no firmware-supplied value can outrange the actual ring. The next two lines ((hw == 0) ? WPI_RX_RING_COUNT - 1 : hw - 1) and the loop predicate then become provably terminating. This matches the analogous masking done by iwm(4) / iwn(4) in their RX completion handlers.

Build verification of the fix

if_wpi.ko built successfully from the patched source on the guest (cc 8.3 [DragonFly]):

--- if_wpi.ko ---
cc -Wl,--build-id=sha1 -nostdlib -Wl,--hash-style=sysv   -r -Wl,-d -o if_wpi.ko if_wpi.o
BUILD_EXIT=0

Full output: build.log. The fix is build-clean (no warnings/errors).

Fix-validation status

not_testable β€” the bug requires Intel 3945ABG WiFi HW (or a faithful emulator that drives the firmware DMA descriptor) to runtime-trigger. The guest has neither. We confirmed the fix applies cleanly and the patched module compiles with cc 8.3 against the audit source tree; the change is a one-character mask correction with no behavioral side-effect on the well-behaved-firmware path.

Fix verification

not_testable

compile validated

module build rc=0

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

Source+harness. wpi_notif_intr RX index mask 0xfff vs WPI_RX_RING_COUNT=64 -> infinite loop. wpi in GENERIC, no WiFi HW.