# CVE-2026-32070 — Windows CLFS `clfs.sys` Reference-Count Inversion in `CClfsManagedLog::AddNewClient` → Use-After-Free

---

## Summary

| | |
|---|---|
| **Product** | Windows — `clfs.sys` (Common Log File System driver; managed-log client) |
| **CVE ID** | CVE-2026-32070 |
| **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 (reference-count lifetime inversion) |
| **Delivery** | Local race — a second thread `Release`s the client during registration |
| **KB / Fixed build** | KB5083769 — `clfs.sys` 10.0.26100.8246 (Win11 24H2 x64) |
| **Patch Date** | April 14, 2026 (2026-Apr) |
| **Pre-patch binary** | `clfs.sys` 10.0.26100.8115 — SHA256 `1e0744c8e88853a05b07635daf5eadccd381658e28c2703316decb7e58e90bc9` |
| **Post-patch binary** | `clfs.sys` 10.0.26100.8246 — SHA256 `d7e779b93dc05dcbc745b1ad100637b0376c29ffdff90ccad4e4fc69dcb932df` |
| **Feature flag** | `Feature_1664551224` — **the fix is CFR-gated** |
| **Exploitability** | Exploitation Less Likely; not publicly disclosed; not exploited (per MSRC) |

---

## Product Description

`clfs.sys` is the Windows Common Log File System driver — a long-standing local
privilege-escalation target. `CClfsManagedLog::AddNewClient` creates a new
`CClfsManagedLogClient` (a COM-style reference-counted object), initializes it,
and takes a reference on it. All managed-log clients — reachable via
`CreateLogFile` and related APIs — go through this path.

---

## Vulnerability Summary

`AddNewClient` performed the object's setup in the **wrong order**:

1. allocate the `CClfsManagedLogClient` and store it in `*a5`,
2. call `Initialize` (vtable+32) — which **registers the object externally**
   (kernel structures / observer/callback chains, e.g. `InstallLogObserver`),
3. only then `AddRef` (vtable+0).

Because `Initialize` publishes the object to externally reachable locations
**before** any reference is taken, the reference count is still **0** at the
moment other threads can find it. If another thread discovers the registered
client and calls `Release`, the count goes below zero and the object is **freed**;
when the original thread finally reaches `AddRef`, it touches **freed memory** — a
lifetime-inversion use-after-free (CWE-416).

The failure path compounded it: on `Initialize` failure the pre-patch code
destroyed the object by calling its **destructor directly** (vtable+80, `bFinal=1`)
rather than `Release` — correct only under the "no reference taken yet" ordering.

---

## Prerequisites and Constraints

- Local, low-privileged (`PR:L`, `AV:L`); `AC:H` reflects the narrow race window.
- Race: a concurrent thread `Release`s the just-registered client between its
  external registration (`Initialize`) and the original thread's `AddRef`.
- CLFS managed-log clients are broadly reachable (`CreateLogFile`), so the path is
  readily triggerable.

---

## Vulnerability Details

### Root Cause

The object was made externally discoverable before a reference was held, so a
concurrent `Release` could drop the count below zero and free the object while
`AddNewClient` still intended to use it (reference-count inversion → UAF).

### The patch (confirmed — diff, .8115 → .8246)

Gated behind `Feature_1664551224`, `AddNewClient` is reordered to **`AddRef` first,
then `Initialize`**, so a reference is already held by the time the object is
registered externally; the failure path is correspondingly changed to call the
standard **`Release` (vtable+8)** instead of the direct destructor, balancing the
earlier `AddRef`:

```c
// CClfsManagedLog::AddNewClient (10.0.26100.8246) — PATCHED, feature-enabled branch
if (Feature_1664551224__private_IsEnabledDeviceUsage()) {
    (*v13)();                                  // *** AddRef FIRST (ref held before exposure) ***
    v11 = (*(*a5 + 32))(*a5, a2, a3, this);    // Initialize (registers externally)
    if (v11 < 0) {
        (*(*a5 + 8))(*a5);                     // *** Release on failure (balances AddRef) ***
        *a5 = 0;
        goto LABEL_31;
    }
}
```

The list-insertion integrity check was also tightened from a fall-through to an
explicit guard (`if (*v18 != i) __fastfail(3u)`), and control flow consolidated to
a single exit (`LABEL_31`) so the managed-log mutex is released in exactly one
place. Our diff of `clfs.sys` 10.0.26100.8115 → .8246 confirms `AddNewClient` is the
changed function and gains the `Feature_1664551224` gate at the April KB5083769.

### Patch Completeness Assessment

**CFR-gated behind `Feature_1664551224`.** The reordered AddRef-first path runs only
when the flag is enabled; the original ordering still ships when disabled. Verify
`Feature_1664551224` is enabled to confirm the fix is live.

---

## Detection Guidance

**Behavioural.** Concurrent `CreateLogFile` / managed-log client create+release
churn; UAF / low-ref bugchecks in `clfs!CClfsManagedLog::AddNewClient` /
`CClfsManagedLogClient` teardown on unpatched/flag-disabled builds.

**Config.** The fix is CFR-gated — confirm `Feature_1664551224` is enabled.

---

## References

- MSRC advisory — CVE-2026-32070 (Windows Common Log File System Driver Elevation of Privilege), released 2026-04-14, KB5083769.
- Full binary diff: `/data/patch_diffs/clfs_sys-cve-2026-32070-ghidriff.md`
