EDK II - Missing Security-Relevant Feedback in BspApAcceptMemoryResourceRange
In the multi-vCPU boot-time memory-acceptance path, BspApAcceptMemoryResourceRange calls TdAcceptPages (which performs TDCALL(TDACCEPTPAGE)) and stores the result in Status.
- Advisory
- BYTERAY-2026-0234
- CWE
- CWE-1429
- 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 OvmfPkg/IntelTdx/TdxHelperLib/SecTdxHelper.c.
- Credit
- Argus-Systems
Executive Summary
In the multi-vCPU boot-time memory-acceptance path, BspApAcceptMemoryResourceRange calls TdAcceptPages (which performs TDCALL(TDACCEPTPAGE)) and stores the result in Status. On error the loop exits, but line 223 unconditionally returns EFI_SUCCESS, discarding the error entirely. For APs (CpuIndex != 0) the error is always swallowed because the remainder-block error-return at lines 216–218 is BSP-only. MpAcceptMemoryResourceRange compounds this: at line 334 it calls BspApAcceptMemoryResourceRange and ignores its return value, then returns EFI_SUCCESS at line 338. The host hypervisor controls the outcome of TDCALL(TDACCEPTPAGE); a refusal or fault is silently discarded, so the guest loses fail-closed behavior and proceeds with unaccepted memory. Under TDX semantics, a guest cannot use an unaccepted (PENDING) page — every access faults (#VE or TD-exit) — so the realistic impact is a deferred fault/DoS rather than a confidentiality break. Additional sibling instances of the same ASSERT-as-error-handling anti-pattern exist at SecTdxHelper.c:299-300 (MpAcceptMemoryResourceRange unaligned-head call) and SecTdxHelper.c:776-777 (AcceptMemoryForAPsStack result), which should be remediated together.
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 (
OvmfPkg) - Affected commit:
70741a15d9a714323a5598eac47727e60507e22f(master, 2026-06-21). The code is present on that commit and later revisions that have not changed it. - Location:
OvmfPkg/IntelTdx/TdxHelperLib/SecTdxHelper.c:223 - Function:
BspApAcceptMemoryResourceRange/MpAcceptMemoryResourceRange - Category:
error_handling
Technical Details
Controllability: EXTERNAL_UNTRUSTED — confidence: high
Data Flow
The data flow originates at MdePkg/Library/TdxLib/AcceptPages.c:110, where TdCall(TDCALL_TDACCEPTPAGE, ...) is issued — the host/VMM controls the outcome across the confidential-computing trust boundary. On genuine failure, the primitive correctly maps the result to EFI_INVALID_PARAMETER (AcceptPages.c:161–173) and returns it. The multi-vCPU caller BspApAcceptMemoryResourceRange stores this into local Status at SecTdxHelper.c:197 inside the main acceptance loop. The only control — ASSERT(!EFI_ERROR(Status)) at line 198 — is a no-op in release builds. Control then reaches the vulnerable sink at line 223 (return EFI_SUCCESS), which unconditionally discards the host-influenced error; for APs this sink is always reached because the BSP-only remainder return at 216–218 is never taken. MpAcceptMemoryResourceRange then ignores BspApAcceptMemoryResourceRange's return at line 334 and itself returns EFI_SUCCESS at line 338. The EFI_ERROR guard in AcceptMemory (lines 502–505) is dead in the multi-vCPU path because Status is always success. Finally, ProcessHobList (lines 784–787) propagates the always-success status to the SEC core, and boot proceeds over unaccepted memory. Under TDX architecture, unaccepted (PENDING) pages are encrypted under the TD's private key and cannot be read by the host in plaintext; every guest access to a PENDING page faults (#VE when SEPT_VE_DISABLE=0, or TD-exit/termination when SEPT_VE_DISABLE=1). The real impact is loss of fail-closed behavior: the firmware silently swallows a host-controlled acceptance failure instead of hard-terminating (contrast the SEV-SNP path which calls SnpPageStateFailureTerminate + CpuDeadLoop()), leading to a deferred fault/DoS when DXE or the OS first touches an unaccepted page.
Affected Callers
MpAcceptMemoryResourceRangeAcceptMemoryProcessHobListTdxHelperProcessTdHob
Sanitization / Security Controls
- ASSERT-based error reporting (
SecTdxHelper.c:198, 215, 300, 503, 777, 785): Disabled in production firmware builds (PcdDebugPropertyMaskwithoutDEBUG_PROPERTY_ASSERT_ENABLED). Provides no security feedback to the boot path. - AcceptMemory EFI_ERROR break (
SecTdxHelper.c:502-505): Bypassed —MpAcceptMemoryResourceRangealways returnsEFI_SUCCESS. Effective only on the single-vCPU path. - TdAcceptPages internal retry/terminate (
AcceptPages.c:141-160): CoversOPERAND_BUSYonly; genuine host-refusal errors returnEFI_INVALID_PARAMETER, which the caller then discards. No control exists between the primitive and the vulnerable sink.
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
- The malicious host intercepts the guest SEC-phase boot and receives
TDCALL(TDACCEPTPAGE)requests for the guest's memory regions. - For target pages, the host causes
TDCALL(TDACCEPTPAGE)to return a non-success result falling into the "Other errors" branch ofAcceptPages.c:161-173. TdAcceptPagesreturnsEFI_INVALID_PARAMETER; the acceptance loop exits.- The unconditional
return EFI_SUCCESSat line 223 swallows the error (always for APs; for BSP when no remainder block applies). MpAcceptMemoryResourceRangeignores the return and returnsEFI_SUCCESS; theEFI_ERRORguard never fires;ProcessHobListreturns success; boot proceeds normally.- The pages that were not accepted remain PENDING in the Secure EPT while the firmware treats them as usable memory.
- The first guest access to one of those pages (later in boot or in the OS) faults with #VE or a TD exit, and the TD crashes or is terminated at that later point instead of at the failed acceptance.
Impact
Loss of fail-closed behavior in the TDX guest firmware. A host-caused page acceptance failure is not reported, and boot continues with memory the firmware believes is accepted. Pages that were never accepted stay PENDING in the Secure EPT, so the host cannot read or replace their private contents this way; the first guest access to such a page faults (#VE or TD exit), which results in a deferred crash or termination of the TD later in boot or at OS runtime. The effect is a denial of service that is harder to diagnose, which a malicious host can already cause by other means.
Remediation and Mitigation
As of commit 70741a15d9a714323a5598eac47727e60507e22f this issue is unfixed upstream and no CVE has been assigned. Validate the attacker-controlled value described above before it is used; the specific missing control is identified under Technical Details. 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.