1. Overview

Field Value
Unpatched binary pcw_unpatched.sys
Patched binary pcw_patched.sys
Overall similarity 0.9329
Matched functions 135
Changed functions 1 with a real logic change (PcwpCloseNotifier); the remainder is relocation/infrastructure churn
Removed functions 11 (all WIL feature-staging / telemetry helpers)
Added functions 0

Verdict: The patch finalizes a staged feature (Windows Internal Library feature ID 1222059323, name DeviceUsage). In PcwpCloseNotifier the runtime feature gate Feature_1222059323__private_IsEnabledDeviceUsage is removed and the feature-enabled code path is made permanent. All supporting WIL staging/telemetry helpers and the feature's descriptor-table entry are removed, which shifts the read-only data section and relocates several unrelated functions without changing their logic. No security-relevant change is present. There is no double-completion, no use-after-free, and no reachable exploit primitive.


2. Vulnerability Summary

Finding 1 — No security-relevant change

Attribute Detail
Severity None (informational)
Class Feature-staging finalization (WIL Feature_*__private_IsEnabled* gate removal)
Function PcwpCloseNotifier (unpatched 0x1C000C614, patched 0x1C000B614)
Removed call Feature_1222059323__private_IsEnabledDeviceUsage (unpatched 0x1C000C763)

What actually changed

PcwpCloseNotifier iterates the consumer list during notifier close/rundown and, for each consumer that could not be notified normally, decides whether to run the fallback teardown routine PcwpDeliverGenericDisconnectNotification.

In the unpatched build this decision is gated by a Windows Internal Library (WIL) feature check. Feature_1222059323__private_IsEnabledDeviceUsage reads the staging variable Feature_1222059323__private_featureState: if bit 0x10 (the "state is cached/determined" bit) is set it returns bit 0 (feature enabled/disabled), otherwise it calls Feature_1222059323__private_IsEnabledFallback. This is a standard staged-feature gate, not a runtime/per-CPU processor-state check.

The gate selects between two behaviors for the fallback teardown:

  • Feature enabled: run teardown when !(flags & 2) — i.e. deliver the fallback disconnect only to consumers that are still connected.
  • Feature disabled: run teardown when (flags & 2) — i.e. the legacy behavior, delivering to consumers already marked disconnected.

The patched build removes the gate and keeps the feature-enabled behavior unconditionally: teardown when !(flags & 2). The WIL staging infrastructure that supported this feature (Feature_1222059323__private_IsEnabledFallback, the wil_details_FeatureReporting_*, wil_details_FeatureStateCache_*, wil_details_StagingConfig_*, wil_details_GetCurrentFeatureEnabledState, wil_details_IsEnabledFallback helpers) and the feature's entry in the WIL feature-descriptor table are removed as well.

This is the normal end state of a WIL staged rollout: a feature that shipped behind a runtime flag is graduated to always-on and the flag machinery is deleted. It is not a security fix.

Why the earlier "double-completion / UAF" reading does not hold

  • The removed call is a WIL feature-staging gate, not a per-CPU/global runtime-state check and not a read of any data_1c0005468 global. That global and the "KeIsExecutingDpc-style" description do not correspond to the real function body.
  • The consumer flags bit 0x2 (at consumer object +0x68) means "disconnected / not fully connected." It is set in PcwpCreateUserRegistration before ObInsertObject (0x1C000C902) and in PcwpDisconnectUserRegistration (0x1C000CAB5), and cleared on a successful insert (0x1C000C9B9). It is not a "teardown already ran" marker. The teardown sink PcwpDeliverGenericDisconnectNotification ORs the flags with 9 (bits 0 and 3) and never sets bit 0x2, so a prior teardown does not put a consumer into the flags & 2 state.
  • Within a single close/rundown pass each consumer node is visited exactly once, and the "notify, then optionally fall back to teardown" structure is identical in both builds. There is no path that calls PcwpDeliverGenericDisconnectNotification twice on the same consumer, so there is no double-completion to fix. The only difference between the builds is which set of consumers (still-connected vs. already-disconnected) receives the fallback disconnect.

3. Pseudocode Diff

