mrxsmb.sys — WIL feature-staged endpoint-reference path and telemetry
KB5073724
1. Overview
| Field | Value |
|---|---|
| Unpatched binary | mrxsmb_unpatched.sys |
| Patched binary | mrxsmb_patched.sys |
| Overall similarity | 0.9906 |
| Matched functions | 1061 |
| Changed functions | 3 |
| Identical functions | 1058 |
| Unmatched (unpatched) | 0 |
| Unmatched (patched) | 0 |
Verdict: The patch introduces a new Windows-Internal-Library (WIL) feature gate, Feature_84197691__private_IsEnabledDeviceUsage, into three functions. In SmbCeCompleteSrvCallConstructionPhase2 the gate selects, at runtime, between two ways of taking a reference on the first VC endpoint: when the feature is enabled the code uses the pre-existing compare-and-swap helper VctReferenceEndpointSafe (which refuses to increment a zero refcount and whose failure is now checked), and when the feature is disabled the code runs the original path that calls VctReferenceEndpoint (unconditional lock xadd). The unconditional-increment path is fully retained and still reachable; VctReferenceEndpointSafe is not new — it already exists and is called from many sites in the unpatched build. The remaining two changed functions add feature-gated telemetry and a synchronization-state flag write. This is staged feature rollout plus telemetry, not a delivered security fix, and no exploitable primitive is demonstrable from these two builds.
2. Change Summary
Nature of the change in SmbCeCompleteSrvCallConstructionPhase2
- Change class: WIL feature-staging (
Feature_84197691__private_IsEnabledDeviceUsage), old code path retained. - Unpatched function:
SmbCeCompleteSrvCallConstructionPhase2@0x1C0006070 - Patched function:
SmbCeCompleteSrvCallConstructionPhase2@0x1C0011E10(relocated) - Reference helpers (both present in both builds):
VctReferenceEndpoint— unconditionallock xadd. Unpatched @0x1C000F710, patched @0x1C000EE90. 6 callers in each build (unchanged).VctReferenceEndpointSafe—lock cmpxchgwith a zero-refcount check. Unpatched @0x1C0016354, patched @0x1C00168E4. 9 callers unpatched, 10 patched (the one added caller is the new gated block described below).
What the two helpers actually do
VctReferenceEndpoint blindly increments the refcount at [endpoint+0x8] and returns the new value. It carries the diagnostic string "Ref %d->%d".
VctReferenceEndpointSafe reads the refcount, returns false (al = 0) if it is already 0, and otherwise performs a lock cmpxchg compare-and-swap to increment only if the value is unchanged, retrying on contention. It carries the diagnostic string "SafeRef %d->%d". This helper is not introduced by the patch — it is present verbatim in the unpatched binary and is already the reference helper used at nine other call sites there.
What the patch does to SmbCeCompleteSrvCallConstructionPhase2
In the unpatched build the function has a single code path: under the connection spinlock (ExAcquireSpinLockExclusive on [connection+0x784]) it calls SmbCeGetFirstVcEndpoint, then VctReferenceEndpoint without checking a return value.
In the patched build the function is wrapped by the new WIL feature gate and contains both paths:
- Feature enabled: an early block calls
SmbCeGetFirstVcEndpointand thenVctReferenceEndpointSafe, checksal, and on failure releases the lock and returnsSTATUS_CONNECTION_DISCONNECTED(0xC000020C) without using the endpoint. - Feature disabled: a later block runs the original logic:
SmbCeGetFirstVcEndpointfollowed by the unconditionalVctReferenceEndpoint(still no return value; the increment is unconditional).
Because the disabled branch retains the original VctReferenceEndpoint call, the patch does not remove or replace the unconditional-increment path — it stages an alternative behind a runtime flag whose default enablement state is not encoded in the binary (it is resolved by wil_details_IsEnabledFallback against external feature configuration).
3. Assembly Analysis
Unpatched VctReferenceEndpoint (@ 0x1C000F710) — unconditional increment
; ---- VctReferenceEndpoint @ 0x1C000F710 ----
00000001C000F710 mov [rsp+arg_0], rbx
00000001C000F715 push rdi
00000001C000F716 sub rsp, 30h
00000001C000F71A mov rdi, rcx
00000001C000F71D mov ebx, 1
00000001C000F722 lock xadd [rcx+8], ebx ; unconditional refcount increment (no zero-check)
00000001C000F727 mov rcx, [rcx+10h]
00000001C000F72B lea r8, aRefDD ; "Ref %d->%d"
00000001C000F732 inc ebx
00000001C000F741 call cs:__imp_RxDiagnosticTrace
...
00000001C000F76B mov eax, ebx ; returns new refcount
00000001C000F777 retn
VctReferenceEndpointSafe (unpatched @ 0x1C0016354, patched @ 0x1C00168E4) — CAS with zero-check, present in BOTH builds
; ---- VctReferenceEndpointSafe @ 0x1C00168E4 (patched; unpatched @ 0x1C0016354 is identical) ----
00000001C00168E4 mov [rsp+arg_0], rbx
00000001C00168E9 mov [rsp+arg_8], rsi
00000001C00168EE push rdi
00000001C00168EF sub rsp, 30h
00000001C00168F3 mov ebx, [rcx+8] ; read current refcount
00000001C00168F9 test ebx, ebx
00000001C00168FB jz short loc_1C0016964 ; refcount == 0 -> return false
00000001C00168FD lea edx, [rbx+1] ; edx = refcount + 1
00000001C0016900 mov eax, ebx ; eax = expected old value
00000001C0016902 lock cmpxchg [rcx+8], edx ; conditional atomic increment
00000001C0016907 jnz loc_1C0028C40 ; CAS lost the race -> reload and retry
00000001C001691B lea r8, aSaferefDD ; "SafeRef %d->%d"
...
00000001C0016951 mov al, 1 ; return true
00000001C0016962 retn
00000001C0016964 xor al, al ; return false (endpoint dead)
Unpatched SmbCeCompleteSrvCallConstructionPhase2 (@ 0x1C0006070) — single path
00000001C0006123 mov rcx, rsi ; rsi = [connection+0x784]
00000001C0006126 call cs:__imp_ExAcquireSpinLockExclusive
00000001C0006135 mov [r15+782h], bl ; bl = 1 (in-progress flag)
00000001C0006141 call SmbCeGetFirstVcEndpoint
00000001C0006146 mov r14, rax
00000001C0006149 test rax, rax
00000001C000614C jz loc_1C002807E ; null endpoint -> STATUS_CONNECTION_DISCONNECTED
00000001C0006152 mov rcx, rax
00000001C0006155 call VctReferenceEndpoint ; unconditional increment, return value not used
00000001C000615A and qword ptr [r14+148h], 0
00000001C0006168 mov byte ptr [r15+782h], 0
00000001C0006170 call cs:__imp_ExReleaseSpinLockExclusive
; ... continues to SubRdrClaimSrvCall(..., r14, ...)
Note that the STATUS_CONNECTION_DISCONNECTED (0xC000020C) error path already exists in the unpatched build — it is taken at 0x1C002807E when SmbCeGetFirstVcEndpoint returns NULL.
Patched SmbCeCompleteSrvCallConstructionPhase2 (@ 0x1C0011E10) — feature-gated, both paths present
Feature gate at entry:
00000001C0011E60 call Feature_84197691__private_IsEnabledDeviceUsage
00000001C0011E6A test eax, eax
00000001C0011E6C jz short loc_1C0011EE2 ; feature OFF -> skip the safe early block
Feature-ON block (uses the safe helper and checks its result):
00000001C0011E71 call SmbCeAcquireSpinLock
00000001C0011E7C call SmbCeGetFirstVcEndpoint
00000001C0011E81 mov rbx, rax
00000001C0011E84 test rax, rax
00000001C0011E87 jnz short loc_1C0011EA7
00000001C0011E8F mov edi, 0C000020Ch ; STATUS_CONNECTION_DISCONNECTED (null endpoint)
00000001C0011E94 call SmbCeReleaseSpinLock
00000001C0011EA7 mov rcx, rbx
00000001C0011EAA call VctReferenceEndpointSafe
00000001C0011EB5 test al, al
00000001C0011EB7 jnz short loc_1C0011ECC ; success -> proceed
00000001C0011EB9 xor ebx, ebx ; failure -> drop endpoint
00000001C0011EBB mov edi, 0C000020Ch ; STATUS_CONNECTION_DISCONNECTED
00000001C0011EC0 call SmbCeReleaseSpinLock
00000001C0011ECC and [rbx+148h], r12 ; r12 == 0
00000001C0011ED3 call SmbCeReleaseSpinLock
Feature-OFF block (retained original path, still calls the unconditional helper):
00000001C0012038 call Feature_84197691__private_IsEnabledDeviceUsage
00000001C001203F jnz short loc_1C0012099 ; feature ON -> skip this old block
00000001C0012044 call SmbCeAcquireSpinLock
00000001C0012050 call SmbCeGetFirstVcEndpoint
00000001C0012055 mov rbx, rax
00000001C0012058 test rax, rax
00000001C001205B jnz short loc_1C001207B
00000001C0012063 mov edi, 0C000020Ch
00000001C0012068 call SmbCeReleaseSpinLock
00000001C001207B mov rcx, rbx
00000001C001207E call VctReferenceEndpoint ; unconditional increment retained, return not used
00000001C0012083 and [rbx+148h], r12
00000001C0012090 call SmbCeReleaseSpinLock
The feature gate is standard WIL staging
; ---- Feature_84197691__private_IsEnabledDeviceUsage @ 0x1C0022DA4 ----
00000001C0022DAE mov eax, cs:Feature_84197691__private_featureState
00000001C0022DB8 test al, 10h
00000001C0022DBA jz short loc_1C0022DC1
00000001C0022DBC and eax, 1
00000001C0022DBF jmp short loc_1C0022DD0
00000001C0022DC1 call Feature_84197691__private_IsEnabledFallback ; -> wil_details_IsEnabledFallback
The enablement is read from the WIL feature-state cache and, when unresolved, falls back to wil_details_IsEnabledFallback against wil_details_featureDescriptors. The unpatched binary contains zero references to Feature_84197691; the entire gate is new in the patched build. The default runtime state of the feature is not encoded in the driver image.
4. Why this is not a delivered security fix
-
Old path retained. The patched
SmbCeCompleteSrvCallConstructionPhase2still contains and can execute the originalVctReferenceEndpoint(unconditionallock xadd) call at0x1C001207E. Across the whole binary the unconditional helper still has 6 callers in both builds — none were removed. The patch adds one new caller of the pre-existing safe helper and gates it behind a runtime flag. -
The "safe" helper is not new.
VctReferenceEndpointSafeexists in the unpatched build at0x1C0016354and is already used at nine call sites there. The patch does not add a new safety mechanism to the driver; it optionally routes one more call site through an existing one when a feature flag is on. -
WIL feature staging. The controlling function is
Feature_84197691__private_IsEnabledDeviceUsage, the standard WIL feature-state/enabled-fallback machinery. Feature-flag graduations of this kind are servicing/rollout mechanics, not a committed code change; the vulnerable-shaped path is preserved for the flag-off case. -
No demonstrable primitive. Whether the unpatched unconditional-increment path is a genuinely reachable use-after-free cannot be established from these two builds. The endpoint is fetched and referenced while holding the connection spinlock at
[connection+0x784], and the teardown/free locking discipline that would be required to open a race is not observable here. There is no evidence in the binaries for a controllable free, a pool-spray reclaim, or any read/write/control-flow primitive. Claims of a specific pool tag, asynchronous-teardown timing window, KASLR/SMEP/HVCI/CFG interaction, or privilege escalation are not supported and are not asserted.
5. Changed Functions — Full Triage
SmbCeCompleteSrvCallConstructionPhase2 — feature-staging, not a security fix
- Similarity: 0.5024 (unpatched
0x1C0006070vs patched0x1C0011E10) - Change type: WIL feature-staging (
Feature_84197691__private_IsEnabledDeviceUsage) with the original path retained. - What changed: The function is wrapped in the new feature gate. When enabled, endpoint referencing goes through the pre-existing
VctReferenceEndpointSafe(CAS with zero-check) and its result is checked (STATUS_CONNECTION_DISCONNECTEDon failure); when disabled, the originalSmbCeGetFirstVcEndpoint+VctReferenceEndpoint(unconditionallock xadd) path runs. The patched build also switches the gated blocks to theSmbCeAcquireSpinLock/SmbCeReleaseSpinLockwrappers, duplicates the binding-teardown block (enabled vs disabled variants), and adds a feature-gated telemetry call (SmbCeErrorIndat0x1C00122E2). The unconditional-increment path is not removed.
GetDialectInfoFromNegotiateResponse — feature-gated telemetry, no logic change
- Similarity: 0.9453
- Change type: Feature-gated telemetry / trace churn.
- What changed: Adds
Feature_84197691__private_IsEnabledDeviceUsage-gated telemetry writes on the error/unsupported-dialect branches (the added instructions record a status code plus a source-line constant into a local structure) and reorders some ETW/trace emission. The dialect-detection and size-validation logic is unchanged. The WPP trace GUID reference reflects the new build. No security impact.
SmbCeUpdateTransportDispatchVectors — synchronization-flag write, no security impact
- Similarity: 0.9845
- Change type: Minor behavioral (synchronization state flag) plus register reallocation.
- What changed: Inside the spinlock the patched version now sets
byte [connection+0x782] = 1(at0x1C0035A54) before the conditional store and clears it to0afterward; the unpatched version only cleared it to0. The+0x782byte is an in-progress marker on the connection object (the same field set/cleared inside the spinlock inSmbCeCompleteSrvCallConstructionPhase2). Registers holding the connection object and the argument were reshuffled (rsi/rdi/rbx), changing the final store from[rdi+0x150]to[rsi+0x150]. This is defensive state-tracking consistency, not a security fix.
6. Independent Change Enumeration
An independent content-based diff of both builds (matching functions by name across relocation and comparing instruction-mnemonic sequences) finds exactly three functions with real logic changes: SmbCeCompleteSrvCallConstructionPhase2, GetDialectInfoFromNegotiateResponse, and SmbCeUpdateTransportDispatchVectors. All other apparent differences are relocation artifacts (renamed WPP trace-GUID symbols, string-literal label addresses, and jump-target labels) with identical mnemonic and call-target sequences. Three helper functions appear only in the patched build (Feature_84197691__private_IsEnabledDeviceUsage, Feature_84197691__private_IsEnabledFallback, and a renamed SmbCeGetFirstBindingObject), consistent with the new feature-gate scaffolding. No security-relevant change was found outside the three functions above.
7. Exploit Primitive
Not applicable. No delivered, demonstrable primitive exists in these builds. The patched driver still contains and can execute the unconditional-increment path, and the safety alternative it stages already exists in the unpatched driver. There is no supported evidence of a controllable free, pool reclamation, or any read/write/execute primitive, so no exploit primitive, spray strategy, or privilege-escalation chain is claimed.
8. Trigger / Reachability Notes
The affected function, SmbCeCompleteSrvCallConstructionPhase2, lies on the server-call construction path reached when the redirector establishes an SMB server connection (UNC-path access driving MRxSmbCreateSrvCall → SmbCeCreateSrvCall and the negotiate exchange). Both the enabled and disabled feature branches take references on the first VC endpoint under the connection spinlock. Because the change is a runtime feature selection with the original behavior preserved for the flag-off case, there is no security-relevant state transition introduced by the patch to trigger.
9. Unmatched Functions
No functions were removed. Three functions are new in the patched build (Feature_84197691__private_IsEnabledDeviceUsage, Feature_84197691__private_IsEnabledFallback, and a renamed SmbCeGetFirstBindingObject — the unpatched build has the equivalently-named SmbCeGetFirstBindingObjectEx), all part of the feature-gate scaffolding rather than a security change.
10. Confidence & Caveats
Confidence: High that this is WIL feature-staging plus telemetry, not a delivered security fix.
- The unconditional
VctReferenceEndpoint(lock xadd) call is retained in the patchedSmbCeCompleteSrvCallConstructionPhase2(at0x1C001207E) and gated only byFeature_84197691__private_IsEnabledDeviceUsage. VctReferenceEndpointSafe(lock cmpxchgwith zero-check) is present and used in the unpatched build; it is not introduced by the patch.- The unconditional helper still has the same number of callers (6) in both builds; the safe helper gains exactly one caller (9 → 10), the new gated block.
- The default enablement of
Feature_84197691is not encoded in the driver image and cannot be determined from these binaries.
Whether the unpatched unconditional-increment path constitutes a genuinely reachable use-after-free is not established by these builds and is not asserted here. If a real race exists, this patch stages a mitigation behind a runtime flag rather than delivering it.