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

NVM sections parsed at fixed word offsets against firmware-returned lengths (exact-size kmallocs) - heap OOB reads incl. regulatory[0x507] LAR read requiring >=2576 bytes

Summary

iwm_nvm_init() allocates each NVM section at the exact firmware-returned length, but iwm_parse_nvm_sections() only NULL-checks the pointers and iwm_parse_nvm_data() reads fixed offsets without consulting lengths: regulatory[0x507] reads bytes 0xA0E..0xA0F; 7000 channel flags memcpy reads SW bytes [64,142); 8000 flags read REGULATORY bytes [0,102); SKU/radio/n_hw_addrs/version; MAC override and HW addr nvm_hw[0x15]. Short sections -> OOB reads past small kmallocs into sc->nvm_data and a console-printed MAC. Firmware-controlled NVM content (same boundary as DF-1898/DF-2056): kernel heap OOB reads up to ~2.5KB past exact-size allocations, crash potential at page-adjacent boundaries. Fix: family-aware minimum section length validation.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3632 Β· 6 files
FileTypeDescriptionSize
README.md β€” 2.3 KB ↓ raw
VERDICT.md β€” 2.3 KB ↓ raw
env.txt β€” 740 B view raw
fix.diff β€” 1.7 KB view raw
manifest.json β€” 902 B view raw
verdict.json β€” 2.5 KB view raw

DF-3632 β€” NVM sections parsed with fixed offsets against firmware-returned lengths (heap OOB reads)

Where

sys/dev/netif/iwm/if_iwm.c:2215-2276 (iwm_parse_nvm_data and its helpers iwm_get_sku/iwm_get_nvm_version/iwm_get_radio_cfg/iwm_get_n_hw_addrs :2120-2161, iwm_set_hw_address :2184-2213, iwm_set_hw_address_family_8000 :2061-2118), fed by iwm_nvm_init() :2339-2393.

What

Each NVM section is read from the firmware into an exact-size heap allocation:

temp = kmalloc(len, M_DEVBUF, M_WAITOK);   /* len == bytes the FW returned */
memcpy(temp, nvm_buffer, len);
nvm_sections[section].data = temp;         /* if_iwm.c:2367-2375 */

iwm_parse_nvm_sections() only checks that section pointers are non-NULL (:2290-2333) β€” the section lengths are never validated β€” and then iwm_parse_nvm_data() reads fixed word offsets from them:

  • iwm_get_sku(): nvm_sw[IWM_SKU=2], phy_sku[IWM_SKU_8000=2] (:2125-2127)
  • iwm_get_n_hw_addrs(): nvm_sw[3] (:2156-2158)
  • family 7000 channel flags: memcpy(..., &nvm_sw[IWM_NVM_CHANNELS], IWM_NUM_CHANNELS*2) where IWM_NVM_CHANNELS = 0x1E0-0x1C0 = 32 (u16 index) β†’ reads SW bytes [64, 142) (:2266-2269)
  • family 8000 channel flags: memcpy(..., &regulatory[IWM_NVM_CHANNELS_8000=0], 51*2) β†’ REGULATORY bytes [0, 102) (:2270-2273)
  • LAR config: regulatory[IWM_NVM_LAR_OFFSET_8000=0x507] β†’ REGULATORY bytes [0xA0E, 0xA10) = offset 2574 (:2251-2255) β€” the section must be β‰₯ 2576 bytes, never checked
  • MAC override: mac_override[1..4] (:2072-2073), HW addr: nvm_hw[IWM_HW_ADDR=0x15] β†’ HW bytes [0x2A, 0x30) (:2194)

A firmware that returns a short section (e.g. an 8-byte SW section) makes each of these a heap OOB read past a small kmalloc allocation. The read values populate sc->nvm_data (channel map, radio cfg, MAC address) and the MAC is printed to the console (if_iwm.c:6386-6389).

Threat model

