1. Overview

Field Value
Unpatched binary ntfs_unpatched.sys (image base 0x1C0000000)
Patched binary ntfs_patched.sys (image base 0x140000000)
Overall similarity 0.5882
Matched functions 3938
Changed functions 3137
Identical functions 801
Unmatched (either direction) 0 / 0

Verdict: No security-relevant change was found. The two binaries are different builds of ntfs.sys (different image base, and the patched image is roughly 750 KB larger), which is why the overall similarity is only 0.59 and 3137 functions differ. The differences examined are recompilation drift: renamed locals, changed structure offsets, reordered corruption checks, different internal debug-message constant IDs, and reference-counting/feature-gate refactors. The reparse-point out-of-bounds read described in the original draft could not be reproduced against the binaries: the function it named is an Enterprise Data Protection (EDP/WIP) telemetry routine, the telemetry string it quoted does not exist in either build, and the real stat-fill routine already performs the relevant reparse validation identically in both builds.


2. Vulnerability Summary

Severity Class Affected function
None (informational) No security-relevant change NtfsFillStatInfoFromMftRecord (reparse path unchanged)

Why the claimed out-of-bounds read is not present

Symbols are present in both disassemblies, so functions can be matched by name rather than by reused address.

  1. The named "helper" is EDP telemetry, not a reparse/stat helper. The address 0x1C02A8BC8 in the unpatched build is EdpEnforcementLog_FileRequiresEncryption, an Enterprise Data Protection enforcement-logging routine (it calls EEL_GetProcessInfo). It has nothing to do with reparse-point parsing or with filling stat information.

  2. The "patched" counterpart is a different function. The address 0x140252D28 in the patched build is EdpEnforcementLog_AutomaticEncryption, a separate EDP telemetry routine — not a patched version of EdpEnforcementLog_FileRequiresEncryption (which lives at 0x14011B804 in the patched build). The "added push r12/push r13, frame grown by 0x10" prologue difference is simply the difference between two unrelated EDP functions, not an inserted bounds check.

  3. The telemetry string does not exist. No string NtfsFillStatInfoFromMftRecordBailedBecauseOfNonResReparsePointCount, nor any ...Bailed... / NonResReparsePointCount string, appears in either build.

  4. The real stat-fill routine validates reparse attributes identically in both builds. NtfsFillStatInfoFromMftRecord is at 0x1C0200F98 (unpatched) and 0x140237014 (patched). In both builds, after locating the $REPARSE_POINT (type 0xC0) attribute it:

  5. rejects the attribute if it is non-resident (the byte at attribute-header offset +8), and
  6. caps the resident reparse data length at 0x4000 (16 KB).

There is no missing check and no direction change: the unpatched build is exactly as strict as the patched build on this path.


3. Actual Diff

NtfsFillStatInfoFromMftRecord differs between the two builds only by recompilation artifacts. Same logic, different codegen:

  • Base structure offset changed (*(a1 + 96) in the unpatched build vs *(a1 + 104) in the patched build) — a layout change consistent with a different build, not a security fix.
  • Locals renumbered/reallocated; the order of the "bad or orphan FRS" corruption checks was rearranged; internal debug-message constant IDs shifted (for example 216868216909).
  • The shared-security release at the end changed shape: the unpatched build calls NtfsDereferenceSharedSecurity gated by EnableFeatureServicing_48459730, while the patched build inlines an interlocked decrement plus ExFreePoolWithTag. This is a reference-counting/feature-staging refactor, not a memory-safety fix.

The reparse-point handling itself is unchanged. Both builds contain the same two guards before any reparse data is consumed:

// Both builds, NtfsFillStatInfoFromMftRecord, reparse ($REPARSE_POINT = 0xC0) path:
// 1. reject non-resident reparse attribute (attribute-header +8 byte != 0)
// 2. cap resident reparse data length at 0x4000
if ( *(_BYTE *)(reparse_attr + 8) != 0 )        // non-resident -> bail
    /* status = 0xC0000004-class error, return */;
if ( *(_DWORD *)(reparse_attr + 16) > 0x4000u ) // length > 16 KB -> corruption
    /* raise corruption, return */;

4. Assembly Analysis

The function the draft named (EdpEnforcementLog_FileRequiresEncryption @ 0x1C02A8BC8, unpatched)

Real prologue (this is EDP telemetry, not a reparse handler):

00000001C02A8BC8  mov     rax, rsp
00000001C02A8BCB  mov     [rax+8], rbx
00000001C02A8BCF  mov     [rax+10h], rsi
00000001C02A8BD3  mov     [rax+18h], rdi
00000001C02A8BD7  push    rbp
00000001C02A8BD8  push    r14
00000001C02A8BDA  push    r15
00000001C02A8BDC  lea     rbp, [rax-268h]
00000001C02A8BE3  sub     rsp, 350h
00000001C02A8C1B  call    EEL_GetProcessInfo

The instructions the draft quoted as the "vulnerable reads" and "telemetry-string load" do exist at those addresses, but they are not what was claimed and they are inside this EDP routine:

Address Real instruction Draft claimed
0x1C02A8D31 mov rax, [rbx+8] (matches operand form, but this is EDP logging, not reparse)
0x1C02A8D5B mov eax, [rbx+4] (same)
0x1C02A8D81 jz short loc_1C02A8D94 falsely quoted as lea rax,[rel telemetry_str]
0x1C02A8DA6 mov rax, r8 falsely quoted as lea rax,&data_...

The real stat-fill routine reparse guards

Unpatched NtfsFillStatInfoFromMftRecord @ 0x1C0200F98:

00000001C0201355  mov     [rsp+0D8h+PreviouslyGrantedAccess], 0C0h   ; look up $REPARSE_POINT
00000001C0201388  cmp     [r14+8], r9b                               ; non-resident? -> bail
00000001C0201393  cmp     dword ptr [r14+10h], 4000h                 ; length cap 0x4000

Patched NtfsFillStatInfoFromMftRecord @ 0x140237014:

0000000140237416  mov     [rsp+0D8h+PreviouslyGrantedAccess], 0C0h   ; look up $REPARSE_POINT
0000000140237449  cmp     [r14+8], r8b                               ; non-resident? -> bail
00000001402377E6  cmp     dword ptr [r14+10h], 4000h                 ; length cap 0x4000

Both guards are present in both builds. The unpatched build is not missing a bounds check.


5. Trigger Conditions

None. There is no reachable out-of-bounds read on this path to trigger. Non-resident reparse attributes are rejected before any reparse data is read, and resident reparse data length is capped at 0x4000 in both builds.


6. Exploit Primitive & Development Notes

None. No kernel-pool out-of-bounds read, information-disclosure, or denial-of-service primitive is supported by the binaries on this path. The originally described primitive, pool-shaping/KASLR-bypass chain, and mitigation table were not backed by the code and are not reproduced here.


7. Debugger PoC Playbook

Not applicable. There is no vulnerable code path to instrument. The addresses the draft proposed as breakpoints belong to EDP telemetry (EdpEnforcementLog_FileRequiresEncryption), not to reparse-point stat processing.


8. Changed Functions — Triage

The single entry the draft treated as a security fix does not hold up:

Reported pairing Reality
sub_1c02a8bc8 (unpatched) ↔ sub_140252d28 (patched), described as an "ETW telemetry / reparse bail-out helper" These are two different EDP telemetry functions: EdpEnforcementLog_FileRequiresEncryption (0x1C02A8BC8, unpatched) and EdpEnforcementLog_AutomaticEncryption (0x140252D28, patched). The prologue "delta" is the natural difference between two unrelated functions, not an inserted validation block. Not security-relevant.

The remaining ~3136 differing functions are consistent with a full rebuild (different image base, different structure layout, codegen churn). An independent name-matched diff of the untrusted-metadata parsing routines (reparse, extended-attribute, attribute-list, index, and FSCTL paths) surfaced no function where the patched build adds a bounds/length/overflow check that the unpatched build lacks.


9. Unmatched Functions

removed: []
added:   []

No functions were added or removed.


10. Confidence & Caveats

Overall confidence: High that there is no security-relevant change on the reported path.

  • The named "helper" resolves to EDP telemetry, not reparse handling.
  • The quoted telemetry string is absent from both builds.
  • Two of the quoted "vulnerable" instructions were misquoted (0x1C02A8D81, 0x1C02A8DA6).
  • The real NtfsFillStatInfoFromMftRecord rejects non-resident reparse attributes and caps reparse length at 0x4000 in both builds, at the addresses listed in section 4.

Caveat: the two images are substantially different builds, so the 3137 "changed" functions are dominated by recompilation drift. This report resolves the one claimed finding and the untrusted-metadata parsing paths; a broader per-function security diff of an entire build-to-build delta is out of scope and was not exhaustively performed.