# applockerfltr.sys Patch Diff — CVE-2026-25184

| | |
|---|---|
| Binary | applockerfltr.sys (AppLocker Minifilter Driver) |
| Pre-patch version | 10.0.26100.7920 |
| Post-patch version | 10.0.26100.8246 |
| KB | KB5083769 |
| CVE | CVE-2026-25184 — Concurrent Execution Using Shared Resource with Improper Synchronization / Race Condition (CWE-362), Elevation of Privilege, CVSS 7.0 |
| Diff tool | ghidriff (Ghidra VersionTrackingDiff engine) |
| Functions changed | 1 with security-relevant code changes (out of 112 total, 97.3% avg similarity) |
| Functions added | 4 (lock wrapper + WIL Controlled-Feature-Rollout accessors for `Feature_423566650`) |
| Credited researcher | [Souhail Hammou (@dark_puzzle)](https://x.com/dark_puzzle) |

## Summary

The fix addresses a **TOCTOU race condition** in
`SmRegisterUninstallStringWithSessionOrigin` — the function that registers
uninstall string data entries in AppLocker's per-session tracking linked
list.

**Pre-patch behaviour.** When a registry callback triggers
`SmpRegistryCallback → SmRegisterUninstallStringWithSessionOrigin`, the
function acquires the track lock in **shared mode** via `SmAcquireTrackLock()`
before walking and modifying the linked list of session-origin tracking
entries. It then:

1. Calls `SmGetTrackSessionOriginDataLocked()` to get the list head.
2. Walks the doubly-linked list comparing `param_1+0x20` and `param_1+0x28`
   against each node's session-origin fields (at `plVar[4]+0x20` and
   `plVar[4]+0x28`) to find a matching session.
3. Calls `SmAllocUninstallStringData()` to allocate a new node.
4. **Inserts the new node into the doubly-linked list** — a write operation
   performed under a shared (reader) lock.

The bug: a shared lock permits multiple threads to enter simultaneously.
Two concurrent calls to `SmRegisterUninstallStringWithSessionOrigin` can
both be walking and modifying the same linked list under a shared lock,
corrupting the forward/backward pointers. The list insertion at lines like
`*puVar4 = plVar3; puVar4[1] = puVar5; *puVar5 = puVar4; plVar3[1] = puVar4`
is a classic non-atomic multi-pointer update — if two threads interleave
here, the list is corrupted and subsequent traversals can follow dangling
or stale pointers, leading to use-after-free or arbitrary write.

**Post-patch behaviour.** A new Controlled-Feature-Rollout flag
(`Feature_423566650`) is checked at the top of the function:

```c
uVar3 = Feature_423566650__private_IsEnabledDeviceUsageNoInline();
if ((int)uVar3 == 0) {
    SmAcquireTrackLock();           // shared (old path, flag disabled)
} else {
    SmAcquireTrackLockExclusive();  // EXCLUSIVE (new path, flag enabled)
}
```

When the flag is enabled (production rollout path),
`SmAcquireTrackLockExclusive()` is called instead, which wraps
`FltAcquireResourceExclusive(DAT_140006140)` — the minifilter manager's
ERESOURCE exclusive acquisition. This ensures only one thread can modify
the linked list at a time, closing the race window entirely.

**Why shared → exclusive matters here.** `SmAcquireTrackLock()` acquires
the same ERESOURCE in shared mode. ERESOURCE allows unlimited concurrent
shared acquisitions — fine for read-only traversals, but
`SmRegisterUninstallStringWithSessionOrigin` is a *writer*. The pre-patch
code was effectively doing a write under a reader lock. The fix promotes
the lock to exclusive for the code path that modifies the list. The shared
path is preserved as a kill-switch fallback when `Feature_423566650` is
disabled.

**Confidence:** high. The single meaningful code change (shared → exclusive
lock acquisition gated on a new CFR flag) directly addresses the CWE-362
race condition described in the advisory. The function modifies a
doubly-linked list under what was previously a shared lock, and the fix
upgrades to exclusive — textbook race condition remediation. The rest of
the diff is WIL CFR plumbing.

