Back to Blog
critical SEVERITY8 min read

Critical Integer Overflow in C: How a Simple Multiplication Almost Caused Heap Corruption

A critical integer overflow vulnerability was discovered and patched in `src/asb_governor.c`, where an unchecked multiplication during buffer reallocation could produce a dangerously undersized heap allocation on 32-bit systems. An attacker with a crafted session file could trigger heap corruption, potentially leading to arbitrary code execution. The fix adds proper overflow detection before any memory allocation, closing a classic but devastating class of memory safety bug.

O
By Orbis AppSec
Published May 23, 2026Reviewed June 3, 2026

Answer Summary

An integer overflow vulnerability (CWE-190) in C occurs when arithmetic operations exceed the maximum value a data type can hold, causing the value to wrap around. In `src/asb_governor.c`, an unchecked multiplication during buffer reallocation created a heap corruption risk on 32-bit systems. The fix adds overflow detection by checking if `count > SIZE_MAX / element_size` before performing the multiplication, ensuring the allocation size is valid before calling `realloc()`.

Vulnerability at a Glance

cweCWE-190 (Integer Overflow or Wraparound)
fixAdd overflow detection check before memory allocation
riskHeap corruption leading to arbitrary code execution
languageC
root causeUnchecked multiplication before realloc() on 32-bit systems
vulnerabilityInteger Overflow in Buffer Reallocation

Critical Integer Overflow in C: How a Simple Multiplication Almost Caused Heap Corruption

Introduction

At first glance, the line of code that caused this vulnerability looks completely harmless:

new_size = new_cap * sizeof(char *);

One multiplication. No pointers dereferenced, no user input directly involved, no obvious footgun. Yet on a 32-bit Android system, this single expression could be weaponized by an attacker to corrupt the heap, potentially enabling arbitrary code execution — all by feeding the application a carefully crafted session file.

This is the story of CWE-190: Integer Overflow or Wraparound, one of the oldest and most persistently dangerous vulnerability classes in systems programming. It was patched in src/asb_governor.c, and understanding why it matters is essential for any developer writing C, C++, or any language that exposes raw memory operations.


The Vulnerability Explained

Background: Dynamic Arrays in C

The vulnerable code implements a classic dynamic array (also called a growable buffer). The pattern is familiar to any C programmer:

  1. Start with a small initial capacity (e.g., 4 slots).
  2. When the array fills up, double the capacity.
  3. Call realloc to resize the backing memory.
  4. Continue filling the array.

This pattern is efficient and widely used. The problem arises at step 3, specifically in how the new allocation size is computed.

The Vulnerable Code

The vulnerable logic, located at src/asb_governor.c:478, looked something like this:

// VULNERABLE CODE — Do not use
size_t new_cap = old_cap * 2;
char **new_buf = realloc(buf, new_cap * sizeof(char *));
if (!new_buf) {
    // handle allocation failure
}
buf = new_buf;
cap = new_cap;

The critical flaw: new_cap * sizeof(char *) is computed without any overflow check.

Why This Is Dangerous on 32-bit Systems

On a 32-bit Android system, size_t is a 32-bit unsigned integer, meaning it can hold values from 0 to 4,294,967,295 (2³² − 1). sizeof(char *) is 4 bytes on a 32-bit platform.

Now consider what happens when new_cap reaches a large value through repeated doublings. If new_cap is, say, 0x40000001 (1,073,741,825):

new_cap * sizeof(char *)
= 0x40000001 * 4
= 0x100000004

But 0x100000004 doesn't fit in a 32-bit size_t. It wraps around to 0x00000004 — just 4 bytes.

realloc is then called asking for a 4-byte buffer. It happily obliges. The code then proceeds to write hundreds of millions of pointers into that 4-byte allocation. This is heap corruption.

The Attack Scenario

The vulnerability is triggered by reading a crafted session file. Here's how an attacker could exploit it:

  1. Craft a malicious session file containing an extremely large number of lines — enough to push new_cap past the overflow boundary through repeated doublings.
  2. Deliver the file to the application (e.g., by replacing a session file on a rooted device, via a man-in-the-middle attack on session sync, or through any other file-delivery mechanism the application exposes).
  3. The application reads the file, doubling its buffer capacity with each reallocation.
  4. At the overflow boundary, realloc receives a wrapped-around size and returns a tiny allocation.
  5. Subsequent writes go far beyond the allocated buffer, corrupting adjacent heap metadata and data.
  6. Heap corruption can be leveraged for a variety of follow-on attacks, including arbitrary code execution, privilege escalation, or denial of service.

The number of lines required to trigger this on a 32-bit system is large but not impractical — especially if the attacker can automate file generation and the application processes session files automatically in the background.

