Skip to content

Commit eacbd82

Browse files
whisper : default-initialize whisper_mel to avoid uninitialized read (#3981)
whisper_full()/whisper_full_with_state() only compute the mel spectrogram when n_samples > 0. For n_samples == 0 on a freshly allocated state the mel is never touched, but struct whisper_mel had no member initializers, so n_len / n_len_org / n_mel were indeterminate heap garbage (the state is allocated with new whisper_state). seek_end is derived from that garbage and, depending on it, the call either quietly returns 0 or runs the encoder with garbage dimensions over an empty (NULL) mel buffer, dereferencing address 0 in the mel copy loop. Give whisper_mel default member initializers so a never-computed mel reads as 0 frames and the n_samples == 0 case deterministically takes the existing too-short path. Fixes #3978
1 parent c4ac001 commit eacbd82

3 files changed

Lines changed: 43 additions & 3 deletions

File tree

src/whisper.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -416,9 +416,9 @@ static const std::map<whisper_alignment_heads_preset, whisper_aheads> g_aheads {
416416
static std::vector<uint32_t> get_alignment_heads_by_layer(const whisper_context_params & cparams, int il, int32_t n_text_layer, int32_t n_head);
417417

418418
struct whisper_mel {
419-
int n_len;
420-
int n_len_org;
421-
int n_mel;
419+
int n_len = 0;
420+
int n_len_org = 0;
421+
int n_mel = 0;
422422

423423
std::vector<float> data;
424424
};

tests/CMakeLists.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,16 @@ target_link_libraries(${BUFFER_LOADER_TEST} PRIVATE common)
103103
add_test(NAME ${BUFFER_LOADER_TEST} COMMAND ${BUFFER_LOADER_TEST})
104104
set_tests_properties(${BUFFER_LOADER_TEST} PROPERTIES LABELS "unit;gh")
105105

106+
# whisper_full() with n_samples == 0 must not read an uninitialized mel (#3978)
107+
set(ZERO_SAMPLES_TEST test-whisper-zero-samples)
108+
add_executable(${ZERO_SAMPLES_TEST} ${ZERO_SAMPLES_TEST}.cpp)
109+
target_include_directories(${ZERO_SAMPLES_TEST} PRIVATE ../include ../ggml/include ../examples)
110+
target_link_libraries(${ZERO_SAMPLES_TEST} PRIVATE common)
111+
target_compile_definitions(${ZERO_SAMPLES_TEST} PRIVATE
112+
WHISPER_MODEL_PATH="${PROJECT_SOURCE_DIR}/models/for-tests-ggml-tiny.bin")
113+
add_test(NAME ${ZERO_SAMPLES_TEST} COMMAND ${ZERO_SAMPLES_TEST})
114+
set_tests_properties(${ZERO_SAMPLES_TEST} PROPERTIES LABELS "tiny;gh")
115+
106116
# VAD test tests VAD in isolation
107117
set(VAD_TEST test-vad)
108118
add_executable(${VAD_TEST} ${VAD_TEST}.cpp)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#include "whisper.h"
2+
3+
#include <cstdio>
4+
5+
#ifdef NDEBUG
6+
#undef NDEBUG
7+
#endif
8+
#include <cassert>
9+
10+
int main() {
11+
struct whisper_context_params cparams = whisper_context_default_params();
12+
cparams.use_gpu = false;
13+
14+
struct whisper_context * ctx = whisper_init_from_file_with_params(WHISPER_MODEL_PATH, cparams);
15+
assert(ctx != nullptr);
16+
17+
struct whisper_full_params params = whisper_full_default_params(WHISPER_SAMPLING_GREEDY);
18+
params.no_timestamps = true;
19+
params.print_progress = false;
20+
params.print_realtime = false;
21+
22+
const int rc = whisper_full(ctx, params, nullptr, 0);
23+
assert(rc == 0);
24+
assert(whisper_full_n_segments(ctx) == 0);
25+
26+
whisper_free(ctx);
27+
28+
printf("test-whisper-zero-samples: OK\n");
29+
return 0;
30+
}

0 commit comments

Comments
 (0)