# CVE-2026-21242 — Windows 9P Redirector `p9rdr.sys` UserCallback Lifetime UAF on the `SetCallbackTimeout` Timer-Allocation Failure Path

---

## Summary

| | |
|---|---|
| **Product** | Windows — `p9rdr.sys` (Plan 9 / 9P redirector; WSL `\\wsl.localhost` file access) |
| **CVE ID** | CVE-2026-21242 |
| **Impact** | Elevation of Privilege |
| **MSRC severity** | Important |
| **CVSS** | 7.0 / 6.1 — `CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H/E:U/RL:O/RC:C` |
| **CWE** | CWE-416: Use After Free |
| **Delivery** | Local — open `\Device\P9Rdr\wsl.localhost\...` under kernel-pool exhaustion |
| **KB / Fixed build** | KB5077181 — `p9rdr.sys` 10.0.26100.8328 (Win11 24H2 x64) |
| **Patch Date** | February 10, 2026 (2026-Feb) |
| **Pre-patch binary** | `p9rdr.sys` 10.0.26100.7824 — SHA256 `f52bad554e22d669e8129ce19b7eb8507bdbbd1306284e9a3afac6316bad5e97` |
| **Post-patch binary** | `p9rdr.sys` 10.0.26100.8328 — SHA256 `e532c53349f770f2ba3ce7d503d7f3e24f792c09983bb5649a93b4d3c373354d` |
| **Feature flag** | `Feature_1655910715` / `Feature_3906416953` — **the fix is CFR-gated** |
| **Exploitability** | Exploitation Less Likely; not publicly disclosed; not exploited (per MSRC) |

---

## Product Description

`p9rdr.sys` is the Windows 9P (Plan 9) redirector used by WSL for
`\\wsl.localhost\...`. Opening `\Device\P9Rdr\wsl.localhost\...` invokes
`p9fs::P9CreateVNetRoot` (from `rdbss!RxConstructNetRoot`), which calls
`p9fs::Device::CreateNetRootAndVNetRoot` →
`p9fs::UserCallbackManager::CallUserCallback`. When no callback is registered it
calls `NotifyUserProcess`, which signals `P9RdrService` (a WNF update), creates a
`p9fs::UserCallback`, adds it to the manager list, and calls
`p9fs::UserCallback::SetCallbackTimeout` to arm a timeout:

```
p9rdr!p9fs::UserCallbackManager::NotifyUserProcess
p9rdr!p9fs::UserCallbackManager::CallUserCallback
p9rdr!p9fs::Device::CallUserCallback
p9rdr!p9fs::Device::CreateNetRootAndVNetRoot
p9rdr!p9fs::P9CreateVNetRoot
rdbss!RxConstructNetRoot
```

---

## Vulnerability Summary

`SetCallbackTimeout` allocated the timer inline with `ExAllocateTimer`, which
internally does `ExAllocatePool2(0xA0, 'mTxE')`. If an attacker exhausts kernel
pool so that `ExAllocateTimer` **fails**, `SetCallbackTimeout` returns
`STATUS_INSUFFICIENT_RESOURCES` (`0xC000009A`). That error propagates up through
`NotifyUserProcess` → `CallUserCallback` → `Device::CallUserCallback`, and because
`Device::CallUserCallback` returns a failure, `CreateNetRootAndVNetRoot` calls
`rdbss!RxFinishPhysicalNetRootConstruction` to **free the `NetRootConstructionContext`**.

But the `UserCallback` that was already registered (added to the manager list, and
referencing the create `RxContext`/NetRoot) is not torn down on this failure path,
so it now points at freed construction state — a use-after-free (CWE-416). The
redirector runs in the kernel and the open path is reachable by a low-privileged
local user (the reporter reaches it from WSL), making the freed-object reuse a
local EoP primitive; `AC:H` reflects the need to reliably force the allocation
failure.

---

## Prerequisites and Constraints

- Local, low-privileged (`PR:L`, `AV:L`); `AC:H` — the attacker must drive
  `ExAllocateTimer` to fail (kernel-pool exhaustion) at the moment a WSL create is
  registering its user callback.
- Open `\Device\P9Rdr\wsl.localhost\...` to reach
  `NotifyUserProcess` → `SetCallbackTimeout`.
- Result: the create context is freed while the registered callback still
  references it.

---

## Vulnerability Details

### Root Cause

On the `SetCallbackTimeout` timer-allocation failure path, the already-registered
`UserCallback` (and its reference to the create context) was not released before
`CreateNetRootAndVNetRoot` freed the `NetRootConstructionContext`, leaving a
dangling reference.

### The patch (confirmed — diff, .7824 → .8328)

Gated behind `Feature_1655910715` / `Feature_3906416953`, the UserCallback
registration and timeout are reworked so the callback is **reference-counted and
released under the manager lock**, and `SetCallbackTimeout` no longer performs the
failure-prone inline `ExAllocateTimer`/`DeleteTimer` that could strand a callback:

```c
// p9fs::UserCallback::SetCallbackTimeout (.7824) — PRE (removed in the fix):
//   uVar3 = Feature_1655910715__private_IsEnabledDeviceUsageNoInline();
//   lVar4 = ExAllocateTimer(TimerCallback, this, 0);
//   if (this->Timer != NULL) details::DeleteTimer(this->Timer);   // failure could strand the callback
// post: timer lifecycle reworked; ExAllocateTimer/DeleteTimer inline block removed
//
// p9fs::UserCallbackManager::FindOrInsertCallbackWithLockHeld (.8328) — PATCHED:
UserCallback::ReleaseReference(callback);   // release under the manager lock
```

By reference-counting the callback and releasing it under the lock (and removing
the strand-prone inline timer allocation), a timer-allocation failure no longer
leaves a callback referencing a freed construction context, closing the UAF.

### Patch Completeness Assessment

**CFR-gated behind `Feature_1655910715` / `Feature_3906416953`.** The
reference-managed path runs only when the flags are enabled; the original path
still ships when disabled. Verify the flags are enabled to confirm the fix is live.

---

## Detection Guidance

**Behavioural.** Repeated `\\wsl.localhost\...` / `\Device\P9Rdr\...` opens under
deliberate kernel-pool pressure (forcing `'mTxE'` timer allocations to fail); UAF /
pool-corruption bugchecks in `p9rdr!p9fs::UserCallbackManager::*` /
`NotifyUserProcess` on unpatched/flag-disabled builds.

**Config.** The fix is CFR-gated — confirm `Feature_1655910715` /
`Feature_3906416953` are enabled.

---

## References

- MSRC advisory — CVE-2026-21242 (Windows 9P / WSL Redirector Elevation of Privilege), released 2026-02-10, KB5077181.
- Full binary diff: `/data/patch_diffs/p9rdr_sys-cve-2026-21242-ghidriff.md`
- Related same-KB p9rdr UserCallback lifetime UAF: CVE-2026-21237.