Real-World Impact

  • Heap corruption is one of the most powerful primitives an attacker can have. Modern heap exploitation techniques (House of Force, tcache poisoning, etc.) can turn heap corruption into arbitrary write, and from there into code execution.
  • On Android, this could mean a malicious app or a compromised sync service gains the privileges of the vulnerable process.
  • The vulnerability is silent — no crash, no error log, just quietly corrupted memory that may not manifest until much later, making it extremely hard to diagnose without a dedicated security review.

The Fix

What Changed

The fix adds an explicit integer overflow check before the realloc call. The corrected logic looks like this:

// SAFE CODE — After the fix
size_t new_cap = old_cap * 2;

// Check for integer overflow before computing allocation size
if (new_cap == 0 || new_cap > SIZE_MAX / sizeof(char *)) {
    // Overflow would occur — abort safely
    handle_error("Integer overflow in buffer capacity calculation");
    return ERROR_OVERFLOW;
}

size_t alloc_size = new_cap * sizeof(char *);

char **new_buf = realloc(buf, alloc_size);
if (!new_buf) {
    handle_error("Memory allocation failed");
    return ERROR_NOMEM;
}

buf = new_buf;
cap = new_cap;

How the Fix Works

The key insight is the guard condition:

if (new_cap > SIZE_MAX / sizeof(char *))

This checks whether new_cap * sizeof(char *) would exceed SIZE_MAX before performing the multiplication. This is the standard safe pattern for detecting integer overflow in C:

Instead of checking after the multiplication (too late — the damage is done), divide the maximum safe value by one operand and compare it to the other.

If new_cap is greater than SIZE_MAX / sizeof(char *), then the multiplication would overflow, and the code returns an error instead of proceeding with a dangerously undersized allocation.

Additionally, the fix checks new_cap == 0, which guards against the edge case where old_cap itself was already at the maximum and doubling wrapped to zero.

Before vs. After at a Glance

Aspect Before (Vulnerable) After (Fixed)
Overflow check ❌ None ✅ Pre-multiplication guard
Allocation size Could wrap to tiny value Always proportional to new_cap
Error handling Silent heap corruption Explicit error return
32-bit safety ❌ Unsafe ✅ Safe

Prevention & Best Practices

1. Always Validate Before Multiplying for Allocation Sizes

The golden rule for allocation size arithmetic in C:

// UNSAFE
size_t alloc = count * element_size;
void *buf = malloc(alloc);

// SAFE
if (count > SIZE_MAX / element_size) {
    // overflow would occur
    return ERROR;
}
size_t alloc = count * element_size;
void *buf = malloc(alloc);

This pattern should be second nature whenever you compute an allocation size from a variable count.

2. Use Safe Integer Libraries

For C/C++ projects, consider using established safe integer libraries:

  • SafeInt (Microsoft) — C++ template library for safe integer operations.
  • CERT's intsafe.h (Windows) — Windows-specific safe integer functions.
  • __builtin_mul_overflow (GCC/Clang) — Compiler built-in for detecting overflow:
size_t alloc_size;
if (__builtin_mul_overflow(new_cap, sizeof(char *), &alloc_size)) {
    // overflow detected
    return ERROR;
}
void *new_buf = realloc(buf, alloc_size);

3. Impose Reasonable Upper Bounds

Even with overflow checks, it's good practice to cap the maximum capacity at a sensible limit:

#define MAX_SESSION_LINES 10000000UL  // 10 million lines is already extreme

if (new_cap > MAX_SESSION_LINES) {
    return ERROR_TOO_LARGE;
}

This provides defense-in-depth: even if the overflow check were somehow bypassed, the size limit prevents runaway memory consumption.

4. Enable Compiler Sanitizers During Development

The UndefinedBehaviorSanitizer (UBSan) can catch integer overflows at runtime during testing:

# Compile with UBSan
clang -fsanitize=undefined,integer src/asb_governor.c -o asb_governor_test

# Or with GCC
gcc -fsanitize=undefined src/asb_governor.c -o asb_governor_test

UBSan will print a detailed error and abort the program the moment an integer overflow occurs, making these bugs trivial to find in testing.

5. Use Static Analysis Tools

Several static analysis tools can detect this class of vulnerability before code even runs:

  • Coverity — Detects integer overflow in allocation contexts.
  • CodeQL — GitHub's semantic code analysis engine with queries for CWE-190.
  • Clang Static Analyzer — Free, built into LLVM.
  • Flawfinder — Lightweight C/C++ security scanner.

6. Write Regression Tests for Boundary Conditions

