iwm phy_db: unbounded firmware channel-group id β up-to-1 MiB M_WAITOK allocations from interrupt context + uint8_t truncation of the group-count upload loop (-1β255 hard-fails firmware load)
Summary
set_section takes the fw-supplied chg_id verbatim (no bound): first CHG notif allocates up to 1MiB M_WAITOK|M_ZERO from the RX interrupt path, each later chunk adds up to 65535B held until detach. Independently, send_all_channel_groups takes uint8_t while callers pass int - n>=256 truncates mod 256 (groups silently never uploaded, TX-power hazard) and init -1 becomes 255 -> get_section NULL -> EINVAL -> iwm_load_firmware aborts (device DoS) whenever fw sends no groups of a type. Truncation-only (no memory corruption); hostile-fw trust model; no iwm NIC on guest. Cap-chg_id + int-typed-count + skip-when-unset fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4816 Β· 5 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 3.5 KB | β raw | |
| VERDICT.md | β | 4.6 KB | β raw | |
| verdict.json | β | 4.0 KB | view raw | |
| fix.diff | β | 2.7 KB | view raw | |
| manifest.json | β | 1.3 KB | view raw |
DF-4816 β iwm phy_db: unvalidated firmware chg_id (group index) β unbounded
group-array allocation + uint8_t truncation of group count in send path
File
sys/dev/netif/iwm/if_iwm_phy_db.c (pass 2, GLM 5.3 re-audit)
Class
CWE-197 (numeric truncation error) + CWE-770 (allocation without bounds / uncontrolled total). Correctness / resource-exhaustion defect reachable only by the device firmware (hostile-device model: compromised or buggy Wi-Fi firmware image on Intel Wireless 7265/8000+ NICs). Not kernel memory corruption and not reachable by an unprivileged local user through any syscall path.
Build / run
There is no runnable PoC for this finding on the audit QEMU guest: the
defect requires an Intel Wireless 7265+/8000+ PCI device (8086:095a/095b/24f*/β¦)
whose firmware emits IWM_CALIB_RES_NOTIF_PHY_DB (0x6b) notifications with a
crafted first channel-group id. The guest has no such device (verified:
pciconf -l shows 7 generic QEMU devices, no 8086 wireless, no iwm module).
Verification is therefore static (code-path proof below); runtime reproduction
requires bare-metal hardware with a patched firmware image.
What a hostile firmware sends (trigger description)
During the init-ucode calibration phase (driver flow: iwm_run_init_ucode() β
notif-wait callback iwm_wait_phy_db_entry() β iwm_phy_db_set_section()),
the firmware emits:
struct iwm_calib_res_notif_phy_db {
uint16_t type; /* 4 = IWM_PHY_DB_CALIB_CHG_PAPD or 5 = CHG_TXP */
uint16_t length;
uint8_t data[]; /* data[0..1] = chg_id (group index), fw-chosen */
} __packed;
chg_id is taken verbatim from data[0..1] (if_iwm_phy_db.c:279 / :293) with
no upper bound, and two independent defects follow:
-
Unbounded allocation / accumulation (if_iwm_phy_db.c:285-290, 299-304): on the first CHG notification of each type the driver allocates
(chg_id + 1) * sizeof(struct iwm_phy_db_entry)β up to 65536 entries = 1 MiB per type β withM_WAITOK|M_ZEROfrom the RX interrupt path (iwm_intrβiwm_notif_intrβiwm_handle_rxbβiwm_notification_wait_notifyβiwm_wait_phy_db_entry). Each subsequent CHG notification for a fresh index allocateslength(up to 65535) more bytes ofM_DEVBUFheld until detach, bounded only by the calibration wait timeout β i.e. hundreds of MB of unreclaimable kernel memory from a few seconds of hostile notifications. -
uint8_t truncation (if_iwm_phy_db.c:455-458 param
uint8_t max_ch_groups; call sites 541-542 and 551-552 pass theintn_group_papd/n_group_txp): -n_group_* >= 256β count truncated mod 256 β groups β₯ 256 are silently never uploaded to the runtime firmware (radio runs with missing PAPD/TXP calibration β TX-power correctness hazard), or nothing at all is sent when the count is a multiple of 256. -n_group_* == -1(init value β fw sent no groups of that type) β passed as 255 β firstiwm_phy_db_get_section()returns NULL βiwm_phy_db_send_all_channel_groups()returns EINVAL βiwm_send_phy_db_data()fails βiwm_load_firmware()aborts (if_iwm.c:4856-4857) β interface cannot start (device DoS).
Expected result if run on real hardware with hostile fw
- dmesg: either
iwm_send_phy_db_datafailure / firmware load abort, or silent partial calibration upload; sustainedM_DEVBUFgrowth invmstat -mduring the calibration window.
Fix
See fix.diff (cap chg_id at 255; pass the group count as int and skip
non-positive counts).
DF-4816 β VERDICT
Status: untested (static proof only β hardware absent from audit guest) Impact class: none demonstrated at runtime; code-path defect proven certain by inspection
What was verified, and how
-
Reachability of the defect (proven, static). - Only caller of
iwm_phy_db_set_section()in the tree:iwm_wait_phy_db_entry()β sys/dev/netif/iwm/if_iwm.c:2831, registered as the notif-wait callback iniwm_run_init_ucode()(if_iwm.c:2952-2957). - Callback invocation chain (interrupt context):iwm_intr(if_iwm.c:5740, registered viabus_setup_intrwithwlan_global_serializer, if_iwm.c:6028-6030) βiwm_notif_intr(if_iwm.c:5876) βiwm_handle_rxb(if_iwm.c:5724) βiwm_notification_wait_notify(if_iwm.c:5407 β if_iwm_notif_wait.c:118-153, invokeswait_entry->fnunderlk_lk) βiwm_wait_phy_db_entryβiwm_phy_db_set_section. -chg_idisle16toh()of rawphy_db_notif->data[0..1](if_iwm_phy_db.c:279, :293). Nothing upstream bounds it; nothing inset_sectionbounds it. The array sized(chg_id+1)*16bytes is allocated withM_WAITOK|M_ZERO(if_iwm_phy_db.c:285-287, 299-301);(chg_id+1)max 65536 β max 1 MiB per type, no integer overflow (uint16 β int promotion; 65536*16 = 2^20 fits int). - Send-side truncation:iwm_phy_db_send_all_channel_groups()declaresuint8_t max_ch_groups(if_iwm_phy_db.c:455-458); call sites pass theintfieldsphy_db->n_group_papd/phy_db->n_group_txp(if_iwm_phy_db.c:541-542, 551-552), init value -1 (if_iwm_phy_db.c:187-188). C integer conversion: -1 β 255, 65536 β 0, 300 β 44. - Consequence chain for n_group == -1: loop i=0 βiwm_phy_db_get_sectionchg_id(0) >= n_group(-1)β NULL (if_iwm_phy_db.c:212-214) β EINVAL (if_iwm_phy_db.c:469-470) βiwm_send_phy_db_dataerror (if_iwm_phy_db.c:543-548) βiwm_load_firmwaregoto error (if_iwm.c:4856-4857). -
Why it is NOT memory corruption (negative results). - The truncated loop count can only shrink (
mod 256), never exceed the allocated group count, so the walk iniwm_phy_db_send_all_channel_groupscannot go out of bounds. - Entry allocation andentry->sizeare always the samesize(if_iwm_phy_db.c:314-321); the send path usesentry->sizebytes fromentry->data(if_iwm_phy_db.c:476-479) andiwm_send_cmdskips zero-length fragments and rejects oversized payloads (if_iwm_util.c:271-277, 214-226). No OOB via the u16 length fields. - The OOB read against the actual RX packet size inset_section(memcpy at if_iwm_phy_db.c:319 and the chg_id reads at :279/:293) is DF-1898 (known, pass 1) β deliberately not re-reported here. -
Why runtime reproduction was not attempted. - Guest: DragonFly 6.5-DEVELOPMENT X86_64_GENERIC (QEMU).
pciconf -lshows 7 devices, none an Intel Wireless 7265/8000+ NIC; no iwm module. The trigger is emitted only by the NIC firmware on the calibration completion path; there is no software-only injection point (the RX notification comes from DMA'd device buffers, not from any syscall). - No unprivileged-local-user path exists, so the mandatory uid=0 escalation objective of Phase V does not apply; per the review contract, Low-severity hardware/firmware-gated findings may be filed with static certainty and fix validation "not_testable".
Exploit chain
(none β not an escalation primitive; hostile-firmware-triggered correctness / resource-exhaustion defect. A compromised firmware already has stronger primitives, e.g. DF-1898.)
Fix validation
fix.diff authored (chg_id cap + int-typed group count + non-positive skip);
not buildable against this guest (no iwm hardware to attach the driver;
kernel build would succeed but the code path cannot execute), so
fix_status = not_testable.
Bottom line
Certain static finding, Low severity: firmware-controlled group index without
bounds produces up-to-1 MiB M_WAITOK allocations from interrupt context per
group type plus modulo-256 truncation of the upload loop (incl. the -1β255
case that hard-fails firmware load). No kernel memory unsafety; matches
upstream iwlwifi 3.14 heritage (upstream later added group-count validation).
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_testablefix.diff authored against the read-only sys/ tree (cap + int count + non-positive skip). Not testable: the guest has no Intel Wireless 7265+ NIC, so neither baseline nor patched kernel can execute the calibration path. Compile-correctness of the diff verified by inspection (types and call sites updated consistently).
findings/poc/DF-4816/fix.diff
Confirmed kernel references
- sys/dev/netif/iwm/if_iwm_phy_db.c:279
- sys/dev/netif/iwm/if_iwm_phy_db.c:285-290
- sys/dev/netif/iwm/if_iwm_phy_db.c:293
- sys/dev/netif/iwm/if_iwm_phy_db.c:299-304
- sys/dev/netif/iwm/if_iwm_phy_db.c:455-458
- sys/dev/netif/iwm/if_iwm_phy_db.c:541-542
- sys/dev/netif/iwm/if_iwm_phy_db.c:551-552
- sys/dev/netif/iwm/if_iwm.c:2831
- sys/dev/netif/iwm/if_iwm.c:4856
Detail
Evidence (decisive lines)
VERDICT.md (full static proof with call chain and C-conversion analysis); README.md (trigger description); fix.diff (chg_id cap at 255 + int-typed group count + skip non-positive counts)
PoC changes
No seed PoC existed; no runnable PoC is possible without the iwm NIC and a hostile firmware image (RX notifications are DMA'd by the device, not injectable via syscalls). Pack documents the trigger and the static proof instead.
Verified recommended fix
Cap firmware-supplied chg_id at 255 in iwm_phy_db_set_section (EINVAL otherwise), change iwm_phy_db_send_all_channel_groups max_ch_groups param to int, and skip send when n_group_papd/n_group_txp <= 0
Verdict
Static-proof finding, Low severity. iwm_phy_db_set_section() takes the firmware-supplied channel-group id (chg_id) verbatim (if_iwm_phy_db.c:279,:293) with no upper bound: the first CHG notification of a type allocates (chg_id+1)*16 bytes (up to 1 MiB) with M_WAITOK|M_ZERO from the RX interrupt path (iwm_intr -> iwm_notif_intr -> iwm_handle_rxb -> iwm_notification_wait_notify -> iwm_wait_phy_db_entry, if_iwm.c:2831), and subsequent per-index chunks accumulate M_DEVBUF bounded only by the calibration wait timeout. Independently, iwm_phy_db_send_all_channel_groups() takes the group count as uint8_t (if_iwm_phy_db.c:455-458) while callers pass the int n_group_papd/n_group_txp (if_iwm_phy_db.c:541-542,:551-552): counts >=256 truncate mod 256 (calibration groups silently never uploaded to the runtime firmware), and the init value -1 becomes 255, making get_section() return NULL and send_all_channel_groups() return EINVAL so iwm_send_phy_db_data() fails and iwm_load_firmware() aborts (if_iwm.c:4856-4857) - a device DoS when the firmware sends no channel groups. Not kernel memory corruption (truncation can only shrink the loop; entry->size always equals the allocation), and not reachable by an unprivileged local user: the only trigger is a firmware-emitted IWM_CALIB_RES_NOTIF_PHY_DB notification. Runtime reproduction impossible on the audit guest: pciconf shows no Intel Wireless 7265/8000+ device and no iwm module (DragonFly 6.5-DEVELOPMENT X86_64_GENERIC QEMU guest, verified this run).
No comments yet.