Back to Blog
critical SEVERITY8 min read

Critical Buffer Overflow in Cache.c: How Unsigned Integer Underflow Opens the Door to Remote Code Execution

A critical memory safety vulnerability was discovered and patched in `src/cache.c`, where an unchecked `memcpy` operation could be exploited via attacker-controlled network responses to cause out-of-bounds memory reads and writes. The root cause — a silent unsigned integer underflow — is a classic but devastatingly dangerous pattern that can lead to remote code execution, data corruption, or application crashes. Understanding this vulnerability is essential for any developer working with low-lev

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

Answer Summary

This vulnerability is a critical buffer overflow (CWE-122, CWE-191) in C, located in `src/cache.c`. The root cause is a silent unsigned integer underflow: when a size calculation subtracts a larger value from a smaller one using unsigned arithmetic, the result wraps around to a massive positive number, causing a subsequent `memcpy` to write far beyond the intended buffer boundary. An attacker who controls the network response can craft input that triggers this underflow, enabling out-of-bounds memory writes and potentially remote code execution. The fix adds an explicit bounds check before the `memcpy` call, ensuring the computed size is valid before any memory operation proceeds.

Vulnerability at a Glance

cweCWE-122 (Heap-based Buffer Overflow), CWE-191 (Integer Underflow)
fixAdd explicit pre-condition check ensuring the computed copy length is non-negative and within buffer bounds before calling memcpy
riskRemote code execution, heap corruption, application crash
languageC
root causeUnsigned integer subtraction wraps to a huge positive value, bypassing implicit size checks on memcpy
vulnerabilityBuffer Overflow via Unsigned Integer Underflow

Severity: 🔴 Critical | File: src/cache.c:1121 | CVE Class: CWE-119 (Improper Restriction of Operations within the Bounds of a Memory Buffer)


Introduction

Somewhere in the vast landscape of software bugs, few are as simultaneously subtle and catastrophic as the unsigned integer underflow leading to a buffer overflow. It looks innocent at a glance — just a subtraction and a memory copy — but under the right conditions, it hands an attacker the keys to your process's memory.

This post breaks down a recently patched critical vulnerability in src/cache.c, explains exactly how it could be weaponized, and walks through the best practices that prevent this class of bug from ever reaching production.

If you write C, review C, or ship software that depends on C libraries (hint: almost everyone does), this one's for you.


The Vulnerability Explained

What Was the Code Doing?

At line 1121 of src/cache.c, the code was performing a memcpy to transfer data from a receive buffer (recv_buf) into an output buffer (output_buf). The intent was straightforward: copy send bytes from a calculated position within the receive buffer into the output.

The problematic logic looked something like this:

// VULNERABLE CODE (illustrative reconstruction)
size_t copy_offset = offset_start - dl_offset;  // ⚠️ No validation
memcpy(output_buf, recv_buf + copy_offset, send); // ⚠️ No bounds check

Three things were missing, and each one is a loaded gun:

  1. No validation that offset_start >= dl_offset before the subtraction
  2. No bounds check confirming the source range stays within recv_buf
  3. No validation that send bytes don't exceed output_buf's allocated capacity

The Silent Killer: Unsigned Integer Underflow

In C, size_t is an unsigned type. This is important. When you subtract a larger value from a smaller one using unsigned integers, you don't get a negative number — you get a massive positive number due to wraparound arithmetic.

size_t offset_start = 10;
size_t dl_offset    = 50;

size_t result = offset_start - dl_offset;
// Expected (if signed): -40
// Actual (unsigned):    18446744073709551576  (on 64-bit systems)

That enormous value is now used as a pointer offset into recv_buf. The resulting pointer doesn't point anywhere near your buffer — it points to some arbitrary location in the process's address space, potentially including:

  • Other heap allocations containing sensitive data
  • Stack frames with return addresses
  • Function pointers or vtables

How Could It Be Exploited?

The vulnerability note highlights a critical detail: both offset_start and dl_offset are influenced by attacker-controlled network responses. This means a remote attacker can:

  1. Craft a malicious network response that sets offset_start to a value smaller than dl_offset
  2. Trigger the underflow, causing recv_buf + copy_offset to point to an attacker-chosen memory region
  3. Control send to specify how many bytes get copied, potentially overflowing output_buf

This creates two distinct but related attack primitives:

Attack Primitive Mechanism Potential Impact
Out-of-Bounds Read Underflowed source pointer reads from outside recv_buf Information disclosure, memory leaks, key material exposure
Out-of-Bounds Write send exceeds output_buf capacity Heap/stack corruption, control flow hijacking, RCE

Real-World Attack Scenario

Imagine this application fetches content from a remote server and caches it locally. An attacker controls a malicious server (or performs a man-in-the-middle attack on an unprotected connection):

