mup.sys — WIL feature-staging library churn
KB5089549
1. Overview
| Field | Value |
|---|---|
| Unpatched binary | mup_unpatched.sys |
| Patched binary | mup_patched.sys |
| Overall similarity | 0.9873 |
| Functions matched by name | 357 |
| Functions with a real logic change | 3 (all WIL feature-staging) |
| Functions identical (mnemonics; only relocated references differ) | 354 |
| Functions only in one build | 1 removed / 3 added (WIL/WPP helpers) |
Verdict: There is no security-relevant change in this patch. The only functions whose instruction logic actually changed are three internal Windows Implementation Library (WIL) feature-staging routines. WIL is Microsoft's velocity/feature-flag library that is statically linked into many drivers; it decides at runtime whether an experimental "feature" is enabled. The change refactors and slightly tightens how a feature's dependencies are evaluated. None of the changed code is on the UNC/SMB attack surface, none of it consumes attacker-controlled input, and every Mup* UNC function (create dispatch, prefix resolution, UNC-hardening configuration, provider selection) is byte-for-byte identical between the two builds apart from relocated branch/reference targets. This is ordinary library churn from a WIL rebuild, not a fix.
An earlier reading of this diff described a "UNC Hardening integrity/mutual-auth bypass." That reading was based on masked sub_* addresses and does not survive symbol resolution: the named functions are WIL feature-flag internals, and the bit values interpreted as RequireIntegrity/RequireMutualAuth are WIL feature-state encodings, not SMB security flags. The sections below document the real functions, the real change, and why it carries no security weight.
2. Change Summary
The three functions that actually changed
| Named function | Unpatched addr | Patched addr | Nature |
|---|---|---|---|
wil_details_GetCurrentFeatureEnabledState |
0x140003244 |
0x1400032E4 |
Feature-enabled-state computation; dependency-walk loop factored out into a new helper and gated on a (state & 0xC00) == 0xC00 condition. |
wil_details_EvaluateFeatureDependencies_ReevaluateCachedFeatureEnabledState |
0x140012200 |
0x140012200 |
Same dependency-gating logic in the cached-state lock cmpxchg re-evaluation variant. |
wil_InitializeFeatureStaging |
0x1400230F0 |
0x1400230F0 |
Registration refactor: an inline RtlRegisterFeatureUsageProvider sequence is replaced by a call to a new helper wil_details_RegisterFeatureUsageProvider. |
All three are wil_details_* / wil_* symbols — internal WIL feature-staging code. There is no Mup* (Multiple UNC Provider) function among them.
Functions present in only one build (all library helpers, not Mup logic)
- Added in patched:
wil_details_AreDependenciesEnabled(0x140002C3C),wil_details_RegisterFeatureUsageProvider(0x14001238C),WPP_SF_d(a WPP trace-format helper). - Removed from unpatched:
WPP_SF_D(a WPP trace-format helper differing only in a format specifier).
The two added wil_details_* functions are the extracted helpers that the three changed functions now call; they are not new security checks. WPP_SF_D → WPP_SF_d is a tracing-format-string helper churn.
What the WIL bits actually are
wil_details_GetCurrentFeatureEnabledState calls wil_RtlStagingConfig_QueryFeatureState (the ntoskrnl feature-staging configuration query) and packs the result into a small state word. The bit values are WIL feature-state encodings — an experimental-feature enable/variant state and a dependency/change-tracking marker — not SMB or UNC-hardening flags. The same feature-state word is what wil_details_FeatureStateCache_ReevaluateCachedFeatureEnabledState tests (e.g. test dword ptr [rcx+2Ch], 4000000h), and that function is identical in both builds. There is no RequireIntegrity/RequireMutualAuth/0xc00-SMB-policy semantics anywhere in this code.
3. What actually changed (decompilation)
wil_details_GetCurrentFeatureEnabledState
Unpatched (0x140003244) computes the feature-enabled-state word and, when the "walk dependencies" bit is set, inlines a loop over the dependency list at *(a1 + 32), evaluating each dependency via wil_details_FeatureStateCache_ReevaluateCachedFeatureEnabledState:
// unpatched: dependency loop is inlined
if ( (v8 & 0x40) != 0 ) {
v11 = *(unsigned int ****)(a1 + 32);
if ( v11 != nullptr ) {
do {
...
LOBYTE(v13) = wil_details_FeatureStateCache_ReevaluateCachedFeatureEnabledState(*v12, v21, (__int64)v12);
...
} while ( ... );
}
}
Patched (0x1400032E4) factors that same loop into the new helper wil_details_AreDependenciesEnabled and adds a gate on the combined feature-state bits:
// patched: dependency loop is now a helper, plus a (state & 0xC00)==0xC00 gate
if ( (v10 & 0xC00) == 0xC00 )
v12 = 1;
else { v12 = 0; if ( (v10 & 0x40) == 0 ) { v6 = 0; goto LABEL_18; } }
v13 = *(_QWORD *)(a1 + 32) == 0 || wil_details_AreDependenciesEnabled(a1);
if ( v12 != 0 && !v13 )
v10 &= ~0x400u; // clear a feature-state bit when dependencies not satisfied
if ( (v10 & 0x40) == 0 || !v13 )
goto LABEL_17;
wil_details_AreDependenciesEnabled (0x140002C3C, patched-only) is exactly the loop that used to be inlined — it walks *(a1 + 32) and evaluates each dependency feature through wil_details_FeatureStateCache_ReevaluateCachedFeatureEnabledState, returning whether all dependencies are enabled.
This is a feature-flag dependency evaluation: a staged feature is only reported "enabled" if the features it depends on are also enabled. It is not an authorization or integrity check, and the list at *(a1 + 32) is a WIL feature-dependency list, not a UNC provider list.
wil_details_EvaluateFeatureDependencies_ReevaluateCachedFeatureEnabledState
The same dependency-gating logic in the cached-state re-evaluation path. The patched build adds and eax, 0xC00; cmp eax, 0xC00; setz bpl and, after the dependency loop, if (both-bits-requested && no-satisfying-dependency) btr edx, 0Ah before the existing lock cmpxchg that updates the cached feature-state word. Same WIL feature-staging semantics as above.
wil_InitializeFeatureStaging
Pure refactor. The unpatched build inlines:
lea rax, wil_details_RecordFeatureUsageReporting
lea r8, g_wil_details_featureUsageProvider
mov cs:g_wil_details_recordFeatureUsage, rax
lea rcx, wil_details_OnFeatureUsageProviderFlushNotification
call cs:__imp_RtlRegisterFeatureUsageProvider
The patched build replaces the whole sequence with call wil_details_RegisterFeatureUsageProvider. Feature-usage-telemetry registration extracted into a helper; no behavioral or security-boundary change.
4. Assembly evidence
wil_details_GetCurrentFeatureEnabledState
Unpatched, the dependency evaluation is an inline call inside the loop:
; wil_details_GetCurrentFeatureEnabledState @ 0x140003244 (unpatched)
000000014000334C call wil_details_FeatureStateCache_ReevaluateCachedFeatureEnabledState
Patched, the loop is a helper call preceded by the new combined-bit gate and followed by the conditional bit clear:
; wil_details_GetCurrentFeatureEnabledState @ 0x1400032E4 (patched)
0000000140003389 mov edx, 0C00h ; combined feature-state mask
000000014000338E mov eax, ebx
0000000140003390 and eax, edx
0000000140003394 cmp eax, edx ; both bits set?
00000001400033A9 jz short loc_1400033B7 ; dependency list == 0 -> treat as satisfied
00000001400033AE call wil_details_AreDependenciesEnabled
00000001400033C2 btr ebx, 0Ah ; clear feature-state bit when deps unsatisfied
wil_details_AreDependenciesEnabled at 0x140002C3C exists only in the patched build. At that same address the unpatched build holds a different function, wil_details_FeatureReporting_IncrementOpportunityInCache — the address was reused, so the two must not be treated as one function across builds.
wil_details_FeatureStateCache_ReevaluateCachedFeatureEnabledState (unchanged)
This function (the one that evaluates a cached feature state) is present in both builds — unpatched 0x1400030E0, patched 0x140003180 — with an identical mnemonic sequence; the only differences are relocated loc_* branch targets. It is not part of the change.
5. Trigger Conditions
None applicable. There is no vulnerability here to trigger. The changed code is WIL feature-staging library logic that runs during feature-flag queries; it does not process UNC paths, network data, IRP contents, or any other attacker-influenced input, and its outcome is governed by the compiled-in feature-staging configuration, not by a remote server or a local caller.
6. Exploit Primitive & Development Notes
None. No memory-corruption, information-disclosure, or authorization primitive exists in this diff. The change neither adds nor removes any security check on the UNC/SMB path. Mitigations such as SMEP/SMAP/CFG/HVCI are irrelevant because there is no code-execution or policy primitive to discuss.
7. Debugger Notes
No security-relevant behavior to reproduce. For orientation only: the three changed functions are wil_details_GetCurrentFeatureEnabledState (patched 0x1400032E4), wil_details_EvaluateFeatureDependencies_ReevaluateCachedFeatureEnabledState (0x140012200), and wil_InitializeFeatureStaging (0x1400230F0). They are reached from WIL feature-flag query stubs, not from IRP_MJ_CREATE on a UNC path. mup.sys uses a 0x140000000 preferred base; rebase any address by the actual load delta.
8. Changed Functions — Full Triage
| Function | Change type | Note |
|---|---|---|
wil_details_GetCurrentFeatureEnabledState (0x140003244 → 0x1400032E4) |
WIL feature-staging | Dependency-walk loop factored into wil_details_AreDependenciesEnabled; adds a (state & 0xC00) == 0xC00 gate that clears one feature-state bit when a staged feature's dependencies are not all enabled. Not a security boundary. |
wil_details_EvaluateFeatureDependencies_ReevaluateCachedFeatureEnabledState (0x140012200) |
WIL feature-staging | Same dependency-gating logic in the cached-state lock cmpxchg re-evaluation variant. Not a security boundary. |
wil_InitializeFeatureStaging (0x1400230F0) |
WIL feature-staging | Feature-usage-provider registration extracted from an inline sequence into wil_details_RegisterFeatureUsageProvider. Refactor only. |
No Mup* function changed. An independent content diff of all 357 name-matched functions found only these three with differing instruction sequences; every other function (including all UNC-hardening, prefix-resolution, provider-selection, and create-path code) is identical apart from relocated references.
9. Unmatched / Added-Removed Functions
- Added in patched:
wil_details_AreDependenciesEnabled(0x140002C3C),wil_details_RegisterFeatureUsageProvider(0x14001238C) — the two helpers the changed functions now call — andWPP_SF_d, a WPP trace-format helper. - Removed from unpatched:
WPP_SF_D, a WPP trace-format helper differing only in a format specifier.
All are WIL/WPP library helpers. None is a Mup security routine, an added sanitizer, or a removed mitigation.
10. Confidence & Caveats
Confidence: high that there is no security-relevant change.
- An independent, name-matched content diff of both builds shows only three functions with real instruction-level changes, all
wil_details_*/wil_*feature-staging symbols. - The changed code queries the ntoskrnl feature-staging configuration (
wil_RtlStagingConfig_QueryFeatureState) and evaluates feature dependencies; it does not touch UNC parsing, SMB negotiation, provider security context, or any attacker-controlled input. - The bit values (
0x400,0x800,0xC00,0x40) are WIL feature-state encodings;wil_details_FeatureStateCache_ReevaluateCachedFeatureEnabledState, which consumes the same word, is identical in both builds. - Every
Mup*UNC function is unchanged except for relocated branch/reference targets.
Nature of the change. WIL feature-staging library churn from a rebuild: a feature-dependency evaluation loop was extracted into a helper and a staged feature is now gated on its dependencies being enabled. This is normal velocity/feature-flag plumbing, not a security fix.