CVE-2026-20753: Integer Overflow in Slim Bootloader’s ext2 Reader
SecMate’s automated analysis identified this issue using the gpt-oss-120b model, after which SecMate researchers validated the finding and reported it to Intel PSIRT. The vulnerability was published as CVE-2026-20753 / INTEL-SA-01425 on May 12, 2026. Intel rates the issue High with CVSS 4.0 score 8.7, describing it as an integer overflow in Slim Bootloader UEFI firmware that can allow escalation of privilege and local code execution when attack requirements are present [1]
.
Slim Bootloader is Intel’s open-source boot firmware project for systems that want a small, configurable boot path instead of a full general-purpose firmware stack [2] . It initializes platform hardware, uses Intel FSP for silicon initialization, and then hands control to a payload or operating system loader. In practice, that means it may parse filesystems and load boot artifacts before the operating system, kernel hardening, endpoint security, or disk policy is alive.
That pre-OS placement is what makes filesystem parser bugs more interesting than their code footprint suggests.
At a code level, the bug is a classic allocation-size overflow. At a platform level, it matters because the attacker-controlled filesystem image is parsed before the operating system, before most telemetry, and potentially before later boot artifacts become immutable.
From Filesystem Geometry to Heap Geometry
The affected code lives in BootloaderCommonPkg/Library/Ext23Lib/Ext2Fs.c
, Slim Bootloader’s ext2/3/4 reader. The interesting part is that ext2’s filesystem geometry becomes allocator geometry inside the bootloader.
In the vulnerable path, ReadSBlock() reads the on-disk superblock and computes the number of block groups:
1FileSystem->Ext2FsNumCylinder =
2 HOWMANY (Ext2FsBlockCount - Ext2FsFirstDataBlock,
3 Ext2FsBlocksPerGroup);
Those values come from the filesystem image. A crafted image can report a very large block count and a very small BlocksPerGroup, producing a large Ext2FsNumCylinder.
Ext2fsOpen() then used that count directly to size the group descriptor table:
1FileSystem->Ext2FsGrpDes =
2 AllocatePool (sizeof(EXT2GD) * FileSystem->Ext2FsNumCylinder);
3Status = ReadGDBlock (File, FileSystem);
On 32-bit builds, the multiplication can wrap. The allocation then succeeds with a buffer smaller than the number of descriptors the parser believes it has.
The follow-on write is controlled by the same filesystem image. ReadGDBlock() walks the descriptor blocks and copies group descriptors into FileSystem->Ext2FsGrpDes using the original block-group count. In other words, the attacker first shapes the allocation through the superblock, then shapes the overflow stream through the group descriptor table.
This is the core of the bug: a disk-controlled filesystem geometry value crosses from “metadata” into “heap layout” before the OS exists.
Figure 1. The ext2 image follows the usual block-group layout. The Super Block carries the crafted count fields, while the Group Descriptor Table provides the descriptor bytes consumed later by ReadGDBlock().
Exploitability
This is not a network bug. The attacker needs a path to make Slim Bootloader parse a crafted ext2/3 image during boot. Realistic paths include:
- a system configured to boot from removable media such as USB, SD, or eMMC-backed service media
- a local privileged attacker who can modify the boot partition or replace a boot image
- a supply-chain or provisioning path where boot media is assembled from untrusted input
Intel’s CVSS vector reflects that shape: local attack vector, low attack complexity, high privileges required, no user interaction, and attack requirements present [1] . That is a narrow entry point compared with remote exploitation, but the privilege gained is unusually valuable because the parser runs in firmware before the operating system can enforce its own trust boundaries.
Slim Bootloader also makes the 32-bit case a first-class concern. BuildLoader.py defaults to ia32 when no architecture is specified [3]
. Some platforms build X64 payloads, but the default build path and many CI/platform combinations are IA32, which is exactly where a UINTN allocation-size wrap is most direct.
The security boundary is not ordinary user-to-root escalation. The value is crossing from OS-level or provisioning-time control into pre-OS execution, where the attacker may affect boot artifacts before runtime protections, telemetry, or OS policy are active.
The practical exploit path is target-specific, but the high-level strategy is:
- craft ext2 metadata that forces a too-small descriptor allocation
- place controlled group descriptor bytes so the copy corrupts adjacent bootloader heap state
- seek control-flow corruption or alter later bootloader decisions before the OS payload is launched
- use that pre-OS control to influence the kernel, initrd, command line, boot parameters, or a verified container after it has been accepted, depending on the target boot flow
Heap Shape and Mitigations
The OS-loader filesystem path binds Ext23Lib to FullMemoryAllocationLib, an EDK2-style pool allocator, not to the simpler bootloader-core bump allocator [4]
. Small allocations are served from page-sized carved blocks, and leftover chunks are grouped into fixed size-class bins. Each bin has one doubly linked free list: Pool.FreeList[bin] is the list head, and the list may contain zero or many free chunks. Allocated chunks are not on a matching allocated list. They carry a POOL_HEAD and POOL_TAIL, and a chunk only becomes a POOL_FREE list node after it is freed [5]
.
The important point is that the overflow does not need to hit another allocated object first. If the next physical chunk is free, the attacker can corrupt allocator metadata that later list operations trust.
Figure 2. One carved pool block containing an allocated descriptor table followed by free chunks. The lower row shows the logical free list for one size bin; arrows show free-list links, not physical memory order.
The checks are late. When a chunk is freed, CoreFreePoolI() validates the POOL_HEAD signature, the POOL_TAIL signature, and that the size in the head matches the size in the tail. That can catch some corruption during cleanup, but it does not stop ReadGDBlock() from writing past Ext2FsGrpDes in the first place.
If the chunk sitting immediately after the allocated descriptor table is free, its memory currently contains a POOL_FREE structure whose Link field is a raw doubly linked LIST_ENTRY:
1typedef struct {
2 UINT32 Signature; // 'pfr0'
3 UINT32 Index;
4 LIST_ENTRY Link; // ForwardLink, BackLink
5} POOL_FREE;
Because the overflow content is fully attacker-controlled ext2 group-descriptor data, the attacker can overwrite both ForwardLink and BackLink with arbitrary values. There is no safe-linking or runtime free-list integrity check on the allocate path: when the allocator later services another request from the corrupted chunk’s bin, CoreAllocatePoolI() pops the first entry with RemoveEntryList().
RemoveEntryList performs the classic unsafe unlink:
1Entry->ForwardLink->BackLink = Entry->BackLink;
2Entry->BackLink->ForwardLink = Entry->ForwardLink;
If the attacker corrupted Entry->ForwardLink to TargetAddress and Entry->BackLink to Value, the first assignment writes Value to *(TargetAddress + sizeof(UINTN)). The second performs the reciprocal list repair, writing TargetAddress through Value. On IA32 builds, all pointers are 32-bit, so the ext2 descriptor fields map cleanly onto the link pointers. This turns the overflow into a write-what-where-style allocator unlink primitive in a single allocation, with the usual linked-list side effect.
A similar unlink surface exists in the page-coalescing path. If a later FreePool() makes every chunk in the carved page free, CoreFreePoolI() walks the page and removes each free chunk from its bin before returning the page:
1while (Offset < Granularity) {
2 Free = (POOL_FREE *) &NewPage[Offset];
3 RemoveEntryList (&Free->Link);
4 Offset += LIST_TO_SIZE (Free->Index);
5}
If the overflow preserves the free chunk’s Signature and Index while corrupting Link, that removal can reach the same unsafe unlink primitive.
In practice, an exploit would tune the wrapped allocation size so that Ext2FsGrpDes is followed by a free chunk from a predictable bin, then use the group-descriptor overflow to corrupt that chunk’s Link fields. A later allocation from that bin, or a free/coalescing path that removes the corrupted chunk from its free list, can trigger unsafe unlink and provide a strong target-specific exploitation building block. Depending on the platform memory layout and available control-flow or data targets, it may be usable to corrupt function pointers, saved return state, boot variables, or later bootloader decision data.
It is unclear from source alone whether heap pool memory is non-executable on every final platform build. What we did not find is evidence of NX/W^X enforcement in this path: PagingLib builds mappings with present and writable attributes, and the long-mode transition in EnablePaging64.nasm sets EFER.LME without setting EFER.NXE [6]
. If pool memory is executable in the target build, turning the write primitive into code execution becomes much easier: attacker-controlled bytes could already live in a heap buffer, and the write primitive could redirect a later control-flow target to that buffer. If pool memory is not executable, the arbitrary write is still a strong primitive, but exploitation would likely need a target-specific control-flow or data-only path. We did not validate executable heap behavior on every affected production platform, so this should be read as a source-level hardening observation rather than a universal platform claim.
There are not many allocator-side mitigations to lean on. The local build configuration disables stack protectors in toolchain flags, uses explicit IA32 non-PIE flags, and shows no evidence of heap randomization, guard pages, or safe-linking-style free-list protection [7] .
The overflow payload is not a completely free-form byte stream. It is descriptor-shaped: block bitmap, inode bitmap, inode-table block addresses, counters, and reserved fields. That constraint matters, but these fields are still attacker-controlled enough to damage allocator metadata or redirect later filesystem reads if the heap layout lines up.
Pre-OS Impact
That is why this bug is in a different risk class from a user-space parser overflow. The immediate bug is a pre-OS heap overflow. The likely security consequence is target-specific allocator metadata corruption, arbitrary-write-style unlink behavior, or control-flow corruption. The higher-level platform consequence, when boot policy allows attacker-controlled media or writable boot partitions, is possible compromise of the boot chain before OS defenses are active.
A successful exploit can run before kernel protections, EDR, disk policy, and OS logging. It can also undermine the assurance normally associated with Secure Boot or verified boot for later OS artifacts: the firmware may correctly verify a signed component, but compromised bootloader control can tamper with memory after the trust decision or steer the boot flow around it. Whether this undermines a specific verified-boot chain depends on where verification occurs, what is measured, and whether later stages re-validate the loaded artifact.
This should not be confused with a generic Intel Boot Guard bypass. Boot Guard verifies earlier firmware stages. This issue is about gaining control inside an already-running Slim Bootloader flow by feeding it a crafted filesystem image.
Persistence is also plausible in the right deployment. If the attacker can keep the malicious boot partition, removable medium, recovery image, or service media in the boot path, the exploit is naturally re-triggered on every boot. If the compromised bootloader context exposes a firmware-update or storage-write path on the target, the same primitive could be used to install a more durable bootkit. Those details depend on platform policy and write protections, so persistence should be described as a realistic objective, not an automatic outcome.
Patch Review
Intel recommends updating Slim Bootloader to commit 193129e6a1ea675527670785c8c75ed09b423211 from January 22, 2026 or later [1]
. That commit is narrowly targeted and adds a pre-allocation overflow guard:
1if ((UINT32)(FileSystem->Ext2FsNumCylinder) >=
2 (MAX_UINTN / sizeof(EXT2GD))) {
3 Status = EFI_DEVICE_ERROR;
4 goto out;
5}
The intent is direct: reject an ext2 image whose computed descriptor count would make sizeof(EXT2GD) * Ext2FsNumCylinder overflow UINTN before calling AllocatePool() [8]
.
The current slimbootloader/ checkout contains additional hardening around the same area, notably in follow-up commits 3e853aa2 and 6e29e8b5. The most relevant pieces are:
Ext2SbValidate()now rejects zeroBlocksPerGroupandINodesPerGroup, oversized log block sizes, and invalid 64-bit group descriptor sizes.ReadSBlock()capsExt2FsNumCylinderwithEXT2_MAX_BLOCK_GROUPS(0x100000), limiting both overflow risk and excessive descriptor-table allocation.Ext2fsOpen()checks allocation failures for both the block buffer and descriptor table before use.ReadGDBlock()caps copies when the on-disk group descriptor size is larger than Slim Bootloader’s in-memoryEXT2GDstructure.
Commit 193129e6 is the minimum fix for the reported CVE. Deployments that can do so should prefer a later revision including 3e853aa2 and 6e29e8b5, because those commits harden adjacent parser assumptions. The later hardening is the more complete parser posture: reject impossible filesystem geometry early, then keep allocation and copy sizes tied to bounded invariants.
Affected Systems and Remediation
Intel lists affected Slim Bootloader deployments across multiple Intel Core, Atom, Core Ultra, Xeon E, and Xeon D product families, depending on CPU ID and platform segment [1] . Operators should check the Intel advisory against their platform rather than infer exposure from processor branding alone.
Recommended actions:
- update Slim Bootloader to a fixed revision
- treat boot media and service images as privileged inputs
The Lesson for Boot Firmware
This bug is a reminder that bootloaders do not just load files. They parse attacker-shaped data structures while sitting on the shortest path to platform control.
For firmware teams, the defensive rule is simple: any disk field that contributes to an allocation, loop bound, block address, or copy length needs a combined invariant check before use. In pre-OS code, “valid enough to parse” is not a security boundary.
SecMate also identified and shared ten additional security findings in the same broader research effort. Those issues are currently under coordinated disclosure.
Disclosure Timeline
- November 12, 2025: SecMate reported the ext2 group-descriptor allocation overflow to Intel PSIRT, including technical details and PoC material in an encrypted archive.
- November 13, 2025: Intel PSIRT acknowledged receipt and assigned tracking ID
3d1027ebdf49b610d7c5b294c23889a2. - November 20, 2025: Intel confirmed positive triage, stated that it intended to remediate the issue, assign a CVE, and coordinate disclosure.
- December 9, 2025: Intel shared the expected schedule: fix release to the repository in early February 2026, followed by an Intel Security Advisory with CVE in early May 2026.
- December 10, 2025: SecMate asked whether disclosure could happen once the fix was ready or should wait until May 2026.
- December 10, 2025: Intel requested delaying public disclosure until May 2026 so downstream consumers would have time to integrate the fixes.
- December 12, 2025: SecMate agreed to accommodate the May 2026 coordinated disclosure timeline.
References
[1] Intel Security Advisory INTEL-SA-01425
[2] Slim Bootloader project README
[3] Slim Bootloader BuildLoader.py, default IA32 build architecture
[4] Slim Bootloader build configuration mapping OsLoader.inf to FullMemoryAllocationLib
[5] Slim Bootloader FullMemoryAllocationLib pool allocator
[6] Slim Bootloader PagingLib page-table setup without NX
and EnablePaging64.nasm EFER.LME without NXE
[7] Slim Bootloader toolchain flags
[8] Slim Bootloader commit 193129e6: Check to see if Ext2FsGrpDes size overflows UINTN