1. Overview

Field Value
Unpatched binary csc_unpatched.sys
Patched binary csc_patched.sys
Overall similarity 0.9877
Matched functions 1415
Changed functions 1
Identical functions 1414
Unmatched (either direction) 0 / 0

Verdict: A single, surgical patch makes an existing rdi (LVIEW) NULL check unconditional in CscDclMRxTransition, closing a possible kernel NULL-pointer dereference (CWE-476). In the unpatched build the NULL check at both guard points is gated behind a WIL velocity/servicing feature (Feature_4082332987); when that check evaluates to 0, control branches past the test rdi, rdi NULL check into the block that dereferences rdi. The patch removes the feature gate and performs the NULL check unconditionally. Practical impact is bounded: at most a local kernel denial of service (bug check), and only if a NULL LVIEW pointer can reach this path with the feature disabled — which is not demonstrated here, and the LVIEW pointer is internal driver state rather than attacker-supplied input.


2. Vulnerability Summary

Finding #1 — NULL Pointer Dereference in CscDclMRxTransition

Attribute Value
Severity Low
Class NULL pointer dereference (CWE-476) / potential local kernel DoS; velocity-feature-gated NULL check made unconditional
Affected function CscDclMRxTransition (sub_1C005FF5C unpatched, sub_1c005ddcc patched)
Primitive Kernel NULL deref → bug check (local DoS) if reachable; no memory-corruption or code-execution primitive

Root cause. The function reads rdi (a CSC LVIEW / logical-view pointer) from *(*(arg1+0x38)+0x70) at 0x1C005FFA1. Given the callees (CscAcquireLViewLock, CscLViewIsRootGhosted, CscReleaseLViewLock, CscFlushLViewWasPreviouslyOffline) this is a logical-view object. The pre-patch code guards the main block with:

if ((op - 1) <= 2) {
    if (Feature_4082332987__private_IsEnabledDeviceUsage() == 0 || rdi != NULL) {
        // block that dereferences rdi
    }
}

sub_1c0007a70 is Feature_4082332987__private_IsEnabledDeviceUsage, a WIL velocity/servicing feature check (the mechanism Windows uses to stage a change so it can be rolled back server-side). It reads the global Feature_4082332987__private_featureState, tests bit 0x10, and if the bit is clear falls back to Feature_4082332987__private_IsEnabledFallbackwil_details_IsEnabledFallback, which can return 0. In the assembly the gate is call feature; test eax,eax; jz block_entry: when the feature check returns 0 (the feature-disabled path) the jz jumps past the test rdi, rdi NULL check and enters the block, so rdi is dereferenced without the NULL check through:

  • CscAcquireLViewLock(rdi) (sub_1c000c340) at 0x1C005FFF3 — first crash site
  • CscLViewIsRootGhosted(rdi) (sub_1c00654b8), CscReleaseLViewLock(rdi) (sub_1c000c49c), and related LVIEW calls off a NULL base.

The identical pattern repeats at the function exit gate (0x1C0060538), where CscFlushLViewWasPreviouslyOffline(rdi) (sub_1c00474b0) is called at 0x1C0060549 with the same potentially-NULL rdi.

The fix removes both feature-check gates and performs an unconditional test rdi, rdi NULL check before the dereferences. The corrected guard is effectively:

if ((op - 1) <= 2 && rdi != NULL)   // rdi MUST be non-NULL

Entry point and data flow:

  1. An FSCTL / DeviceIoControl request against the CSC device reaches the CSC FSCTL handler.
  2. CscFsCtl (sub_1c004c760) receives the RX_CONTEXT.
  3. CscDclInternalFsControl (sub_1c004c938) routes the request and calls CscDclMRxTransition at 0x1C004CDA7.
  4. CscDclMRxTransition (sub_1c005ff5c) dereferences rdi = *(*(arg1+0x38)+0x70) when the feature-gated branch skips the NULL check.

Reachability caveat: rdi is loaded from internal redirector/LVIEW state reached through RX_CONTEXT, not from a user-supplied buffer. The rest of the function dereferences rdi in many places without a NULL check, which indicates rdi is normally non-NULL; the two guarded points are defensive. Whether a low-privileged caller can actually drive *(*(arg1+0x38)+0x70) to NULL on this path, with the feature disabled, is not established here.

(The MINIRDR dispatch table is populated by CscInitializeDispatchTable (sub_1c008f338); DriverEntry is a separate function at 0x1C008F950.)


3. Pseudocode Diff