[Attacker's Server]
  │
  │  Crafted HTTP response:
  │  Content-Range: bytes 9999999-10000000/10000001
  │  (where dl_offset > offset_start after processing)
  │
  ▼
[Vulnerable Application]
  │
  │  offset_start = 5
  │  dl_offset    = 100
  │  copy_offset  = 5 - 100 = 18446744073709551521 (underflow!)
  │
  ▼
[memcpy reads from recv_buf + 18446744073709551521]
  → Reads from arbitrary memory location
  → Potentially writes attacker-influenced data into output_buf + overflow
  → Process corruption / RCE

This is not a theoretical scenario. Vulnerabilities of this exact class have led to real-world exploits in widely deployed software, including web servers, media parsers, and network daemons.


The Fix

What Changed

The patch introduced explicit bounds validation before the memcpy is ever executed. The fix follows a "validate everything, trust nothing" approach for values derived from network input.

A properly hardened version of this code enforces three invariants:

// FIXED CODE (illustrative)

// 1. Validate that subtraction won't underflow
if (offset_start < dl_offset) {
    // Log error, return failure — do NOT proceed
    log_error("Invalid offset: offset_start (%zu) < dl_offset (%zu)",
              offset_start, dl_offset);
    return CACHE_ERR_INVALID_OFFSET;
}

size_t copy_offset = offset_start - dl_offset;

// 2. Validate source range stays within recv_buf
if (copy_offset >= recv_buf_size || send > recv_buf_size - copy_offset) {
    log_error("Source range out of bounds");
    return CACHE_ERR_OUT_OF_BOUNDS;
}

// 3. Validate destination capacity
if (send > output_buf_size) {
    log_error("send (%zu) exceeds output_buf capacity (%zu)",
              send, output_buf_size);
    return CACHE_ERR_OVERFLOW;
}

// Safe to proceed
memcpy(output_buf, recv_buf + copy_offset, send);

Why This Fix Works

Each check addresses a specific attack vector:

  • Check 1 prevents the unsigned underflow entirely. If the math would produce a nonsensical result, we fail fast and loudly.
  • Check 2 ensures the source pointer and length stay within the allocated bounds of recv_buf, preventing out-of-bounds reads.
  • Check 3 ensures we never write more bytes than output_buf can hold, preventing heap/stack corruption.

The key insight is that all three checks must be present. A common mistake is to add only one or two guards, leaving a remaining attack surface.


A Note on the Broader Context

The vulnerability report also references a separate concern: OAuth tokens and API keys stored in plaintext on the filesystem. While that issue involves different code (plugins/auth-oauth2/src/store.ts), it's worth noting that memory safety vulnerabilities and credential storage weaknesses often compound each other. An attacker who achieves out-of-bounds read capability via a bug like this one could potentially exfiltrate plaintext credentials from adjacent memory regions — making both issues more dangerous in combination than either is alone.

Defense in depth means fixing both classes of vulnerability, not just the most obvious one.


Conclusion

The vulnerability patched in src/cache.c is a textbook example of why low-level memory management demands meticulous validation of every value that crosses a trust boundary. The bug itself — an unsigned integer underflow enabling out-of-bounds memory access — is simple to describe but potentially catastrophic in impact, enabling everything from information disclosure to full remote code execution.

The key takeaways:

  • Unsigned arithmetic doesn't protect you from underflow — it makes it silent and dangerous
  • Network-controlled values must be validated before use in pointer arithmetic or size calculations
  • All three invariants must hold: no underflow, source in bounds, destination in bounds
  • Tooling exists to catch this: ASan, UBSan, and fuzzers can find these bugs before attackers do
  • Code review checklists for memory operations are a lightweight, high-value practice

Security is a discipline, not a feature. Every memcpy that touches external data is a potential vulnerability waiting for the wrong input. The fix here is straightforward — validate before you calculate, check before you copy — and applying that discipline consistently is what separates resilient software from exploitable software.

Stay safe, validate your inputs, and may your pointers always stay in bounds. 🔐


This post is part of our ongoing series on real-world security vulnerabilities and their fixes. Automated security analysis powered by OrbisAI Security.

Prevention and further reading

Related Articles

medium

path-to-regexp 0.1.12 DoS: Catastrophic Backtracking on Malformed URL

path-to-regexp version 0.1.12 contains a regular expression engine vulnerability where maliciously crafted URL parameters trigger exponential backtracking, causing CPU exhaustion and service unavailability. Upgrading to version 0.1.13 eliminates the vulnerable pattern from the parsing logic.

critical

CVE-2026-59873: node-tar 7.5.11 DoS via Crafted Gzip Bomb

node-tar versions 7.5.11 through 7.5.18 are vulnerable to a denial-of-service attack through maliciously crafted gzip archives that decompress to disproportionately large sizes. An attacker can exploit this to exhaust memory and CPU resources by submitting a small, highly compressed archive that expands beyond configured limits during extraction.

high

MapManager.get() Race Condition Duplicates API Requests

The MapManager's `get(mapUid, cache)` method used a check-then-act pattern that permitted multiple concurrent requests to pass the cache miss check simultaneously, triggering redundant API calls and risking cache corruption. The fix introduces a `_pending` promise map to deduplicate in-flight fetches for identical map UIDs.

critical

Lampa Desktop Auto-Update Heuristic Bypass: Execution of Unverified

Lampa Desktop's auto-update mechanism downloaded JavaScript and CSS from `raw.githubusercontent.com` using only heuristic validation—file size thresholds and string pattern matching—that attackers could trivially satisfy. The fix introduces cryptographic integrity verification by cross-referencing Git blob hashes from the GitHub Contents API, ensuring downloaded code matches the repository's authoritative state before execution.

high

adm-zip 0.6.0 Preserves SUID Bits From ZIPs: CVE-2026-102282

The `adm-zip` dependency resolved to 0.6.0 in this project's dependency tree, a version affected by CVE-2026-102282: during extraction it applies the Unix permission bits stored in each ZIP entry's external file attributes verbatim, including the setuid (`04000`), setgid (`02000`), and sticky bits. An attacker who controls an archive passed to `extractAllTo()` or `extractEntryTo()` can therefore have the extractor create a setuid binary owned by whatever user the extraction process runs as. The

high

requestInput() Type Confusion: NaN and Object Bypass in JavaScript

The `requestInput()` utility function lacked validation on its `type` parameter and failed to handle `NaN` results from float conversions, creating a type confusion weakness. An attacker could supply malformed inputs that propagate unhandled `NaN` values or unexpected object types through the type system. The fix adds explicit guards against `NaN` type parameters and rejects non-primitive type values.