FastMM4-AVX Integer Overflow in Large Block Allocation

Security Advisory CVE-2026-27123 / GHSA-f6jf-6w84-w2h7. Published: March 4, 2026. Fixed in FastMM4-AVX v1.0.9 (November 26, 2025).

Summary

FastMM4-AVX memory manager before version 1.0.9 is vulnerable to an integer overflow in the AllocateLargeBlock function. When an application processes attacker-controlled input that determines an allocation size near High(NativeUInt), the size calculation wraps to a small value, causing VirtualAlloc to allocate an undersized buffer. Subsequent writes by the caller produce a heap-based buffer overflow that can lead to memory corruption, crash, and arbitrary code execution.

Severity: Critical (CVSS 4.0 Score: 9.3)

Vulnerability Details

CVE ID CVE-2026-27123
GHSA ID GHSA-f6jf-6w84-w2h7
Vulnerability Type Integer Overflow (CWE-190) leading to Heap-based Buffer Overflow (CWE-122)
Attack Type Remote
Attack Vector Network (via application-layer input: file parsing, protocol handling, deserialization)
Maintainer Maxim Masiutin
Product FastMM4-AVX Memory Manager for Delphi and FreePascal
Affected Component AllocateLargeBlock, FastGetMem (Pascal and Assembly paths) in FastMM4.pas
Affected Versions All versions prior to v1.0.9
Fixed Version v1.0.9 (November 26, 2025)
Impact Memory Corruption, Heap-based Buffer Overflow, Code Execution, Denial of Service

CVSS Score

CVSS Version Score Severity Vector String
CVSS 4.0 9.3 Critical AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N

Technical Details

Background

FastMM4-AVX is a high-performance memory manager for 32-bit and 64-bit Delphi and FreePascal applications. Large block allocations (above roughly 256 KB) are routed through AllocateLargeBlock, which computes the actual VirtualAlloc size by adding header overhead and rounding up to granularity boundaries.

Root Cause

The vulnerable size calculation in AllocateLargeBlock:

LLargeUsedBlockSize := (ASize + LargeBlockHeaderSize + LargeBlockGranularity - 1 + BlockHeaderSize)
  and LargeBlockGranularityMask;

When ASize is close to High(NativeUInt), the addition of header overhead overflows the unsigned integer, wrapping to a small value. For example, on a 64-bit system, requesting $FFFFFFFFFFFF0000 bytes causes the sum to wrap to approximately 64 KB. VirtualAlloc allocates this tiny buffer, but the calling code believes it holds the full multi-exabyte allocation and writes far beyond the buffer end, corrupting heap memory.

Attack Scenario

  1. Attacker sends crafted input to a Delphi or FreePascal application (file, network packet, API call, deserialization payload)
  2. Application reads a size field from the input and calls GetMem(size), SetLength(arr, size), or another allocation API
  3. The size value near High(NativeUInt) triggers integer overflow in AllocateLargeBlock
  4. VirtualAlloc allocates a small buffer, typically around 64 KB
  5. Application writes attacker-controlled data past the buffer end into adjacent heap metadata
  6. Heap metadata corruption enables arbitrary code execution or process crash

Affected Code Paths

  • AllocateLargeBlock: primary vulnerable function in both Debug and Release builds
  • FastGetMem Pascal path (FPC/Linux): was missing size check before calling AllocateLargeBlock
  • FastGetMem 32-bit assembly path: no overflow guard before large block branch
  • FastGetMem 64-bit assembly path: no overflow guard before large block branch

Proof of Concept

All proof-of-concept cases require a build from a revision prior to commit fa63fde with FastMM4-AVX active.

PoC A: Direct Oversized Allocation (64-bit)

Request a size near High(NativeUInt) and check whether a pointer is returned:

program PocOverflowA;
{$APPTYPE CONSOLE}
uses FastMM4;
var p: Pointer;
begin
  p := GetMem(NativeUInt($FFFFFFFFFFFF0000));
  if p <> nil then
  begin
    Writeln('VULNERABLE: pointer returned');
    FreeMem(p);
  end
  else
    Writeln('Protected or allocation failed safely');
