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
Published May 24, 2026Reviewed 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

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

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.


Prevention & Best Practices

1. Always Validate External Input Before Arithmetic

Any value derived from a file, network packet, or user input that feeds into a size calculation is a potential attack vector. Validate before you compute:

// BAD: compute then check (too late if overflow already happened)
size_t size = element_size * count;
if (size > MAX_ALLOWED) return NULL;

// GOOD: check then compute (overflow impossible)
if (count > MAX_ALLOWED / element_size) return NULL;
size_t size = element_size * count;

2. Use Safe Integer Libraries

In C/C++, consider using safe integer arithmetic helpers:

  • __builtin_mul_overflow (GCC/Clang): Returns true if multiplication overflows
  • SafeInt (Microsoft): C++ template library for safe integer operations
  • intsafe.h (Windows SDK): Safe integer functions for Windows
size_t result;
if (__builtin_mul_overflow(sizeof(kiss_fft_cpx), (size_t)nfft, &result)) {
    return NULL; // overflow detected
}

3. Fuzz Your Parsers

Integer overflow bugs in format parsers are a perfect target for fuzzing. Tools like AFL++, libFuzzer, and Honggfuzz can find these bugs automatically by mutating input files:

# Example: fuzz the audio parser with AFL++
afl-fuzz -i seeds/ -o findings/ -- ./audio_processor @@

4. Enable Compiler Sanitizers During Development

# AddressSanitizer catches heap buffer overflows at runtime
clang -fsanitize=address,undefined -o app src/*.c

# UndefinedBehaviorSanitizer catches integer overflow
clang -fsanitize=undefined -fsanitize=integer -o app src/*.c

5. Apply the Principle of Least Surprise for Limits

Define and document maximum safe values explicitly, rather than relying on implicit platform behavior:

#define KISS_FFT_MAX_NFFT  (1 << 24)  // 16M samples — document why this is the limit

if (nfft <= 0 || (size_t)nfft > KISS_FFT_MAX_NFFT) {
    return NULL;
}

Relevant Standards & References


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.

Frequently Asked Questions

What is a heap buffer overflow in kiss_fft?

It is a memory safety bug where the FFT size parameter `nfft` causes an integer overflow during the `malloc()` size calculation, resulting in an allocation too small for the data that is subsequently written into it, corrupting adjacent heap memory.

How do you prevent integer-overflow-induced heap overflows in C?

Validate all size-controlling inputs before arithmetic operations, use checked-arithmetic macros or compiler intrinsics (e.g., `__builtin_mul_overflow`), and enforce a maximum bound on parameters like `nfft` before calling `malloc()`.

What CWE is heap buffer overflow via integer overflow?

The heap overflow itself is CWE-122 (Heap-based Buffer Overflow); the enabling integer overflow is CWE-190 (Integer Overflow or Wraparound). Both apply here.

Is ASLR enough to prevent exploitation of this vulnerability?

No. ASLR raises the bar for reliable exploitation but does not prevent the heap corruption from occurring. Deterministic heap layouts or heap spray techniques can still make exploitation feasible; the only reliable mitigation is fixing the integer overflow at the source.

Can static analysis detect this type of vulnerability?

Yes. Tools like Semgrep, Coverity, and CodeQL can flag unchecked arithmetic on allocation size expressions involving externally controlled values. Orbis AppSec detected this specific instance automatically by tracing the tainted `nfft` value from audio file metadata to the unsafe `malloc()` call.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #13

Related Articles

high

How missing Dependabot cooldown happens in GitHub Actions and how to fix it

A high-severity configuration vulnerability was discovered in a `.github/dependabot.yml` file that lacked a cooldown period for package updates. Without this safeguard, Dependabot could immediately propose updates to newly published package versions—including potentially malicious or unstable releases. The fix adds a simple `cooldown` block with a 7-day waiting period before any new package version is suggested.

high

How Server-Sent Events Injection via Unsanitized Newlines happens in Node.js h3 and how to fix it

A high-severity Server-Sent Events (SSE) injection vulnerability (CVE-2026-33128) was discovered in the h3 HTTP framework, where unsanitized newline characters in event stream fields could allow attackers to inject arbitrary SSE messages. The fix upgrades h3 from version 1.15.5 to 1.15.6 in the frontend's dependency tree, ensuring that newline characters are properly sanitized before being written to event streams.

high

How Memory Exhaustion via Large Comma-Separated Selector Lists happens in Python Soup Sieve and how to fix it

A high-severity memory exhaustion vulnerability (CVE-2026-49476) was discovered in Soup Sieve version 2.8.3, affecting Python applications that parse CSS selectors from user-controlled input. The vulnerability allows attackers to craft malicious selector lists that consume excessive memory, potentially causing denial of service. The fix involves upgrading to soupsieve 2.8.4, which implements proper resource limits on selector parsing.

high

How prototype pollution via `__proto__` key happens in Node.js defu and how to fix it

A high-severity prototype pollution vulnerability (CVE-2026-35209) was discovered in the `defu` package version 6.1.4, which allowed attackers to inject properties into JavaScript's `Object.prototype` via the `__proto__` key in defaults arguments. The fix upgrades `defu` to version 6.1.5 in the frontend's dependency tree, protecting downstream consumers like `c12` and `dotenv` configuration loaders from malicious property injection.

critical

How buffer overflow in memcpy() happens in Node.js N-API bindings and how to fix it

A critical buffer overflow vulnerability was discovered in the GetBufferAsVector() function in examples_nodejs/src/zupt_napi.cpp, where memcpy() copied data from JavaScript Uint8Array buffers without proper bounds validation. This vulnerability could allow attackers to trigger memory corruption by providing maliciously crafted input arrays to the native Node.js module, potentially leading to crashes or arbitrary code execution.

high

How memory exhaustion via large comma-separated selector lists happens in Python soupsieve and how to fix it

A high-severity memory exhaustion vulnerability (CVE-2026-49476) was discovered in soupsieve 2.8.3, a CSS selector library used by BeautifulSoup in Python. An attacker who could influence CSS selector input could craft large comma-separated selector lists to exhaust system memory, causing denial of service. The fix upgrades soupsieve from 2.8.3 to 2.8.4 in the backend's `uv.lock` dependency file.