Back to Blog
high SEVERITY6 min read

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.

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

Answer Summary

Integer overflow in malloc() (CWE-190/CWE-122) occurs in C when multiplying allocation size components without overflow checks, causing undersized buffers and heap overflow. In bipartite_matching.c, expressions like `malloc(sizeof(int) * V)` were vulnerable when V was large. The fix uses `calloc(count, size)` which performs safe multiplication internally, combined with explicit bounds checks (`if (V <= 0) return`) before any allocation.

Vulnerability at a Glance

cweCWE-190 (Integer Overflow), CWE-122 (Heap-based Buffer Overflow)
fixReplace malloc(a * b) with calloc(a, b) and add bounds validation
riskHeap corruption enabling arbitrary code execution or denial of service
languageC
root causeUnchecked arithmetic multiplication in malloc() size calculation
vulnerabilityInteger Overflow Leading to Heap Buffer Overflow

Introduction

In the advanced graph algorithms module, we discovered a high-severity integer overflow vulnerability in src/advanced_graph_algorithms/bipartite_matching.c at line 15. The bipartite_color(), bfs_dinic_matching(), and max_bipartite_matching() functions all contained the same dangerous pattern: computing allocation sizes using unchecked multiplication like malloc(sizeof(int) * V).

When processing graph data where V (the vertex count) comes from external input, an attacker could supply a crafted value that causes sizeof(int) * V to overflow, wrapping around to a small number. The subsequent memory operations would then write beyond the allocated buffer, corrupting the heap and potentially enabling arbitrary code execution.

This vulnerability is particularly concerning because bipartite matching algorithms are commonly used in resource allocation, scheduling systems, and network flow analysis—contexts where input data often originates from untrusted sources.

The Vulnerability Explained

The Dangerous Pattern

The vulnerable code appeared in three separate functions within bipartite_matching.c. Here's the original pattern from bipartite_color():

bool bipartite_color(Graph* graph, int* color)
{
    int V = graph->V;
    for (int i = 0; i < V; i++)
        color[i] = -1;

    int* queue = malloc(sizeof(int) * V);  // VULNERABLE: unchecked multiplication
    if (queue == NULL)
        return false;
    // ... queue operations assume V elements available
}

The same pattern appeared in bfs_dinic_matching():

int* queue = malloc(sizeof(int) * V);  // Line 60: same vulnerability

And in max_bipartite_matching():

int* color = malloc(sizeof(int) * V);           // Vulnerable
int** residual = malloc(sizeof(int*) * total_vertices);  // Vulnerable
int* level = malloc(sizeof(int) * total_vertices);       // Vulnerable
int* start = malloc(sizeof(int) * total_vertices);       // Vulnerable

How Integer Overflow Causes Heap Corruption

On a 32-bit system (or when size_t is 32 bits), consider what happens when V = 1073741825 (which is 2^30 + 1):

sizeof(int) * V = 4 * 1073741825 = 4294967300

But 4294967300 exceeds 2^32 - 1 (the maximum 32-bit unsigned value), so it wraps around:

4294967300 mod 2^32 = 4

The malloc(4) call allocates only 4 bytes—space for a single integer—while the code proceeds to use this buffer as if it held over a billion integers. Every subsequent array access beyond index 0 corrupts adjacent heap memory.

Attack Scenario

An attacker targeting this containerized service could:

  1. Craft malicious graph input: Submit a graph structure with V set to a value like 0x40000001 (1073741825)
  2. Trigger the allocation: Call any function that processes bipartite matching
  3. Exploit heap overflow: The undersized buffer allocation followed by normal graph traversal writes attacker-controlled data past the buffer boundary
  4. Achieve code execution: Overwrite heap metadata or adjacent objects to hijack control flow

Because this is a containerized service, the attack surface depends on network exposure, but any endpoint accepting graph data becomes a potential entry point.

The Fix

The fix applies three defensive measures consistently across all vulnerable allocation sites:

1. Bounds Validation Before Allocation

Each function now validates that V (or total_vertices) is positive before any allocation:

// BEFORE (vulnerable)
int* queue = malloc(sizeof(int) * V);

// AFTER (fixed)
if (V <= 0)
    return false;
int* queue = calloc(V, sizeof(int));

This prevents negative values (which would be interpreted as huge unsigned values) and zero-size allocations.

2. Replace malloc() with calloc()

The critical change replaces malloc(sizeof(type) * count) with calloc(count, sizeof(type)):

// BEFORE: Unchecked multiplication happens in application code
int* queue = malloc(sizeof(int) * V);

