← ALL ADVISORIES

LOW BYTERAY-2026-0234Disclosed

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

  • MpAcceptMemoryResourceRange
  • AcceptMemory
  • ProcessHobList
  • TdxHelperProcessTdHob

Sanitization / Security Controls

  • ASSERT-based error reporting (SecTdxHelper.c:198, 215, 300, 503, 777, 785): Disabled in production firmware builds (PcdDebugPropertyMask without DEBUG_PROPERTY_ASSERT_ENABLED). Provides no security feedback to the boot path.
  • AcceptMemory EFI_ERROR break (SecTdxHelper.c:502-505): Bypassed — MpAcceptMemoryResourceRange always returns EFI_SUCCESS. Effective only on the single-vCPU path.
  • TdAcceptPages internal retry/terminate (AcceptPages.c:141-160): Covers OPERAND_BUSY only; genuine host-refusal errors return EFI_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

  1. The malicious host intercepts the guest SEC-phase boot and receives TDCALL(TDACCEPTPAGE) requests for the guest's memory regions.
  2. For target pages, the host causes TDCALL(TDACCEPTPAGE) to return a non-success result falling into the "Other errors" branch of AcceptPages.c:161-173.
  3. TdAcceptPages returns EFI_INVALID_PARAMETER; the acceptance loop exits.
  4. The unconditional return EFI_SUCCESS at line 223 swallows the error (always for APs; for BSP when no remainder block applies).
  5. MpAcceptMemoryResourceRange ignores the return and returns EFI_SUCCESS; the EFI_ERROR guard never fires; ProcessHobList returns success; boot proceeds normally.
  6. The pages that were not accepted remain PENDING in the Secure EPT while the firmware treats them as usable memory.
  7. 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.

References