// === UNPATCHED CscDclMRxTransition (sub_1c005ff5c) ===
void CscDclMRxTransition(RX_CONTEXT* ctx, ULONG op)  // arg1=rcx, arg2=edx/r8d
{
    void* tmp   = ctx->field_0x38;          // mov rax, [rcx+0x38]
    LVIEW* rdi  = *(void**)(tmp + 0x70);    // mov rdi, [rax+0x70]   <-- CAN BE NULL

    if (op - 1 <= 2)                         // op in {1,2,3}
    {
        // In asm: call feature; test eax,eax; jz block  --> when the feature
        // check returns 0 (disabled), the branch jumps PAST the `test rdi, rdi` NULL check.
        if (Feature_4082332987__private_IsEnabledDeviceUsage() == 0 || rdi != NULL)
        {
            CscAcquireLViewLock(rdi);          // deref (0x1C005FFF3), NULL not checked on feature-disabled path
            CscLViewIsRootGhosted(rdi);        // deref (0x1C005FFFB)
            CscReleaseLViewLock(rdi);          // ...
        }
    }

    // Exit path — same feature-gated pattern:
    if (Feature_4082332987__private_IsEnabledDeviceUsage() == 0 || rdi != NULL)
        CscFlushLViewWasPreviouslyOffline(rdi);   // CRASH if rdi==NULL  (0x1C0060549)
}

// === PATCHED CscDclMRxTransition (sub_1c005ddcc) ===
void CscDclMRxTransition(RX_CONTEXT* ctx, ULONG op)
{
    void* tmp  = ctx->field_0x38;
    LVIEW* rdi = *(void**)(tmp + 0x70);      // still possibly NULL

    if (op - 1 <= 2 && rdi != NULL)          // FIX: unconditional NULL check, feature call removed
    {
        CscAcquireLViewLock(rdi);            // safe
        ...
    }

    if (rdi != NULL)                          // FIX: unconditional NULL check at exit
        CscFlushLViewWasPreviouslyOffline(rdi);   // safe
}

Key changes: - Removed: two call Feature_4082332987__private_IsEnabledDeviceUsage (sub_1c0007a70) invocations. - Added: test rdi, rdi / jz at both guard points (0x1C005DE52 entry, 0x1C005E39F exit in the patched binary). - All other deltas are module-base relocation (patched function moves from 0x1C005FF5C to 0x1C005DDCC) and the resulting call-target address shifts.


4. Assembly Analysis

4.1 Vulnerable function — unpatched CscDclMRxTransition (sub_1c005ff5c)

; ====== Entry block ======
0x1C005FF5C: mov     rax, rsp
0x1C005FF67: mov     [rax+0x18], r8d          ; arg2 (operation code)
0x1C005FF6B: mov     [rax+0x8],  rcx          ; arg1 (RX_CONTEXT)
...
; rdi = LVIEW pointer
0x1C005FF85: mov     rax, [rcx+0x38]          ; rax = *(arg1+0x38)
0x1C005FFA1: mov     rdi, [rax+0x70]          ; rdi = LVIEW pointer   <-- NULL possible
...
; ====== Vulnerable entry gate @ 0x1C005FFE2 ======
0x1C005FFD3: cmp     ecx, 0x2                  ; (arg2-1) <= 2  (op in {1,2,3})
0x1C005FFD6: jbe     0x1C005FFE2
0x1C005FFE2: call    Feature_4082332987__private_IsEnabledDeviceUsage
0x1C005FFE7: test    eax, eax
0x1C005FFE9: jz      0x1C005FFF0                ; feature==0 -> ENTER BLOCK, skipping NULL check
                                                ;   ^^^^ THIS IS THE BUG
0x1C005FFEB: test    rdi, rdi                   ; only reached when feature!=0
0x1C005FFEE: jz      0x1C005FFD8
; ----- Block 0x1C005FFF0: first dereference of possibly-NULL rdi -----
0x1C005FFF0: mov     rcx, rdi                  ; rcx = rdi
0x1C005FFF3: call    CscAcquireLViewLock       ; deref rdi -> CRASH if NULL
0x1C005FFF8: mov     rcx, rdi
0x1C005FFFB: call    CscLViewIsRootGhosted     ; another deref of rdi
...
; ====== Vulnerable exit gate @ 0x1C0060538 ======
0x1C0060538: call    Feature_4082332987__private_IsEnabledDeviceUsage
0x1C006053D: test    eax, eax
0x1C006053F: jz      0x1C0060546                ; feature==0 -> skip NULL check, proceed to deref
0x1C0060541: test    rdi, rdi
0x1C0060544: jz      0x1C006054E
0x1C0060546: mov     rcx, rdi                  ; rcx = rdi (possibly NULL)
0x1C0060549: call    CscFlushLViewWasPreviouslyOffline   ; dereferences rdi -> CRASH if NULL