end.

On a vulnerable build, GetMem returns a non-nil pointer for the tiny underlying allocation. On a fixed build, it returns nil.

PoC B: Pure Pascal Path (FPC Linux)

Compile the dedicated regression suite with the Pascal-only path to exercise the gap that motivated commit da01357:

cd Tests/Simple
fpc -B -Mdelphi -Tlinux -Px86_64 -dDontUseASMVersion IntegerOverflowTest.dpr
./IntegerOverflowTest

Expected on vulnerable build: one or more overflow checks fail and the test exits with code 1. Expected on fixed build: all overflow checks return nil and the test exits with code 0.

PoC C: Edge Value (Commit 5141af4 Hardening)

This value previously passed the initial guard but triggered a range check error in FPC DEBUG mode:

program PocOverflowC;
{$APPTYPE CONSOLE}
uses FastMM4;
var p: Pointer;
begin
  p := GetMem(NativeUInt($FFFFFFFFFFEFFFA9));
  if p <> nil then
  begin
    Writeln('VULNERABLE: returned pointer for dangerous edge value');
    FreeMem(p);
  end
  else
    Writeln('Protected: rejected edge value');
end.

Fix Applied in Version 1.0.9

The fix was delivered in three commits on November 26, 2025, covering all affected code paths:

Commit 1: Primary Fix

Added MaxSafeLargeBlockSize constant and pre-calculation guard in AllocateLargeBlock, plus equivalent guards in the 32-bit and 64-bit assembly FastGetMem paths. Added Tests/Simple/IntegerOverflowTest.dpr and updated CI workflow.

  • Commit: fa63fde: Add integer overflow protection for large block allocations; add overflow checks in assembly; add IntegerOverflowTest
if ASize > MaxSafeLargeBlockSize then
begin
  Result := nil;
  Exit;
end;

Commit 2: Pascal FastGetMem Path

Added the missing overflow check to the pure Pascal FastGetMem path used by FPC and Linux builds. Without this commit, the Pascal route could reach AllocateLargeBlock without the size precheck.

  • Commit: da01357: Add overflow check to Pascal FastGetMem path for FPC/Linux
if (ASize > 0) and (NativeUInt(ASize) <= MaxSafeLargeBlockSize) then
  Result := AllocateLargeBlock(ASize ...)
else
  Result := nil;

Commit 3: Threshold Hardening

Lowered the 64-bit MaxSafeLargeBlockSize threshold from $FFFFFFFFFFFE0000 to $FFFFFFFFFFE00000 to reject additional edge values that triggered range check errors in FPC DEBUG builds. Updated CI policy to maintain DEBUG coverage on Windows.

  • Commit: 5141af4: Lower MaxSafeLargeBlockSize to $FFFFFFFFFFE00000 to reject DEBUG-mode edge values

Performance impact: less than 0.01% (one comparison instruction on a path that is never taken for any legitimate allocation size).

Fix Verification

The fix covers four independent code paths. Each path handles both signed-negative and unsigned-overflow attack values and returns nil in both cases.

Path 1: Pascal AllocateLargeBlock (FastMM4.pas:8811)

The parameter is NativeUInt, so any signed-negative value passed from a caller becomes a large unsigned value and is caught by the unsigned comparison:

function AllocateLargeBlock(ASize: NativeUInt; ...): Pointer;
  if ASize > MaxSafeLargeBlockSize then  {unsigned comparison}
  begin
    Result := nil;
    Exit;
  end;

Constants: 32-bit: $FFFE0000; 64-bit: $FFFFFFFFFFE00000.

Path 2: Pascal FastGetMem (FastMM4.pas:9991)

Double guard rejects both signed-negative values and unsigned-overflow values:

if (ASize > 0) and (NativeUInt(ASize) <= MaxSafeLargeBlockSize) then
  Result := AllocateLargeBlock(ASize ...)
else
  Result := nil;

Path 3: 32-bit Assembly FastGetMem (FastMM4.pas:10652)

test eax,eax plus js rejects signed-negative values (sign flag set); then unsigned cmp plus ja rejects overflow values:

