Summary
Two UBSan-detected undefined-behavior bugs occur in whisper_model_load() when parsing a crafted
GGUF model file. The first passes a null pointer to memcpy in the buffer loader's read callback;
the second is a signed integer overflow when computing the mel filter buffer size from unvalidated
header fields. Both are undefined behavior under the C/C++ standards; the practical impact (whether
either becomes a hard crash) is platform- and build-dependent and is not a confirmed SEGV.
Affected component
src/whisper.cpp — whisper_model_load()
- Bug 1: null pointer passed to
memcpy at src/whisper.cpp:3688
- Bug 2: signed integer overflow at
src/whisper.cpp:1583
Reproduction
PoCs:
poc-051-whisper-null-ptr.bin (68 bytes) — triggers the null-pointer-to-memcpy UB
poc-051-whisper-int-overflow.bin (52 bytes) — triggers the signed integer overflow
Load either file through the buffer-based model loader (e.g.
whisper_init_from_buffer_with_params). On a UBSan-instrumented build:
src/whisper.cpp:3688:16: runtime error: null pointer passed as argument 1, which is declared to never be null
src/whisper.cpp:1583:43: runtime error: signed integer overflow: 1920274432 * -1094795586 cannot be represented in type 'int32_t'
Root cause
Bug 1 — null pointer to memcpy (whisper.cpp:3688)
The custom buffer loader's read callback passes a null-derived source pointer to memcpy() when
the buffer context is exhausted or its base pointer is null. Passing a null source to memcpy is
undefined behavior regardless of the copy size:
loader.read = [](void * ctx, void * output, size_t read_size) {
buf_context * buf = reinterpret_cast<buf_context *>(ctx);
size_t size_to_copy = buf->current_offset + read_size < buf->size
? read_size : buf->size - buf->current_offset;
memcpy(output, buf->buffer + buf->current_offset, size_to_copy); // null source -> UB
buf->current_offset += size_to_copy;
return size_to_copy;
};
Bug 2 — signed integer overflow (whisper.cpp:1583)
filters.n_mel and filters.n_fft are read directly from the model file with no validation, and
their product is computed in int32_t before being passed to resize(). Crafted values overflow
the multiplication (undefined behavior); the subsequent resize() may throw std::length_error or
allocate a garbage-sized buffer:
read_safe(loader, filters.n_mel); // attacker-controlled int32_t
read_safe(loader, filters.n_fft); // attacker-controlled int32_t
filters.data.resize(filters.n_mel * filters.n_fft); // int32_t overflow -> UB
Suggested fix
Bug 1 — guard the copy:
if (size_to_copy > 0 && buf->buffer != nullptr) {
memcpy(output, buf->buffer + buf->current_offset, size_to_copy);
}
Bug 2 — validate the dimensions and widen the multiplication:
if (filters.n_mel <= 0 || filters.n_fft <= 0 ||
filters.n_mel > 1024 || filters.n_fft > 32768) {
WHISPER_LOG_ERROR("%s: invalid mel filter dimensions: n_mel=%d, n_fft=%d\n",
__func__, filters.n_mel, filters.n_fft);
return false;
}
filters.data.resize(static_cast<size_t>(filters.n_mel) * filters.n_fft);
Revalidation
Revalidated live on 2026-06-14 against HEAD df7638d8 (UBSan build):
null pointer passed as argument 1 ... declared never null @ src/whisper.cpp:3688
signed integer overflow: 1920274432 * -1094795586 @ src/whisper.cpp:1583
Both are UBSan-detected undefined behavior. Hard-crash severity is platform-dependent: Bug 1's
impact depends on the copy size and the build's release behavior (not a confirmed SEGV); Bug 2 wraps
on a release build unless it drives a downstream out-of-bounds access. Frame as undefined behavior,
not as a confirmed crash. Precedent for a whisper.cpp UB-class CVE in this area: CVE-2026-10298.
Proof-of-concept files (base64)
Decode with base64 -d > file.
poc-051-whisper-null-ptr.bin (68 bytes):
bG1nZ0dHVUYDAABHAEcAAwAAIgAAAAAAAAAAAABVRgAAIgAAAAAAAAAAAAAAAAAAAAAAAAD///////////////8AAAA=
poc-051-whisper-int-overflow.bin (52 bytes):
bG1nZ0dHVUYDAAgAAAAAAAAAAAABAAAAAAAAAEYDAAAAAAAAAAAAAAABAAAAAAAAABB1cg==
Summary
Two UBSan-detected undefined-behavior bugs occur in
whisper_model_load()when parsing a craftedGGUF model file. The first passes a null pointer to
memcpyin the buffer loader'sreadcallback;the second is a signed integer overflow when computing the mel filter buffer size from unvalidated
header fields. Both are undefined behavior under the C/C++ standards; the practical impact (whether
either becomes a hard crash) is platform- and build-dependent and is not a confirmed SEGV.
Affected component
src/whisper.cpp—whisper_model_load()memcpyatsrc/whisper.cpp:3688src/whisper.cpp:1583Reproduction
PoCs:
poc-051-whisper-null-ptr.bin(68 bytes) — triggers the null-pointer-to-memcpyUBpoc-051-whisper-int-overflow.bin(52 bytes) — triggers the signed integer overflowLoad either file through the buffer-based model loader (e.g.
whisper_init_from_buffer_with_params). On a UBSan-instrumented build:Root cause
Bug 1 — null pointer to memcpy (
whisper.cpp:3688)The custom buffer loader's
readcallback passes a null-derived source pointer tomemcpy()whenthe buffer context is exhausted or its base pointer is null. Passing a null source to
memcpyisundefined behavior regardless of the copy size:
Bug 2 — signed integer overflow (
whisper.cpp:1583)filters.n_melandfilters.n_fftare read directly from the model file with no validation, andtheir product is computed in
int32_tbefore being passed toresize(). Crafted values overflowthe multiplication (undefined behavior); the subsequent
resize()may throwstd::length_errororallocate a garbage-sized buffer:
Suggested fix
Bug 1 — guard the copy:
Bug 2 — validate the dimensions and widen the multiplication:
Revalidation
Revalidated live on 2026-06-14 against HEAD
df7638d8(UBSan build):null pointer passed as argument 1 ... declared never null@src/whisper.cpp:3688signed integer overflow: 1920274432 * -1094795586@src/whisper.cpp:1583Both are UBSan-detected undefined behavior. Hard-crash severity is platform-dependent: Bug 1's
impact depends on the copy size and the build's release behavior (not a confirmed SEGV); Bug 2 wraps
on a release build unless it drives a downstream out-of-bounds access. Frame as undefined behavior,
not as a confirmed crash. Precedent for a whisper.cpp UB-class CVE in this area: CVE-2026-10298.
Proof-of-concept files (base64)
Decode with
base64 -d > file.poc-051-whisper-null-ptr.bin (68 bytes):
poc-051-whisper-int-overflow.bin (52 bytes):