**Credit.** MSRC credits [Souhail Hammou](https://x.com/dark_puzzle).

## Function changed

### SmRegisterUninstallStringWithSessionOrigin

| | |
|---|---|
| Change type | code, length, called |
| Similarity | 0.96 (b_ratio) |
| Length | 218 → 234 bytes |
| Fix pattern | Shared lock upgraded to exclusive lock via CFR flag |

Before (pre-patch — the race):

```c
// applockerfltr.sys 10.0.26100.7920 — VULNERABLE
undefined8 SmRegisterUninstallStringWithSessionOrigin(longlong param_1)
{
    // ...
    lVar2 = SmGetTrackSessionOriginData();
    if (lVar2 != 0) {
        SmAcquireTrackLock();          // ← SHARED lock for a WRITE operation
        plVar3 = (longlong *)SmGetTrackSessionOriginDataLocked();
        if (plVar3 != NULL) {
            // Walk linked list comparing session-origin fields
            plVar9 = (longlong *)*plVar3;
            // ...
            // Allocate and INSERT into doubly-linked list (write!)
            puVar4 = SmAllocUninstallStringData();
            *puVar4 = plVar3;          // flink = prev
            puVar4[1] = puVar5;        // blink = next
            *puVar5 = puVar4;          // next->flink = new
            plVar3[1] = puVar4;        // prev->blink = new
        }
        SmReleaseTrackLock();
    }
}
```

After (post-patch — the fix):

```c
// applockerfltr.sys 10.0.26100.8246 — FIXED
undefined8 SmRegisterUninstallStringWithSessionOrigin(longlong param_1)
{
    // ...
    lVar2 = SmGetTrackSessionOriginData();
    if (lVar2 != 0) {
        uVar3 = Feature_423566650__private_IsEnabledDeviceUsageNoInline();
        if ((int)uVar3 == 0) {
            SmAcquireTrackLock();          // shared (kill-switch fallback)
        } else {
            SmAcquireTrackLockExclusive(); // ← EXCLUSIVE lock (production path)
        }
        plVar4 = (longlong *)SmGetTrackSessionOriginDataLocked();
        // ... same linked list walk and insertion, now under exclusive lock
        SmReleaseTrackLock();
    }
}
```

The `called` list confirms the two new dependencies:

```
+Feature_423566650__private_IsEnabledDeviceUsageNoInline
+SmAcquireTrackLockExclusive
```

### New functions

**SmAcquireTrackLockExclusive** — wrapper that calls
`FltAcquireResourceExclusive(DAT_140006140)` to acquire the track
ERESOURCE in exclusive mode. 28 bytes, no other logic — pure lock
promotion wrapper.

**Feature_423566650__private_IsEnabledDeviceUsageNoInline**,
**Feature_423566650__private_IsEnabledFallback** — standard WIL
Controlled Feature Rollout accessors for the new flag gating the
exclusive-lock code path.

**FLTMGR.SYS::FltAcquireResourceExclusive** — external import from the
Filter Manager, called by `SmAcquireTrackLockExclusive`.

### Other modified functions (WIL infrastructure only)

The remaining 5 modified functions (`wil_details_FeatureReporting_*`,
`wil_details_IsEnabledFallback`,
`Feature_Servicing_ApplockerPickBestClaim__private_IsEnabledFallback`,
`wil_details_FeatureStateCache_TryEnableDeviceUsageFastPath`) are all
WIL infrastructure refactoring to support the new `Feature_423566650`
descriptor alongside the pre-existing
`Feature_Servicing_ApplockerPickBestClaim` flag. These changes make the
WIL reporting and state-caching functions generic (parameterised by
descriptor pointer) rather than hardcoded to a single feature. No
security-relevant logic changes.

---

<sub>Source: ghidriff diff of applockerfltr-2026-03.sys (10.0.26100.7920,
pre-patch) vs applockerfltr-2026-04.sys (10.0.26100.8246, post-patch) —
[download pre](/data/patch_diffs/binaries/applockerfltr-2026-03.sys) /
[download post](/data/patch_diffs/binaries/applockerfltr-2026-04.sys).
Credited researcher:
[Souhail Hammou (@dark_puzzle)](https://x.com/dark_puzzle).</sub>