// AFTER: calloc() performs overflow-safe multiplication internally
int* queue = calloc(V, sizeof(int));

The calloc() function is required by modern C standards to check for overflow when computing count * size. If overflow would occur, calloc() returns NULL rather than allocating an undersized buffer.

3. Consistent Application Across All Allocation Sites

The fix addresses every vulnerable allocation in the file:

Function Line Before After
bipartite_color() 15 malloc(sizeof(int) * V) calloc(V, sizeof(int))
bfs_dinic_matching() 62 malloc(sizeof(int) * V) calloc(V, sizeof(int))
max_bipartite_matching() 118 malloc(sizeof(int) * V) calloc(V, sizeof(int))
max_bipartite_matching() 137 malloc(sizeof(int*) * total_vertices) calloc(total_vertices, sizeof(int*))
max_bipartite_matching() 177-178 malloc(sizeof(int) * total_vertices) calloc(total_vertices, sizeof(int))

Complete Before/After Comparison

Before (vulnerable max_bipartite_matching):

int max_bipartite_matching(Graph* graph, int** match_pairs, int* match_count)
{
    if (graph == NULL || match_pairs == NULL || match_count == NULL)
        return 0;

    int V = graph->V;
    int* color = malloc(sizeof(int) * V);  // No overflow check
    if (color == NULL)
        return 0;
    // ...
    int** residual = malloc(sizeof(int*) * total_vertices);  // No overflow check

After (fixed):

int max_bipartite_matching(Graph* graph, int** match_pairs, int* match_count)
{
    if (graph == NULL || match_pairs == NULL || match_count == NULL)
        return 0;

    int V = graph->V;
    if (V <= 0)           // NEW: bounds validation
        return 0;
    int* color = calloc(V, sizeof(int));  // FIXED: safe multiplication
    if (color == NULL)
        return 0;
    // ...
    int** residual = calloc(total_vertices, sizeof(int*));  // FIXED: safe multiplication

Prevention & Best Practices

Safe Allocation Patterns in C

  1. Always use calloc() for array allocations: The two-argument form provides built-in overflow protection
    ```c
    // Prefer this:
    int* arr = calloc(count, sizeof(int));

// Over this:
int* arr = malloc(count * sizeof(int));
```

  1. Validate bounds before allocation: Check that counts are positive and within reasonable limits
    c if (count <= 0 || count > MAX_REASONABLE_SIZE) { return ERROR_INVALID_SIZE; }

  2. Use safe multiplication helpers: For cases where malloc() is required, use overflow-checking multiplication:
    c size_t size; if (__builtin_mul_overflow(count, sizeof(int), &size)) { return NULL; // Overflow detected } int* arr = malloc(size);

Static Analysis Integration

Configure your CI pipeline to detect this pattern:

  • Semgrep rule: utils.custom.integer-overflow-malloc catches malloc(a * b) patterns
  • Compiler warnings: Enable -Warray-bounds and -Walloc-size-larger-than=
  • AddressSanitizer: Runtime detection of heap overflows during testing

Code Review Checklist

When reviewing C code that handles dynamic allocation:

  • [ ] Are all malloc() size calculations checked for overflow?
  • [ ] Is calloc() used for array allocations?
  • [ ] Are size parameters validated before use?
  • [ ] Is the allocation result checked for NULL?

Key Takeaways

  • Never use malloc(sizeof(type) * count) with untrusted count values—the multiplication can overflow silently
  • The bipartite_matching.c functions now validate V <= 0 before any allocation, preventing both negative wraparound and zero-size edge cases
  • calloc(count, size) is safer than malloc(count * size) because calloc performs overflow-checked multiplication internally
  • Graph algorithm implementations are high-risk because vertex/edge counts often come from parsed input data
  • All five allocation sites in this file required the same fix pattern—when you find one instance, search for similar patterns throughout the codebase

How Orbis AppSec Detected This

  • Source: The graph->V field containing the vertex count, which originates from graph input data
  • Sink: malloc(sizeof(int) * V) calls in bipartite_color(), bfs_dinic_matching(), and max_bipartite_matching() functions
  • Missing control: No overflow check on the multiplication sizeof(int) * V before allocation
  • CWE: CWE-190 (Integer Overflow or Wraparound) leading to CWE-122 (Heap-based Buffer Overflow)
  • Fix: Replaced malloc(sizeof(type) * count) with calloc(count, sizeof(type)) and added bounds validation (if (V <= 0) return)

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

Integer overflow vulnerabilities in memory allocation are among the most dangerous bugs in C programs because they combine silent arithmetic errors with heap corruption—a recipe for exploitable memory safety violations. The fix in bipartite_matching.c demonstrates the defensive pattern every C developer should follow: validate bounds, use calloc() for arrays, and never trust arithmetic with untrusted inputs.

This vulnerability in graph algorithm code highlights that security issues aren't limited to obvious attack surfaces like network parsers. Any code path that processes external data and performs memory allocation is a potential target. By adopting safe allocation patterns and integrating static analysis into your development workflow, you can catch these issues before they reach production.

References

Frequently Asked Questions

What is integer overflow in malloc?

Integer overflow in malloc occurs when multiplying values to compute allocation size causes the result to wrap around to a small number, allocating an undersized buffer that leads to heap overflow when filled.

How do you prevent integer overflow in malloc in C?

Use calloc(count, size) instead of malloc(count * size), as calloc performs internal overflow checking. Additionally, validate that size parameters are positive and within reasonable bounds before allocation.

What CWE is integer overflow in malloc?

This vulnerability maps to CWE-190 (Integer Overflow or Wraparound) and CWE-122 (Heap-based Buffer Overflow), as the overflow leads directly to heap corruption.

Is using calloc enough to prevent integer overflow in malloc?

calloc provides overflow protection for the multiplication, but you should also validate input bounds before allocation to prevent denial-of-service from extremely large but non-overflowing allocations.

Can static analysis detect integer overflow in malloc?

Yes, static analyzers like Semgrep can detect patterns like `malloc(sizeof(type) * count)` and flag them as potential integer overflow vulnerabilities requiring overflow checks.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #774

Related Articles

critical

How arbitrary code execution via injected protobuf definition type fields happens in Node.js and how to fix it

A critical arbitrary code execution vulnerability (CVE-2026-41242) was discovered in protobufjs versions prior to 7.5.5 and 8.0.1, allowing attackers to inject malicious code through crafted protobuf definition type fields. The fix upgrades the dependency from the vulnerable version 6.11.4 to 7.5.5, which properly sanitizes type field inputs during protobuf parsing. This vulnerability is especially dangerous because protobufjs is widely used in Node.js applications for serialization, meaning a s

high

How command injection happens in Node.js child_process and how to fix it

A high-severity command injection vulnerability was discovered in `bootstrap.js` at line 34, where the `exec()` function was used to run shell commands constructed from a `folder` variable. By replacing `exec()` with `execFile()` and passing arguments as an array, the fix eliminates the shell interpolation entirely, closing the door on command injection attacks that could have affected all downstream consumers of this Node.js library.

high

How unsafe pickle deserialization happens in NumPy's np.load() and how to fix it

A high-severity arbitrary code execution vulnerability was discovered in `tools/ardy-engine/retarget.py` where `np.load()` was called with `allow_pickle=True`, enabling attackers to embed malicious pickle payloads in `.npz` files. The fix was a single-character change—switching `allow_pickle=True` to `allow_pickle=False`—that eliminates the deserialization attack vector while preserving the file's legitimate array data loading functionality.

high

How SSRF and Credential Leakage via Absolute URLs happens in axios and how to fix it

A high-severity vulnerability (CVE-2025-27152) in axios versions prior to 1.8.2 allowed Server-Side Request Forgery (SSRF) attacks and credential leakage when making HTTP requests with absolute URLs. This vulnerability was fixed by upgrading from axios 1.7.4 to 1.8.2 in both package.json and package-lock.json, eliminating the attack vector that could have exposed authentication tokens and enabled unauthorized server-side requests.

high

How pickle-based arbitrary code execution happens in PyTorch and how to fix it

A high-severity arbitrary code execution vulnerability was discovered in `scripts/export_joyvasa_audio.py` where `torch.load()` was called with `weights_only=False`, allowing any pickle-serialized Python object — including malicious code — to execute during checkpoint loading. The fix switches to `weights_only=True` and explicitly allowlists only the two non-standard classes the checkpoint actually requires: `argparse.Namespace` and `pathlib.PosixPath`. This closes a real code execution path tha

critical

How WebSocket protocol length header abuse happens in Node.js and how to fix it

A critical vulnerability (CVE-2026-54466) in websocket-driver versions prior to 0.7.5 allows attackers to corrupt WebSocket messages by manipulating protocol length headers. This can lead to data integrity issues, denial of service, or potentially arbitrary code execution in affected Node.js applications. The fix involves upgrading the websocket-driver dependency to version 0.7.5.