Back to Blog
high SEVERITY6 min read

How integer truncation heap overflow happens in C++ UEFI ACPI parsing and how to fix it

A high-severity integer truncation vulnerability was discovered in `Mobility.Uefi.Acpi.cpp` where heap allocation sizes were stored in a 16-bit integer (`MO_UINT16`), causing silent truncation when the computed size exceeded 65535 bytes. This led to undersized heap allocations followed by out-of-bounds writes, exploitable by an attacker who can influence ACPI SRAT table contents in virtualized environments. The fix promotes the size variable to `MO_UINTN` (platform-native width) to prevent trunc

O
By Orbis AppSec
Published July 10, 2026Reviewed July 10, 2026

Answer Summary

This is an integer truncation leading to heap buffer overflow (CWE-680/CWE-122) in C++ UEFI firmware code. The `MO_UINT16` type was used to store a size calculation (`sizeof(MO_UEFI_ACPI_SIMPLE_MEMORY_RANGE_ITEM) * Count`) that can exceed 65535, causing silent truncation and undersized heap allocation. The fix changes the type from `MO_UINT16` to `MO_UINTN` (native pointer-width integer), ensuring the full multiplication result is preserved for the heap allocation call.

Vulnerability at a Glance

cweCWE-680 (Integer Overflow to Buffer Overflow)
fixChanged size variable type from MO_UINT16 to MO_UINTN (platform-native width)
riskHeap corruption enabling arbitrary code execution in firmware context
languageC++ (UEFI firmware)
root causeSize calculation stored in MO_UINT16 truncates when sizeof(struct) * Count > 65535
vulnerabilityInteger truncation leading to heap buffer overflow

How Integer Truncation Heap Overflow Happens in C++ UEFI ACPI Parsing and How to Fix It

Introduction

The Mobility.Uefi.Acpi.cpp file handles parsing of ACPI SRAT (System Resource Affinity Table) data to query memory ranges in a UEFI firmware environment. A critical flaw in the MoUefiAcpiQueryMemoryRanges, MoUefiAcpiQueryMergedMemoryRanges, and MoUefiAcpiQueryMemoryHoles functions created a heap buffer overflow vulnerability that could be exploited by an attacker who controls ACPI table contents—a realistic scenario in virtualized environments where the hypervisor provides firmware tables.

The root cause? A single type choice: MO_UINT16 was used to store the result of a size multiplication that can easily exceed 65535. This two-byte integer silently truncates the result, causing MoPlatformHeapAllocate to allocate a buffer far smaller than needed, while the subsequent loop writes data far beyond the allocation boundary.

The Vulnerability Explained

Let's look at the vulnerable code at line 262 (and similar patterns at lines 370 and 433):

MO_UINT16 Size = static_cast<MO_UINT16>(
    sizeof(MO_UEFI_ACPI_SIMPLE_MEMORY_RANGE_ITEM) * Count);

PMO_UEFI_ACPI_SIMPLE_MEMORY_RANGE_ITEM Ranges = nullptr;
if (MO_RESULT_SUCCESS_OK != ::MoPlatformHeapAllocate(
    reinterpret_cast<PMO_POINTER>(&Ranges),
    Size))

Here's the math that makes this dangerous:

  • MO_UEFI_ACPI_SIMPLE_MEMORY_RANGE_ITEM is at least 16 bytes (likely containing base address, length, and flags for a memory range descriptor)
  • Count is a MO_UINT16, so it can be up to 65535
  • The multiplication 16 * Count can produce values up to 1,048,560 (0xFFFF0)
  • But MO_UINT16 can only hold values up to 65535 (0xFFFF)

When Count exceeds 4095 (for a 16-byte struct), the multiplication overflows the 16-bit Size variable. For example:

Count True Size (bytes) Truncated MO_UINT16 Size Buffer Shortfall
4096 65,536 0 65,536 bytes
5000 80,000 14,464 65,536 bytes
65535 1,048,560 1,048,560 & 0xFFFF = 65,520 983,040 bytes

After the undersized allocation, a loop iterates Count times, writing sizeof(MO_UEFI_ACPI_SIMPLE_MEMORY_RANGE_ITEM) bytes per iteration into the Ranges buffer. This writes potentially hundreds of kilobytes beyond the allocated heap buffer.

Attack Scenario

In a virtualized environment (e.g., running under a malicious or compromised hypervisor), an attacker can craft an ACPI SRAT table with a large number of Memory Affinity Structures. When the guest OS firmware parses this table:

  1. The SRAT parser counts memory affinity entries → Count = 5000
  2. Size = (MO_UINT16)(16 * 5000)Size = 14464 (truncated from 80000)
  3. MoPlatformHeapAllocate allocates only 14,464 bytes
  4. The filling loop writes 80,000 bytes into a 14,464-byte buffer
  5. 65,536 bytes overflow into adjacent heap memory

This heap corruption can be leveraged for arbitrary code execution at the firmware level—a particularly devastating outcome since firmware code runs at the highest privilege level, below the operating system.

The Fix

The fix is elegant in its simplicity: change the type of Size from MO_UINT16 to MO_UINTN (the platform-native unsigned integer type, equivalent to size_t). This was applied in three locations:

Before (vulnerable):

MO_UINT16 Size = static_cast<MO_UINT16>(
    sizeof(MO_UEFI_ACPI_SIMPLE_MEMORY_RANGE_ITEM) * Count);

After (fixed):

MO_UINTN Size =
    sizeof(MO_UEFI_ACPI_SIMPLE_MEMORY_RANGE_ITEM) * Count;

The changes were made at three call sites:

  1. Line 231 (MoUefiAcpiQueryMemoryRanges): Allocates buffer for raw memory ranges from SRAT
  2. Line 373 (MoUefiAcpiQueryMergedMemoryRanges): Allocates buffer for merged/consolidated ranges
  3. Line 436 (MoUefiAcpiQueryMemoryHoles): Allocates buffer for memory hole descriptors

By using MO_UINTN (which is 32-bit on 32-bit platforms and 64-bit on 64-bit platforms), the multiplication result is preserved without truncation. On a 64-bit UEFI platform, MO_UINTN can hold values up to 2^64-1, making overflow from this multiplication impossible. Even on 32-bit platforms, the maximum result (16 × 65535 = 1,048,560) fits comfortably within a 32-bit unsigned integer.

Notice also that the explicit static_cast<MO_UINT16> was removed. That cast was actually masking a compiler warning about narrowing conversion—it told the compiler "yes, I know this might truncate, do it anyway." Removing the cast and using the correct type eliminates both the vulnerability and the need for the cast.

Prevention & Best Practices

1. Use Appropriate Types for Size Calculations

Always use size_t, MO_UINTN, or equivalent platform-native types for memory size calculations. Never store allocation sizes in types narrower than the platform's addressing width.

// BAD: Narrow type for size
MO_UINT16 size = element_size * count;

// GOOD: Platform-native type
MO_UINTN size = element_size * count;

// BETTER: With explicit overflow check
MO_UINTN size;
if (count > MO_UINTN_MAX / element_size) {
    return MO_RESULT_ERROR_ARITHMETIC_OVERFLOW;
}
size = element_size * count;

2. Enable Compiler Warnings

Use -Wconversion and -Wnarrowing (GCC/Clang) or /W4 (MSVC) to catch implicit narrowing conversions. Treat these warnings as errors in security-critical code.

3. Audit static_cast to Narrower Types

Any static_cast that narrows an integer (e.g., static_cast<uint16_t>(...)) in a size calculation context should be treated as a red flag during code review.

4. Validate Counts Before Multiplication

For firmware code processing hardware tables, validate that counts are within reasonable bounds before performing size calculations:

if (Count > MAX_EXPECTED_MEMORY_RANGES) {
    return MO_RESULT_ERROR_INVALID_PARAMETER;
}

5. Use Safe Integer Arithmetic Libraries

Consider using safe integer libraries (like SafeInt for C++ or checked arithmetic intrinsics) that detect overflow at runtime.

Key Takeaways

  • Never use MO_UINT16 (or uint16_t) to store the result of a size multiplication — even if the count itself fits in 16 bits, the product of count × element_size likely does not.
  • static_cast to a narrower type is not validation — it's silent truncation. The explicit cast at line 262 gave a false sense of intentionality while hiding a critical bug.
  • ACPI table parsing is an attack surface in virtualized environments — hypervisors control the firmware tables presented to guests, making count/size fields attacker-controlled.
  • The same bug pattern appeared three times in the same file (MoUefiAcpiQueryMemoryRanges, MoUefiAcpiQueryMergedMemoryRanges, MoUefiAcpiQueryMemoryHoles), demonstrating how copy-paste propagates vulnerabilities.
  • Firmware-level heap overflows are uniquely dangerous — they execute below the OS, potentially compromising the entire trust chain from boot through runtime.

How Orbis AppSec Detected This

  • Source: The Count value derived from parsing ACPI SRAT Memory Affinity Structures in the firmware table (attacker-controlled in virtualized environments)
  • Sink: MoPlatformHeapAllocate(reinterpret_cast<PMO_POINTER>(&Ranges), Size) in Mobility.Uefi.Acpi.cpp:262 (and lines 373, 436)
  • Missing control: No use of an appropriately-sized integer type for the size calculation; the MO_UINT16 type silently truncates the multiplication result before it reaches the allocator
  • CWE: CWE-680 (Integer Overflow to Buffer Overflow)
  • Fix: Changed the Size variable type from MO_UINT16 to MO_UINTN in all three affected allocation sites, preventing truncation of the size calculation

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.