Firmware-controlled NVM content (same trust boundary as DF-1145/DF-1898; DF-1898 rated Low, DF-2056 rated Medium for the paging variant). No guest iwm hardware β€” source-level certainty (env.txt).

Fix

fix.diff β€” validate minimum section lengths in iwm_parse_nvm_sections() before parsing (family-aware requirements, incl. the 0x507/0x4C7 LAR offsets).

VERDICT.md
↓ download raw

DF-3632 VERDICT

Status: inconclusive (kernel path not exercisable β€” no iwm hardware on guest). Confidence in the defect: certain (source-level).

Verified in source

  1. Section buffers are exact-size heap allocations of the firmware-returned length: temp = kmalloc(len, ...) at sys/dev/netif/iwm/if_iwm.c:2367 with len from iwm_nvm_read_section() (bytes the firmware actually returned, :1950-1955).
  2. The only "validation" before parsing is non-NULL pointer checks (if_iwm.c:2290-2333). struct iwm_nvm_section.length (if_iwm.c:247-250) is recorded and then never consulted again.
  3. The parse performs fixed-offset reads listed in README.md; the largest is le16_to_cpup(regulatory + 0x507) (if_iwm.c:2251-2255), which reads bytes 0xA0E..0xA0F of the REGULATORY section; the channel-flag memcpys read 78/102 bytes at fixed offsets (:2266-2273); MAC/SKU/radio reads touch fixed words (:2072-2073, 2125-2158, 2194). With a short section each is a heap OOB read past a small kmalloc.
  4. Data sink: sc->nvm_data fields β†’ channel map (iwm_init_channel_map :2028-2059), MAC printed via ether_sprintf() at :6386-6389.

Precedent: DF-1898 (phy_db FW lengths unvalidated, Low) and DF-2056 (paging FW sections unvalidated, Medium) β€” same driver, same trust boundary; this is the NVM-sections instance living in if_iwm.c, missed in pass 1.

Why not reproduced on the guest

No iwm PCI device; iwm_nvm_init() runs only from the firmware-bring-up path (iwm_run_init_ucode(sc, 1) β†’ iwm_preinit), which requires the device.

Impact ceiling

Kernel heap OOB reads (tens of bytes to ~2.5KB past exact-size kmallocs) of attacker(untrusted-FW)-influenced content into driver state and console/log output (MAC string). Crash potential when adjacent allocations end at page boundaries. Severity: Low (privileged/physical trust boundary), consistent with DF-1898.

Fix validation

fix.diff authored; in-kernel validation impossible (no device) β†’ fix_status: not_testable.

Fix status (batch validation)

Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean

fix.diff validates family-aware minimum section lengths in iwm_parse_nvm_sections(); applies cleanly; no device to validate in-kernel

fix.diff verified with patch --dry-run against sys/dev/netif/iwm/if_iwm.c; no kernel-level validation possible (no iwm device)
↓ fix.diffper-fix-DF-3632

Confirmed kernel references

Detail

Evidence (decisive lines)

VERDICT.md; README.md (per-offset byte requirements); env.txt; fix.diff (minimum section-length validation, applies clean)

PoC changes

n/a - source-level verification

Verified recommended fix

fix.diff validates family-aware minimum section lengths in iwm_parse_nvm_sections()

Verdict

NVM sections are kmalloc'd at the exact firmware-returned length (if_iwm.c:2367-2375) but iwm_parse_nvm_data() and helpers read fixed word offsets without ever consulting nvm_sections[].length: e.g. regulatory[0x507] LAR read needs >=2576 bytes (:2251-2255), 7000 channel flags read SW bytes [64,142) (:2266-2269), 8000 flags read REGULATORY bytes [0,102) (:2270-2273), SKU/radio/n_hw_addr/MAC reads at fixed indices (:2072,:2125-2158,:2194). A short section yields heap OOB reads past small exact-size allocations; parsed data feeds the channel map and a console-printed MAC (:6386-6389). Same trust boundary as DF-1898 (Low). No iwm device on guest -> not exercisable (env.txt); source-level certainty.