mup.sys — WIL feature-staging library churn
KB5095051
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_d → WPP_SF_D, case only) also appears but has no code effect.
wil_details_FeatureStateCache_ReevaluateCachedFeatureEnabledState@ 0x14000321C (Change Type: Not security-relevant)- 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. wil_details_RegisterFeatureUsageProvider@ 0x140012F2C (Change Type: Not security-relevant)- Change: Patched build adds two stack-local initializations (
var_C = 0,var_10 = 1) passed to theRtlRegisterFeatureUsageProvidercall. 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.