test eax, eax                    ; sign bit set = signed-negative attack value
js   @DontAllocateLargeBlock
cmp  eax, MaxSafeLargeBlockSize  ; unsigned comparison
ja   @DontAllocateLargeBlock     ; JA = Jump if unsigned Above

Path 4: 64-bit Assembly FastGetMem (FastMM4.pas:11373)

Same pattern with 64-bit registers:

xor  rax, rax                    ; pre-set return value to nil
test rcx, rcx                    ; check sign bit
js   @Done                       ; reject signed-negative
mov  r8, MaxSafeLargeBlockSize
cmp  rcx, r8                     ; unsigned comparison
ja   @Done                       ; reject unsigned overflow values
call AllocateLargeBlock

Coverage Matrix

Attack Value 32-bit Signed 32-bit Unsigned 64-bit Signed 64-bit Unsigned
-1 ($FF..FF) Caught by js Caught by ja Caught by js Caught by ja
$FFFFFFFFFFFF0000 N/A (32-bit) N/A (32-bit) Caught by js Caught by ja
MaxSafe + 1 Caught by ja Caught by ja Caught by ja Caught by ja
0 Returns nil Returns nil Returns nil Returns nil
Normal (1 MB) Passes OK Passes OK Passes OK Passes OK

Workarounds

Upgrading to v1.0.9 or later is the only complete fix. If upgrading is not immediately possible:

  • Validate all allocation sizes in application code before calling GetMem, ReallocMem, or SetLength. Reject any size that exceeds a reasonable application-specific maximum well below High(NativeUInt).
  • There is no compile-time option in vulnerable FastMM4-AVX releases that enables the overflow check without patching the source.
  • The upstream repository (pleriche/FastMM4) does not contain this fix and remains vulnerable.

CWE Mapping

  • CWE-190: Integer Overflow or Wraparound. The size arithmetic in AllocateLargeBlock wraps around when ASize is near High(NativeUInt), causing the computed block size to be far smaller than requested.
  • CWE-122: Heap-based Buffer Overflow. The undersized allocation from the wrapped size causes subsequent writes by the caller to overflow the heap buffer, corrupting adjacent heap metadata.

Related Issues in Other Software

This vulnerability belongs to a well-documented class of integer overflow bugs in memory allocator size arithmetic. The following CVEs share the same root cause pattern:

  • CVE-2017-17426: glibc 2.26 tcache path lacks a needed overflow check near SIZE_MAX, so malloc can return an undersized chunk and later writes trigger heap overflow. NVD
  • CVE-2018-6485: glibc posix_memalign or memalign in 2.26 and earlier can overflow internal arithmetic, returning too-small heap memory and enabling heap corruption on use. NVD
  • CVE-2018-6551: glibc malloc on powerpc 2.24 to 2.26 and i386 2.26 can mishandle near-SIZE_MAX allocations, returning smaller buffers and causing heap corruption. NVD
  • CVE-2021-27502: TI-RTOS HeapMem_allocUnprotected can return a valid pointer for huge sizes after overflow, producing an undersized allocation and possible code execution. NVD

The 2021 CISA BadAlloc advisory documented 25 or more similar overflow bugs across RTOS allocator implementations. CISA ICSA-21-119-04

Timeline

November 26, 2025 Vulnerability identified by Maxim Masiutin; fix implemented across three commits (fa63fde, da01357, 5141af4); FastMM4-AVX v1.0.9 released
February 15, 2026 GitHub Security Advisory GHSA-f6jf-6w84-w2h7 created
March 4, 2026 Advisory published; CVE-2026-27123 assigned
March 6, 2026 Developer advisory published at masiutin.net

Credit

Discovered and fixed by Maxim Masiutin, maintainer of FastMM4-AVX.

Other FastMM4-AVX Security Advisories

GHSA-3x29-6h9j-vcvm FPU Stack Corruption in 32-bit Move Procedures (CWE-908, CWE-703, CWE-754). Fixed in v1.0.10. CVSS 4.0: 5.9 Medium. Advisory

References