Back to Blog
critical SEVERITY7 min read

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

O
By Orbis AppSec
•Technically reviewed by Anupam Mediratta•Published May 28, 2026•Reviewed June 3, 2026

Answer Summary

This vulnerability is a critical integer overflow leading to heap buffer overflow (CWE-190) in the centitoml TOML parser, written in C. When parsing a TOML string value, the code called `MALLOC(len+1)` without first checking whether `len` was close to `SIZE_MAX`, meaning an attacker could supply a crafted TOML file where `len` wraps around to a near-zero value, causing a drastically undersized heap allocation followed by a heap buffer overflow write. The fix adds a one-line guard that aborts parsing if `len` equals `SIZE_MAX` (or exceeds a safe threshold) before the `MALLOC` call is ever reached.

Vulnerability at a Glance

cweCWE-190
fixOne-line guard added before the `MALLOC` call to reject any `len` value that would overflow when incremented
riskRemote code execution via crafted TOML configuration / mod files
languageC
root cause`MALLOC(len+1)` called without validating that `len < SIZE_MAX`, allowing integer wraparound to produce an undersized allocation
vulnerabilityInteger Overflow leading to Heap Buffer Overflow

Introduction

Configuration file parsers are everywhere — they quietly read your settings, load your preferences, and initialize your application. Because they sit at the boundary between untrusted data and trusted program logic, they are a historically rich target for attackers. When that parser is written in C and handles attacker-controlled input without careful bounds checking, the consequences can be severe.

This post breaks down a critical heap buffer overflow (CWE-190) discovered and patched in deps/centitoml/toml_api.c, a C-based TOML parser embedded in a game engine. The root cause is a classic integer overflow that produces an undersized heap allocation, followed by a memcpy that happily writes beyond the buffer's end. We'll walk through exactly how it works, how an attacker could exploit it, and what the fix does to close the door.


The Vulnerability Explained

What Is an Integer Overflow Leading to Heap Overflow?

Integer overflow bugs in C are deceptively simple: arithmetic on an integer type wraps around when it exceeds the type's maximum value. In the context of memory allocation, this means a calculation meant to produce a large buffer size can silently produce a tiny one — or even zero. The allocator happily returns a valid pointer to that undersized buffer, and any subsequent write that assumes the buffer is full-sized corrupts adjacent heap memory.

The Vulnerable Code

Here is the relevant function before the fix:

// deps/centitoml/toml_api.c (BEFORE)
static char *STRNDUP(const char *s, size_t n)
{
    size_t len = strnlen(s, n);
    char *p = MALLOC(len + 1);   // ← integer overflow possible here
    if (p)
    {
        memcpy(p, s, len);       // ← writes `len` bytes into undersized buffer
        p[len] = '\0';
    }
    return p;
}

Let's trace the problem step by step:

  1. len comes from attacker-controlled TOML data. The n parameter passed to strnlen is derived from values parsed out of a TOML file — a file that could be a community-distributed mod or map.

  2. len + 1 can overflow. size_t is an unsigned type. On a 64-bit system, SIZE_MAX is 0xFFFFFFFFFFFFFFFF. If len equals SIZE_MAX (i.e., (size_t)-1), then len + 1 wraps to 0.

  3. MALLOC(0) returns a valid but tiny pointer. The C standard permits malloc(0) to return a non-NULL pointer. The buffer is effectively zero bytes (or implementation-defined minimum), but the pointer is valid.

  4. memcpy(p, s, len) writes len bytes anyway. With len equal to SIZE_MAX, this attempts to copy an astronomically large number of bytes starting from the tiny allocation — smashing everything on the heap after it.

Real-World Attack Scenario

The game loads TOML configuration files from its config/ directory. Community channels distribute mod and map files, which may include or reference TOML configs. An attacker crafts a malicious TOML file containing a string value whose encoded length causes strnlen to return (size_t)-1. When the game loads this file:

  • The parser calls STRNDUP with the oversized string.
  • MALLOC(0) returns a small allocation.
  • memcpy overflows the heap, corrupting allocator metadata or adjacent objects.
  • The attacker can potentially achieve arbitrary code execution by carefully controlling heap layout — a well-known technique in heap exploitation.

Because this is triggered simply by opening a file, the attack requires no authentication, no network access, and no special privileges. A player downloading a popular-looking mod could silently execute attacker code.

CWE Reference: CWE-190: Integer Overflow or Wraparound


The Fix

What Changed

The patch adds a single guard clause that checks for the sentinel overflow value before any allocation occurs:

// deps/centitoml/toml_api.c (AFTER)
static char *STRNDUP(const char *s, size_t n)
{
    size_t len = strnlen(s, n);
+   if (len == (size_t)-1)       // ← guard: overflow sentinel check
+       return NULL;
    char *p = MALLOC(len + 1);
    if (p)
    {
        memcpy(p, s, len);
        p[len] = '\0';
    }
    return p;
}

How Does It Work?

strnlen(s, n) returns at most n. If n itself is (size_t)-1 (the maximum value of size_t), it signals that the input length is at or beyond the type boundary — a condition that should never occur with legitimate TOML data.

The guard if (len == (size_t)-1) return NULL; intercepts this before len + 1 can wrap to zero. The caller receives NULL, which the existing if (p) check already handles gracefully — no allocation, no copy, no overflow.

Why This Is the Right Fix

Concern Before After
Integer overflow on len+1 Possible Prevented by early return
Undersized MALLOC call Possible Never reached
Heap corruption via memcpy Possible Never reached
Graceful failure on bad input No Yes — returns NULL

The fix is minimal, targeted, and does not change behavior for any legitimate input. It follows the fail-fast principle: detect the anomaly at the earliest possible point and return a safe error value rather than proceeding with corrupted state.


Conclusion

This vulnerability is a textbook example of how a single missing bounds check in a low-level parser can open the door to critical heap corruption. The attack path is realistic — community mod files are a well-established vector for game engine exploits — and the impact is severe: heap corruption in C can escalate to arbitrary code execution in the hands of a skilled attacker.

The fix is elegantly simple: one if statement, two lines of code, zero behavior change for legitimate inputs. But reaching that fix requires understanding why len + 1 can overflow, what MALLOC(0) returns, and how memcpy blindly trusts its size argument.

Key takeaways for developers:

  • 🔢 Treat size arithmetic as a security boundary — always check for overflow before allocation.
  • 📂 Parser inputs are attacker-controlled — even local config files in user-modifiable directories.
  • 🛡️ Fail fast and fail safely — return NULL early rather than proceeding with invalid state.
  • 🔍 Fuzz your parsers — automated input generation finds these bugs faster than code review alone.
  • 🧰 Use sanitizers in CI — AddressSanitizer and UBSan catch these issues before they reach production.

Memory safety bugs don't disappear on their own. Systematic validation, modern tooling, and a security-first mindset are the best defenses we have — and as this fix shows, applying them is often much simpler than the damage they prevent.

Prevention and further reading

Related Articles

critical

SGX Enclave ecall_store_data memcpy Buffer Overflow in 256-Byte

The Intel SGX enclave's trusted bridge functions `ecall_store_data` and `ecall_retrieve_data` used `memcpy()` to move data into and out of a fixed 256-byte `secure_storage` buffer without validating that `data_len` fit within destination boundaries. An attacker providing oversized `data_len` values could corrupt enclave memory, breaking SGX's confidentiality guarantees.

critical

How NULL pointer dereference from unchecked malloc() happens in C and how to fix it

A critical memory safety vulnerability was discovered in `bench/tokenizer/tokenizer.c` where `malloc()` was called without checking its return value before passing the pointer to `memcpy()`. If allocation fails and `malloc()` returns NULL, the subsequent `memcpy()` writes to address zero, causing heap corruption or potential arbitrary code execution. The fix adds a single NULL check immediately after allocation, exiting cleanly on failure rather than proceeding with a dangerously invalid pointer

critical

How Stack Buffer Overflows Happen in C with sprintf() and How to Fix Them

A critical stack buffer overflow was discovered in `libuv/Learn-libuv/docs/code/tty-gravity/main.c` where `sprintf()` wrote ANSI escape sequences and user-controlled variables into a fixed 500-byte buffer without any bounds checking. An attacker controlling the `pos`, `width`, or `message` variables could overflow the stack, overwrite return addresses, and potentially achieve arbitrary code execution. The fix replaces `sprintf()` with `snprintf()` and adds explicit length validation to ensure wr

high

How insecure string copy functions happen in C and how to fix them

A high-severity buffer overflow risk was discovered in `login/main.c` where `strcpy()` was used to copy the `HOME` environment variable into a fixed-size 512-byte buffer without any bounds checking. An attacker controlling the `HOME` environment variable could overflow `pwd_file_name`, potentially corrupting memory or hijacking execution. The fix replaces the two-step `strcpy`/`strcat` pattern with a single, bounds-safe `snprintf` call.

high

How c.lang.security.use-after-free.use-after-free happens in C and how to fix it

A use-after-free vulnerability was discovered in `ggml-alloc.c` where `galloc->leaf_allocs` could be referenced after being freed during graph memory reallocation. The fix nullifies the pointer immediately after `free()` and uses explicit `sizeof(struct leaf_alloc)` to prevent undefined behavior. This defensive hardening eliminates an exploit primitive in a speech-to-text processing pipeline.

critical

How buffer overflow in Intel SGX enclave ECALLs happens in C and how to fix it

A critical buffer overflow vulnerability was discovered in Intel SGX enclave functions `ecall_encrypt_data` and `ecall_decrypt_data` in `backend/sgx/enclave/enclave.c`. The functions performed memory operations without validating that the provided buffer lengths matched the actual allocated buffer sizes, allowing an attacker controlling the untrusted application to trigger heap corruption within the secure enclave by passing oversized length parameters.