EDK II - Integer Overflow / Heap Out-of-Bounds Write in XenStoreProcessMessage
XenStoreProcessMessage reads an xsd_sockmsg header (containing a 32-bit Header.len field) from the XenStore shared-memory ring, which is populated by the Xen host/dom0 toolstack.
- Advisory
- BYTERAY-2026-0235
- CWE
- CWE-190, CWE-787
- 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/XenBusDxe/XenStore.c.
- Credit
- Argus-Systems
Executive Summary
XenStoreProcessMessage reads an xsd_sockmsg header (containing a 32-bit Header.len field) from the XenStore shared-memory ring, which is populated by the Xen host/dom0 toolstack. The value Message->Header.len is then used directly in the expression AllocatePool(Message->Header.len + 1) with no overflow validation, no ASSERT, and no size cap. When Header.len == 0xFFFFFFFF, the +1 wraps to 0 in 32-bit unsigned arithmetic, and AllocatePool(0) returns a valid non-NULL pointer to a zero-size buffer per the EDK2 memory allocation contract. Subsequent operations write host-fed ring data into this under-allocated buffer, producing a linear heap out-of-bounds write with host-controlled contents. The same unchecked Header.len also causes a NULL pointer write: the return value of AllocatePool at line 635 is never checked, so a large Header.len that does not wrap (e.g. 0x80000000, more than the guest has free) makes the allocation fail and XenStoreReadStore copies ring data through the NULL Body pointer (in OVMF this writes to physical page 0 rather than faulting, unless NULL-page protection is enabled). XENSTORE_PAYLOAD_MAX (4096, xs_wire.h:107) exists but is never used in XenStore.c.
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/XenBusDxe/XenStore.c:635 - Function:
XenStoreProcessMessage - Category:
CWE-190 / CWE-787 (integer overflow → heap out-of-bounds write)
Technical Details
Controllability: SEMI_TRUSTED (hypervisor-controlled shared-memory communication channel) — High confidence.
Data Flow
The XenStore shared-memory ring page (xs.XenStore) is mapped at XenStore.c:1100-1101 via XenHypercallHvmGetParam(HVM_PARAM_STORE_PFN); its contents (rsp[], rsp_cons, rsp_prod) are written by the Xen host/dom0 toolstack and are HOST_HYPERVISOR_CONTROLLED. XenStoreProcessMessage reads the 16-byte xsd_sockmsg header from this ring at line 628 via XenStoreReadStore(&Message->Header, sizeof(Message->Header)), populating Message->Header.len (UINT32, per xs_wire.h:80) with whatever value the host placed in the ring. At line 635, AllocatePool(Message->Header.len + 1) performs 32-bit unsigned arithmetic with no overflow check; when Header.len == 0xFFFFFFFF, the sum wraps to 0, and AllocatePool(0) returns a valid non-NULL zero-size buffer. XenStoreReadStore(Body, Message->Header.len) at line 636 then loops writing host-fed ring data out of bounds via CopyMem at line 582. Finally, Body[Message->Header.len] = '\0' at line 644 performs a single-byte OOB write at offset 0xFFFFFFFF. For a large non-wrapping Header.len, AllocatePool returns NULL and the same CopyMem at line 582 writes through the NULL Body.
Affected Callers
XenStoreReadReply(line 762),XenStoreWaitWatch(line 986)
Sanitization / Security Controls
XenStoreCheckIndexes(lines 566-569) — verifies ring index sanity (Cons/Prod within bounds). Ineffective: does not validateHeader.lenor cap the allocation size; orthogonal to the integer overflow.- Status check after
XenStoreReadStore(lines 637-642) — Ineffective: the OOB write has already occurred by the time status is checked. - Overflow check on
Header.len + 1at line 635 — Absent. NoASSERT, no size cap, no overflow check exists between readingHeader.lenand using it inAllocatePool. This missing check is the root cause. - NULL check on
BodyafterAllocatePoolat line 635: Absent.
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 (requires already-compromised host/dom0).
Reproduction
- Compromised dom0 places a crafted
xsd_sockmsgheader in the XenStore ring withHeader.len = 0xFFFFFFFF. - Guest UEFI calls
XenStoreProcessMessage, which reads the header. AllocatePool(0)returns a valid zero-size buffer pointer.XenStoreReadStore(Body, 0xFFFFFFFF)loops copying attacker data from the ring past the allocation, causing massive heap corruption.Body[0xFFFFFFFF] = '\0'performs a single-byte OOB write.- Alternatively, set
Header.len = 0x80000000:AllocatePoolfails and returnsNULL, andXenStoreReadStorecopies ring data through the NULLBody.
Impact
Host-controlled heap memory corruption (or a write through NULL to physical page 0) within the guest UEFI firmware during DXE, in the XenBusDxe driver built only into OvmfXen. Note: dom0 already holds capabilities (direct memory access, VM lifecycle control) that subsume this impact — this is a defense-in-depth concern rather than a meaningful privilege boundary crossing.
Remediation and Mitigation
As of commit 70741a15d9a714323a5598eac47727e60507e22f this issue is unfixed upstream and no CVE has been assigned. Perform the size arithmetic in a wide enough type and reject values that do not fit the destination, using the Safe integer helper routines. Validate the computed destination offset and length against the real buffer size before writing, using width-safe arithmetic so the check cannot be bypassed by overflow. 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.