Back to Blog
critical SEVERITY8 min read

Heap Buffer Overflow in kiss_fft: How Integer Overflow Kills Memory Safety

A high-severity heap buffer overflow vulnerability was discovered and patched in the kiss_fft audio processing library, where integer overflow in allocation size calculations could allow attackers to trigger memory corruption via crafted audio file metadata. The fix adds bounds checking before memory allocation, preventing adversarial `nfft` values from causing undersized heap allocations followed by catastrophic buffer overflows. This class of vulnerability is a reminder that untrusted input mu

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

Answer Summary

This vulnerability is a critical heap buffer overflow (CWE-122) in the kiss_fft C library, caused by integer overflow in the allocation size calculation when processing the `nfft` parameter. An attacker who can supply crafted audio file metadata can trigger the integer wrap, causing `malloc()` to allocate a buffer far smaller than needed, followed by out-of-bounds writes that corrupt the heap. The fix adds a pre-allocation bounds check on `nfft` to reject values large enough to cause the integer overflow before any memory is allocated.

Vulnerability at a Glance

cweCWE-122 (Heap-based Buffer Overflow) / CWE-190 (Integer Overflow or Wraparound)
fixAdd pre-allocation bounds check rejecting `nfft` values that would cause size integer overflow
riskHeap memory corruption leading to remote code execution or denial of service via crafted audio input
languageC
root causeInteger overflow in `nfft`-based allocation size expression produces an undersized heap buffer
vulnerabilityHeap Buffer Overflow via Integer Overflow in Allocation Size

Introduction

Integer overflow vulnerabilities are among the oldest and most dangerous bugs in systems programming — and they keep showing up in production code, even in well-known libraries. This post covers a high-severity heap buffer overflow that was recently patched in kiss_fft.h, a popular lightweight FFT (Fast Fourier Transform) library used in audio processing pipelines.

The vulnerability is deceptively simple: a multiplication used to calculate a memory allocation size can overflow, producing a number far smaller than intended. The result is a tiny heap allocation followed by a massive memcpy that gleefully writes beyond the buffer's end — classic heap buffer overflow territory.

If you write C or C++, process audio or binary file formats, or ship WebAssembly modules that touch native libraries, this one is for you.


The Vulnerability Explained

What Is kiss_fft?

kiss_fft ("Keep It Simple, Stupid FFT") is a compact C library for computing Fast Fourier Transforms. It's widely embedded in audio applications, signal processing tools, and increasingly in WebAssembly modules that run in browsers or desktop apps. Its small footprint makes it attractive — but that same simplicity means it historically lacked defensive input validation.

The Root Cause: Integer Overflow in Size Calculation

The vulnerable code path looks something like this:

// VULNERABLE: No bounds check on nfft before allocation
kiss_fft_cfg kiss_fft_alloc(int nfft, int inverse_fft, void *mem, size_t *lenmem) {
    kiss_fft_cfg st = NULL;
    size_t memneeded = sizeof(kiss_fft_state)
        + sizeof(kiss_fft_cpx) * nfft; // ← DANGER: integer overflow here

    if (lenmem == NULL) {
        st = (kiss_fft_cfg) KISS_FFT_MALLOC(memneeded);
    }
    // ...
    memcpy(st->twiddles, twiddle_data, sizeof(kiss_fft_cpx) * nfft); // ← overflow-driven OOB write
}

Here's the problem: sizeof(kiss_fft_cpx) is typically 8 bytes (two 32-bit floats for real and imaginary components). When you multiply that by nfft, if nfft is large enough, the result wraps around in a 32-bit integer:

sizeof(kiss_fft_cpx) * nfft
= 8 * 536870912          (nfft = 2^29)
= 4,294,967,296           (= 2^32)
= 0                       (in 32-bit arithmetic — overflow!)

The allocator receives a near-zero size, hands back a tiny buffer, and then the subsequent memcpy tries to copy gigabytes of data into it. This is a textbook heap buffer overflow.

Where Does nfft Come From?

This is what makes it exploitable: nfft can be derived from audio file metadata. An attacker crafts a malicious audio file with a specially chosen nfft value embedded in its headers. When the application parses and processes that file, it passes the attacker-controlled value directly into kiss_fft_alloc — no questions asked.

Attack Scenario

Consider a desktop audio application that accepts user-uploaded files or processes audio from the web:

  1. Attacker crafts a malicious audio file with nfft = 4294967295 (MAX_UINT32) in its metadata.
  2. Application parses the file and extracts nfft without validation.
  3. kiss_fft_alloc is called with the attacker-controlled value.
  4. Integer overflow occurs: 8 * 4294967295 wraps to 4294967287 on 32-bit or produces unexpected results.
  5. A small buffer is allocated, but memcpy writes as if the buffer were enormous.
  6. Heap corruption occurs — potentially enabling:
    - Remote code execution (overwriting function pointers, vtables, or return addresses on the heap)
    - Denial of service (application crash)
    - Information disclosure (reading adjacent heap memory)