Unpatched — PcwpCloseNotifier teardown decision (feature-gated)

KeEnterCriticalRegion();
ExAcquirePushLockExclusiveEx(&rsi[8], 0);           // consumer lock at consumer+0x48
int enabled = Feature_1222059323__private_IsEnabledDeviceUsage();
char flags = *(rsi + 0x60);                          // consumer+0x68 flags
if (enabled) {
    if (!(flags & 2))                                // still connected -> fallback teardown
        goto teardown;
} else {
    if (flags & 2)                                   // legacy: already-disconnected -> teardown
        goto teardown;
}
goto unlock;

teardown:
    if (flags & 8) { flags |= 4; *(rsi + 0x60) = flags; }
    else PcwpDeliverGenericDisconnectNotification(rsi - 8);
unlock:
    ExReleasePushLockExclusiveEx(&rsi[8], 0);
    KeLeaveCriticalRegion();

Patched — PcwpCloseNotifier teardown decision (gate removed)

KeEnterCriticalRegion();
ExAcquirePushLockExclusiveEx(&rsi[8], 0);
char flags = *(rsi + 0x60);
if (!(flags & 2)) {                                  // feature-enabled path made permanent
    if (flags & 8) *(rsi + 0x60) = flags | 4;
    else PcwpDeliverGenericDisconnectNotification(rsi - 8);
}
ExReleasePushLockExclusiveEx(&rsi[8], 0);
KeLeaveCriticalRegion();

Fallback teardown sink PcwpDeliverGenericDisconnectNotification (unchanged between builds)

int64_t status = *(consumer + 0x38);
*(consumer + 0x68) |= 9;                             // set bits 0 and 3 (NOT bit 2)
*(consumer + 0x60) = 0;                              // clear in-flight notification pointer
return IoSetIoCompletionEx(
    *(consumer + 0x20),                              // IoCompletion handle
    *(consumer + 0x28),                              // KeyContext
    0, 0, 0xffffffff, 0, status);

The sink is called at most once per consumer per pass in both builds. Its body is byte-for-byte identical (only its address moved: unpatched 0x1C000BA60, patched 0x1C000AA60).


4. Assembly Analysis

Unpatched PcwpCloseNotifier — teardown decision block

00000001C000C745  call    cs:__imp_KeEnterCriticalRegion
00000001C000C751  xor     edx, edx
00000001C000C753  lea     rcx, [rsi+40h]
00000001C000C757  call    cs:__imp_ExAcquirePushLockExclusiveEx
00000001C000C763  call    Feature_1222059323__private_IsEnabledDeviceUsage  ; WIL feature gate (removed in patch)
00000001C000C768  test    eax, eax
00000001C000C76A  mov     al, [rsi+60h]                                     ; consumer flags (consumer+0x68)
00000001C000C76D  jz      short loc_1C000C775                               ; feature disabled -> legacy leg
00000001C000C76F  test    al, 2                                            ; feature enabled: skip if disconnected
00000001C000C771  jnz     short loc_1C000C78D
00000001C000C773  jmp     short loc_1C000C779                               ; connected -> teardown
00000001C000C775  test    al, 2                                            ; feature disabled: teardown if disconnected
00000001C000C777  jz      short loc_1C000C78D
00000001C000C779  test    al, 8                                            ; teardown path (deferred flag?)
00000001C000C77B  jnz     short loc_1C000C788
00000001C000C77D  lea     rcx, [rsi-8]
00000001C000C781  call    PcwpDeliverGenericDisconnectNotification         ; fallback teardown (once per consumer)
00000001C000C786  jmp     short loc_1C000C78D
00000001C000C788  or      al, 4
00000001C000C78A  mov     [rsi+60h], al
00000001C000C78D  xor     edx, edx
00000001C000C78F  lea     rcx, [rsi+40h]
00000001C000C793  call    cs:__imp_ExReleasePushLockExclusiveEx
00000001C000C79F  call    cs:__imp_KeLeaveCriticalRegion

The removed WIL gate Feature_1222059323__private_IsEnabledDeviceUsage (unpatched 0x1C0001E9C)