The regression test suite included with this fix is an excellent model. It tests:

  • Power-of-2 boundaries (1, 2, 4, 8, 16, ..., 131072) — exactly where capacity doublings occur.
  • Near-overflow values (2^31 - 1, 2^32 - 1, 2^63 - 1).
  • Adversarial content (path traversal strings, format strings, SQL injection payloads, null bytes).
  • Empty and degenerate inputs (empty string, newlines only, single line).

This kind of parametric boundary testing is exactly what's needed to catch integer overflow bugs before they reach production.

7. Know the Relevant Standards

This vulnerability maps to well-documented security standards:


Conclusion

The vulnerability fixed in this PR is a textbook example of why integer arithmetic in C demands explicit, defensive validation — especially when the result feeds into a memory allocation. The code was doing everything else right: using realloc correctly, doubling capacity efficiently, handling allocation failures. But one missing overflow check was enough to turn a routine buffer resize into a potential code execution primitive on 32-bit systems.

The key takeaways for developers:

  1. Never trust multiplication for allocation sizes without an overflow pre-check — use the if (n > SIZE_MAX / size) pattern or compiler built-ins.
  2. 32-bit platforms are still real — embedded systems, older Android devices, and IoT hardware are 32-bit, and overflow boundaries are much easier to reach there.
  3. Heap corruption is serious — it's not "just a crash." In the hands of a skilled attacker, heap corruption is often a path to arbitrary code execution.
  4. Sanitizers and static analysis are your friends — UBSan, CodeQL, and Coverity can catch these bugs automatically during development.
  5. Regression tests at boundaries matter — the test suite accompanying this fix is a model worth emulating: test power-of-2 boundaries, near-overflow values, and adversarial inputs systematically.

Security bugs like this one are a reminder that in systems programming, the most dangerous vulnerabilities often hide in the most ordinary-looking code. A disciplined habit of overflow-checking every allocation computation is a small investment with an enormous security payoff.


This vulnerability was identified and patched as part of an automated security review. The fix was verified by build, scanner re-scan, and LLM code review. The accompanying regression test suite guards against future regressions of this vulnerability class.

Frequently Asked Questions

What is integer overflow in C?

Integer overflow occurs when an arithmetic operation produces a result larger than the maximum value the data type can store, causing the value to wrap around to a small or negative number.

How do you prevent integer overflow in C?

Check if the operation will overflow before performing it, using patterns like `if (a > SIZE_MAX / b)` before multiplying, or use safe integer arithmetic libraries.

What CWE is integer overflow?

CWE-190 (Integer Overflow or Wraparound) covers this vulnerability class, with CWE-122 (Heap-based Buffer Overflow) often being the resulting exploitable condition.

Is using size_t enough to prevent integer overflow?

No, even size_t can overflow on 32-bit systems where it's only 32 bits wide. Explicit overflow checks are required before any multiplication used in memory allocation.

Can static analysis detect integer overflow?

Yes, static analysis tools can detect many integer overflow patterns, especially those involving allocation sizes, though some complex cases require runtime checks or formal verification.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #6

Related Articles

critical

How integer overflow in CsoundMYFLTArray constructor happens in C++ and how to fix it

A critical integer overflow vulnerability was discovered in `Java/cs_glue.cpp` at line 324, where the `CsoundMYFLTArray` constructor multiplied a user-controlled integer `n` by `sizeof(MYFLT)` without checking for overflow before passing the result to `malloc`. An attacker supplying a value near `INT_MAX` could trigger the overflow, causing an undersized heap allocation that subsequent writes would overflow. The fix adds an explicit `SIZE_MAX / sizeof(MYFLT)` guard and replaces `malloc` with `ca

high

How integer overflow in malloc happens in C bipartite matching and how to fix it

A high-severity integer overflow vulnerability was discovered in the bipartite matching algorithm implementation where unchecked multiplication operations for memory allocation could wrap around, causing undersized buffer allocations and subsequent heap overflow. The fix replaces vulnerable `malloc(sizeof(int) * V)` patterns with safe `calloc(V, sizeof(int))` calls and adds proper bounds validation to prevent exploitation.

high

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

critical

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

A critical integer overflow vulnerability was discovered in the `nsh_setvar()` function in `nshlib/nsh_vars.c`, where the buffer size calculation `newsize = pstate->varsz + varlen` could wrap around, causing a heap buffer overflow. The fix adds overflow checking before the addition, preventing attackers with shell access from corrupting memory by setting variables with crafted names and values.

critical

How integer overflow happens in C reliable.c and how to fix it

A critical integer overflow vulnerability was discovered in `reliable.c` at line 1299, where the `packet_buffer_size` calculation used signed `int` arithmetic that could wrap to a negative or undersized value when large `fragment_size` values were involved. By casting each operand to `size_t` before multiplication, the fix eliminates the overflow risk entirely and ensures the allocated buffer is always large enough to hold the reassembled packet data.

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.