# CVE-2026-34340 — Windows Projected File System `prjflt.sys` TOCTOU Use-After-Free: Tombstone Attribute Read After Lock Release in `PrjfRevertInMemoryTombstonesForDirectory`

---

## Summary

| | |
|---|---|
| **Product** | Windows — `prjflt.sys` (Windows Projected File System / ProjFS filter; in-memory tombstones) |
| **CVE ID** | CVE-2026-34340 |
| **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 (TOCTOU race) |
| **Delivery** | Local race — modify a tombstone hash-table entry during the cleanup callback's lock-release/wait window |
| **KB / Fixed build** | KB5089549 — `prjflt.sys` 10.0.26100.8457 (Win11 24H2 x64) |
| **Patch Date** | May 12, 2026 (2026-May) |
| **Pre-patch binary** | `prjflt.sys` 10.0.26100.8328 — SHA256 `121ecc0a3d9b49304077082b57e6514cc73c9ed2e9bedf6587be9fb79175d445` |
| **Post-patch binary** | `prjflt.sys` 10.0.26100.8457 — SHA256 `877f38b110b40bd8e7af604849a493500337df0df1d9ebda62a807251fb9244a` |
| **Feature flag** | `Feature_1526925624` — **the fix is CFR-gated** |
| **Exploitability** | Exploitation Less Likely; not publicly disclosed; not exploited (per MSRC) |

---

## Product Description

`prjflt.sys` is the **Windows Projected File System (ProjFS)** mini filter (used
by Git VFS / virtualized directories). It maintains an in-memory **tombstone**
hash table per virtualization context — an `RTL_DYNAMIC_HASH_TABLE` at context
offset **`+240`**, protected by an ERESOURCE exclusive lock at offset **`+136`**.
`PrjfRevertInMemoryTombstonesForDirectory` is invoked from the `IRP_MJ_CLEANUP`
post-processing callback (`PrjfPostCleanup`). Because the driver runs in kernel
and the path is reachable by a local user, a lifetime bug in tombstone handling
is a local elevation vector.

---

## Vulnerability Summary

For each tombstone entry the function selects the entry **while holding the
ERESOURCE lock**, then releases the lock (`FltReleaseResource`) and calls
`KeWaitForSingleObject` with **no timeout** (indefinite wait). Pre-patch, only
*after* the wait ends does it read the tombstone **attribute byte** directly from
the shared hash-table entry and pass it as the third argument to
`PrjfCreateTombstone`:

```c
// PrjfRevertInMemoryTombstonesForDirectory (10.0.26100.8328) — PRE-PATCH, from our diff
FltReleaseResource(local_a8);                    // *** lock released ***
KeWaitForSingleObject(lVar8 + 0x120, 0, 0, 0);   // *** indefinite wait (no timeout) ***
if (*(char *)(lVar8 + 0x11a) == '\0') {
    uVar4 = PrjfCreateTombstone(param_1, &name,
                                *(char *)(lVar5 + 0x18));   // *** re-reads shared entry AFTER release+wait ***
```

The attribute byte (`*(char *)(entry + 0x18)`, i.e. `LOBYTE(v14[1].Linkage.Flink)`)
is a **time-of-use** read that happens after the lock is gone. In that window
another thread can acquire the same ERESOURCE and modify the entry, so
`PrjfCreateTombstone` receives a **corrupted / attacker-influenced** attribute
flag instead of the intended value — creating an incorrect tombstone type and
corrupting ProjFS virtualization state (CWE-416, TOCTOU). The no-timeout
`KeWaitForSingleObject` widens the window from microseconds to potentially
seconds.

---

## Prerequisites and Constraints

- Local, low-privileged (`PR:L`, `AV:L`); `AC:H` reflects the race timing.
- Race: while the cleanup callback is between `FltReleaseResource` and the return
  of `KeWaitForSingleObject`, another thread takes the ERESOURCE and rewrites the
  tombstone entry's attribute byte.
- The indefinite wait makes the window large, improving the odds of winning the
  race.

---

## Vulnerability Details

### Root Cause

The tombstone attribute byte is read from shared state that is no longer
lock-protected (after `FltReleaseResource`, and even after an unbounded
`KeWaitForSingleObject`), so the value validated/selected under the lock and the
value actually consumed by `PrjfCreateTombstone` can differ (TOCTOU).

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

Gated behind `Feature_1526925624`, the function applies the standard
**capture-before-release** pattern: it snapshots the attribute byte into a stack
local (`local_e7`) **while still holding the lock**, then uses the snapshot after
the wait rather than re-reading the shared entry:

```c
// PrjfRevertInMemoryTombstonesForDirectory (10.0.26100.8457) — PATCHED, feature-enabled branch
if (Feature_1526925624__private_IsEnabledDeviceUsageNoInline() != 0)
    local_e7 = *(char *)(lVar6 + 0x18);           // *** capture snapshot UNDER the lock ***
FltReferenceContext(lVar9);
FltReleaseResource(local_b8);                     // release lock
KeWaitForSingleObject(lVar9 + 0x120, 0, 0, 0);    // indefinite wait
if (*(char *)(lVar9 + 0x11a) == '\0') {
    cVar4 = local_e7;                             // *** use the snapshot ***
    if (Feature_1526925624__private_IsEnabledDeviceUsageNoInline() == 0)
        cVar4 = *(char *)(lVar6 + 0x18);          // (feature-disabled: original re-read)
    uVar5 = PrjfCreateTombstone(local_b0, &name, cVar4);   // stable attribute value
}
```

Because `local_e7` was read while the ERESOURCE protected the entry, a concurrent
modification between the release and the wait no longer affects the value passed
to `PrjfCreateTombstone` — closing the TOCTOU.

### Patch Completeness Assessment

**CFR-gated behind `Feature_1526925624`.** The snapshot path runs only when the
flag is enabled; with the flag disabled the original post-wait re-read
(`*(char *)(entry + 0x18)`) still ships in .8457. Patch state is not determined by
file version alone — the runtime-gated pattern seen across this corpus. Verify
`Feature_1526925624` is enabled to confirm the capture-before-release path is
live.

---

## Detection Guidance

**Behavioural.** Concurrent access to a ProjFS virtualization directory during
`IRP_MJ_CLEANUP` — one thread in the cleanup/tombstone-revert path while another
modifies tombstone entries.

**Crash signature.** ProjFS state corruption / UAF attributable to
`prjflt!PrjfRevertInMemoryTombstonesForDirectory` / `PrjfCreateTombstone` on
unpatched or flag-disabled builds.

**Config.** The fix is CFR-gated — confirm `Feature_1526925624` is enabled so the
attribute snapshot is used.

---

## References

- MSRC advisory — CVE-2026-34340 (Windows Projected File System Elevation of Privilege), released 2026-05-12, KB5089549.
- Full binary diff: `/data/patch_diffs/prjflt_sys-cve-2026-34340-ghidriff.md`
