http.sys — feature-staged user-mode-access refactor, legacy path retained
KB5079466
1. Overview
- Unpatched Binary:
http_unpatched.sys - Patched Binary:
http_patched.sys - Overall Similarity:
0.9105(91.05%) - Diff Statistics: Matched: 3865 | Changed: 282 | Identical: 3583 | Unmatched: 0 (both directions)
- Verdict: The patched build introduces a new global,
UxKirNewUma, which is assigned from a staged feature flag at init:UxKirNewUma = Feature_NewUMAHttpSys__private_IsEnabledDeviceUsageNoInline() != 0. Across the IOCTL structure-parsing functions the patch adds an alternate code path taken only whenUxKirNewUma != 0, which alignment-checks embedded pointers, reads each length/pointer field through user-memory helpers (RtlReadUShortFromUser,RtlReadULong64FromUser,RtlReadULongFromUser), and copies payload bytes with a fault-tolerant routine (UxTryCopyFromMode) instead ofmemmove. WhenUxKirNewUma == 0(the default state, and every code path in the unpatched build) the patched code executes the identical legacy path. The old path is fully retained in theelsebranch, so in its default configuration the patched binary is behaviorally identical to the unpatched binary. This is a feature-staged rollout of a user-mode-access hardening refactor, not a delivered fix for a demonstrable, reachable vulnerability.
2. Vulnerability Summary
- Severity: Informational (no security-relevant change delivered)
- Vulnerability Class: Not applicable. The change is a defense-in-depth user-mode-access refactor (single-capture reads, pointer-alignment checks, structured-exception-guarded copy) staged behind a runtime feature flag. No CWE is asserted because no demonstrable, reachable memory-safety defect is fixed in the shipped default path.
- Affected Functions:
UlpCopySingleMultipleHeader(unpatched0x1400DBD58/ patched0x1400DB3B0),UlpGetMultipleKnownHeader(unpatched0x1400DD284/ patched0x1400DCD90),UlpComputeAuthHeaderSize(unpatched0x1400DB618/ patched0x1400DA0C0),UlReceiveHttpRequestFastIo(unpatched0x140014E50/ patched0x140094DC0). - Root Cause / Nature of Change: The patched build adds a runtime-gated alternate path across the IOCTL structure-parsing functions. The gate is the global
UxKirNewUma, which is not a security check but a Windows feature-staging flag set fromFeature_NewUMAHttpSys__private_IsEnabledDeviceUsageNoInline()during initialization (alongside the otherUxKir*feature/rollback gates). When the flag is clear, the parsers run the same logic as the unpatched build: one coarseUlProbeForReadof the header array, direct reads of each entry's length and value pointer, a per-valueUlProbeAnsiStringreadability probe, a destination bounds check before the write, and amemmovecopy. When the flag is set, the added path pre-validates array alignment (UlpProbeAlignmentOfHeaders, patched0x1400DDAF4), reads each field once throughRtlRead*FromUserhelpers, enforces pointer alignment (ptr & 7for 64-bit /ptr & 3for 32-bit) returningSTATUS_INVALID_PARAMETER(0xC000000D), re-validates each value pointer (UlProbeAlignmentAnsiString, patched0x1400D4D74), and copies throughUxTryCopyFromMode. Because the legacy path is retained verbatim in theUxKirNewUma == 0branch and the flag is off by default, the patched binary as shipped behaves identically to the unpatched binary. The legacy path already probes each value pointer before copying and performs a destination bounds check (if ( a7 < *v13 + v34 + 3 )/&v13[v19 + *v23 + 3] <= &a8[a7]) before the write; the header length is a 16-bit field. No out-of-bounds primitive is demonstrable from either build's default behavior. - Attacker-Reachable Entry Point & Data Flow: These functions are reached through the HTTP Server API (
HttpSendHttpResponse/HttpReceiveHttpRequest/HttpSetServiceConfiguration→NtDeviceIoControlFile→ the driver's IOCTL dispatch → the request/response structure handlers). The data flow is real, but the added validation is not active in the default configuration and no demonstrable defect exists in the retained legacy path, so this reachability does not establish a delivered fix.
3. Pseudocode Diff
UlpCopySingleMultipleHeader — unpatched 0x1400DBD58 vs patched 0x1400DB3B0
The unpatched function has a single path. The patched function keeps that exact path under if ( UxKirNewUma == 0 ) and adds a second path under else for the feature-on state.
// === UNPATCHED 0x1400DBD58 (single path) ===
v14 = IoIs32bitProcess(a1);
UlProbeForRead(v9, v10 * (... ? 8 : 16), v14 ? 4 : 8, a2); // one coarse probe
for ( i = 0; i < a3; ++i ) {
v17 = &v9[16 * i]; // 64-bit entry, array base not alignment-checked
v18 = *((volatile void **)v17 + 1); // value pointer, captured once into Src
v19 = *(unsigned __int16 *)v17; // length, captured once
if ( (_WORD)v19 != 0 ) {
UlProbeAnsiString(v18, v19, v16); // per-value readability probe
if ( &v13[v19 + *v23 + 3] <= &a8[a7] ) { // destination bounds check
memmove(v13, v23 + 16, *v23); // copy name
memmove(v20 + 1, Src, v19); // copy value via memmove
v12 = UlSetParsedHeader(a6, v23 + 64, *v23, v20 + 1, v19);
} else return STATUS_INSUFFICIENT_RESOURCES;
}
}
// === PATCHED 0x1400DB3B0 ===
if ( UxKirNewUma == 0 ) {
// identical to the unpatched single path above (UlProbeForRead, direct reads,
// UlProbeAnsiStringOld, destination bounds check, memmove name + memmove value)
} else {
v12 = UlpProbeAlignmentOfHeaders(a1, a3, a4); // added array-wide alignment pre-validation
if ( v12 < 0 ) return v12;
for ( j = 0; j < a3; ++j ) {
UShortFromUser = RtlReadUShortFromUser(&v9[16*j]); // single-capture length (guarded)
Ptr32FromMode = RtlReadULong64FromUser(v26 + 8); // single-capture value ptr (guarded)
if ( UShortFromUser != 0 ) {
v12 = UlProbeAlignmentAnsiString(Ptr32FromMode, UShortFromUser); // per-value alignment+probe
if ( v12 < 0 ) return v12;
if ( &v14[UShortFromUser + 3 + *Src] > &a8[a7] ) return STATUS_INSUFFICIENT_RESOURCES;
memmove(v14, Src + 16, *Src); // copy name
v12 = UxTryCopyFromMode(v27 + 1, Ptr32FromMode, UShortFromUser); // fault-tolerant value copy
v12 = UlSetParsedHeader(a6, Src + 64, *Src, v27 + 1, UShortFromUser);
}
}
}
The two branches differ only by the added alignment/single-capture/fault-tolerant-copy logic; the legacy branch is retained unchanged. Note the legacy branch already performs the destination bounds check and the per-value probe present in the unpatched build.
4. Assembly Analysis
The following instructions are real and present in the unpatched build. They are the legacy path that is retained verbatim in the patched build's UxKirNewUma == 0 branch; they are not evidence of a delivered fix.
Unpatched UlpGetMultipleKnownHeader (0x1400DD284)
00000001400DD2B2 mov r14, [rdx+8] ; embedded descriptor ptr
00000001400DD300 movups xmm1, xmmword ptr [r14] ; direct read of the descriptor
00000001400DD366 cmp cx, ax ; count < 0x2710 (10000)
00000001400DD3D0 cmp esi, 0x1d ; index <= 29
In the patched UlpGetMultipleKnownHeader (0x1400DCD90) the descriptor pointer from [a2+8] is alignment-checked (ptr & 7 for 64-bit, ptr & 3 for 32-bit) returning 0xC000000D, and the 0x18-byte descriptor is read field-by-field through RtlReadULongFromUser / RtlReadUShortFromUser — but only inside the if ( UxKirNewUma != 0 ) branch. The if ( UxKirNewUma == 0 ) branch retains the UlProbeForRead + direct-read logic. The count < 0x2710 and index <= 0x1d checks exist in both builds.
Unpatched UlpCopySingleMultipleHeader (0x1400DBD58) — copy loop
00000001400DBE4F mov rcx, [rax+8] ; value pointer load (captured once)
00000001400DBED5 call memmove ; copy value
In the patched build these instructions remain in the UxKirNewUma == 0 branch; the UxKirNewUma != 0 branch substitutes RtlReadULong64FromUser for the value-pointer load and UxTryCopyFromMode for the value memmove, and adds UlpProbeAlignmentOfHeaders before the loop and UlProbeAlignmentAnsiString per entry.
5. Trigger Conditions
There is no demonstrable security trigger. The functions are reachable from the HTTP Server API IOCTL surface (HttpInitialize(HTTP_INITIALIZE_SERVER) → queue handle → HttpSendHttpResponse / HttpReceiveHttpRequest / HttpSetServiceConfiguration), but in both builds' default path each header entry's value pointer is validated by UlProbeAnsiString and the destination write is guarded by the bounds check &v13[v19 + *v23 + 3] <= &a8[a7]; the entry length is a 16-bit field. No input has been shown to reach an out-of-bounds read or write in either build. The only behavioral difference requires the staged feature Feature_NewUMAHttpSys to be enabled at runtime, which does not change the shipped default behavior.
6. Exploit Primitive & Development Notes
No exploit primitive is demonstrable. The legacy path retained in both builds probes each value pointer before the copy and bounds-checks the destination before the write, and the length field is 16-bit. Neither an out-of-bounds read (information disclosure) nor an out-of-bounds write (pool corruption) has been shown to be reachable in the default path, so no primitive, grooming strategy, or mitigation-bypass claim is asserted.
7. Observation Playbook
The addresses below are real and can be used to observe where the feature-gated path diverges. They are diagnostic anchors for the code difference, not a proof-of-concept for a vulnerability.
- Divergence points:
text bp http+0xDBD58 ; UlpCopySingleMultipleHeader entry (patched 0x1400DB3B0) bp http+0xDBED5 ; legacy value memmove (retained under UxKirNewUma == 0) bp http+0xDD284 ; UlpGetMultipleKnownHeader entry (patched 0x1400DCD90) - What to inspect:
0x1400DBE4F(mov rcx, [rax+8]): per-entry value-pointer load in the legacy path.0x1400DBED5(call memmove): legacy value copy; in the feature-on path this is replaced byUxTryCopyFromMode.0x1400DD2B2(mov r14, [rdx+8]) /0x1400DD300(movups [r14]): legacy descriptor read; the feature-on path alignment-checksr14and reads viaRtlRead*FromUserfirst.- Runtime gate:
UxKirNewUma(a data global) is 0 unlessFeature_NewUMAHttpSys__private_IsEnabledDeviceUsageNoInline()returns true; the added checks execute only when it is nonzero. - Struct/offset notes:
- Header array count validation:
< 0x2710(10000) in both builds. - Known-header index validation:
<= 0x1d(29) in both builds. - 64-bit array entry stride:
0x10bytes.
8. Changed Functions — Full Triage
UlpCopySingleMultipleHeader(unpatched0x1400DBD58/ patched0x1400DB3B0) (response header string builder) | Similarity: 0.6055 | Not security-relevant as delivered. Patched adds aUxKirNewUma-gated path (array alignment pre-validationUlpProbeAlignmentOfHeaders, single-capture readsRtlReadUShortFromUser/RtlReadULong64FromUser, per-valueUlProbeAlignmentAnsiString, andUxTryCopyFromModein place ofmemmove). TheUxKirNewUma == 0branch retains the unpatched logic verbatim, including the destination bounds check and per-value probe.UlpGetMultipleKnownHeader(unpatched0x1400DD284/ patched0x1400DCD90) (known-header descriptor validator) | Similarity: 0.6512 | Not security-relevant as delivered. Patched addsptr & 7/ptr & 3alignment validation on the descriptor pointer at[a2+8](returning 0xC000000D) and single-captures the 0x18-byte descriptor viaRtlReadULongFromUser/RtlReadUShortFromUser, but only underUxKirNewUma != 0. Thecount < 0x2710andindex <= 0x1dchecks exist in both builds; the legacyUlProbeForRead+ direct-read path is retained.UlReceiveHttpRequestFastIo(unpatched0x140014E50/ patched0x140094DC0) (fast-I/O HTTP request receive handler) | Similarity: 0.6071 | Not security-relevant as delivered. Patched adds a top-level structure capture (UxCaptureUserMemory,0x140030D20) andUxKirNewUma-gated alignment checks; the legacy inline parse is retained in theUxKirNewUma == 0branch.UlpComputeAuthHeaderSize(unpatched0x1400DB618/ patched0x1400DA0C0) (auth/known-header size accumulator) | Similarity: 0.9078 | Not security-relevant as delivered. Patched routes each entry through single-capture helpers (RtlReadULongFromUser/RtlReadUShortFromUser) underUxKirNewUma != 0; the legacy direct-read accumulation is retained in theUxKirNewUma == 0branch. The scheme-name validator is the same function, rebased.UxSslConnectionControl(unpatched0x14009B0A0/ patched0x14009C080) andUlSendResponseOrEntityBodyFastIoOld(unpatched0x14009ECF0/ patched0x14009FDA0) (large HTTP parsers) | Similarity: 0.9502 | Behavioral (feature-staging). These referenceUxKirNewUmaat multiple sites, threading the same feature-gated path into the primary request/response processing surfaces; the remaining diff is helper-rename and ETW/globals rebasing. Old path retained in each case.
9. Unmatched Functions
- Removed: None.
- Added: None.
(The change is delivered by modifying existing structure-parsing functions and gating an alternate capture/validation path behind the staged feature flag
UxKirNewUma, with the legacy path retained. No IOCTL sanitizer functions are added or removed.)
10. Confidence & Caveats
- Confidence Level: High. The
UxKirNewUmagate is assigned fromFeature_NewUMAHttpSys__private_IsEnabledDeviceUsageNoInline()at init, and every affected function retains its legacy path in theUxKirNewUma == 0branch, as confirmed in both the disassembly and the decompiled output. The added path is not active in the default configuration. - Assessment: This is a feature-staged (
Feature_NewUMAHttpSys, "new user-mode access") hardening refactor of the IOCTL structure parsers, rolled out behind a runtime flag with the old path retained. It is defense-in-depth robustness work (single-capture reads, pointer-alignment enforcement, fault-tolerant copy), not a fix for a demonstrable, reachable memory-safety vulnerability in the shipped default path. Severity: Informational; no CWE asserted. - Assumptions Made: Entry points (
HttpSendHttpResponse,HttpReceiveHttpRequest,HttpSetServiceConfiguration) are inferred from the structure-parsing behavior ofhttp.sysand the Windows HTTP API; symbols are stripped and dispatch is function-pointer routed. This does not affect the verdict, which rests on the retained legacy path and the feature-flag gate.