00000001C0001E9C  sub     rsp, 28h
00000001C0001EA6  mov     eax, cs:Feature_1222059323__private_featureState  ; WIL feature state variable
00000001C0001EB0  test    al, 10h                                          ; is the state cached/determined?
00000001C0001EB2  jz      short loc_1C0001EB9
00000001C0001EB4  and     eax, 1                                           ; yes -> return enabled bit
00000001C0001EB7  jmp     short loc_1C0001EC8
00000001C0001EB9  mov     rcx, [rsp+28h+arg_0]
00000001C0001EBE  mov     edx, 3
00000001C0001EC3  call    Feature_1222059323__private_IsEnabledFallback    ; no -> fallback evaluation
00000001C0001EC8  add     rsp, 28h
00000001C0001ECC  retn

Patched PcwpCloseNotifier — teardown decision block (gate gone)

00000001C000B742  call    cs:__imp_KeEnterCriticalRegion
00000001C000B74E  xor     edx, edx
00000001C000B750  lea     rcx, [rsi+40h]
00000001C000B754  call    cs:__imp_ExAcquirePushLockExclusiveEx
00000001C000B760  mov     al, [rsi+60h]                                    ; consumer flags
00000001C000B763  test    al, 2
00000001C000B765  jnz     short loc_1C000B77B                              ; disconnected -> skip (no gate)
00000001C000B767  test    al, 8
00000001C000B769  jnz     short loc_1C000B776
00000001C000B76B  lea     rcx, [rsi-8]
00000001C000B76F  call    PcwpDeliverGenericDisconnectNotification         ; teardown (at most once)
00000001C000B774  jmp     short loc_1C000B77B
00000001C000B776  or      al, 4
00000001C000B778  mov     [rsi+60h], al
00000001C000B77B  xor     edx, edx
00000001C000B77D  lea     rcx, [rsi+40h]
00000001C000B781  call    cs:__imp_ExReleasePushLockExclusiveEx
00000001C000B78D  call    cs:__imp_KeLeaveCriticalRegion

There is no call Feature_1222059323__private_IsEnabledDeviceUsage anywhere in the patched binary; the feature gate and its supporting functions are gone.

Fallback teardown sink PcwpDeliverGenericDisconnectNotification (unpatched 0x1C000BA60, unchanged logic)

00000001C000BA64  mov     rax, [rcx+38h]
00000001C000BA6B  or      byte ptr [rcx+68h], 9        ; sets bits 0 and 3 (not bit 2)
00000001C000BA72  and     qword ptr [rcx+60h], 0       ; clear in-flight notification pointer
00000001C000BA77  mov     rdx, [rcx+28h]               ; KeyContext
00000001C000BA7B  mov     rcx, [rcx+20h]               ; IoCompletion handle
00000001C000BA93  call    cs:__imp_IoSetIoCompletionEx

5. Trigger Conditions

There is no vulnerability to trigger. The change is a WIL staged-feature graduation.

For reference, PcwpCloseNotifier is reached from the PcwObject delete procedure PcwpDeleteObject (0x1C000CCB0), which dispatches on *(obj+0): value 0 frees the list, value 1 calls PcwpCloseUserRegistration, value 2 calls PcwpCloseNotifier (0x1C000CCD5). This dispatch and the surrounding notifier-close logic are identical in both builds; only the feature-gate branch inside PcwpCloseNotifier differs.


6. Exploit Primitive & Development Notes

No exploit primitive exists. The claimed double-completion / use-after-free is not present:

  • The fallback teardown sink PcwpDeliverGenericDisconnectNotification is invoked at most once per consumer per close/rundown pass in both builds.
  • The consumer flags bit 0x2 marks "disconnected / not connected," not "teardown already performed," so a consumer that has been torn down is not re-selected by any branch.
  • The IoSetIoCompletionEx handle/KeyContext values are consumer-registration state and are not double-completed.

Because there is no memory-safety or completion-lifecycle defect, there is nothing to develop into a primitive — no pool grooming, no information leak, and no mitigation-bypass considerations apply.


7. Debugger PoC Playbook

Not applicable — there is no vulnerability to reproduce.

