From a722846cb60859d0ed140559df3b45a2c2212a88 Mon Sep 17 00:00:00 2001 From: Ben Younes Date: Tue, 25 Aug 2026 12:40:19 +0200 Subject: [PATCH] whisper : guard null source in buffer loader read callback (#3982) * whisper : guard null source in buffer loader read callback whisper_init_from_buffer_with_params_no_state installs a read callback that copies from buf->buffer + current_offset. When the buffer is exhausted (or the supplied buffer is empty), size_to_copy is 0 and the source pointer can be null; passing a null pointer to memcpy is undefined behavior even for a zero-length copy (UBSan: 'null pointer passed as argument 2' at the memcpy). Loading a crafted/short model through the buffer loader could hit this. Skip the memcpy when there is nothing to copy. Loading from a null/empty or truncated buffer now fails gracefully (returns NULL) with no UB. This addresses bug 1 of #3879. Bug 2 (integer overflow when sizing the mel filter buffer) is covered by the open PR #3780. * fixup! whisper : guard null source in buffer loader read callback --------- Co-authored-by: Ben Younes <2910651+ousamabenyounes@users.noreply.github.com> --- src/parakeet.cpp | 4 +++- src/whisper.cpp | 4 +++- tests/CMakeLists.txt | 8 +++++++- tests/test-whisper-buffer-loader.cpp | 24 ++++++++++++++++++++++++ 4 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 tests/test-whisper-buffer-loader.cpp diff --git a/src/parakeet.cpp b/src/parakeet.cpp index 59ad5c739..1d46c44d8 100644 --- a/src/parakeet.cpp +++ b/src/parakeet.cpp @@ -3109,7 +3109,9 @@ struct parakeet_context * parakeet_init_from_buffer_with_params_no_state(void * 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); + if (size_to_copy > 0 && buf->buffer != nullptr) { + memcpy(output, buf->buffer + buf->current_offset, size_to_copy); + } buf->current_offset += size_to_copy; return size_to_copy; diff --git a/src/whisper.cpp b/src/whisper.cpp index da91c4b2e..f0f08c795 100644 --- a/src/whisper.cpp +++ b/src/whisper.cpp @@ -3766,7 +3766,9 @@ struct whisper_context * whisper_init_from_buffer_with_params_no_state(void * bu 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); + if (size_to_copy > 0 && buf->buffer != nullptr) { + memcpy(output, buf->buffer + buf->current_offset, size_to_copy); + } buf->current_offset += size_to_copy; return size_to_copy; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index aecc6f3b2..b8bbbe84d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -96,6 +96,13 @@ target_link_libraries(${UTF8_TEST} PRIVATE common) add_test(NAME ${UTF8_TEST} COMMAND ${UTF8_TEST}) set_tests_properties(${UTF8_TEST} PROPERTIES LABELS "unit") +set(BUFFER_LOADER_TEST test-whisper-buffer-loader) +add_executable(${BUFFER_LOADER_TEST} ${BUFFER_LOADER_TEST}.cpp) +target_include_directories(${BUFFER_LOADER_TEST} PRIVATE ../include ../ggml/include ../examples) +target_link_libraries(${BUFFER_LOADER_TEST} PRIVATE common) +add_test(NAME ${BUFFER_LOADER_TEST} COMMAND ${BUFFER_LOADER_TEST}) +set_tests_properties(${BUFFER_LOADER_TEST} PROPERTIES LABELS "unit;gh") + # VAD test tests VAD in isolation set(VAD_TEST test-vad) add_executable(${VAD_TEST} ${VAD_TEST}.cpp) @@ -177,4 +184,3 @@ add_parakeet_transcription_test( samples/diffusion2023-07-03.flac tests/parakeet-expected-diffusion-output.txt 0.95) - diff --git a/tests/test-whisper-buffer-loader.cpp b/tests/test-whisper-buffer-loader.cpp new file mode 100644 index 000000000..785b383f6 --- /dev/null +++ b/tests/test-whisper-buffer-loader.cpp @@ -0,0 +1,24 @@ +#include "whisper.h" + +#include +#include + +#ifdef NDEBUG +#undef NDEBUG +#endif +#include + +int main() { + struct whisper_context_params cparams = whisper_context_default_params(); + cparams.use_gpu = false; + + struct whisper_context * ctx_empty = whisper_init_from_buffer_with_params(nullptr, 1, cparams); + assert(ctx_empty == nullptr); + + uint8_t truncated[8] = { 0 }; + struct whisper_context * ctx_trunc = whisper_init_from_buffer_with_params(truncated, sizeof(truncated), cparams); + assert(ctx_trunc == nullptr); + + printf("test-whisper-buffer-loader: OK\n"); + return 0; +}