Skip to content

SEGV in whisper_free_state via uninitialized whisper_batch pointer #3788

@qi-scape

Description

@qi-scape

Summary

A SEGV (null/wild pointer dereference) occurs in whisper_free_state() at src/whisper.cpp:3824 when error cleanup frees an uninitialized whisper_batch structure. When whisper_init_state() fails during KV cache initialization, whisper_batch fields contain uninitialized memory, and the subsequent whisper_batch_free() call attempts to free wild pointers.

CWE-824 (Access of Uninitialized Pointer), CVSS 6.5 (Medium).

Tested version

  • Commit: fc67457 (current master HEAD, 2026-04-20)
  • Build: cmake with -fsanitize=address,undefined -fno-omit-frame-pointer -g
  • Platform: Ubuntu 22.04 x86_64, GCC 11

Root cause

In whisper_init_state(), whisper_batch is allocated as part of whisper_state but not zero-initialized before error paths that call cleanup:

// whisper.cpp:3392 (error path in whisper_init_state)
whisper_free_state(state);  // state->batch contains uninitialized memory

// whisper.cpp:3824 (in whisper_free_state)
whisper_batch_free(state->batch);  // frees wild pointers

// whisper.cpp:499 (in whisper_batch_free)
free(batch.token);    // wild pointer → SEGV

When kv_cache_init() fails (e.g., due to corrupted model parameters), the error cleanup path calls whisper_free_state(), which tries to free state->batch.token — a pointer that was never initialized.

ASAN output

==3137759==ERROR: AddressSanitizer: SEGV on unknown address
The signal is caused by a READ memory access.
Hint: this fault was caused by a dereference of a high value address.
    #0 in __asan::Allocator::Deallocate
    #1 in __interceptor_free
    #2 in whisper_batch_free(whisper_batch) whisper.cpp:499
    #3 in whisper_free_state whisper.cpp:3824
    #4 in whisper_init_state whisper.cpp:3392
    #5 in whisper_init_from_file_with_params whisper.cpp:3741

Suggested fix

Zero-initialize whisper_batch before any error path can trigger cleanup:

// In whisper_init_state(), after allocating state:
state->batch = {};  // or memset(&state->batch, 0, sizeof(state->batch));

Or add a null check in whisper_batch_free():

void whisper_batch_free(struct whisper_batch batch) {
    if (batch.token)   free(batch.token);
    if (batch.embd)    free(batch.embd);
    // ...
}

Impact

  • Denial of Service: Crafted model file crashes any application using whisper.cpp model loading
  • Potential UAF: If the uninitialized pointer happens to point to a valid-but-freed heap region, this becomes a use-after-free with potential code execution impact

Disclosure

These vulnerabilities were found via generative fuzzing with a custom harness. The report was polished with AI assistance.

PoC file will be provided via gist link in comment below.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions