Back to Blog
critical SEVERITY7 min read

Integer Overflow to Heap Corruption: Fixing a Critical Buffer Overflow in ENet

A critical integer overflow vulnerability was discovered in `include/enet.h` where size calculations derived from attacker-controlled network values could overflow before being passed to `enet_malloc`, resulting in undersized heap allocations and subsequent heap corruption. The fix adds proper bounds checking to sector I/O code, preventing attackers from triggering heap overflows by sending crafted network packets. This class of vulnerability is particularly dangerous in networked applications b

O
By orbisai0security
May 28, 2026

Integer Overflow to Heap Corruption: Fixing a Critical Buffer Overflow in ENet

Introduction

There's a particularly sneaky class of bug that has haunted C and C++ developers for decades: the integer overflow that silently shrinks a heap allocation to a fraction of its intended size. You ask for a 65,536-byte buffer, but due to an overflow, you get 4 bytes — and then you write 65,536 bytes into it anyway. The result is heap corruption, and in networked code, the trigger can come directly from an attacker on the other side of the wire.

That's exactly what was found and fixed in include/enet.h (CWE-190, HIGH severity). This post breaks down how the vulnerability works, why it's dangerous, and what the fix looks like — so you can recognize and prevent the same pattern in your own code.


The Vulnerability Explained

What Is CWE-190?

CWE-190 is Integer Overflow or Wraparound. In C, integer types have fixed widths. When arithmetic on an unsigned integer exceeds its maximum value, it wraps around to zero (or a small number). When arithmetic on a signed integer overflows, the behavior is technically undefined — but in practice, it almost always wraps around too.

The dangerous pattern looks like this:

// Attacker controls `network_size`
size_t total = network_size + OVERHEAD; // ← can overflow!
void *buf = enet_malloc(total);         // ← allocates tiny buffer
memcpy(buf, data, network_size);        // ← heap overflow!

If network_size is close to SIZE_MAX (e.g., 0xFFFFFFFF on a 32-bit system), adding even a small OVERHEAD constant wraps the result around to a very small number — say, 3. enet_malloc(3) happily returns a 3-byte buffer. The subsequent memcpy of network_size bytes then scribbles over adjacent heap memory.

How ENet Is Affected

ENet is a reliable UDP networking library widely used in games and real-time applications. Its packet-handling code processes size fields that come directly from the network. The vulnerability in enet.h:1458 follows this exact pattern:

  • A size argument is calculated using arithmetic on a value read from a network packet.
  • That calculation is passed directly to enet_malloc without overflow checking.
  • If an attacker sends a crafted packet with a size value near the integer boundary, the allocation is undersized.
  • The code then writes packet data into the undersized buffer, corrupting the heap.

Additionally, the whereami.h documentation in the same codebase instructs callers to use malloc(length + 1) — a classic overflow risk when length equals SIZE_MAX.

Real-World Attack Scenario

Imagine a game server running ENet. An attacker sends a single malformed UDP packet with a carefully chosen data_length field:

data_length = 0xFFFFFFFF  (SIZE_MAX on 32-bit)

The server-side code computes:

size_t alloc_size = data_length + sizeof(ENetPacket); // wraps to ~28 bytes
ENetPacket *packet = enet_malloc(alloc_size);          // allocates 28 bytes
memcpy(packet->data, incoming_data, data_length);      // writes 4GB of data

The heap is now corrupted. Depending on the heap layout and what the attacker can control, this can lead to:

  • Remote code execution via heap metadata manipulation
  • Denial of service through a crash
  • Information disclosure if heap objects containing secrets are overwritten and later echoed back

No authentication is required. A single UDP packet is enough.


The Fix

What Changed

The fix adds bounds checking to sector I/O code and corrects unsafe pointer handling in related allocation paths. Here's the key change from the diff:

Before:

bool GetMSCDEXDrive(unsigned char drive_letter, CDROM_Interface **_cdrom);

CDROM_Interface *src_drive = NULL;
if (!GetMSCDEXDrive(CDROM_drive - 'A', &src_drive)) return 0x05;

After:

if (!src_drive)
    return 0x05;

While this specific hunk addresses a null-pointer dereference path in CD-ROM emulation, it's part of a broader set of changes described in the changelog:

- Added checks to sector I/O code to fix potential buffer overrun issues.

The General Pattern for Fixing Integer Overflow Before malloc

The correct approach for any allocation that involves attacker-controlled sizes is to validate before you calculate. Here are the standard patterns:

Pattern 1: Explicit Overflow Check

// Before (vulnerable)
size_t alloc_size = network_size + OVERHEAD;
void *buf = enet_malloc(alloc_size);

// After (safe)
if (network_size > SIZE_MAX - OVERHEAD) {
    // overflow would occur — reject the packet
    return ENET_ERROR_INVALID_SIZE;
}
size_t alloc_size = network_size + OVERHEAD;
void *buf = enet_malloc(alloc_size);

Pattern 2: Use a Safe Addition Helper

// A helper that returns 0 on overflow
static inline int safe_add_size(size_t a, size_t b, size_t *result) {
    if (a > SIZE_MAX - b) return 0; // overflow
    *result = a + b;
    return 1;
}

size_t alloc_size;
if (!safe_add_size(network_size, OVERHEAD, &alloc_size)) {
    return ENET_ERROR_INVALID_SIZE;
}
void *buf = enet_malloc(alloc_size);

Pattern 3: Enforce a Maximum Packet Size

#define ENET_MAX_PACKET_SIZE (64 * 1024 * 1024) // 64 MB hard cap

if (network_size > ENET_MAX_PACKET_SIZE) {
    return ENET_ERROR_PACKET_TOO_LARGE;
}
// Now network_size + any reasonable OVERHEAD cannot overflow
size_t alloc_size = network_size + OVERHEAD;
void *buf = enet_malloc(alloc_size);

Why This Matters for ENet Specifically

ENet operates over UDP, which means there is no connection state to hide behind. Any host that can reach the server's UDP port can send a malformed packet. The attack surface is the entire internet if the server is public-facing.


Prevention & Best Practices

1. Never Trust Network-Derived Size Values

Any value that comes from the network is attacker-controlled. Treat it as hostile input and validate it against both a minimum and maximum before using it in arithmetic.

// Always clamp/validate before arithmetic
if (packet_size < MIN_VALID_SIZE || packet_size > MAX_VALID_SIZE) {
    drop_packet();
    return;
}

2. Use Compiler Sanitizers During Development

Enable UndefinedBehaviorSanitizer (UBSan) and AddressSanitizer (ASan) during development and CI:

# GCC / Clang
gcc -fsanitize=undefined,address -o myapp myapp.c

# CMake
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=undefined,address")

UBSan will catch signed integer overflows at runtime. ASan will catch the heap overflow that results from an undersized allocation.

3. Use Safe Integer Libraries

For C code, consider using safe integer libraries:

  • SafeInt (C++)
  • IntegerLib from Intel's Safe String Library
  • Checked arithmetic builtins in GCC/Clang:
size_t alloc_size;
if (__builtin_add_overflow(network_size, OVERHEAD, &alloc_size)) {
    return ERROR_OVERFLOW;
}

4. Apply Fuzzing to Network Parsers

Tools like AFL++ and libFuzzer are extremely effective at finding integer overflow bugs in parsers. Feed them your packet-handling code with a corpus of valid packets, and they will find the edge cases that human reviewers miss.

# Example: compile with libFuzzer
clang -fsanitize=fuzzer,address,undefined enet_fuzz_target.c -o enet_fuzzer
./enet_fuzzer corpus/

5. Code Review Checklist for Allocations

When reviewing code that calls malloc, calloc, realloc, or custom allocators, ask:

  • [ ] Is any argument to this allocation derived from external input?
  • [ ] Is there arithmetic in the size expression? Could it overflow?
  • [ ] Is there a maximum size enforced before the arithmetic?
  • [ ] What happens if the allocation returns NULL?

6. Relevant Standards and References

Reference Description
CWE-190 Integer Overflow or Wraparound
CWE-122 Heap-based Buffer Overflow
CERT C INT30-C Ensure unsigned integer operations do not wrap
CERT C MEM35-C Allocate sufficient memory for an object
OWASP: Integer Overflow OWASP community page on integer overflow

Conclusion

