ndis.sys — Out-of-bounds read in receive-path Ethernet/SNAP parsers (CWE-125), fixed
KB5078883
1. Overview
- Binaries:
ndis_unpatched.sysvs.ndis_patched.sys - Overall Similarity: 98.66%
- Diff Statistics: 3451 matched functions (3409 identical, 42 changed), 0 unmatched functions in either direction.
- Verdict: The patch fixes the same out-of-bounds read (CWE-125) in two NDIS receive-path Ethernet/SNAP parsers and adds a NULL guard on the MDL pointer in each.
ndisSortNetBufferListsclassifies/sorts receivedNET_BUFFER_LISTs and is reached from the standard miniport receive-indication dispatch (ndisMDispatchReceiveNetBufferListsand related), so its instance is reachable on the ordinary receive path.ndisParseReceivedNBLis the same parser reached only from the datapath receive verifier, which is enabled per adapter, so that instance is not reachable on the default path. In both, the LLC/SNAP branch reads a 16-bit field at data offset0x14(bytes 20-21) while the preceding length check only guaranteed the remaining mapped length was at least0xE(14); the patch adds aremaining >= 0x16(22) check before that read. The out-of-bounds value is used only for the driver's internal EtherType/protocol classification and is not returned to the frame sender. The patch also tightens embedded-descriptor validation on the NDIS user-mode PnP/IOCTL path (ndisValidateEmbeddedBufferBoundsgains a minimum-offset check); that change is input-validation hardening, not a memory-safety fix, because the sub-buffer was already bounded to the parent buffer in both builds. All remaining changed functions are WIL feature-staging, tracing, or register-allocation churn.
2. Vulnerability Summary
Finding 1: Out-of-bounds read in the receive-verifier Ethernet/SNAP parser
- Severity: Low
- Vulnerability Class: Out-of-bounds read (CWE-125); added NULL-pointer guard (defense in depth)
- Affected Function:
ndisParseReceivedNBL@0x1C006E370(unpatched) →0x1C00141EC(patched) - Root Cause: On the 802.3 path the routine loads
CurrentMdl = FirstNetBuffer->CurrentMdland readsCurrentMdl->ByteCountwith no NULL check, then verifies that the remaining mapped data length (ByteCount - CurrentMdlOffset) is at least0xE(14). On the LLC/SNAP branch (data bytes0xAA,0xAA,0x03at offsets 14/15/16) it reads a 16-bit field at data offset0x14(bytes 20-21) without first requiring the remaining length to be at least0x16(22). A truncated SNAP frame whose remaining length is between 14 and 21 bytes therefore causes a read of up to ~8 bytes past the valid MDL data. - Reachability:
ndisParseReceivedNBLhas exactly one caller,ndisNblVerifyRxIndication(the NDIS datapath receive verifier).NdisFIndicateReceiveNetBufferListsandNdisMIndicateReceiveNetBufferListsinvoke that verifier only when the adapter's datapath-verification flag is set (filter handle+0x38bit0x200; miniport handle+0xE68bit0x800). The datapath verifier is a diagnostic feature (ndisDatapathVerifierMode/MmIsVerifierEnabled) that is off by default. The normal receive fast path does not call this parser. - Data Flow (Call Chain):
NdisMIndicateReceiveNetBufferLists/NdisFIndicateReceiveNetBufferLists(only when datapath verification is enabled for the adapter)ndisNblVerifyRxIndication(0x1C00722BC)ndisParseReceivedNBL(0x1C006E370)
Finding 3: Out-of-bounds read in the receive NBL-sort Ethernet/SNAP parser
- Severity: Low
- Vulnerability Class: Out-of-bounds read (CWE-125); added NULL-pointer guard (defense in depth)
- Affected Function:
ndisSortNetBufferLists@0x1C0012960(unpatched) →0x1C0012B00(patched) - Root Cause: This is the same defect as Finding 1 in a second, separately compiled parser. When it classifies a received
NET_BUFFER_LISTit loadsCurrentMdl = FirstNetBuffer->CurrentMdland readsCurrentMdl->ByteCountwith no NULL check, then requires the remaining mapped length (ByteCount - CurrentMdlOffset) to be at least0xE(14). On the LLC/SNAP branch (data bytes0xAA,0xAA,0x03at offsets 14/15/16) it reads a 16-bit field at data offset0x14(bytes 20-21) without first requiring the remaining length to be at least0x16(22). A truncated SNAP frame whose remaining mapped length is between 14 and 21 bytes therefore causes a read of up to ~8 bytes past the valid MDL data. - Reachability: Unlike Finding 1, this parser is not verifier-gated.
ndisSortNetBufferListsis called from the standard miniport receive-indication dispatch:ndisMTopReceiveNetBufferLists(0x1C0011EA0),ndisMDispatchReceiveNetBufferLists(0x1C009DD90),ndisMDispatchReceiveNetBufferListsWithLock(0x1C00322D0),ndisDoPeriodicReceivesIndication(0x1C00A99D4), andndisMIndicateSplitNblChain(0x1C00AA6B0). The L2 header is parsed to classify/sort the indicated NBLs; the parse is taken unless a precomputed protocol value is already present (*(NDIS_NBL_RCV_TRACKER->field0 + 0x1D0) != 0selects the precomputed value atNET_BUFFER_LIST+0xC8instead). The out-of-bounds value becomes the NBL's internal EtherType/protocol classification; it is not returned to the frame sender. On a typical hardware receive buffer (a fixed-size DMA buffer larger than the frame) the read stays inside the buffer and merely misreads stale bytes; it crosses an allocation boundary only when the MDL is sized to the frame with no mapped page following, in which case a boundary-crossing read under Special Pool bugchecks. - Data Flow (Call Chain):
- Miniport receive indication (
ndisMTopReceiveNetBufferLists/ndisMDispatchReceiveNetBufferLists/ndisMDispatchReceiveNetBufferListsWithLock/ndisDoPeriodicReceivesIndication/ndisMIndicateSplitNblChain) ndisSortNetBufferLists(0x1C0012960)
- Miniport receive indication (
Finding 2: Missing minimum-offset check in embedded-descriptor validation
- Severity: Low (input-validation hardening)
- Vulnerability Class: Improper input validation (CWE-20); no demonstrated out-of-bounds access
- Affected Function:
ndisValidateEmbeddedBufferBounds@0x1C0116760(unpatched) →0x1C010C3CC(patched), plus its wrappers. - Root Cause / What Changed: In both builds the validator already enforced that the embedded sub-buffer
[base+Offset, base+Offset+Length)lies entirely within the parent input buffer[base, base+parent_len), so downstream reads never exceeded the parent allocation. The patch adds one parameter (a minimum) and rejects the request whenOffset < minimum. TheNDIS_VAR_DATA_DESCwrappers now pass that minimum (0x28 for the 32-bit thunk descriptor variant, 0x48 for the native variant), forcing the descriptor's variable data to begin beyond the fixed request header rather than overlapping it. This prevents an attacker-supplied descriptor from aliasing the fixed structure fields; it is not an out-of-bounds read, because containment was, and remains, enforced in both builds and the calling handler's own size and overflow checks (0x48 minimum,offset+sizeoverflow rejection) are present in both builds. - Reachability: NDIS user-mode PnP operation path, exposed locally via
IRP_MJ_DEVICE_CONTROLto the NDIS device. - Data Flow (Call Chain):
IRP_MJ_DEVICE_CONTROLdispatchndisHandlePnPRequest(0x1C0144008)ndisValidateNdisVarDataDesc32InputString(0x1C012DF58) /ndisValidateNdisVarDataDescInputString(0x1C01166DC)ndisValidateEmbeddedBufferBounds(0x1C0116760)
3. Decompilation Diff
Finding 1: Parser (ndisParseReceivedNBL)
Unpatched (ndis_unpatched.sys, 0x1C006E370), 802.3 path:
FirstNetBuffer = a2->FirstNetBuffer;
CurrentMdl = FirstNetBuffer->CurrentMdl; // no NULL check
ByteCount = CurrentMdl->ByteCount;
if ( ByteCount < 0xE
|| (CurrentMdlOffset = FirstNetBuffer->CurrentMdlOffset, ByteCount <= (unsigned int)CurrentMdlOffset)
|| ByteCount - (unsigned int)CurrentMdlOffset < 0xE ) // remaining only checked >= 0xE
{ ... return 0; }
...
v14 = &MappedSystemVa[CurrentMdlOffset];
...
if ( v14[14] == 0xAA && v14[15] == 0xAA && v14[16] == 3 )
{
v7 = *((_WORD *)v14 + 10); // OOB: reads data offset 0x14 with only remaining >= 0xE guaranteed
...
}
Patched (ndis_patched.sys, 0x1C00141EC):
FirstNetBuffer = a2->FirstNetBuffer;
CurrentMdl = FirstNetBuffer->CurrentMdl;
if ( CurrentMdl == nullptr ) // FIX 1: added NULL guard
goto LABEL_4;
ByteCount = CurrentMdl->ByteCount;
...
v13 = ByteCount - CurrentMdlOffset;
...
if ( v13 >= 0x16 ) // FIX 2: require remaining >= 0x16 before the SNAP read
{
if ( v15[14] != 0xAA || v15[15] != 0xAA || v15[16] != 3 )
goto LABEL_24;
v7 = *((_WORD *)v15 + 10); // data offset 0x14, now reached only when safe
...
}
else { result = 0; }
Note: the 802.1Q VLAN branch (v17 == 0x8100) already re-validated ByteCount >= 0x12 and remaining >= 0x12 before its offset-0x10 read in both builds; only the SNAP branch lacked the corresponding size check.
Finding 3: NBL-sort parser (ndisSortNetBufferLists)
Unpatched (ndis_unpatched.sys, 0x1C0012960), SNAP branch:
CurrentMdl = FirstNetBuffer->CurrentMdl; // no NULL check
ByteCount = CurrentMdl->ByteCount;
if ( ByteCount < 0xE
|| ByteCount <= CurrentMdlOffset
|| ByteCount - CurrentMdlOffset < 0xE ) // remaining only checked >= 0xE
{ ... }
...
if ( data[14] == 0xAA && data[15] == 0xAA && data[16] == 3 )
protocol = *((_WORD *)data + 10); // OOB: reads data offset 0x14 with only remaining >= 0xE guaranteed
Patched (ndis_patched.sys, 0x1C0012B00):
CurrentMdl = FirstNetBuffer->CurrentMdl;
if ( CurrentMdl == nullptr ) // FIX 1: added NULL guard
goto reject;
ByteCount = CurrentMdl->ByteCount;
...
remaining = ByteCount - CurrentMdlOffset;
...
if ( remaining < 0x16 ) // FIX 2: require remaining >= 0x16 before the SNAP read
goto reject;
if ( data[14] == 0xAA && data[15] == 0xAA && data[16] == 3 )
protocol = *((_WORD *)data + 10); // data offset 0x14, now reached only when safe
As in Finding 1, the 802.1Q VLAN branch (0x8100) already re-validated ByteCount >= 0x12 and remaining >= 0x12 in both builds; only the SNAP branch lacked the check.
Finding 2: Buffer Validation (ndisValidateEmbeddedBufferBounds)
Unpatched (0x1C0116760) — containment only, no minimum:
v8 = a1 + a2; // parent_end
v9 = a1 + a5; // sub_start = base + Offset
v10 = v9 + a6; // sub_end = sub_start + Length
v11 = a3 + a4;
if ( v8 < a1 || v9 < a1 || v10 < v9 || v10 > v8 // overflow + containment
|| a7 != 0 && a6 != 0 && ((a7 - 1) & (unsigned int)v9) != 0
|| v9 <= a3 && v10 > a3
|| v9 <= v11 && v10 > v11 )
{ return 0; }
*a8 = v9;
return 1;
Patched (0x1C010C3CC) — adds the Offset < minimum rejection (a5 is the new minimum, a6 is Offset):
v10 = a1 + a2;
v11 = a1 + a6; // sub_start = base + Offset
v12 = v11 + a7; // sub_end = sub_start + Length
v13 = a3 + a4;
if ( v10 < a1
|| a5 != 0 && a6 < a5 // NEW: reject when Offset < minimum
|| v11 < a1
|| v12 < v11
|| v12 > v10 // containment (unchanged intent)
|| a8 != 0 && a7 != 0 && ((a8 - 1) & (unsigned int)v11) != 0
|| v11 <= a3 && v12 > a3
|| v11 <= v13 && v12 > v13 )
{ return 0; }
*a9 = v11;
return 1;
The wrappers supply the minimum: ndisValidateNdisVarDataDesc32InputString passes 0x28, ndisValidateNdisVarDataDescInputString passes 0x48, and ndisValidateNdisOffsetAndLengthInputBufferBounds passes 0.
4. Assembly Analysis
Finding 1: Parser
Unpatched sequence in ndis_unpatched.sys (ndisParseReceivedNBL):
0x1c006e3a7 mov rax, [rdx+8] ; NET_BUFFER_LIST->FirstNetBuffer
0x1c006e3ab mov rcx, [rax+8] ; NET_BUFFER->CurrentMdl -- no NULL check
0x1c006e3af mov edi, [rcx+28h] ; MDL->ByteCount
0x1c006e3b2 cmp edi, 0Eh ; ByteCount >= 14
0x1c006e3b5 jnb 0x1c006e3be
...
0x1c006e3c7 sub esi, ebp ; remaining = ByteCount - CurrentMdlOffset
0x1c006e3c9 cmp esi, 0Eh ; remaining >= 14 (only 14, not 22)
0x1c006e3cc jb 0x1c006e3b7
...
0x1c006e43f cmp byte ptr [rdx+0Eh], 0AAh ; SNAP DSAP
0x1c006e447 cmp byte ptr [rdx+0Fh], 0AAh ; SNAP SSAP
0x1c006e44d cmp byte ptr [rdx+10h], 3 ; SNAP Control
0x1c006e453 movzx eax, word ptr [rdx+14h] ; OOB read at data offset 0x14
0x1c006e459 mov [r15], ax ; store parsed EtherType
Patched (ndis_patched.sys, ndisParseReceivedNBL):
0x1c0014223 mov rax, [rdx+8]
0x1c0014227 mov rcx, [rax+8] ; CurrentMdl
0x1c001422b test rcx, rcx ; ADDED: NULL check on MDL pointer
0x1c001422e jnz 0x1c0014237
0x1c0014230 ... ; NULL -> bail out
...
0x1c00142c3 cmp edi, 16h ; ADDED: require remaining >= 22
0x1c00142c6 jnb 0x1c00142cc
0x1c00142c8 xor al, al ; too small -> return 0
0x1c00142cc cmp byte ptr [rdx+0Eh], 0AAh
...
0x1c00142e0 movzx eax, word ptr [rdx+14h] ; reached only when remaining >= 22
Here edi holds the remaining mapped length (ByteCount - CurrentMdlOffset), so cmp edi, 16h is the guard that bounds the offset-0x14 read.
Finding 3: NBL-sort parser
Unpatched sequence in ndis_unpatched.sys (ndisSortNetBufferLists):
0x1c0012a24 mov rcx, [rax+8] ; NET_BUFFER->CurrentMdl -- no NULL check
0x1c0012a2b mov esi, [rcx+28h] ; MDL->ByteCount
0x1c0012a2e cmp esi, 0Eh ; ByteCount >= 14
0x1c0012a3f mov ebp, esi
0x1c0012a41 sub ebp, edi ; remaining = ByteCount - CurrentMdlOffset
0x1c0012a43 cmp ebp, 0Eh ; remaining >= 14 (only 14, not 22)
...
0x1c0012c5c cmp byte ptr [r8+0Eh], 0AAh ; SNAP DSAP
0x1c0012c67 cmp byte ptr [r8+0Fh], 0AAh ; SNAP SSAP
0x1c0012c72 cmp byte ptr [r8+10h], 3 ; SNAP Control
0x1c0012c7d movzx r14d, word ptr [r8+14h] ; OOB read at data offset 0x14
Patched (ndis_patched.sys, ndisSortNetBufferLists):
0x1c0012bc8 mov rcx, [rax+8] ; CurrentMdl
0x1c0012bcc test rcx, rcx ; ADDED: NULL check on MDL pointer
0x1c0012bcf jz 0x1c0012e0f ; NULL -> reject
0x1c0012bd5 mov r14d, [rcx+28h] ; ByteCount
0x1c0012bf0 mov ebp, r14d
0x1c0012bf3 sub ebp, r15d ; remaining = ByteCount - CurrentMdlOffset
...
0x1c0012ca8 cmp ebp, 16h ; ADDED: require remaining >= 22
0x1c0012cab jb 0x1c0012e0f ; too small -> reject
0x1c0012cb3 cmp byte ptr [r8+0Eh], 0AAh
...
0x1c0012cc8 movzx edi, word ptr [r8+14h] ; reached only when remaining >= 22
Here ebp holds the remaining mapped length, so cmp ebp, 16h bounds the offset-0x14 read, mirroring the Finding 1 fix.
Finding 2: Buffer Validation
The added check in ndis_patched.sys (ndisValidateEmbeddedBufferBounds, 0x1C010C3CC):
0x1c010c3f3 mov eax, [rsp+arg_20] ; new minimum argument
0x1c010c3f7 test eax, eax
0x1c010c3f9 jz 0x1c010c401 ; minimum == 0 -> skip
0x1c010c3fb cmp [rsp+arg_28], eax ; Offset vs minimum
0x1c010c3ff jb 0x1c010c447 ; Offset < minimum -> reject
The unpatched 0x1C0116760 has no equivalent instructions; it performs only the containment and boundary/alignment comparisons before returning the sub-buffer pointer. The parent-buffer containment check (sub_end <= parent_end) is present and identical in both builds.
5. Trigger Conditions
Finding 1 (Receive-verifier OOB read)
- NDIS datapath verification must be enabled for the target adapter (e.g. via Driver Verifier's NDIS/datapath verification). Without it,
ndisNblVerifyRxIndicationand thereforendisParseReceivedNBLare never called. - A frame indicated on that adapter must have a
NET_BUFFERwhose remaining mapped length (ByteCount - CurrentMdlOffset) is between 14 and 21 bytes. - The mapped data must present an 802.3 length field
<= 0x600at offset0xCand the SNAP markers0xAA,0xAA,0x03at offsets0xE,0xF,0x10, so the offset-0x14 read is reached. - Observable effect: the routine reads up to ~8 bytes past the valid MDL data. The value is written to the parser's 16-bit EtherType output used by the verifier; it is not returned to any external party. When datapath verification is combined with Special Pool, a read that crosses the allocation boundary is caught and bugchecks.
Finding 3 (Receive NBL-sort OOB read)
- A miniport indicates a received
NET_BUFFER_LISTup the stack (ndisMDispatchReceiveNetBufferListsand the related dispatch routines listed above). No datapath-verification flag is required; the L2 classification/sort parse runs on the ordinary receive path (unless a precomputed protocol value is already set on the tracker). - The indicated
NET_BUFFERmust have a remaining mapped length (ByteCount - CurrentMdlOffset) between 14 and 21 bytes, present an 802.3 length field<= 0x600at data offset0xC, and carry the SNAP markers0xAA,0xAA,0x03at offsets0xE,0xF,0x10. - Observable effect: the routine reads up to ~8 bytes past the valid MDL data at offset
0x14. The value becomes the NBL's internal protocol classification and is not returned to the frame sender. On a typical DMA receive buffer larger than the frame the read stays inside the buffer (a stale-byte misread); it crosses the allocation boundary only when the MDL is sized to the frame with no following mapped page, in which case a boundary-crossing read under Special Pool bugchecks.
Finding 2 (Local descriptor-offset validation)
- Open a handle to the NDIS device and issue an
IRP_MJ_DEVICE_CONTROLrequest routed tondisHandlePnPRequest. - The input buffer must contain an
NDIS_VAR_DATA_DESCwhoseOffsetfield points before the fixed request header (Offset < 0x28for the 32-bit thunk variant,< 0x48for the native variant) while still lying inside the parent buffer. - In the unpatched build the validator accepts such a descriptor, so the resulting
UNICODE_STRINGoverlaps the fixed structure fields. All reads remain inside the parent input buffer (containment is enforced), so this is a structural/aliasing issue, not an out-of-bounds access. The patched build rejects it.
6. Exploit Primitive & Development Notes
- Finding 1 (receive-verifier OOB read): A bounded read of up to ~8 bytes immediately following the packet data in the MDL-mapped region. The value influences only the verifier's internal EtherType classification and is not disclosed to the frame sender. Its practical effect is a possible bugcheck when datapath verification runs with Special Pool, and otherwise a benign misread. It is gated behind a non-default diagnostic mode, so it is not a remote information-disclosure or denial-of-service primitive against a default configuration.
- Finding 1 (NULL guard): The added
CurrentMdl == NULLcheck is defense in depth. A NULLCurrentMdlon an indicatedNET_BUFFERwould be a miniport defect rather than attacker-controlled input. - Finding 3 (receive NBL-sort OOB read): The same bounded read of up to ~8 bytes following the packet data, but on the standard receive path rather than the verifier-only path. The value influences only the NBL's internal protocol classification and is not disclosed to the frame sender. On typical hardware receive buffers the read stays within the DMA buffer; its practical worst case is a bugcheck under Special Pool with a frame-sized MDL. The added
CurrentMdl == NULLguard is defense in depth, as in Finding 1. - Finding 2: No out-of-bounds primitive is demonstrable; the parent-buffer containment check bounds every access in both builds. The added minimum-offset check prevents an embedded descriptor's variable data from overlapping the fixed request header.
7. Debugger Notes
For a follow-on agent with a kernel debugger attached to ndis_unpatched.sys, and with NDIS datapath verification enabled on the target adapter, Finding 1 can be observed at:
ndis!ndisParseReceivedNBL (0x1c006e370) entry; rdx = NET_BUFFER_LIST
ndis!ndisParseReceivedNBL+0x3b (0x1c006e3ab) mov rcx,[rax+8] loads CurrentMdl (NULL -> fault at +0x3f)
ndis!ndisParseReceivedNBL+0x42 (0x1c006e3b2) cmp edi,0xe ; edi = ByteCount
ndis!ndisParseReceivedNBL+0xe3 (0x1c006e453) movzx eax,word [rdx+0x14] ; the OOB read
- At
+0x3b, if[rax+8](CurrentMdl) is NULL the followingmov edi,[rcx+28h]at+0x3ffaults. - At
+0x42,ediisByteCount; the SNAP path is entered when the remaining length is in[0xE, 0x15]. - At
+0xe3,rdxpoints at the mapped data;rdx+0x14is the 2-byte read that can exceed the valid data when the remaining length is below0x16. Under Special Pool a boundary-crossing read bugchecks (PAGE_FAULT_IN_NONPAGED_AREA, 0x50).
A 20-byte 802.3 + LLC/SNAP frame (6-byte destination MAC, 6-byte source MAC, a length field <= 0x600, AA AA 03, then padding) indicated on a verification-enabled adapter reaches +0xe3 with rdx+0x14 at the last valid byte and one byte beyond.
8. Changed Functions — Full Triage
The patch diff contains 42 changed functions.
- Security-relevant logic change:
ndisSortNetBufferLists(0x1C0012960→0x1C0012B00): added NULL guard onCurrentMdland aremaining >= 0x16check before the SNAP offset-0x14 read (Finding 3); reachable on the ordinary receive path.ndisParseReceivedNBL(0x1C006E370→0x1C00141EC): added NULL guard onCurrentMdland aremaining >= 0x16check before the SNAP offset-0x14 read (Finding 1); verifier-gated.ndisValidateEmbeddedBufferBounds(0x1C0116760→0x1C010C3CC): added a minimum-offset parameter andOffset < minimumrejection (Finding 2).
- Receive-dispatch wrappers adjusted for the
ndisSortNetBufferListssignature change (not independent fixes):ndisMDispatchReceiveNetBufferLists,ndisMDispatchReceiveNetBufferListsWithLock,ndisMIndicateSplitNblChain, andndisDoPeriodicReceivesIndicationwere recompiled to match the changedndisSortNetBufferLists; their own logic is unchanged. - Callers adjusted to pass the new minimum (not independent fixes):
ndisValidateNdisVarDataDescInputString(0x1C01166DC→0x1C010C4E0): passes minimum0x48.ndisValidateNdisVarDataDesc32InputString(0x1C012DF58→0x1C010C458): passes minimum0x28.ndisValidateNdisOffsetAndLengthInputBufferBounds(0x1C012DEA8→0x1C012EEF8): passes minimum0x0.ndisHandlePnPRequest(0x1C0144008→0x1C0145008): recompiled/relocated (WPP tracing and block reordering). Its input-buffer minimum-size (0x48), integer-overflow, andIoIs32bitProcessthunk checks are present in both builds; only the wrapper calls now carry the new minimum.ndisNblVerifyRxIndication(0x1C00722BC): register-allocation change following thendisParseReceivedNBLsignature change.
- WIL feature-staging migration (not security fixes): a cluster of functions (e.g.
0x1C0033984,0x1C0035D0C,0x1C00751E0,0x1C007F214,0x1C0134C10,0x1C0152E1C,0x1C01445F8) moved from querying global feature configuration (RtlQueryFeatureConfiguration) to a per-adapter atomic field check. - Tracing / register-reallocation churn:
0x1C00B7CBC,0x1C0002B08,0x1C0002AE0,0x1C0160A20, and similar exhibit recompilation noise with no security-relevant logical change.
9. Unmatched Functions
There are no added or removed functions in the provided diff. The patch modifies existing logic and relocates several functions without introducing or removing dispatch routines.
10. Confidence & Caveats
- Confidence: High. All three changes are confirmed against the decompilation and disassembly of both builds. The reachability of
ndisParseReceivedNBL(single verifier-only caller, gated by the datapath-verification flag) and ofndisSortNetBufferLists(called from the standard miniport receive-indication dispatch) is confirmed at every call site. - Caveats:
- Findings 1 and 3 are the same out-of-bounds read fixed in two parsers. Finding 1 (
ndisParseReceivedNBL) is reachable only when NDIS datapath verification is enabled and is not exposed on the default receive path. Finding 3 (ndisSortNetBufferLists) is on the ordinary receive path but the out-of-bounds value is used only for internal NBL classification, is not disclosed to the sender, and typically stays within the DMA receive buffer; its worst realistic impact is a bugcheck under Special Pool with a frame-sized MDL. Both are rated Low. - Finding 2 is input-validation hardening. The parent-buffer containment check bounds every access in both builds, so no out-of-bounds read is demonstrable; the added check prevents an embedded descriptor's variable data from overlapping the fixed request header.
- Findings 1 and 3 are the same out-of-bounds read fixed in two parsers. Finding 1 (