4.2 Helper — Feature_4082332987__private_IsEnabledDeviceUsage (sub_1c0007a70)

0x1C0007A70: sub     rsp, 0x28
0x1C0007A74: and     qword [rsp+..], 0x0
0x1C0007A7A: mov     eax, cs:Feature_4082332987__private_featureState  ; WIL feature state
0x1C0007A84: test    al, 0x10                        ; bit 0x10
0x1C0007A86: jz      0x1C0007A8D                     ; if clear -> fallback path
0x1C0007A88: and     eax, 0x1                         ; if set, return bit 0
0x1C0007A8B: jmp     0x1C0007A9C
0x1C0007A8D: mov     rcx, [rsp+..]
0x1C0007A92: mov     edx, 0x3
0x1C0007A97: call    Feature_4082332987__private_IsEnabledFallback  ; -> wil_details_IsEnabledFallback, may return 0
0x1C0007A9C: add     rsp, 0x28
0x1C0007AA0: retn

This is a WIL staged-feature check (Feature_4082332987), not an Offline-Files-specific flag. When bit 0x10 of Feature_4082332987__private_featureState is clear, it falls back to Feature_4082332987__private_IsEnabledFallback, which can return 0 — the condition that makes the entry/exit gates skip the rdi NULL check.

4.3 Patched equivalent — CscDclMRxTransition (sub_1c005ddcc)

; Entry gate
0x1C005DE43: cmp     ecx, 0x2
0x1C005DE46: jbe     0x1C005DE52
0x1C005DE48: mov     esi, 0xC000000D
0x1C005DE4D: jmp     0x1C005E392
0x1C005DE52: test    rdi, rdi                  ; <-- DIRECT NULL CHECK (feature call removed)
0x1C005DE55: jz      0x1C005DE48               ; rdi==0 -> error path, block skipped
0x1C005DE57: mov     rcx, rdi
0x1C005DE5A: call    CscAcquireLViewLock       ; safe — rdi is non-NULL

; Exit gate
0x1C005E39F: test    rdi, rdi                  ; <-- DIRECT NULL CHECK (no feature call)
0x1C005E3A2: jz      0x1C005E3AC
0x1C005E3A4: mov     rcx, rdi
0x1C005E3A7: call    CscFlushLViewWasPreviouslyOffline   ; safe

Side-by-side, the swap is unambiguous: call Feature_4082332987__private_IsEnabledDeviceUsage + test eax,eax + jz is replaced by test rdi, rdi + jz at both guard locations.


5. Trigger Conditions

To reach and fire the bug:

  1. Reach the FSCTL dispatch interface. Issue an FSCTL / DeviceIoControl against the CSC device that routes through CscFsCtl (sub_1c004c760) → CscDclInternalFsControl (sub_1c004c938) → CscDclMRxTransition (sub_1c005ff5c). Confirm the exact operation codes against the CscDclInternalFsControl / CscFsCtl dispatch before building a PoC.
  2. Force the WIL feature check to return 0. Feature_4082332987__private_IsEnabledDeviceUsage (sub_1c0007a70) must return 0, i.e. the staged feature Feature_4082332987 is disabled: Feature_4082332987__private_featureState bit 0x10 clear and Feature_4082332987__private_IsEnabledFallback (wil_details_IsEnabledFallback) returning 0. This is the WIL feature-staging state, not an Offline-Files registry flag.
  3. Supply a NULL LVIEW pointer. The request must arrive on an RX_CONTEXT whose *(*(arg1+0x38)+0x70) (the LVIEW pointer loaded into rdi) is NULL.
  4. Pass arg2 ∈ {1,2,3}. The cmp ecx, 0x2 / jbe check at 0x1C005FFD6 requires the operation code (arg2 - 1) to be ≤ 2.
  5. Observe the crash. Expected result: a kernel access violation (0xC0000005, STATUS_ACCESS_VIOLATION) faulting on a small offset from NULL, with the faulting instruction inside CscAcquireLViewLock (called at 0x1C005FFF3) or CscFlushLViewWasPreviouslyOffline (called at 0x1C0060549), surfacing as a kernel bug check.

