← ALL ADVISORIES

MEDIUM BYTERAY-2026-0287Disclosed

EDK II - Out-of-Bounds Read / Out-of-Bounds Write in PeCoffLoaderRelocateImage

The PE/COFF relocation apply loop in PeCoffLoaderRelocateImage resolves each fixup target via PeCoffLoaderImageAddress, which validates only that the base byte offset satisfies Address < ImageSize + TeStrippedOffset.

Advisory
BYTERAY-2026-0287
CWE
CWE-125, 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 MdePkg/Library/BasePeCoffLib/BasePeCoff.c.
Credit
Argus-Systems

Executive Summary

The PE/COFF relocation apply loop in PeCoffLoaderRelocateImage resolves each fixup target via PeCoffLoaderImageAddress, which validates only that the base byte offset satisfies Address < ImageSize + TeStrippedOffset. It does not account for the access width of the relocation type being applied. The subsequent relocation handlers dereference fixed multi-byte widths (4 bytes for HIGHLOW, 8 bytes for DIR64 on X64, 16 bytes for MARK_LA on LoongArch) from the returned pointer without per-byte bounds verification. An attacker who supplies a crafted PE/COFF image can set a relocation RVA in the range [ImageSize - access_width + 1, ImageSize - 1], which passes the base-byte check but causes the multi-byte dereference to read and write past the end of the allocated buffer.

The relocation directory fields RelocBase->VirtualAddress and *Reloc & 0xFFF are read directly from untrusted PE/COFF image bytes (line 1120). PeCoffLoaderImageAddress (line 917) checks only if (Address >= ImageContext->ImageSize + TeStrippedOffset) return NULL — validating only the base byte. The returned pointer is then dereferenced as *(UINT32*)Fixup (HIGHLOW, line 1150) or *(UINT64*)Fixup (DIR64, line 1161), each reading and writing multiple bytes past the validated offset. The buffer is allocated as exactly ImageSize bytes (page-aligned per PE/COFF spec) at Image.c:655–658. A concrete trigger: set relocation RVA = ImageSize - 1; the check (ImageSize - 1) >= ImageSize is false (passes); *(UINT64*)Fixup reads 7 bytes past the buffer end. This function is confirmed present in the OvmfPkgX64 build, making DIR64 and HIGHLOW reachable on X64 platforms.

The same base-byte-only check is used by PeCoffLoaderRelocateImageForRuntime (line 1746), which re-applies the relocations of registered runtime images during SetVirtualAddressMap. At line 1902 it computes Fixup = PeCoffLoaderImageAddress(&ImageContext, RelocBase->VirtualAddress + (*Reloc & 0xFFF), 0), and the HIGHLOW (lines 1930-1933) and DIR64 (lines 1940-1943) handlers read 4 or 8 bytes at Fixup and write them back when they match the saved FixupData value. FixupData holds the value written during the load-time relocation (lines 1155, 1166), so for the same out-of-bounds fixup it normally matches and the write happens again, up to 7 bytes past the image end.

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 (MdePkg)
  • Affected commit: 70741a15d9a714323a5598eac47727e60507e22f (master, 2026-06-21). The code is present on that commit and later revisions that have not changed it.
  • Location: MdePkg/Library/BasePeCoffLib/BasePeCoff.c:1120 (and :1902 in PeCoffLoaderRelocateImageForRuntime)
  • Function: PeCoffLoaderRelocateImage
  • Category: CWE-125 / CWE-787 (Out-of-bounds Read / Out-of-bounds Write)

Technical Details

Controllability: EXTERNAL_UNTRUSTED — confidence: high

Data Flow

