1. Overview

  • Unpatched Binary: mup_unpatched.sys
  • Patched Binary: mup_patched.sys
  • Overall Similarity Score: 99.19%
  • Diff Statistics: Matched: 363 | Changed: 2 | Identical: 361 | Unmatched (Unpatched): 0 | Unmatched (Patched): 0
  • Verdict: The only behavioral difference between the two builds is inside the statically-linked Windows Implementation Library (WIL) feature-staging code compiled into mup.sys. The function wil_details_FeatureStateCache_ReevaluateCachedFeatureEnabledState (0x14000321C) recomputes a per-feature cached "enabled state" value and updates it with an atomic compare-and-swap. In the patched build the recomputed value always has bit 0x40000 (bit 18) set; in the unpatched build it does not. This bit is the WIL feature-state-cache "recorded" marker, an internal caching bookkeeping bit. It is not a UNC provider lock, is not driven by attacker input, and has no demonstrable security impact. The change is WIL library churn, not a security fix.

2. Vulnerability Summary

  • Severity: None (informational)
  • Vulnerability Class: No security-relevant change. Internal WIL feature-state-cache bookkeeping bit.
  • Affected Function: wil_details_FeatureStateCache_ReevaluateCachedFeatureEnabledState @ 0x14000321C

What actually changed: wil_details_FeatureStateCache_ReevaluateCachedFeatureEnabledState maintains a single 32-bit cached feature-enabled-state value for one WIL feature. It reads the current feature state via wil_details_GetCurrentFeatureEnabledState, recomputes an updated cache value by masking and combining state bits, and commits it with _InterlockedCompareExchange (lock cmpxchg), retrying on contention. The patched build unconditionally ORs 0x40000 into the recomputed value at the top of every CAS iteration (bts eax, 0x12 at 0x140003288); the unpatched build does not force this bit. In WIL's feature-state-cache encoding this bit records that the cache value has been evaluated/stored. If it is absent, WIL treats the cached value as not-yet-recorded and re-evaluates the feature configuration on a later query. The observable effect is at most redundant re-evaluation of a feature flag, never memory corruption, privilege change, or a crossing of any security boundary.

The pointer updated by the CAS (a1) is the WIL per-feature cache dword, not a provider or lock object. The byte read at a3+28 is a field of the WIL feature descriptor/property (used by wil_details_GetCurrentFeatureEnabledState), not a provider-object field. There is no UNC path, IRP, or remote input on this code path.

Reachability: This function is internal WIL feature-staging plumbing. Its only callers in mup.sys are wil_details_AreDependenciesEnabled (0x140002CA8, call site 0x140002CFD) and wil_details_IsEnabledFallback (0x140003488, call site 0x1400034C4), both of which decide whether a compile-time-registered feature is enabled. The input is the driver's own feature descriptors and the OS feature-configuration store, not attacker-controlled network or file-path data.

3. Pseudocode Diff

Decompilation of the changed function in both builds. The difference is the forced | 0x40000 on the recomputed cache value in the patched build.

// UNPATCHED wil_details_FeatureStateCache_ReevaluateCachedFeatureEnabledState (CAS loop)
for ( i = v5; ; i = v10 )
{
    LODWORD(v13) = v5;
    if ( v8 != 0 )
    {
        LODWORD(v13) = v5;
        if ( (i & 2) == 0 )
        {
            v5 = CurrentFeatureEnabledState & 0x9C1 | i & 0xFFFFF63E | 2;
            LODWORD(v13) = v5;
        }
    }
    if ( (i & 4) == 0 )
    {
        v5 = v5 & 0xFFFFFBFF | CurrentFeatureEnabledState & 0x400 | 4;
        LODWORD(v13) = v5;
    }
    v10 = _InterlockedCompareExchange(a1, v5, i);   // writes value without bit 0x40000
    if ( i == v10 )
        break;
    v5 = v10;
}

// PATCHED wil_details_FeatureStateCache_ReevaluateCachedFeatureEnabledState (CAS loop)
for ( i = v5; ; i = v9 )
{
    LODWORD(v14) = v9 | 0x40000;          // bit 0x40000 forced on every iteration
    v11 = v9 | 0x40000;
    if ( v8 != 0 && (i & 2) == 0 )
    {
        v11 = CurrentFeatureEnabledState & 0x9C1 | v9 & 0xFFFBF63E | 0x40000 | 2;
        LODWORD(v14) = v11;
    }
    if ( (v5 & 4) == 0 )
    {
        v11 = v11 & 0xFFFFFBFF | CurrentFeatureEnabledState & 0x400 | 4;
        LODWORD(v14) = v11;
    }
    v9 = _InterlockedCompareExchange(a1, v11, v5);  // writes value with bit 0x40000
    if ( v5 == v9 )
        break;
    v5 = v9;
}

4. Assembly Analysis

