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 — unconditional lock xadd. Unpatched @ 0x1C000F710, patched @ 0x1C000EE90. 6 callers in each build (unchanged).
  • VctReferenceEndpointSafelock cmpxchg with 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 SmbCeGetFirstVcEndpoint and then VctReferenceEndpointSafe, checks al, and on failure releases the lock and returns STATUS_CONNECTION_DISCONNECTED (0xC000020C) without using the endpoint.
  • Feature disabled: a later block runs the original logic: SmbCeGetFirstVcEndpoint followed by the unconditional VctReferenceEndpoint (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

  1. Old path retained. The patched SmbCeCompleteSrvCallConstructionPhase2 still contains and can execute the original VctReferenceEndpoint (unconditional lock xadd) call at 0x1C001207E. 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.

  2. The "safe" helper is not new. VctReferenceEndpointSafe exists in the unpatched build at 0x1C0016354 and 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.

  3. 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.

  4. 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 0x1C0006070 vs patched 0x1C0011E10)
  • 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_DISCONNECTED on failure); when disabled, the original SmbCeGetFirstVcEndpoint + VctReferenceEndpoint (unconditional lock xadd) path runs. The patched build also switches the gated blocks to the SmbCeAcquireSpinLock/SmbCeReleaseSpinLock wrappers, duplicates the binding-teardown block (enabled vs disabled variants), and adds a feature-gated telemetry call (SmbCeErrorInd at 0x1C00122E2). 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 (at 0x1C0035A54) before the conditional store and clears it to 0 afterward; the unpatched version only cleared it to 0. The +0x782 byte is an in-progress marker on the connection object (the same field set/cleared inside the spinlock in SmbCeCompleteSrvCallConstructionPhase2). 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 MRxSmbCreateSrvCallSmbCeCreateSrvCall 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 patched SmbCeCompleteSrvCallConstructionPhase2 (at 0x1C001207E) and gated only by Feature_84197691__private_IsEnabledDeviceUsage.
  • VctReferenceEndpointSafe (lock cmpxchg with 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_84197691 is 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.