If verifying the behavioral difference itself, the observable change is confined to PcwpCloseNotifier:

; Unpatched: the feature gate exists
bp pcw_unpatched!PcwpCloseNotifier+0x14f   ; 0x1C000C763 = call Feature_1222059323__private_IsEnabledDeviceUsage
;   eax after the call = the feature's enabled state
;   al = byte ptr [rsi+0x60] = consumer flags (bit 0x2 = disconnected)

; Patched: no such call; the flags&2 test directly gates teardown
bp pcw_patched!PcwpCloseNotifier+0x14f      ; 0x1C000B760 = mov al,[rsi+0x60]

Both builds call PcwpDeliverGenericDisconnectNotification at most once per consumer; a second call on the same consumer within one pass does not occur.


8. Changed Functions — Full Triage

PcwpCloseNotifier (unpatched 0x1C000C614 → patched 0x1C000B614)

Attribute Value
Similarity 0.9293
Change type Feature-staging finalization (not security-relevant)
Size 534 bytes → 512 bytes

What changed: The call Feature_1222059323__private_IsEnabledDeviceUsage at 0x1C000C763 was removed and the two-leg feature-gated branch collapsed to a single if (!(flags & 2)) guard — the feature-enabled behavior made permanent. No memory-safety change.

Removed functions (WIL staging/telemetry infrastructure)

The following functions exist only in the unpatched build and were removed when the feature was graduated:

Feature_1222059323__private_IsEnabledDeviceUsage, Feature_1222059323__private_IsEnabledFallback, wil_details_FeatureReporting_RecordUsageInCache, wil_details_FeatureReporting_ReportUsageToService, wil_details_FeatureReporting_ReportUsageToServiceDirect, wil_details_FeatureStateCache_ReevaluateCachedFeatureEnabledState, wil_details_FeatureStateCache_TryEnableDeviceUsageFastPath, wil_details_GetCurrentFeatureEnabledState, wil_details_IsEnabledFallback, wil_details_StagingConfig_Load, wil_details_StagingConfig_QueryFeatureState.

Other functions that differ (relocation/infrastructure only)

  • wil_InitializeFeatureStaging, wil_details_EvaluateFeatureDependencies, wil_details_PopulateInitialConfiguredFeatureStates, wil_details_ReevaluateOnFeatureConfigurationChange: differ only because the WIL feature-descriptor table lost feature 1222059323's entry (loop bounds change from &Feature_1222059323__private_descriptor / featureDescriptors_z to featureDescriptors_a). Staging-infrastructure churn.
  • McGenEventWrite_EtwWriteTransfer, PcwpFastIoDeviceControl, PcwpInitDefaultSecurityDescriptor, SepSddlGetAclForString, SepSddlGetSidForString: byte-identical logic; only referenced data-section addresses shifted (e.g. unk_1C0003000unk_1C0002000, off_1C0005160off_1C0004160) because removing the WIL code/data moved the read-only section by 0x1000. No behavioral change.

9. Unmatched Functions

Added: none.

Removed: the 11 WIL feature-staging / telemetry helpers listed in section 8. Their removal is the mechanical consequence of finalizing feature 1222059323 and is not a security change.


10. Confidence & Caveats

Confidence: High

Rationale: - The removed call target is named Feature_1222059323__private_IsEnabledDeviceUsage, and its body is a textbook WIL feature-state read (Feature_1222059323__private_featureState, bit 0x10 cache check, bit 0 result, fallback call). This is a staged-feature gate, not a runtime processor-state check. - The patched build keeps the feature-enabled branch (if (!(flags & 2))) and deletes the gate plus the entire WIL staging/telemetry support set and the feature's descriptor entry — the standard shape of a graduated staged feature. - The fallback teardown sink PcwpDeliverGenericDisconnectNotification is byte-identical between builds, is invoked at most once per consumer per pass, and sets flags bits 0/3 (never bit 0x2), so there is no re-entrant teardown and no double-completion. - An independent function-by-function comparison of both decompilations shows every other differing function is either WIL staging-table churn or pure data-address relocation, with no memory-safety or access-control change.

Net assessment: No security-relevant change. Severity: none. No CWE applies.