In a WebAssembly context, the impact is somewhat sandboxed, but heap corruption within the WASM linear memory can still lead to logic bypasses, data corruption, and application crashes.


The Fix

What Changed

The fix adds explicit bounds checking before any allocation or memory operation involving nfft. The patched code validates the parameter against safe limits before the dangerous multiplication ever occurs:

// FIXED: Bounds check before allocation
kiss_fft_cfg kiss_fft_alloc(int nfft, int inverse_fft, void *mem, size_t *lenmem) {

    // Validate nfft is positive and within safe bounds
    if (nfft <= 0) {
        return NULL; // reject non-positive values
    }

    // Guard against integer overflow in size calculation
    // sizeof(kiss_fft_cpx) == 8; MAX_SAFE_NFFT prevents 8*nfft from overflowing size_t
    const size_t MAX_SAFE_NFFT = (SIZE_MAX - sizeof(kiss_fft_state)) / sizeof(kiss_fft_cpx);
    if ((size_t)nfft > MAX_SAFE_NFFT) {
        return NULL; // reject values that would cause overflow
    }

    kiss_fft_cfg st = NULL;
    size_t memneeded = sizeof(kiss_fft_state)
        + sizeof(kiss_fft_cpx) * (size_t)nfft; // safe: overflow impossible

    if (lenmem == NULL) {
        st = (kiss_fft_cfg) KISS_FFT_MALLOC(memneeded);
    }
    // ...
}

Key Security Improvements

Before After
No validation of nfft Explicit check: nfft > 0
No overflow guard on size calculation nfft checked against MAX_SAFE_NFFT before multiply
Integer promotion ambiguity Explicit cast to size_t before arithmetic
Crash or heap corruption on bad input Clean NULL return on invalid input

Why This Works

The fix applies the "validate before compute" principle. By checking nfft against a precomputed safe maximum before the multiplication, we guarantee that sizeof(kiss_fft_cpx) * nfft can never overflow size_t, regardless of the platform's word size.

The maximum safe value is computed as:

const size_t MAX_SAFE_NFFT = (SIZE_MAX - sizeof(kiss_fft_state)) / sizeof(kiss_fft_cpx);

This is conservative, portable, and correct — it accounts for both the element size and the fixed overhead of the state struct.


Regression Test

The fix is accompanied by a comprehensive regression test suite that validates the security invariant across dozens of adversarial inputs:

// Adversarial inputs that MUST be rejected
const adversarialPayloads = [
  ["MAX_UINT32 (4294967295)", 4294967295],
  ["2^31 (signed int overflow)", Math.pow(2, 31)],
  ["allocation overflow boundary", MAX_SAFE_NFFT + 1],
  ["zero", 0],
  ["negative one", -1],
  ["NaN", NaN],
  ["Infinity", Infinity],
  // ... and many more
];

// Valid inputs that MUST succeed
const validPayloads = [
  ["512", 512],
  ["1024", 1024],
  ["4096", 4096],
  ["8192", 8192],
];

The test suite enforces the security invariant: memory allocation size calculations based on nfft must never overflow, and adversarial values must be rejected before any allocation occurs.


A Note on Context

⚠️ Heads up: Readers may notice a mismatch between the vulnerability described here (V-002, heap buffer overflow in kiss_fft.h) and some metadata referencing a different issue (V-001, plaintext credential storage). Both are real and serious vulnerabilities — but they are distinct issues. The plaintext credential storage problem (unencrypted OAuth tokens written to disk) is a separate finding that deserves its own dedicated fix and post. Never let one security fix obscure another: track each vulnerability independently through to remediation.


Conclusion

The kiss_fft integer overflow is a textbook example of why untrusted input must never directly feed into memory arithmetic. The vulnerability is simple, the exploitation path is realistic (crafted audio metadata), and the fix is equally straightforward — a few lines of bounds checking before the dangerous multiplication.

Key takeaways for your own code:

  • ✅ Validate all external input before using it in size calculations
  • ✅ Check for overflow before multiplying, not after
  • ✅ Cast to the widest safe type (size_t) before arithmetic
  • ✅ Fuzz your file parsers — these bugs are hard to find by code review alone
  • ✅ Run sanitizers (-fsanitize=address,undefined) in CI
  • ✅ Write regression tests that specifically exercise adversarial boundary values

Memory safety bugs don't require sophisticated exploitation techniques — they require only that an attacker can influence a number that feeds into your allocator. The best defense is to never let that number reach your allocator unchecked.

Stay safe, validate your inputs, and keep your allocations honest. 🔐


This post is part of our ongoing series on real-world security fixes. Vulnerability details have been responsibly disclosed and the fix has been verified by automated scanner re-scan and LLM code review.

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.