e1i63x64.sys — NDIS OID request-type check already present in both builds; patch relocates OID extraction into a helper
KB5078752
1. Overview
- Unpatched Binary:
e1i63x64_unpatched.sys - Patched Binary:
e1i63x64_patched.sys - Overall Similarity Score:
0.8577 - Diff Statistics:
- Matched Functions: 1201
- Changed Functions: 257
- Identical Functions: 944
- Unmatched Functions (Unpatched/Patched): 0 / 0
- Verdict: No security-relevant change. The NDIS OID request-type validation is present and byte-for-byte identical in both builds at the
MiniportOidRequestdispatch entry: onlyRequestTypevalues{0,1,2,12}are accepted, and any other value is rejected with status0xC0010015before any OID handler runs. The patch does not add this check. What the patch actually does is relocate OID extraction into a new helper,GetOid, that re-reads the same request-type set (a redundant re-check on a path where the type is already guaranteed valid) and emits a trace event; the OID handlers are refactored from taking a raw OID integer to taking theNDIS_OID_REQUESTpointer. The remainder of the changed-function set is an SR-IOV queue-to-VPort refactor, a new registry parameter, and tracing additions. None of these is a reachable security fix.
2. Change Summary
NDIS OID request-type validation — present in both builds (no delivered fix)
- Severity: None (informational)
- Change Class: Refactor / defense-in-depth relocation (no CWE)
- Affected Functions:
MiniportOidRequest (0x140026874 unpatched / 0x14000C5D4 patched)(OID request dispatch),GetOid (0x14000C3F8, patched only)(OID extraction helper),ReqQueryCommonInformation (0x140026E98 / 0x14000CFD4)(common query dispatch),RxFilterQueryOidHandler (0x1400133C8 / 0x14003E318)(receive-filter query handler),RxFilterSetOidHandler (0x140013730 / 0x14003E6B4)(receive-filter/VMQ set handler) - What was claimed vs. what is true: A missing request-type check would mean the unpatched driver acts on an OID under an unexpected
NDIS_OID_REQUEST.RequestType. That is not the case. In both builds the sole NDIS OID entry point,MiniportOidRequest, readsRequestTypeat offset+0x4and dispatches only for0(NdisRequestQueryInformation),1(NdisRequestSetInformation),2(NdisRequestQueryStatistics), and12(NdisRequestMethod); every other value returns0xC0010015immediately, beforeReqQueryInformation,ReqQueryCommonInformation,RxFilterQueryOidHandler, orRxFilterSetOidHandleris ever reached. - What the patch changed: OID extraction was centralized into the new helper
GetOid. In the unpatched build each dispatch reads the OID inline from the request (ReqQueryInformationdoesmov edi, [r8+0x20]) and passes it down as a rawunsigned long. In the patched build the dispatch and the receive-filter handlers instead take theNDIS_OID_REQUEST*and callGetOid, which readsRequestTypeat+0x4, returns the OID from+0x20for types{0,1,2,12}, and otherwise returns0after emitting a trace event. BecauseMiniportOidRequesthas already guaranteed the type is in{0,1,2,12}on every path that reaches these functions,GetOid's reject branch is unreachable for externally supplied requests; the helper always returns the real OID. The change is a refactor plus tracing, not a behavioral gate that was absent before. - Reachability: No
RequestTypeoutside{0,1,2,12}can reach any OID handler in either build. The only callers that construct anNDIS_OID_REQUESTwith a fixed type internally (for exampleReqQueryNdisStatistic) are driver-internal and not attacker-controlled.
3. Pseudocode Diff
The request-type gate exists in both builds at the dispatch entry. The patch moves OID extraction into a helper that repeats the same type set.
// --- PRESENT IN BOTH BUILDS ---
// OID request dispatch (MiniportOidRequest, 0x140026874 unpatched / 0x14000C5D4 patched)
int MiniportOidRequest(void* ctx, _NDIS_OID_REQUEST* req) {
switch (req->RequestType) { // offset +0x4
case 0: return ReqQueryInformation(...); // Query
case 1: return ReqSetInformation(...); // Set
case 2: return ReqQueryInformation(...); // QueryStatistics
case 12: return ReqMethod(...); // Method
default: return 0xC0010015; // rejected before any handler runs
}
}
// --- UNPATCHED: OID read inline, passed as a raw integer ---
// Query dispatch (ReqQueryInformation, 0x140029818)
int ReqQueryInformation(REQUEST* this, ADAPTER_CONTEXT* ctx, _NDIS_OID_REQUEST* req) {
unsigned oid = req->Oid; // mov edi, [r8+0x20]
...
ReqQueryCommonInformation(ctx, oid, ...); // OID passed down as unsigned long
}
// ReqQueryCommonInformation (0x140026E98) receives the OID as unsigned long and switches on it.
// --- PATCHED: OID read through GetOid, same type set ---
// New helper (GetOid, 0x14000C3F8)
unsigned long GetOid(_NDIS_OID_REQUEST* req) {
unsigned type = req->RequestType; // offset +0x4
if (type == 0 || type == 1 || type == 2 || type == 12)
return req->Oid; // offset +0x20
/* emit trace */ return 0; // unreachable for requests that pass MiniportOidRequest
}
// ReqQueryCommonInformation (0x14000CFD4) now takes _NDIS_OID_REQUEST* and does oid = GetOid(req).
// RxFilterQueryOidHandler / RxFilterSetOidHandler likewise take _NDIS_OID_REQUEST* and call GetOid.
4. Assembly Analysis
The request-type gate is identical in both builds (MiniportOidRequest)
Unpatched (0x140026874):
0x140026874 mov r8d, [rdx+4] ; RequestType
0x140026878 test r8d, r8d
0x14002687b jz 0x1400268b7 ; type 0 -> ReqQueryInformation
0x14002687d dec r8d
0x140026880 jz 0x1400268a5 ; type 1 -> ReqSetInformation
0x140026882 dec r8d
0x140026885 jz 0x1400268b7 ; type 2 -> ReqQueryInformation
0x140026887 cmp r8d, 0xa
0x14002688b jz 0x140026893 ; type 12 -> ReqMethod
0x14002688d mov eax, 0xc0010015 ; any other type: rejected
0x140026892 retn
Patched (0x14000C5D4) — same instructions, same constants:
0x14000c5d4 mov r8d, [rdx+4] ; RequestType
0x14000c5d8 test r8d, r8d
0x14000c5db jz 0x14000c617 ; type 0 -> ReqQueryInformation
0x14000c5dd dec r8d
0x14000c5e0 jz 0x14000c605 ; type 1 -> ReqSetInformation
0x14000c5e2 dec r8d
0x14000c5e5 jz 0x14000c617 ; type 2 -> ReqQueryInformation
0x14000c5e7 cmp r8d, 0xa
0x14000c5eb jz 0x14000c5f3 ; type 12 -> ReqMethod
0x14000c5ed mov eax, 0xc0010015 ; any other type: rejected
0x14000c5f2 retn
OID extraction: inline (unpatched) vs. helper call (patched)
Unpatched ReqQueryInformation (0x140029818) reads the OID directly from the request and passes it down:
0x14002982f mov edi, [r8+0x20] ; OID read inline from NDIS_OID_REQUEST+0x20
...
0x1400299ce call ReqQueryCommonInformation ; OID passed as unsigned long (edi)
Unpatched ReqQueryCommonInformation (0x140026E98) receives the OID as an integer argument:
0x140026ed6 mov r15d, r8d ; OID from the raw integer argument
0x140026eea mov eax, 0x10203 ; upper bound of the OID switch
0x140026f0d cmp r15d, eax ; switch dispatch on the OID
Patched ReqQueryCommonInformation (0x14000CFD4) takes the request pointer and obtains the OID through GetOid:
0x14000d026 mov rcx, r8 ; rcx = NDIS_OID_REQUEST*
0x14000d043 call GetOid ; read RequestType (already in {0,1,2,12}), return OID
0x14000d04b mov r15d, eax ; OID
0x14000d04e mov eax, 0x10203
0x14000d05b cmp r15d, eax ; switch dispatch on the OID
The GetOid helper itself (0x14000C3F8, patched only):
0x14000c407 mov esi, [rcx+4] ; RequestType
0x14000c40a xor edi, edi ; default return = 0
0x14000c40e test esi, esi
0x14000c410 jz 0x14000c476 ; type 0
0x14000c412 dec edx
0x14000c414 jz 0x14000c476 ; type 1
0x14000c416 dec edx
0x14000c418 jz 0x14000c476 ; type 2
0x14000c41a cmp edx, 0xa
0x14000c41d jz 0x14000c476 ; type 12
0x14000c41f ... ; otherwise emit trace, return 0
0x14000c476 mov edi, [rcx+0x20] ; accepted type: return the OID
0x14000c483 mov eax, edi
0x14000c48a retn
GetOid checks exactly the set {0,1,2,12} that MiniportOidRequest has already enforced on every reaching path, so its reject branch does not execute for externally supplied requests.
5. Reachability Analysis
- Single external entry: The only NDIS entry for OID requests is the
MiniportOidRequestcallback (registered at0x140003C8Cin the patched build). Query, Set, QueryStatistics, and Method requests all pass through it. - Type gate first: In both builds
MiniportOidRequestreadsRequestTypeat offset+0x4and dispatches only for{0,1,2,12}. A request with any otherRequestType(e.g.4) returns0xC0010015and never reachesReqQueryInformation,ReqQueryCommonInformation,RxFilterQueryOidHandler, orRxFilterSetOidHandler. - Consequence for the handlers: On every path that reaches the receive-filter/VMQ handlers,
RequestTypeis already one of{0,1,2,12}.GetOidtherefore always returns the real OID; there is no request-type value an attacker can supply that reaches a handler under the unpatched build but is blocked under the patched build. - Net effect: The OID a handler acts on is the same in both builds. The patch changes how the OID is fetched (inline read vs. helper call) and adds a trace event, not whether an out-of-type request can reach a handler.
6. Impact Assessment
- Nature: Refactor of OID extraction plus tracing. Not memory corruption and not a reachable logic bypass.
- Affected OIDs: The receive-filter/VMQ OIDs (
0x10224/0x10226/0x10228/0x10230) and the VMQ queue-information query (0xFF020188) are handled identically with respect to request type in both builds; each is reachable only under its legitimate NDIS request type in both. - Demonstrable primitive: None. Because the request-type set enforced by
GetOidis already enforced upstream in both builds, the helper does not remove or add any reachable state transition for attacker input. - Mitigations: Not applicable; there is no exploitable condition to mitigate.
7. Verification Notes
For a researcher confirming this against the two binaries with a kernel debugger:
Breakpoints
bp e1i63x64_unpatched+0x26874andbp e1i63x64_patched+0xC5D4(MiniportOidRequest)- Why: The request-type gate. Break here and inspect
[rdx+4](RequestType). Both builds fall tomov eax, 0xC0010015; retnfor any value outside{0,1,2,12}, so a crafted request never advances to a handler in either build. bp e1i63x64_unpatched+0x29818/bp e1i63x64_patched+0xFB48(ReqQueryInformation)- Why: Query dispatch. In the unpatched build the OID is read inline (
mov edi, [r8+0x20]); in the patched build the equivalent dispatch obtains it viaGetOid. bp e1i63x64_patched+0xC3F8(GetOid, patched only)- Why: Confirm that on every reaching call
RequestTypeis already in{0,1,2,12}, soGetOidreturns the OID from[rcx+0x20]and never takes the reject path for externally supplied requests.
What to Inspect
At MiniportOidRequest in either build:
* poi(rdx+4) = RequestType. Send RequestType = 4 and observe the return value 0xC0010015 at 0x14002688D (unpatched) / 0x14000C5ED (patched); execution does not reach any OID handler.
* poi(rdx+0x20) = Oid. This is the field GetOid returns in the patched build and that ReqQueryInformation reads inline in the unpatched build.
Expected Observation
- Both builds: A request with
RequestTypeoutside{0,1,2,12}is rejected atMiniportOidRequestwith0xC0010015. A request with a legitimate type is dispatched to the corresponding handler with the same OID in both builds. - Difference: In the patched build the dispatch and the receive-filter handlers fetch the OID through
GetOidand emit a trace event; in the unpatched build they read the OID field inline. No handler becomes reachable or unreachable as a result.
Struct/Offset Notes
NDIS_OID_REQUESToffsets used by both builds:+0x04:RequestType(accepted set{0,1,2,12}, enforced atMiniportOidRequestin both builds)+0x20:Oid
8. Changed Functions — Full Triage
OID-handling refactor (not a security fix)
GetOid (0x14000C3F8)(new in patched): ReadsRequestTypeat+0x4and returns the OID at+0x20for types{0,1,2,12}, otherwise0with a trace event. This is the same request-type set already enforced upstream atMiniportOidRequestin both builds, so the helper centralizes OID extraction and adds tracing rather than adding a reachable gate. In the unpatched build this address holds an unrelated function (IntCauseInterruptForVector).ReqQueryCommonInformation (0x140026E98 / 0x14000CFD4)(common query dispatch): Signature changed from taking the OID as a rawunsigned longto taking a_NDIS_OID_REQUEST*; the OID is now obtained viaGetOid. Register/helper relocations and trace calls added. The dispatch acts on the same OID value in both builds.RxFilterQueryOidHandler (0x1400133C8 / 0x14003E318)(OID 0xFF020188, VMQ queue-info query): Signature changed;arg2becomes a_NDIS_OID_REQUEST*and the OID is obtained viaGetOid(arg2)before the0xFF020188check. The buffer/length guard (>= 0x8A08) and theRxFilterGetVMQInfocall are unchanged.RxFilterSetOidHandler (0x140013730 / 0x14003E6B4)(VMQ/VLAN receive filter set, OID 0x10224/0x10226/0x10228/0x10230): Same signature refactor; OID obtained viaGetOid. SR-IOV terminology changed from queue-based to VPort-based helpers (see below), and trace calls added per OID case.
Behavioral & Structural Changes
SRIOV_CTRL::GetNextUnusedQueue (0x14002B910)/SRIOV_CTRL::GetNextUnusedVPort (0x140047140): SR-IOV allocation renamed from queue-based to VPort-based; per-element structure size increased (reported0x26cto0x288) with additional field initialization. Feature refactor.ADAPTER_CONTEXT::~ADAPTER_CONTEXT (0x14002DDEC)/ADAPTER_CONTEXT::~ADAPTER_CONTEXT (0x140049CE8): Adapter-context destructor. The set of buffers freed viaNdisFreeMemory_0changes and the base offset shifts0xa60->0xa68, consistent with the surrounding structure being relaid out by the SR-IOV change. Structural cleanup, not a security fix.RECEIVE_FILTER::RxFilterReadRegistryParameters (0x140013488)/RECEIVE_FILTER::RxFilterReadRegistryParameters (0x14003E408): Registry parameter reader updated to parse a new value (VMQLegacyOn650) for I350 controllers. Feature addition.
Cosmetic Changes
e1000_read_pba_string_generic (0x1400419C4 / 0x140062DD4): NVM/EEPROM PBA string reader. Trace additions and register-allocation differences; core logic (the0xfafasentinel check, hex nibble formatting, length validation) is identical.e1000_init_mac_params_ich8lan (0x140035ADC / 0x140051468): MAC function-pointer table initialization updated to point to relocated subroutines. No logical impact.
9. Unmatched Functions
- Added: 0
- Removed: 0
10. Confidence & Caveats
- Confidence Level: High. The request-type dispatch (
MiniportOidRequest) is present with identical instructions and constants in both builds, and it is the single external entry to the OID handlers. The patched-only helperGetOidre-checks the same request-type set on a path where the type is already guaranteed valid, so it does not change reachability. - Basis: Direct comparison of
MiniportOidRequest(0x140026874vs0x14000C5D4),ReqQueryInformation(0x140029818vs0x14000FB48),ReqQueryCommonInformation(0x140026E98vs0x14000CFD4), and the receive-filter handlers, plus theGetOidbody (0x14000C3F8). - Residual note: The bulk of the diff (SR-IOV queue-to-VPort rename, the new
VMQLegacyOn650registry parameter, and trace additions) is a servicing/feature update. No changed function in the set exposes a reachable security-relevant behavioral difference.