Skip to content

CVE-2026-93796: Verified Reproduction

CVE-2026-93796: 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

CVE-2026-93796 is verified against the affected target. Vulnerability class: Use-After-Free. This medium reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00373.

REPRO-2026-00373 Use-After-Free Sep 24, 2026 CVE entry .txt
Severity
MEDIUM
Confidence
MEDIUM
Reproduced in
55m 40s
Tool calls
167
Spend
$5.12
01 · Overview

What Is CVE-2026-93796?

CVE-2026-93796 is a medium-severity Use-After-Free vulnerability. Pruva has independently reproduced it and publishes a verified, runnable proof-of-concept (reproduction REPRO-2026-00373).

02 · Severity & CVSS

CVE-2026-93796 Severity

CVE-2026-93796 is rated medium severity.

MEDIUM threat level
Weakness CWE-416 — Use After Free

Medium — meaningful risk under specific conditions. Schedule a fix in the normal cycle.

How to Reproduce CVE-2026-93796

$ pruva-verify REPRO-2026-00373
or curl -O https://www.pruva.dev/api/v1/reproductions/REPRO-2026-00373/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh
Run in a VM or disposable container. This exploits a real vulnerability.
06 · Proof of Reproduction

Proof of Reproduction for CVE-2026-93796

Memory corruption — reproduced
  • reached the target end-to-end
  • crash observed
  • medium confidence
Trigger

local trigger of iwlwifi nic-init failure (iwl_pcie_tx_init) followed by driver teardown/retry; reproduced hardware-independently via an in-driver selftest hook that runs the real iwl_pcie_rx_free() unwind+retry sequence

Attack chain
  1. iwl_pcie_nic_init
  2. iwl_pcie_tx_init failure unwind
  3. iwl_pcie_rx_free (1st free, pointers left non-NULL)
  4. later teardown/retry
  5. iwl_pcie_rx_free re-entry
  6. KASAN UAF read in iwl_pcie_free_rbs_pool, BAD_PAGE from repeated __free_pages, fatal oops in iwl_pcie_free_rxq_dma
How the agent worked 367 events · 167 tool calls · 56 min
56 minDuration
167Tool calls
75Reasoning steps
367Events
8Dead-ends
Agent activity over 56 min
Policy
1
Support
8
Repro
211
Judge
43
Variant
99
Verify
1
0:0055:30

Root Cause and Exploit Chain for CVE-2026-93796

Versions: < 6.12.111; 6.13.0 – 6.18.53; 6.19.0 – 7.1.x.Fixed: in: 6.12.111 (6ec5bf430cf7466cb412e47f2761594092781fac),

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).

  • 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:

	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:

	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 insmods 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.

CVE-2026-93796 Reproduction Transcript

The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.

Full session Replay every step — scrub the timeline or play it back.

Event 1/40
0:003:33
0:00
session startedaccounts/fireworks/models/kimi-k3 · CVE-2026-93796 · REPRO-20
0:10
0:12
web search
1:06
1:06
extract_facts
no facts extracted
1:09
1:09
supportclaim_contract
1:16
1:16
1:16
1:20
1:20
1:51
1:51
1:59
$nproc; free -g | head -3; ls -l /dev/kvm 2>/dev/null || echo "no kvm"; df -h /data /tmp | tail -3; ls /data/pruva/project-cache/.run-views/46a9f9d7faa0668cd1d746dcecf0182198c4d82681b334903195a1e1072ebad4/ 2>/dev/null; ls /data/pruva/project-cache/.run-views/46a9f9d7faa0668cd1d746dcecf0182198c4d82681b334903195a1e1072ebad4/repo-mirrors/ 2>/dev/null
✓
8
               total        used        free      shared  buff/cache   available