Conclusion

This vulnerability demonstrates a deceptively simple but devastating pattern: using an integer type that's too small to hold a computed size. The MO_UINT16 type seemed reasonable at first glance—after all, Count itself is a MO_UINT16—but the multiplication by a struct size pushes the result well beyond 16-bit range. In firmware code that processes hardware tables, this creates a directly exploitable heap overflow.

The fix—changing three variable declarations from MO_UINT16 to MO_UINTN—is minimal in code change but maximal in security impact. It's a reminder that type selection is a security decision, especially when that type determines how much memory gets allocated.

References

Frequently Asked Questions

What is integer truncation leading to heap overflow?

It occurs when a size calculation is stored in a variable too small to hold the result, causing the value to wrap around. When this truncated value is used for memory allocation, the allocated buffer is smaller than expected, and subsequent writes overflow it.

How do you prevent integer truncation heap overflows in C++?

Use appropriately-sized integer types (size_t or platform-native types like MO_UINTN) for all size calculations, especially those involving multiplication. Add explicit overflow checks before allocation, and avoid casting size results to narrower types.

What CWE is integer truncation to buffer overflow?

CWE-680 (Integer Overflow to Buffer Overflow) is the primary classification, with CWE-122 (Heap-based Buffer Overflow) as the resulting weakness and CWE-190 (Integer Overflow or Wraparound) as the root cause.

Is using size_t enough to prevent integer truncation overflows?

Using size_t or equivalent platform-native types eliminates truncation for most practical cases, but on 32-bit platforms with very large counts, multiplication can still overflow. Best practice combines proper types with explicit overflow checks (e.g., checking if count > SIZE_MAX / element_size).

Can static analysis detect integer truncation vulnerabilities?

Yes, many static analyzers flag narrowing conversions and truncation in size calculations. Tools like Coverity, PVS-Studio, and compiler warnings (-Wconversion, -Wnarrowing) can detect these patterns, though they may produce false positives requiring triage.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #1

Related Articles

high

How Authentication Bypass happens in PyJWT and how to fix it

A critical authentication bypass vulnerability in PyJWT 2.12.1 allowed attackers to forge valid JSON Web Tokens, potentially bypassing application authentication mechanisms entirely. The vulnerability was fixed in PyJWT 2.13.0 through security improvements to token validation logic. This fix is essential for any application relying on JWT-based authentication.

high

How unsafe pickle deserialization happens in Keras/TensorFlow notebooks and how to fix it

A high-severity untrusted deserialization vulnerability was discovered in `TransferLearningTF.ipynb`, a transfer learning tutorial notebook that loads VGG16 model weights from the internet without verifying their integrity. Because Keras relies on Python's pickle-based serialization format under the hood, a tampered or substituted weights file could execute arbitrary code with the full privileges of the notebook user. The fix adds a SHA-256 checksum verification step immediately after the weight

critical

How Chromium launch-argument injection happens in Python Crawl4AI and how to fix it

A critical unauthenticated remote code execution vulnerability in Crawl4AI 0.8.9 allowed attackers to inject arbitrary Chromium launch arguments through the `browser_config.extra_args` parameter, potentially taking full control of the host process. The fix upgrades to Crawl4AI 0.9.0 and refactors the crawler initialization in `agent/tools/crawler.py` to use the new `BrowserConfig` and `CrawlerRunConfig` APIs, which enforce proper argument validation. This change eliminates the injection surface

critical

How integer overflow in buffer size calculation happens in C++ and how to fix it

A critical integer overflow vulnerability was discovered in OpenCV's HAL filter implementation where multiplying image dimensions without overflow protection could allocate dangerously undersized buffers. An attacker supplying crafted image dimensions (e.g., 65536×65536) could trigger heap corruption through out-of-bounds writes. The fix promotes the calculation to 64-bit arithmetic with a single cast.

critical

How buffer overflow via strcpy() happens in C zlib and how to fix it

A critical buffer overflow vulnerability was discovered in `general/libzlib/gzlib.c` where multiple `strcpy()` and `strcat()` calls operated without bounds checking. An attacker controlling file paths or error messages could overflow destination buffers, potentially achieving arbitrary code execution. The fix replaces these unsafe string operations with bounded `memcpy()` calls that respect pre-calculated buffer lengths.

critical

How hardcoded API key placeholders in documentation happen in Python and how to fix it

A high-severity security issue was discovered in the Context7 API documentation where a hardcoded API key placeholder (`CONTEXT7_API_KEY`) could be copied directly into production code. The fix replaced the static string with a proper environment variable reference using `os.environ`, preventing developers from accidentally deploying exposed credentials.