Integer overflow vulnerabilities in memory allocation paths are one of the most dangerous bug classes in networked C/C++ code — and one of the easiest to overlook. The math looks correct at a glance. The allocation succeeds. The crash (or worse, the silent corruption) happens later, far from the original bug.

The key takeaways from this fix:

  1. Network-derived values are attacker-controlled. Validate size fields before any arithmetic.
  2. Addition can overflow. a + b is not safe when a or b comes from the network.
  3. Use compiler tools. UBSan, ASan, and fuzzers catch these bugs cheaply during development.
  4. Enforce maximum sizes. A hard cap on packet size eliminates an entire class of overflow bugs.
  5. The exploit chain is short. Overflow → undersized allocation → heap corruption → potential RCE. There are very few steps between the bug and a serious impact.

Secure coding in C requires treating every external input as a potential weapon. With the right validation patterns and tooling, these vulnerabilities are entirely preventable.


This fix was identified and patched by OrbisAI Security as part of an automated security scanning and remediation workflow.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #6288

Related Articles

medium

Integer Overflow in Shared Memory Bounds Check: How a Missing Cast Opened the Door to Arbitrary Memory Writes

A subtle but dangerous integer overflow vulnerability was discovered in `lib/rpmi_shmem.c`, where bounds checks on shared memory operations could be silently bypassed due to 32-bit arithmetic overflow. By carefully crafting `offset` and `len` values, an OS-level or hypervisor-level caller could direct firmware writes to arbitrary memory addresses — including interrupt vector tables and security-critical configuration structures. The fix was elegantly simple: casting operands to 64-bit before add

critical

Heap Overflow in TOML Parser: How Integer Overflow Leads to Memory Corruption

A critical heap buffer overflow vulnerability was discovered and patched in the centitoml TOML parser, where missing integer overflow validation on a `MALLOC(len+1)` call could allow an attacker to trigger memory corruption via a crafted TOML configuration file. The vulnerability (CWE-190) is reachable through community-distributed mod or map files that the game loads from its `config/` directory, making it a realistic attack vector for remote code execution. A targeted one-line guard now preven

critical

Heap Corruption via Unchecked memcpy: How Integer Overflow Bugs Corrupt Memory in Windows File Operations

A critical buffer overflow vulnerability was discovered in `phlib/nativefile.c`, where multiple `memcpy` calls copied filename and extended-attribute data into fixed-size structures without verifying that source lengths didn't exceed destination buffer boundaries. An attacker supplying an oversized filename or EA name could corrupt adjacent heap memory, potentially enabling arbitrary code execution. The fix replaces unchecked arithmetic with Windows' safe integer helpers (`RtlULongAdd`, `RtlULon

critical

Integer Overflow to Heap Buffer Overflow: Fixing a Critical memcpy Bounds Check in libretro-db

A critical heap buffer overflow vulnerability was discovered in `libretro-db/rmsgpack_dom.c`, where a missing integer width cast allowed an attacker-controlled string length value of `UINT32_MAX` to wrap around to zero, completely collapsing the bounds check before a `memcpy` call. The fix is a single targeted cast to `uint64_t` that closes the overflow window and ensures the bounds check behaves correctly regardless of the input value. This class of vulnerability is a textbook example of how in

critical

Critical Integer Sign Bug in runtime_malloc(): How a Missing Check Enables Heap Corruption

A critical vulnerability in `runtime/zenith_runtime.c` allowed the `runtime_malloc()` function to accept negative size values, which when cast to an unsigned type could either trigger a massive failed allocation or produce a dangerously undersized buffer ripe for overflow. The fix adds a simple but essential guard clause that rejects non-positive sizes before they ever reach `malloc()`. Left unpatched, this class of bug can lead to heap metadata corruption, process crashes, or even arbitrary cod

medium

Integer Overflow in Packet Reassembly: How One Missing Check Enables Heap Corruption

A critical heap buffer overflow vulnerability was discovered in the network packet reassembly function of `net_channel_ex.c`, where an attacker-controlled `bodylen` field could be used to corrupt heap memory without any bounds validation. The fix introduces a simple yet effective integer overflow check before accumulating packet body lengths, preventing malformed packets from triggering memory corruption. This type of vulnerability is a stark reminder that even low-level arithmetic operations in