Skip to content

fix: reject invalid n_dims in tensor header to prevent stack-buffer-overflow on malformed model files - #3957

Open
shafiuzzaman-md wants to merge 2 commits into
ggml-org:masterfrom
shafiuzzaman-md:fix-model-load-ndims-bounds
Open

fix: reject invalid n_dims in tensor header to prevent stack-buffer-overflow on malformed model files#3957
shafiuzzaman-md wants to merge 2 commits into
ggml-org:masterfrom
shafiuzzaman-md:fix-model-load-ndims-bounds

Conversation

@shafiuzzaman-md

Copy link
Copy Markdown

Problem

whisper_model_load (and the identical copy-pasted loader logic in whisper_vad_init_with_params and parakeet_model_load) reads a tensor header's n_dims field as a raw int32_t from the model file and uses it, unbounded, as the loop count writing into a fixed-size 4-element stack array:

int32_t nelements = 1;
int32_t ne[4] = { 1, 1, 1, 1 };
for (int i = 0; i < n_dims; ++i) {
    read_safe(loader, ne[i]);   // n_dims is fully attacker-controlled, no upper bound
    nelements *= ne[i];
}

A model file with n_dims > 4 writes past the ne[4] array on the stack. Reproduced with a crafted 152-byte model file (n_dims = 20) through the stock whisper-cli -m <file>, under an AddressSanitizer build:

==...==ERROR: AddressSanitizer: stack-buffer-overflow on address ...
WRITE of size 4 at ... thread T0
    #0 memcpy
    #1 read_safe<int>            src/whisper.cpp:964
    #2 whisper_model_load        src/whisper.cpp:1885
    #3 whisper_init_with_params_no_state         src/whisper.cpp:3730
    #4 whisper_init_from_file_with_params_no_state  src/whisper.cpp:3659
    #5 whisper_init_from_file_with_params    src/whisper.cpp:3750
    #6 main                      examples/cli/cli.cpp:1081

Address ... is located in stack of thread T0 at offset ... in frame
    #0 whisper_model_load src/whisper.cpp:1485
  'ne' <== Memory access overflows this variable

This is the sole loader for whisper.cpp's native .bin ggml-format models, reachable from the ordinary, documented whisper_init_from_file_with_params API on any user-supplied or downloaded model file. The identical unbounded loop also exists in whisper_vad_init_with_params (the separate Voice Activity Detection model loader) and parakeet_model_load (NVIDIA Parakeet ASR support, vendored in this repo).

Fix

Add an explicit n_dims range check immediately after it is read, before the loop that populates ne[], at all three sites sharing this pattern:

if (n_dims < 0 || n_dims > 4) {
    WHISPER_LOG_ERROR("%s: invalid n_dims %d in model file (expected 0 <= n_dims <= 4)\n", __func__, n_dims);
    return false;   // (return nullptr in whisper_vad_init_with_params, which returns a pointer)
}

For normal, well-formed model files (0 <= n_dims <= 4) this is a no-op; a malformed file is now rejected with a clear error message instead of corrupting the stack.

Testing

  • Before: the crafted 152-byte model file (n_dims=20) crashes with an ASan stack-buffer-overflow WRITE at whisper_model_load (src/whisper.cpp:1885).
  • After: the same file is rejected cleanly ("invalid n_dims 20 in model file", exit 3), no crash.
  • Regression check: a file with a valid n_dims (e.g. 2) passes the new check unaffected and proceeds to the next validation stage as before.
  • Both whisper-cli and parakeet-cli build cleanly with the change.

Add error handling for invalid n_dims in model file.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant