EDK II - Double Free in EfiHttpRequest
In the HTTP CONNECT (proxy) branch of EfiHttpRequest, EndPointUrlMsg is allocated at line 700 and freed at line 716 without being set to NULL.
- Advisory
- BYTERAY-2026-0232
- CWE
- CWE-1341
- Vendor
- TianoCore
- Product
- EDK II
- Reported
- 2026-06-25
- Disclosure
- 2026-10-29
- Affected
- EDK II at commit 70741a15d9a714323a5598eac47727e60507e22f (master, 2026-06-21); affected code in NetworkPkg/HttpDxe/HttpImpl.c.
- Credit
- Argus-Systems
Executive Summary
In the HTTP CONNECT (proxy) branch of EfiHttpRequest, EndPointUrlMsg is allocated at line 700 and freed at line 716 without being set to NULL. EndPointUrlParser is likewise freed at line 717 (HttpUrlFreeParser) without being nulled. When the subsequent HttpGenRequestMessage() call fails — due to EFI_OUT_OF_RESOURCES, LocateProtocol failure, HttpUtilitiesProtocol->Build failure, or EFI_INVALID_PARAMETER when the caller passes a request with HeaderCount == 0 — control reaches the goto Error3 path, which falls through Error2 to Error1, where the NULL-guarded FreePool(EndPointUrlMsg) at lines 825-826 and the EndPointUrlParser cleanup at lines 829-830 re-free the already-released allocations because the dangling pointers remain non-NULL. If the chunks have not been reused in between, the DXE core pool code (CoreFreePoolI, MdeModulePkg/Core/Dxe/Mem/Pool.c:722-729) sees the free-pool signature written by the first free, asserts in DEBUG builds and returns EFI_INVALID_PARAMETER in RELEASE builds. If a chunk was reallocated in between, the second free releases another owner's live buffer, leaving that owner with a dangling pointer.
This advisory is one of a set reported to VulnCheck on 2026-06-25 for coordinated disclosure to the EDK II maintainers. After several follow-ups over the following months no response was received. 126 days after the initial report, ByteRay is publishing the findings in good faith so that affected integrators can assess and remediate them. All findings were identified against EDK II commit 70741a15d9a714323a5598eac47727e60507e22f (master, 2026-06-21).
Affected Products and Versions
- Vendor: TianoCore
- Product: EDK II (
NetworkPkg) - Affected commit:
70741a15d9a714323a5598eac47727e60507e22f(master, 2026-06-21). The code is present on that commit and later revisions that have not changed it. - Location:
NetworkPkg/HttpDxe/HttpImpl.c:716 - Function:
EfiHttpRequest - Category:
CWE-1341 / CWE-415 (Double Free)
Technical Details
Controllability: SEMI_TRUSTED — confidence: high
Data Flow
The proxy URI originates from the HTTP boot option device path, which is built from the HTTP boot configuration form (HttpBootConfig.c:60-135, HttpBootAddBootOption); at HttpBootImpl.c:184, HttpBootParseFilePath extracts ProxyUri from that device path. It is not supplied by DHCP or the network. It is stored in Private->ProxyUri and used by HttpBootConnectProxy (HttpBootClient.c:926), which at HttpBootClient.c:1022-1037 sets RequestData->Base.Method = HttpMethodConnect and invokes HttpIoSendRequest with two headers (Host, User-Agent), ultimately calling EfiHttpRequest (defined at HttpImpl.c:230). Inside the CONNECT branch, EndPointUrlMsg is allocated at line 700 (AllocateZeroPool(URI_STR_MAX_SIZE)). HttpGenRequestMessage is called at line 714; it fails with EFI_INVALID_PARAMETER when a request is passed with HeaderCount == 0 (DxeHttpLib.c:1809), on LocateProtocol or Build failure (DxeHttpLib.c:1826, 1860), or with EFI_OUT_OF_RESOURCES when an allocation fails (DxeHttpLib.c:1835, 1888-1892). The HeaderCount == 0 case is deterministic, but only for a caller of EFI_HTTP_PROTOCOL.Request that passes such arguments; HTTP boot always passes two headers. On failure, EndPointUrlMsg and EndPointUrlParser are freed at lines 716-717 without being nulled (dangling pointers). The error check at lines 742-743 routes control to Error3, which falls through Error2 to Error1, where the NULL-guarded FreePool(EndPointUrlMsg) at lines 825-826 and HttpUrlFreeParser(EndPointUrlParser) at lines 829-830 re-free the dangling non-NULL pointers.
Affected Callers
HttpBootDxe→HttpBootConnectProxy(HttpBootClient.c:926, request built at line 1022) — setsRequestData->Base.Method = HttpMethodConnectand callsHttpIoSendRequest, which invokesEFI_HTTP_PROTOCOL.Request=EfiHttpRequest.- Any consumer of
EFI_HTTP_PROTOCOL.Request()whenHttpMethodConnectis used with a proxy URI.
Sanitization / Security Controls
PcdAllowHttpConnectionsPCD check (HttpImpl.c:422-426) — partial: only gates non-HTTPS URIs; does not prevent the CONNECT/proxy path and does not address the double-free.Request->Methodswitch validation (HttpImpl.c:293-312) requiring non-NULLConnRequest->ProxyUrlfor CONNECT — effective for parameter validation but irrelevant to the memory-corruption defect.HttpUrlGetHostName/Portparsing of the proxy URL (HttpImpl.c:682-698) — partial: validates URL structure but does not prevent the allocation-failure-triggered double-free.- No sanitization points address the dangling-pointer/double-free; error-path cleanup trusts non-NULL pointers that were freed but not nulled.
Reachability and Preconditions
Attacker position and trust level for this issue are stated in the Controllability line and Data Flow above, and the concrete trigger is in Reproduction below.
Exploitability: Low
Realism: Low
Reproduction
- Configure an HTTP boot option with a proxy URI (HTTP boot configuration form), so
HttpBootConnectProxysends a CONNECT request; or, from a DXE driver, callEFI_HTTP_PROTOCOL.RequestwithMethod = HttpMethodConnect, a non-NULLProxyUrlandHeaderCount = 0. - For the HTTP boot path, an allocation failure must occur in
HttpGenRequestMessage(for exampleAllocateZeroPool(MsgSize)atDxeHttpLib.c:1888), which can be simulated with fault injection. For the direct call,HttpGenRequestMessagereturnsEFI_INVALID_PARAMETERatDxeHttpLib.c:1809. - On the error path,
EndPointUrlMsg/EndPointUrlParserare freed atHttpImpl.c:716-717without being nulled, then re-freed atHttpImpl.c:825-830. - In a DEBUG build, the second free hits the
ASSERTinCoreFreePoolI(Pool.c:725). In a RELEASE build it returnsEFI_INVALID_PARAMETERunless the chunk was reallocated after the first free, in which case the new owner's buffer is freed.
Impact
Double free of two pool buffers in DXE. In a DEBUG build it stops at an ASSERT; in a RELEASE build the second free is rejected unless the chunk was reused in between, in which case another component's live buffer is freed and later use of it can corrupt pool memory or crash the firmware. A network attacker cannot trigger it: the proxy URI comes from the HTTP boot configuration, and the failure needs either a pool allocation failure inside HttpGenRequestMessage or a caller of EFI_HTTP_PROTOCOL.Request that is already DXE code passing a CONNECT request with no headers.
Remediation and Mitigation
As of commit 70741a15d9a714323a5598eac47727e60507e22f this issue is unfixed upstream and no CVE has been assigned. After freeing a buffer, clear the pointer and restructure the error path so each allocation is freed exactly once. The specific control that is absent or ineffective is identified in the Sanitization / Security Controls list under Technical Details.
Timeline
- 2026-06-25 - Reported to VulnCheck for coordinated disclosure to the TianoCore / EDK II security team.
- 2026-07 to 2026-10 - Several follow-ups sent; no response received.
- 2026-10-29 - Public disclosure in good faith, 126 days after the initial report.
Credits
Discovered and reported by Argus-Systems.