# REPRO-2026-00373: Linux kernel iwlwifi PCIe UAF/double-free — iwl_pcie_rx_free() leaves freed RX pointers (rx_pool, global_table, rxq, alloc_page) non-NULL after free ## Summary Status: published Severity: medium CVSS: Unknown CWE: CWE-416 (Use After Free) Type: security Confidence: medium ## Identifiers REPRO ID: REPRO-2026-00373 CVE: CVE-2026-93796 ## Package Name: Unknown Ecosystem: Unknown Affected: Unknown Fixed: Unknown ## Root Cause # CVE-2026-93796 — Root Cause Analysis: iwlwifi PCIe `iwl_pcie_rx_free()` double-free / use-after-free ## Summary The Linux kernel iwlwifi PCIe transport frees its RX bookkeeping objects in `iwl_pcie_rx_free()` (`drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/rx.c`, older trees: `pcie/rx.c`) but never invalidates the pointers on the still-live `struct iwl_trans_pcie`. The function's own re-entry guard uses `trans_pcie->rxq == NULL` as the "nothing allocated" sentinel, so after a first free — performed on the nic-init error unwind when `iwl_pcie_tx_init()` fails inside `iwl_pcie_nic_init()` — any later teardown or retry path (interface down/up, driver unbind/rebind, reprobe, suspend/resume, error recovery) that calls `iwl_pcie_rx_free()` again passes the guard and re-frees `rx_pool`, `global_table`, `rxq` (kfree) and `alloc_page` (__free_pages), producing a double-free, and dereferences the freed `rx_pool[]`/`rxq[]` arrays, producing a slab use-after-free (CWE-416, with missing pointer invalidation CWE-459). ## Impact - Component: Linux kernel `iwlwifi` PCIe transport (`drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/rx.c`). - Affected versions: `< 6.12.111`; `6.13.0 – 6.18.53`; `6.19.0 – 7.1.x`. - Fixed in: 6.12.111 (`6ec5bf430cf7466cb412e47f2761594092781fac`), 6.18.53 (`3456c5bcc987aeb182e50e30c9b02be8420e9953`), upstream 7.2 (`2c79d7a7b583050c9f58041465cb46fe3483ab5d`). - Consequences: kernel memory corruption (double-free / slab use-after-free) in the WiFi driver teardown path — kernel crash (DoS) and a potentially exploitable heap corruption primitive on systems with Intel WiFi PCIe hardware. This run confirms memory corruption only; no code execution is claimed. ## Impact Parity - Disclosed/claimed maximum impact: memory corruption (double-free / UAF) — KASAN-detectable; the advisory does not claim RCE. - Reproduced impact this run: on the v6.18.52 KASAN+SLUB_DEBUG+DEBUG_OBJECTS kernel the second `iwl_pcie_rx_free()` produced (a) a KASAN `use-after-free` 8-byte read of the freed `rx_pool` in `iwl_pcie_free_rbs_pool` <- `iwl_pcie_rx_free` [iwlwifi], (b) a BAD_PAGE taint from the repeated `__free_pages(alloc_page)`, and (c) a kernel oops (NULL/wild dereference in `dma_free_attrs` <- `iwl_pcie_free_rxq_dma` <- `iwl_pcie_rx_free`) from walking the freed `rxq[]` state. The identical trigger on the fixed kernel completes cleanly 2/2 (negative control). - Parity: `full` for the claimed memory-corruption impact class. - Not demonstrated: code execution (out of scope per the advisory/ticket). ## Root Cause `iwl_pcie_rx_free()` (v6.18.52, rx.c line 1204) ends with: ```c kfree(trans_pcie->rx_pool); kfree(trans_pcie->global_table); kfree(trans_pcie->rxq); if (trans_pcie->alloc_page) __free_pages(trans_pcie->alloc_page, trans_pcie->rx_page_order); ``` None of the four freed members are set to `NULL`, yet the function starts with: ```c if (!trans_pcie->rxq) { IWL_DEBUG_INFO(trans, "Free NULL rx context\n"); return; } ``` The first call (nic-init unwind after a TX-init failure) frees everything but leaves `rxq` non-NULL, defeating the sentinel. A second call on the same live transport object walks the freed `rxq[]`/`rx_pool[]` arrays (`iwl_pcie_free_rbs_pool()` reads `rx_pool[i].page`, the queue loop reads `rxq->bd`/`rxq->napi.poll`) and then `kfree()`s the same three pointers again and re-frees `alloc_page` — UAF reads plus double-frees. The fix (upstream `2c79d7a7b583050c9f58041465cb46fe3483ab5d`, "wifi: iwlwifi: pcie: null RX pointers after free", Emmanuel Grumbach, Intel) sets `rx_pool`, `global_table`, `rxq` and `alloc_page` to `NULL` immediately after freeing, so repeated cleanup and retry paths fail safe on the sentinel. ## Reproduction Steps 1. `bundle/repro/reproduction_steps.sh` (self-contained; see that file). 2. The script: - clones linux-stable `v6.18.52` (vulnerable) into the prepared project cache; - verifies `iwl_pcie_rx_free()` lacks the pointer-NULLing fix; - because QEMU cannot emulate an Intel WiFi PCIe device (explicitly anticipated by the ticket), patches a hardware-independent selftest trigger **into the real driver** (`pcie/gen1_2/rxfree-selftest.c` + a `rx_free_selftest=1` module-parameter hook in `iwl_drv_init()`): it builds RX state exactly as `iwl_pcie_rx_alloc()` does (kcalloc of `rxq`/`rx_pool`/`global_table`, `alloc_pages` for `alloc_page`, initialised RB-allocator work/lists) and then calls the **real, unmodified `iwl_pcie_rx_free()` twice** — first = nic-init unwind after `iwl_pcie_tx_init()` failure, second = later teardown/retry on the same live transport object; - builds a `defconfig`+KASAN(outline)+SLUB_DEBUG+DEBUG_OBJECTS kernel with `CONFIG_IWLWIFI=m`, producing a vulnerable `iwlwifi.ko`; then applies the stable fix hunk to `rx.c` and rebuilds a fixed `iwlwifi.ko` against the same kernel; - boots the kernel twice per build in QEMU/KVM with a busybox initramfs that `insmod`s the module with `rx_free_selftest=1`, capturing the serial log. 3. Expected evidence: vulnerable boots emit `BUG: KASAN: use-after-free` in `iwl_pcie_free_rbs_pool` <- `iwl_pcie_rx_free` (`[iwlwifi]`) followed by a BAD_PAGE taint and a fatal oops in `iwl_pcie_free_rxq_dma`; fixed boots print `SELFTEST-COMPLETE` with all four pointers NULLed and no KASAN splat. ## Evidence - `bundle/logs/vm_vuln_attempt1.log`, `bundle/logs/vm_vuln_attempt2.log` — KASAN splats naming the RX teardown path (2/2 attempts). - `bundle/logs/vm_fixed_attempt1.log`, `bundle/logs/vm_fixed_attempt2.log` — negative control, selftest completes cleanly (2/2 attempts). - `bundle/logs/build_bzimage.log`, `bundle/logs/build_mod_vuln.log`, `bundle/logs/build_mod_fixed.log` — build transcripts. - `bundle/repro/runtime_manifest.json` — runtime evidence manifest with artifact hashes and immutable target identity (`git:https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git@8f3741e6feb045da5b406df0a80b42a1adfb289b` = v6.18.52). - Environment: x86_64, QEMU/KVM (`-machine accel=kvm:tcg`), KASAN outline, SLUB_DEBUG, DEBUG_OBJECTS(_FREE/_TIMERS) enabled. ## Recommendations / Next Steps - Backport/apply upstream `2c79d7a7b583050c9f58041465cb46fe3483ab5d` ("null RX pointers after free") — already in 6.12.111 / 6.18.53 / 7.2. - Upgrade affected kernels to >= 6.12.111 / >= 6.18.53 / >= 7.2. - Regression test: an in-tree KUnit or error-injection test that forces `iwl_pcie_tx_init()` failure and then re-runs teardown would lock the invariant "rx free is idempotent" in. ## Additional Notes - Idempotency: the script is re-runnable; it restores `rx.c` to the vulnerable state, caches the bzImage and both module builds in the project cache, and re-executes fresh QEMU proofs on every run. - Evidence scope: QEMU exposes no Intel WiFi PCIe device, so the trigger is a patched-in selftest that executes the **real** vulnerable function with faithful allocator state — matching the ticket's explicit fallback ("reproduce the unwind/retry sequence with a patched-in test trigger … state the scope honestly"). The double-free/UAF depends only on the pointer invalidation bug in `iwl_pcie_rx_free()`, not on hardware behavior: on real hardware the same two calls are reached via nic-init unwind + teardown/retry. - Scope: memory corruption only; RCE is not claimed and was not attempted. ## Reproduction Details Reproduced: 2026-09-24T19:27:06.935Z Duration: 3340 seconds Tool calls: 167 Turns: Unknown Handoffs: 2 ## Quick Verification Run one of these commands to verify locally: pruva-verify REPRO-2026-00373 pruva-verify CVE-2026-93796 Or open in GitHub Codespaces (zero-friction, auto-runs): https://github.com/codespaces/new?ref=repro/REPRO-2026-00373&repo=N3mes1s/pruva-sandbox Or download and run the script manually: curl -O https://api.pruva.dev/v1/reproductions/REPRO-2026-00373/artifacts/bundle/repro/reproduction_steps.sh chmod +x reproduction_steps.sh ./reproduction_steps.sh WARNING: Run in a sandboxed environment. This exploits a real vulnerability. ## References - NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-93796 - Source: https://git.kernel.org/stable/c/2c79d7a7b583050c9f58041465cb46fe3483ab5d ## Artifacts - bundle/repro/rca_report.md (analysis, 7476 bytes) - bundle/repro/reproduction_steps.sh (reproduction_script, 17294 bytes) - bundle/repro/runtime_manifest.json (other, 1657 bytes) - bundle/repro/validation_verdict.json (other, 1598 bytes) ## API Access - JSON: https://api.pruva.dev/v1/reproductions/REPRO-2026-00373 - Script: https://api.pruva.dev/v1/reproductions/REPRO-2026-00373/artifacts/bundle/repro/reproduction_steps.sh - Web: https://www.pruva.dev/reproductions/REPRO-2026-00373 ## For AI Assistants To help users with this reproduction: 1. The `pruva-verify` command is the simplest way to run the reproduction 2. The script downloads and runs automatically in an isolated temp directory 3. Always recommend running in a VM, container, or disposable environment 4. The root cause explains the technical vulnerability --- Generated by Pruva | https://www.pruva.dev