Reachability note: whether a NULL LVIEW pointer can be presented to CscDclMRxTransition via the FSCTL path (and with the feature disabled) must be validated empirically against the dispatch codes.


6. Exploit Primitive & Development Notes

Primitive. If the feature-disabled path is reached with rdi == NULL, the result is a kernel NULL-pointer dereference → bug check (local denial of service). On modern Windows the NULL page is reserved and not user-mappable, so the impact is bounded to a crash; there is no memory-corruption, write, or information-leak primitive here.

No escalation primitive is provided by this bug. The dereference is of a NULL base pointer, not attacker-controlled memory. Reaching code execution would require an unrelated way to map the zero page (blocked by NULL-page protection on Windows 8+) plus separate primitives, none of which this change provides. The report does not claim a code-execution path.

Reachability is not established. As noted in Section 2, rdi (the LVIEW pointer) is internal redirector state, not a user-supplied value, and the function dereferences it unguarded elsewhere — consistent with it normally being non-NULL. Absent a demonstrated way for a caller to present a NULL LVIEW on this path with the feature disabled, the practical severity is Low.


7. Debugger PoC Playbook

For a follow-on agent with WinDbg/KD attached to the unpatched csc.sys, here is the concrete plan.

7.1 Symbols & setup

.reload /f csc.sys
lm m csc                        ; confirm base
? csc!CscDclMRxTransition - csc ; offset 0x5ff5c from module base

If you only have the raw file, addresses in the JSON assume a loaded base of 0x1c000000; rebase to the runtime base via ? csc + 0x5ff5c.

7.2 Breakpoints (ready-to-paste)

bp csc!CscDclMRxTransition       ; function entry — inspect rcx, r8d/edx
bp csc+0x5ffe2                    ; vulnerable entry gate (call Feature_4082332987__private_IsEnabledDeviceUsage)
bp csc+0x5fff3                    ; first NULL-deref site (call CscAcquireLViewLock)
bp csc+0x60538                    ; vulnerable exit gate
bp csc+0x60549                    ; second NULL-deref site (call CscFlushLViewWasPreviouslyOffline)
bp csc!Feature_4082332987__private_IsEnabledDeviceUsage  ; observe return value
bp csc+0x5ffa1                    ; the "mov rdi, [rax+0x70]" — where rdi gets loaded

Why each one matters: - CscDclMRxTransition — capture the RX_CONTEXT (rcx) and op (r8d/edx). - csc+0x5ffe2 — the gate. If the instruction here is call Feature_4082332987__private_IsEnabledDeviceUsage, you are on the vulnerable binary; if it is test rdi, rdi, you are on the patched one. - csc+0x5fff3 — first dereference; if you reach here with rdi==0, the bug fires inside CscAcquireLViewLock. - csc!Feature_4082332987__private_IsEnabledDeviceUsage — observe what the feature check returns.

7.3 What to inspect at each breakpoint

BP Register / memory to inspect
csc!CscDclMRxTransition entry rcx = RX_CONTEXT*; r8d/edx = op (must be 1/2/3)
csc+0x5ffa1 (post load) rdi — the LVIEW pointer that may be NULL. Inspect poi(poi(rcx+0x38)+0x70)
csc!Feature_4082332987__private_IsEnabledDeviceUsage On exit, al — if 0, the NULL check is skipped. Reads Feature_4082332987__private_featureState (bit 0x10)
csc+0x5ffe2 Right before the call: rdi, ecx (should already be op-1 and ≤ 2)
csc+0x5fff3 rcx — should equal rdi; if 0, the deref inside CscAcquireLViewLock faults

7.4 Trigger setup from user mode

  1. Put the WIL staged feature Feature_4082332987 into the disabled state so Feature_4082332987__private_IsEnabledDeviceUsage returns 0 (Feature_4082332987__private_featureState bit 0x10 clear and the fallback returning 0).
  2. Issue an FSCTL / DeviceIoControl against the CSC device that routes through CscFsCtlCscDclInternalFsControlCscDclMRxTransition, with an operation code arg2 ∈ {1,2,3} and an RX_CONTEXT whose *(*(arg1+0x38)+0x70) (the LVIEW pointer) is NULL. Confirm the exact FSCTL codes against the CscFsCtl / CscDclInternalFsControl dispatch.
  3. Confirm the request reached the dispatch by hitting csc!CscFsCtl first, then stepping into CscDclInternalFsControlCscDclMRxTransition.

7.5 Expected observation confirming the bug

EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - STATUS_ACCESS_VIOLATION
FAULTING_IP: csc!CscAcquireLViewLock   ; deref of NULL LVIEW pointer
READ_ADDRESS: small offset from NULL
STACK_TEXT:
  csc!CscAcquireLViewLock
  csc!CscDclMRxTransition              ; the call at 0x1C005FFF3
  csc!CscDclInternalFsControl
  csc!CscFsCtl

Run !analyze -v to confirm; kv to see the chain back through CscFsCtl.

7.6 Struct / offset notes

  • RX_CONTEXT:
  • +0x38 → pointer to the structure whose +0x70 field holds the LVIEW pointer.
  • Structure at *(ctx+0x38):
  • +0x70 → LVIEW pointer (loaded into rdi at 0x1C005FFA1).
  • Feature_4082332987__private_featureState:
  • WIL staged-feature state read by sub_1c0007a70; bit 0x10 gates the fast path, otherwise the fallback decides.
  • Operation code (arg2):
  • Values 1, 2, 3 all satisfy (arg2-1) <= 2 at 0x1C005FFD6.

8. Changed Functions — Full Triage

Only one function changed.

Function Similarity Type Note
CscDclMRxTransition (sub_1C005FF5C) 0.9703 Security-relevant Two Feature_4082332987__private_IsEnabledDeviceUsage feature-check gates replaced with direct test rdi, rdi NULL checks at entry (0x1C005DE52) and exit (0x1C005E39F). Fixes the feature-gated missing NULL check where a feature==0 result branches past test rdi, rdi into the block that dereferences rdi.

Cosmetic / non-behavioral deltas folded into the same change: - Module-base relocation (the function moves from 0x1C005FF5C to 0x1C005DDCC). - Callee-address shifts from the relocation: CscAcquireLViewLock (sub_1c000c340sub_1c000b540), CscFlushLViewWasPreviouslyOffline (sub_1c00474b0sub_1c0045320) — same target functions at shifted addresses.

No other functions in the binary exhibit behavioral change.


9. Unmatched Functions

None. unmatched_unpatched = 0, unmatched_patched = 0. No sanitizer was removed and no mitigation helper was added; the fix is entirely in-line within CscDclMRxTransition (sub_1C005FF5C).


10. Confidence & Caveats

Confidence: High.

Rationale: - The diff is unambiguous and direction-correct: two call Feature_4082332987__private_IsEnabledDeviceUsage sequences (unpatched) are replaced by two test rdi, rdi sequences (patched) at the same guard points. The vulnerable pattern is present only in the unpatched binary. - The missing-NULL-check flaw is plainly visible in the assembly: call feature; test eax,eax; jz block skips the test rdi, rdi at 0x1C005FFEB (entry) / 0x1C0060541 (exit) whenever the feature check returns 0. - sub_1c0007a70 is confirmed as Feature_4082332987__private_IsEnabledDeviceUsage, a WIL staged-feature helper (reads Feature_4082332987__private_featureState, tests bit 0x10, falls back to Feature_4082332987__private_IsEnabledFallbackwil_details_IsEnabledFallback). It is not an Offline-Files registry flag.

Assumptions made (require manual verification before PoC): 1. Call-chain routing. The reachable path is CscFsCtl (sub_1c004c760) → CscDclInternalFsControl (sub_1c004c938, call at 0x1C004CDA7) → CscDclMRxTransition (sub_1c005ff5c), confirmed by the call sites. The MINIRDR dispatch table is populated by CscInitializeDispatchTable (sub_1c008f338); DriverEntry is a separate function at 0x1C008F950. 2. FSCTL codes. The exact operation codes that route through CscDclInternalFsControl to CscDclMRxTransition must be confirmed against the CSC dispatch. 3. Trigger route feasibility. Reaching a NULL LVIEW pointer (*(*(arg1+0x38)+0x70) == NULL) at CscDclMRxTransition with the feature disabled must be validated empirically. 4. Feature state forcing. Confirm how Feature_4082332987__private_featureState bit 0x10 and the fallback are driven to make the check return 0 (WIL feature-staging configuration).

What a researcher should verify manually: - The operation code that maps to arg2 ∈ {1,2,3}. - Whether a low-privileged user can reach the FSCTL dispatch (CscFsCtl) with a NULL LVIEW context. - Whether any earlier in-driver check blocks the NULL-LVIEW case before CscDclMRxTransition. - The exact FSCTL codes that route to CscFsCtl (sub_1c004c760).