The functional difference is that the patched build inserts bts eax, 0x12 (set bit 18 = 0x40000) at the top of the CAS retry loop. Register allocation also differs (patched saves/uses r12 and rbp where the unpatched build used r15 and rbx), which is a recompilation artifact.

Unpatched loop body (0x14000327A–0x1400032C2):

000000014000327A  mov     ecx, ebx
000000014000327C  mov     dword ptr [rsp+48h+arg_8], ebx
0000000140003280  test    esi, esi
0000000140003282  jz      short loc_1400032A5
0000000140003284  mov     dword ptr [rsp+48h+arg_8], ebx
0000000140003288  test    cl, 2
000000014000328B  jnz     short loc_1400032A5
000000014000328D  mov     ebx, ecx
000000014000328F  mov     eax, edi
0000000140003291  and     ebx, 0FFFFF63Eh
0000000140003297  and     eax, 9C1h
000000014000329C  or      ebx, eax
000000014000329E  or      ebx, 2
00000001400032A1  mov     dword ptr [rsp+48h+arg_8], ebx
00000001400032C0  mov     eax, ecx
00000001400032C2  lock cmpxchg [r15], ebx        ; commits value without bit 0x40000

Patched loop body (0x140003284–0x1400032D2):

0000000140003284  mov     eax, edi
0000000140003286  mov     ecx, edi
0000000140003288  bts     eax, 12h              ; set bit 0x40000 in recomputed value
000000014000328C  mov     dword ptr [rsp+48h+arg_8], eax
0000000140003290  mov     esi, eax
0000000140003292  test    ebp, ebp
0000000140003294  jz      short loc_1400032B1
0000000140003296  test    cl, 2
0000000140003299  jnz     short loc_1400032B1
000000014000329B  and     esi, 0FFFFF63Eh
00000001400032A1  mov     eax, ebx
00000001400032A3  and     eax, 9C1h
00000001400032A8  or      esi, eax
00000001400032AA  or      esi, 2
00000001400032AD  mov     dword ptr [rsp+48h+arg_8], esi
00000001400032D0  mov     eax, edi
00000001400032D2  lock cmpxchg [r12], esi        ; commits value with bit 0x40000

5. Trigger Conditions

Not applicable. The function is reached only when WIL evaluates whether one of the driver's registered features is enabled (via wil_details_AreDependenciesEnabled / wil_details_IsEnabledFallback). There is no attacker-controlled input on this path and no concurrency-controlled security state. The presence or absence of bit 0x40000 affects only whether WIL re-evaluates a feature flag, so there is no trigger that produces a security-relevant outcome.

6. Exploit Primitive & Development Notes

None. The difference is an internal WIL feature-state-cache "recorded" bit. It provides no memory-corruption, information-disclosure, privilege, or routing primitive. Standard memory mitigations are not relevant because there is no memory-safety issue.

7. Debugger PoC Playbook

Not applicable. There is no vulnerability to reproduce. For completeness, the only functional difference can be observed by breaking on the CAS at 0x1400032C2 (unpatched) / 0x1400032D2 (patched) and comparing the committed value: the patched build always has bit 0x40000 set, the unpatched build does not. This is a caching-bookkeeping difference, not a security condition.

8. Changed Functions — Full Triage

An independent function-by-function comparison of both builds (matching by content across relocations, ignoring reassigned local-label addresses) confirms exactly two functions changed. Both are WIL feature-staging internals. A cosmetic WPP tracing symbol rename (WPP_SF_dWPP_SF_D, case only) also appears but has no code effect.

  1. wil_details_FeatureStateCache_ReevaluateCachedFeatureEnabledState @ 0x14000321C (Change Type: Not security-relevant)
  2. Change: Patched build forces bit 0x40000 (bts eax, 0x12) into the recomputed cached feature-enabled-state on every CAS iteration; unpatched build does not. Register allocation also differs due to recompilation. This is the WIL feature-state-cache "recorded" marker, internal caching bookkeeping. No security impact.
  3. wil_details_RegisterFeatureUsageProvider @ 0x140012F2C (Change Type: Not security-relevant)
  4. Change: Patched build adds two stack-local initializations (var_C = 0, var_10 = 1) passed to the RtlRegisterFeatureUsageProvider call. The registered callback (wil_details_RecordFeatureUsageReporting) is the same function; its address differs only because of relocation. This is WIL feature-usage telemetry registration, not a security fix.

9. Unmatched Functions

  • Added: None
  • Removed: None

10. Confidence & Caveats

  • Confidence Level: High. An independent whole-binary diff confirms only the two WIL functions above changed; no security-relevant function was modified or omitted.
  • Basis: Bit 0x40000 is set/consumed entirely within WIL's feature-state-cache code paths as a caching marker. The pointer updated by the CAS is a per-feature WIL cache value, and the code is reached only through WIL feature-enablement checks over the driver's own compile-time feature descriptors, not through any UNC path, IRP, or attacker-controlled input.
  • Conclusion: No security-relevant change. WIL feature-staging library churn.