EDK II - Improper Validation of Array Index in PeiUsbGetAllConfiguration
The function PeiUsbGetAllConfiguration parses USB configuration descriptors from a physically connected USB device (EXTERNAL_UNTRUSTED) and uses two fields from the untrusted descriptor as array indices into fixed-size arrays in the page-allocated PEI_USB_DEVICE structure without proper runtime bounds validation.
- Advisory
- BYTERAY-2026-0225
- CWE
- CWE-129
- 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 MdeModulePkg/Bus/Usb/UsbBusPei/UsbPeim.c.
- Credit
- Argus-Systems
Executive Summary
The function PeiUsbGetAllConfiguration parses USB configuration descriptors from a physically connected USB device (EXTERNAL_UNTRUSTED) and uses two fields from the untrusted descriptor as array indices into fixed-size arrays in the page-allocated PEI_USB_DEVICE structure without proper runtime bounds validation. NumInterfaces (UINT8, range 0–255, from USB_CONFIG_DESCRIPTOR at Usb.h:128) drives the InterfaceIndex loop (line 812) which indexes into InterfaceDescList[MAX_INTERFACE] where MAX_INTERFACE=8 (line 833) — with no bounds check. NumEndpoints (UINT8, range 0–255, from USB_INTERFACE_DESCRIPTOR) drives the Index loop (line 845); the only validation is ASSERT(NumOfEndpoint <= MAX_ENDPOINT) at line 843, which is a no-op in RELEASE builds. Index then writes to EndpointDesc[MAX_ENDPOINT] (line 863, size 16) and EndpointDescList[InterfaceIndex][Index] (line 866, size [8][16]).
A malicious USB device supplying NumInterfaces > 8 or NumEndpoints > 16 causes out-of-bounds writes past these arrays during the PEI phase. PEI_USB_DEVICE is allocated as sizeof (PEI_USB_DEVICE) / EFI_PAGE_SIZE + 1 pages (one page on x64), and the only limits on the loop counts are TotalLength <= 1024 and GetExpectedDescriptor finding real 9-byte interface and 7-byte endpoint descriptors in the buffer. About 110 empty interface descriptors followed by one endpoint descriptor fit, which puts the EndpointDescList[110][0] write about 14 KB past the start of the array and outside the allocation, into other PEI memory. The written values are pointers into PeiUsbDevice->ConfigurationData: the attacker controls the bytes they point to and chooses the write offsets (8-byte stride on x64), but not the pointer values themselves.
The same unvalidated counts are used again after PeiConfigureUsbDevice returns. In PeiHubEnumeration (lines 326, 344, 346) and PeiUsbEnumeration (lines 540, 558, 560), the loops for the extra interfaces read InterfaceDescList[InterfaceIndex] and EndpointDescList[InterfaceIndex][EndpointIndex] and write EndpointDesc[EndpointIndex] for InterfaceIndex < NumInterfaces and EndpointIndex < NumEndpoints with no bounds check. These reads go out of bounds of the arrays; the EndpointDesc write stays inside the one-page copy allocated at lines 330-335 / 544-549.
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 (
MdeModulePkg) - Affected commit:
70741a15d9a714323a5598eac47727e60507e22f(master, 2026-06-21). The code is present on that commit and later revisions that have not changed it. - Location:
MdeModulePkg/Bus/Usb/UsbBusPei/UsbPeim.c:833 - Function:
PeiUsbGetAllConfiguration - Category: other
Technical Details
Controllability: EXTERNAL_UNTRUSTED — Confidence: High
Data Flow
The ultimate source is a USB peripheral physically connected during the PEI (Pre-EFI Initialization) phase. The firmware reads the device's self-reported configuration descriptor via PeiUsbGetDescriptor (line 740–747) into PeiUsbDevice->ConfigurationData[1024]. ConfigDesc is then assigned to point at this attacker-controlled data (line 807). The InterfaceIndex loop (line 812) uses ConfigDesc->NumInterfaces as its bound with no check against MAX_INTERFACE (8). This leads to an OOB write into InterfaceDescList[8] at line 833. Separately, NumOfEndpoint is read from InterfaceDescList[InterfaceIndex]->NumEndpoints (line 842–843) and only guarded by ASSERT(NumOfEndpoint <= MAX_ENDPOINT), which is a no-op in RELEASE builds. The subsequent loop (line 845) writes to EndpointDesc[16] at line 863 and EndpointDescList[8][16] at line 866, corrupting the following fields (Translator, Tier) and, with enough descriptors, memory past the end of the one-page EfiBootServicesCode allocation.
Affected Callers
PeiConfigureUsbDevice(line 685)PeiHubEnumeration(line 300), devices behind a hubPeiUsbEnumeration(line 514), devices on a root portPeimInitializeUsb(line 150)
Sanitization / Security Controls
Three validation points were identified, all ineffective for this vulnerability:
- ConfigDescLength header check (line 762–764): Only validates that
TotalLengthcovers the config descriptor header — does not validateNumInterfacesorNumEndpointsagainst array bounds. - ConfigDescLength buffer-size check (line 769–771): Caps total buffer size at 1024 bytes but does not constrain the index field values.
ASSERT(NumOfEndpoint <= MAX_ENDPOINT)(line 843): Compiles to a no-op in RELEASE firmware builds. Even in DEBUG builds, it only halts — it does not clamp the value or skip the write.
Additionally, GetExpectedDescriptor validation (lines 794–804, 816–826, 849–859) checks that expected descriptor types/lengths exist in the buffer but does not constrain NumInterfaces or NumEndpoints against the fixed array sizes. The interface copy loops in PeiHubEnumeration (line 326-347) and PeiUsbEnumeration (line 540-561) have no check at all. No authentication of USB devices exists at the PEI phase.
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: Medium
Reproduction
- Attacker constructs a malicious USB device (e.g., programmable USB microcontroller such as a Teensy, Raspberry Pi Pico, or modified BadUSB/Rubber Ducky) configured to present crafted USB descriptors.
- The malicious device's configuration descriptor sets
NumInterfacesto a value > 8 (e.g., 110) and contains that many 9-byte interface descriptors, and/or an interface descriptor setsNumEndpointsto a value > 16 followed by that many 7-byte endpoint descriptors, all within the 1024-byteTotalLengthlimit. Each loop stops at the first missing descriptor, after the earlier writes have already happened. - The last interface carries one endpoint descriptor, so the write to
EndpointDescList[InterfaceIndex][0]lands far past the array. - Attacker plugs the device into the target machine and powers it on (or forces a reboot) on a platform that runs UsbBusPei.
- During PEI phase,
PeimInitializeUsbcallsPeiUsbEnumeration, which detects the device, allocates a one-pagePEI_USB_DEVICEblock, and callsPeiConfigureUsbDevice→PeiUsbGetAllConfiguration. PeiUsbGetAllConfigurationreads the crafted descriptor and iterates beyond array bounds, writing pointers intoConfigurationDatainto the rest of the structure and the PEI memory past the allocation.- The corrupted PEI memory causes a crash or hang during boot, or further misbehaviour depending on what was overwritten.
Impact
A malicious USB device attached at boot, on a platform that includes UsbBusPei (for example for recovery boot), causes out-of-bounds writes of pointer values into the PEI_USB_DEVICE allocation and the PEI memory beyond it, at offsets the device chooses. This corrupts PEI memory and can at least crash or hang the boot. Turning it into code execution depends on what lies in the overwritten memory, since the written values are addresses inside ConfigurationData and not arbitrary values.
Remediation and Mitigation
As of commit 70741a15d9a714323a5598eac47727e60507e22f this issue is unfixed upstream and no CVE has been assigned. Validate the array index against the number of elements before using it to read or write the array. 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.