Mem:              31           0          24           0           6          30
Swap:              0           0           0
crw-rw---- 1 root kvm 10, 232 Sep 23 14:39 /dev/kvm
Filesystem      Size  Used Avail Use% Mounted on
/dev/vda1       145G   81G   64G  56% /
/dev/vda1       145G   81G   64G  56% /tmp
1:59
2:06
2:07
$id; groups; ls /data/pruva/project-cache/.run-views/46a9f9d7faa0668cd1d746dcecf0182198c4d82681b334903195a1e1072ebad4/repo-mirrors/; apt list --installed 2>/dev/null | grep -Ei 'flex|bison|libelf-dev|libssl-dev|bc |busybox|qemu-system' | head -20; which qemu-system-x86_64 gcc make bc flex bison busybox 2>/dev/null
0.4s✓
{"cwd":"/data/pruva/runs/d4ca1255-f7a4-4d7a-afc0-26790bf36658","duration_ms":342,"exit_code":0,"stderr":"","stdout":"uid=1000(pruva) gid=1001(pruva) groups=1001(pruva),27(sudo),993(kvm),1000(docker)\npruva sudo kvm docker\nbison/noble-updates,noble-security,now 2:3.8.2+dfsg-1ubuntu0.24.04.1 amd64 [installed]\nbusybox-initramfs/noble-updates,noble-security,now 1:1.36.1-6ubuntu3.1 amd64 [installed,automatic]\nbusybox-static/noble-updates,noble-security,now 1:1.36.1-6ubuntu3.1 amd64 [installed,automatic]\nflex/noble,now 2.6.4-8.2build1 amd64 [installed]\nlibelf-dev/noble-updates,noble-security,now 0.190-1.1ubuntu0.1 amd64 [installed]\nlibssl-dev/noble-updates,noble-security,now 3.0.13-0ubuntu3.15 amd64 [installed]\nqemu-system-common/now 1:8.2.2+ds-0ubuntu1.17 amd64 [installed,upgradable to: 1:8.2.2+ds-0ubuntu1.18]\nqemu-system-data/now 1:8.2.2+ds-0ubuntu1.17 all [installed,upgradable to: 1:8.2.2+ds-0ubuntu1.18]\nqemu-system-gui/now 1:8.2.2+ds-0ubuntu1.17 amd64 [installed,upgradable to: 1:8.2.2+ds-0ubuntu1.18]\nqemu-system-modules-opengl/now 1:8.2.2+ds-0ubuntu1.17 amd64 [installed,upgradable to: 1:8.2.2+ds-0ubuntu1.18]\nqemu-system-modules-spice/now 1:8.2.2+ds-0ubuntu1.17 amd64 [insta… [truncated]
2:14
2:14
$CACHE=/data/pruva/project-cache/.run-views/46a9f9d7faa0668cd1d746dcecf0182198c4d82681b334903195a1e1072ebad4; ls -la $CACHE/ $CACHE/repo-mirrors/ 2>&1; cat $CACHE/.pruva/cache_manifest.json 2>/dev/null || echo "no manifest"
✓
/data/pruva/project-cache/.run-views/46a9f9d7faa0668cd1d746dcecf0182198c4d82681b334903195a1e1072ebad4/:
total 16
drwx------ 4 pruva pruva 4096 Sep 24 12:42 .
drwx------ 5 pruva pruva 4096 Sep 24 12:42 ..
drwx------ 2 pruva pruva 4096 Sep 24 12:42 .pruva
drwx------ 2 pruva pruva 4096 Sep 24 12:42 repo-mirrors

/data/pruva/project-cache/.run-views/46a9f9d7faa0668cd1d746dcecf0182198c4d82681b334903195a1e1072ebad4/repo-mirrors/:
2:53
2:53
$mkdir -p /tmp/iwl && curl -sL "https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/plain/drivers/net/wireless/intel/iwlwifi/pcie/rx.c?h=v6.18.52" -o /tmp/iwl/rx_v61852.c; wc -l /tmp/iwl/rx_v61852.c; grep -n "iwl_pcie_rx_free\|_iwl_pcie_rx_init\|iwl_pcie_rx_init" /tmp/iwl/rx_v61852.c | head
0.2s✓
156 /tmp/iwl/rx_v61852.c
2:53
$curl -sL "https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/patch/?id=3456c5bcc987aeb182e50e30c9b02be8420e9953" -o /tmp/iwl/fix.patch; head -80 /tmp/iwl/fix.patch
0.1s✓
{"cwd":"/data/pruva/runs/d4ca1255-f7a4-4d7a-afc0-26790bf36658","duration_ms":97,"exit_code":0,"stderr":"","stdout":"From 3456c5bcc987aeb182e50e30c9b02be8420e9953 Mon Sep 17 00:00:00 2001\nFrom: Emmanuel Grumbach <emmanuel.grumbach@intel.com>\nDate: Tue, 14 Jul 2026 14:20:03 +0300\nSubject: wifi: iwlwifi: pcie: null RX pointers after free\n\n[ Upstream commit 2c79d7a7b583050c9f58041465cb46fe3483ab5d ]\n\nWhen iwl_pcie_tx_init() fails after RX init, nic init unwinds via\niwl_pcie_rx_free().\n\nThe freed RX members stayed non-NULL on the live transport object,\nso later teardown or retry could touch stale RX state.\nSet rx_pool, global_table, rxq, and alloc_page to NULL after free\nto make repeated cleanup and retry paths safe.\n\nAssisted-by: GitHubCopilot:gpt-5.3-codex\nSigned-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>\nLink: https://patch.msgid.link/20260714141909.33e8978d8b36.Ibaedd4b0ce01405b940de7b90223b6d2c5136ffd@changeid\nSigned-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>\nSigned-off-by: Sasha Levin <sashal@kernel.org>\n---\n drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/rx.c | 9 +++++++--\n 1 file changed, 7 insertions(+), 2 deletions(-)\n\ndiff … [truncated]
3:00
3:01
$head -5 /tmp/iwl/rx_v61852.c; curl -sL "https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/plain/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/rx.c?h=v6.18.52" -o /tmp/iwl/rx_g12_v61852.c; wc -l /tmp/iwl/rx_g12_v61852.c; grep -n "iwl_pcie_rx_free" /tmp/iwl/rx_g12_v61852.c
0.2s✓
<!DOCTYPE html>
<html lang='en'>
<head>
<title>kernel/git/stable/linux.git - Linux kernel stable tree</title>
<meta name='generator' content='cgit 1.3.1-korg'/>
2488 /tmp/iwl/rx_g12_v61852.c
1204:void iwl_pcie_rx_free(struct iwl_trans *trans)
3:05
$sed -n 1160,1260p /tmp/iwl/rx_g12_v61852.c
✓
{"cwd":"/data/pruva/runs/d4ca1255-f7a4-4d7a-afc0-26790bf36658","duration_ms":3,"exit_code":0,"stderr":"","stdout":"\t\ttrans_pcie->global_table[i] = rxb;\n\t\trxb->vid = (u16)(i + 1);\n\t\trxb->invalid = true;\n\t}\n\n\tiwl_pcie_rxq_alloc_rbs(trans, GFP_KERNEL, def_rxq);\n\n\treturn 0;\n}\n\nint iwl_pcie_rx_init(struct iwl_trans *trans)\n{\n\tstruct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);\n\tint ret = _iwl_pcie_rx_init(trans);\n\n\tif (ret)\n\t\treturn ret;\n\n\tif (trans->mac_cfg->mq_rx_supported)\n\t\tiwl_pcie_rx_mq_hw_init(trans);\n\telse\n\t\tiwl_pcie_rx_hw_init(trans, trans_pcie->rxq);\n\n\tiwl_pcie_rxq_restock(trans, trans_pcie->rxq);\n\n\tspin_lock_bh(&trans_pcie->rxq->lock);\n\tiwl_pcie_rxq_inc_wr_ptr(trans, trans_pcie->rxq);\n\tspin_unlock_bh(&trans_pcie->rxq->lock);\n\n\treturn 0;\n}\n\nint iwl_pcie_gen2_rx_init(struct iwl_trans *trans)\n{\n\t/* Set interrupt coalescing timer to default (2048 usecs) */\n\tiwl_write8(trans, CSR_INT_COALESCING, IWL_HOST_INT_TIMEOUT_DEF);\n\n\t/*\n\t * We don't configure the RFH.\n\t * Restock will be done at alive, after firmware configured the RFH.\n\t */\n\treturn _iwl_pcie_rx_init(trans);\n}\n\nvoid iwl_pcie_rx_free… [truncated]
3:33

Artifacts and Evidence for CVE-2026-93796

Scripts, logs, diffs, and output captured during the reproduction.

08 · How to Fix

How to Fix CVE-2026-93796

Coming soon

Step-by-step mitigation and hardening guidance for CVE-2026-93796 — configuration checks, workarounds where no patch exists, and how to verify you're protected — is on the way.

10 · FAQ

FAQ: CVE-2026-93796

Is CVE-2026-93796 exploitable?

Yes. Pruva independently reproduced CVE-2026-93796 and verified the exploit fires end-to-end in a sandboxed environment. A runnable proof-of-concept script and the full agent transcript are on this page (reproduction REPRO-2026-00373).

How severe is CVE-2026-93796?

CVE-2026-93796 is rated medium severity.

What type of vulnerability is CVE-2026-93796?

CVE-2026-93796 is classified as CWE-416 (Use After Free), a Use-After-Free vulnerability.

How can I reproduce CVE-2026-93796?

Pruva provides a verified reproduction script on this page. Download it and run it inside an isolated environment such as a container or virtual machine — never against production. The reproduction was confirmed end-to-end by Pruva's automated agents.

Is the CVE-2026-93796 reproduction verified?

Yes. Pruva reproduced CVE-2026-93796 with medium confidence in a sandboxed environment, capturing the full agent transcript and artifacts as evidence.
11 · References

References for CVE-2026-93796

Authoritative sources for CVE-2026-93796 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.