The attack begins with an external PE/COFF image on untrusted boot media (USB, PXE, HTTP boot, removable disk). The EFI LoadImage() boot service (CoreLoadImage, Image.c:1544) accepts a FilePath from the boot manager and reads raw image bytes via GetFileBufferByFilePath (Image.c:1246). The Security2 FileAuthentication check (Image.c:1279) runs before CoreLoadPeImage (line 1385), and Image.c:1316 lets only EFI_SECURITY_VIOLATION through. DxeImageVerificationLib returns EFI_SECURITY_VIOLATION for a failed signature only under the DEFER_EXECUTE_ON_SECURITY_VIOLATION policy (DxeImageVerificationLib.c:2070-2071); with the default policy (0x04, SecurityPkg.dec:305/318/378) it returns EFI_ACCESS_DENIED and the image is never loaded. The image buffer is allocated as exactly ImageSize bytes (page-aligned per PE/COFF spec) at Image.c:655–658. PeCoffLoaderRelocateImage is called at Image.c:774. The relocation loop at BasePeCoff.c:1120 computes Fixup = PeCoffLoaderImageAddress(ImageContext, RelocBase->VirtualAddress + (*Reloc & 0xFFF), TeStrippedOffset) using attacker-controlled relocation directory fields. The base-byte-only check at line 917 passes for RVA = ImageSize - 1. The vulnerable sink at line 1163 performs *Fixup64 = *Fixup64 + (UINT64)Adjust — an 8-byte read+write that extends 7 bytes past the buffer end for a DIR64 relocation. If the image is a runtime driver, CoreLoadPeImage registers it with its FixupData (Image.c:812-816), and at SetVirtualAddressMap RuntimeDriverSetVirtualAddressMap (Runtime.c:328) calls PeCoffLoaderRelocateImageForRuntime, which repeats the out-of-bounds read+write at lines 1942-1943.

Affected Callers

  • PeCoffLoaderLoadImage
  • CoreLoadPeImage (MdeModulePkg/Core/Dxe/Image/Image.c)
  • RuntimeDriverSetVirtualAddressMap (MdeModulePkg/Core/RuntimeDxe/Runtime.c, via PeCoffLoaderRelocateImageForRuntime)

Sanitization / Security Controls

  • Secure Boot / Security2 FileAuthentication (Image.c:1279, Image.c:1316): Effective under the default DENY policy, which returns EFI_ACCESS_DENIED before the image is loaded or relocated. Only the non-default DEFER_EXECUTE_ON_SECURITY_VIOLATION policy returns EFI_SECURITY_VIOLATION, which lets an unsigned image reach relocation before StartImage gating. On platforms without Secure Boot, no verification occurs, but the attacker's image then runs anyway.
  • PeCoffLoaderImageAddress bounds check (BasePeCoff.c:917): Insufficient — validates only the base byte offset, not the multi-byte access width.
  • RelocBase->SizeOfBlock sanity check (BasePeCoff.c:1094–1108): Effective for block size validation only — prevents reading past the relocation directory but does not validate fixup target addresses within the image.

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: Medium
Realism: Low

Reproduction

  1. Attacker has physical access or can influence PXE/HTTP boot configuration; target boots from external media; image has non-stripped relocations (normal case). The platform either has Secure Boot disabled or uses the DEFER_EXECUTE_ON_SECURITY_VIOLATION policy, under which the image is still loaded and relocated on signature failure.
  2. Craft a PE/COFF EFI image with SizeOfImage page-aligned and a relocation directory entry targeting RVA SizeOfImage - k (where k < 8 for DIR64).
  3. Set relocation type to EFI_IMAGE_REL_BASED_DIR64.
  4. Boot from crafted media; firmware relocates the image and the 8-byte dereference at Fixup corrupts adjacent memory.

Impact

Out-of-bounds read-modify-write of up to 7 bytes past the end of the image buffer at firmware privilege (DXE phase, and again at SetVirtualAddressMap for runtime drivers). The bytes read are not returned to the attacker. This only crosses a security boundary when an unsigned image is loaded and relocated, which requires the non-default DEFER_EXECUTE_ON_SECURITY_VIOLATION image verification policy; under the default policy the image is rejected before relocation, and with Secure Boot disabled the attacker's image already runs with full firmware privilege.

Remediation and Mitigation

As of commit 70741a15d9a714323a5598eac47727e60507e22f this issue is unfixed upstream and no CVE has been assigned. Before reading each field, validate that its full width fits within the remaining bytes derived from the packet or record size, and stop parsing or reject the input when it does not. 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.

References