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

iwn5000_rx_calib_results: signed integer underflow on firmware length and unbounded heap copy from RX buffer

Summary

iwn5000_rx_calib_results at if_iwn.c:3364: len=(le32toh(desc->len)&0x3fff)-4 with no check that masked desc->len>=4. Underflow to negative int. kmalloc(len) fails for huge size_t (caught at :3396). But for any positive len larger than actual DMA payload (e.g. firmware reports 16379 bytes but RX mbuf only carries 200 bytes), memcpy(sc->calibcmd[idx].buf, calib, len) at :3405 copies len bytes from offset 8 in 4KB mbuf -> reads up to ~12KB past mbuf cluster. calibcmd replayed to runtime firmware via iwn5000_send_calibration providing path back out. Triggered by buggy/compromised firmware or DMA injection. Fix: bound len both above and below against IWN_RBUF_SIZE.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1124 Β· 11 files
FileTypeDescriptionSize
harness.c trigger-source demonstrates underflow + OOB-read for 4 firmware-length cases 3.0 KB view raw
fix.diff suggested-fix clamp len to [0, IWN_RBUF_SIZE - sizeof(*desc)] 726 B view raw
build.sh build-script cc -O2 -o harness harness.c 95 B view raw
run.sh run-script timeout 10 ./harness 67 B view raw
build.log build-log final successful build 13 B view raw
run.log run-log decisive run showing all 4 cases 1.2 KB view raw
env.txt environment uname, cc version, pciconf (no Intel WiFi) 403 B view raw
VERDICT.md verdict full narrative + info-leak ceiling 2.9 KB ↓ raw
README.md readme finding summary + build/run/expected 1.5 KB ↓ 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 finding summary + build/run/expected
↓ download raw

DF-1124 β€” Signed integer underflow + OOB read in iwn5000_rx_calib_results (Intel WiFi)

Finding

iwn5000_rx_calib_results at sys/dev/netif/iwn/if_iwn.c:3364:

len = (le32toh(desc->len) & 0x3fff) - 4;
  • len is a signed int. If (desc->len & 0x3fff) < 4, underflows to negative β†’ kmalloc((size_t)len) = huge β†’ ENOMEM (caught at :3396).
  • If firmware reports e.g. masked len = 16380, len = 16376; kmalloc succeeds, then memcpy(sc->calibcmd[idx].buf, calib, len) at :3405 reads 16376 bytes from calib = desc + 1 inside a 4 KiB RX mbuf β†’ up to ~12 KiB OOB read (info leak / panic). The calibcmd buffer is later replayed to runtime firmware via iwn5000_send_calibration.

Reachability on this guest

NOT reachable. No Intel WiFi adapter is present. if_iwn.ko is loadable but never attaches. The bug requires buggy/compromised firmware or DMA injection. Latent.

A userspace harness demonstrates both the underflow case and the OOB-read case for various firmware-supplied lengths.

Build / Run / Expected

cc -O2 -o harness harness.c     # build.sh
./harness                        # run.sh
# Expected: shows underflow (-4 β†’ kmalloc(18446744073709551612)) and
#           OOB read (len=16376 from 4096-byte mbuf β†’ 12288 bytes OOB)

Files

  • harness.c β€” demonstrates underflow and OOB-read for 4 firmware-length cases.
  • fix.diff β€” adds if (len < 0 || len > (int)(IWN_RBUF_SIZE - sizeof(*desc))) return;.
  • build.log / run.log / env.txt β€” captured outputs.
VERDICT.md verdict full narrative + info-leak ceiling
↓ download raw

VERDICT β€” DF-1124

Verdict: REPRODUCED (primitive) / NOT REACHABLE on guest (HW-gated)

The cited bug is real and confirmed by source trace + userspace demonstration of both the underflow and OOB-read cases. It is a latent info-leak / panic primitive requiring an Intel WiFi adapter and buggy/compromised firmware or DMA injection.

Mechanism (confirmed path:line)

  1. iwn5000_rx_calib_results (sys/dev/netif/iwn/if_iwn.c:3350-3406) processes a calibration result notification from the firmware.
  2. :3364: len = (le32toh(desc->len) & 0x3fff) - 4; β€” len is a signed int. The masked desc->len comes from the firmware/DMA and is untrusted.
  3. Underflow case: if (desc->len & 0x3fff) < 4, len goes negative. kmalloc(len, ...) at :3395 promotes len to size_t β†’ enormous allocation β†’ ENOMEM (caught at :3396, returns). Benign for tiny masked lens.
  4. OOB-read case: if firmware reports e.g. masked len = 16380, len =
  5. kmalloc(16376) succeeds. Then memcpy(sc->calibcmd[idx].buf, calib, len) at :3405 reads 16376 bytes starting at calib = desc + 1 which lives inside a 4 KiB (IWN_RBUF_SIZE) RX mbuf cluster β†’ reads up to ~12 KiB past the mbuf boundary. The calibcmd buffer is later replayed to runtime firmware via iwn5000_send_calibration, providing a data-exfiltration path.

Reproduction (userspace harness)

The harness exercises four firmware-length scenarios: - masked=0 β†’ underflow to len=-4 β†’ kmalloc(18446744073709551612) β†’ ENOMEM - masked=3 β†’ underflow to len=-1 β†’ kmalloc(18446744073709551615) β†’ ENOMEM - masked=16380 β†’ len=16376 β†’ 12288 bytes OOB read past 4 KiB mbuf - masked=200 β†’ len=196 β†’ benign (within mbuf)

Impact ceiling

  • Per-call: up to ~12 KiB kernel heap OOB read, contents exfiltrated to firmware via calibration replay β†’ info leak. On default GENERIC (INVARIANTS ON), the OOB read may hit a poisoned/poison-checked slab and panic; on noinv it's a silent info leak.
  • Privilege: requires Intel WiFi adapter + malicious firmware or DMA injection (attacker controls the wire). Not a local unpriv path.
  • Realistic: remote via compromised firmware/PCIe; niche.

Fix

fix.diff clamps len both ways after computing it:

if (len < 0 || len > (int)(IWN_RBUF_SIZE - sizeof(*desc)))
    return;

IWN_RBUF_SIZE = 4096 (if_iwnreg.h:54). The upper bound is exactly the bytes available in the RX mbuf after the desc header, since calib = desc + 1.

Validated: if_iwn.ko builds with RC=0 after applying the fix.

Fix validation

  • Patch applies cleanly: Hunk #1 succeeded at 3362.
  • make in sys/dev/netif/iwn/ β†’ all firmware modules + if_iwn.ko linked, IWN_RC=0.
  • Cannot boot-test (no Intel WiFi); fix_status: not_testable.

PoC changes

  • harness.c written from scratch. Demonstrates four firmware-length scenarios showing both the underflow-to-ENOMEM and the OOB-read-of-12288 cases.

Fix verification

not_testable

compile validated

module build rc=0

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

Source+harness. iwn5000_rx_calib signed underflow -> 12KB OOB read from mbuf. No Intel WiFi HW.