From 7269861bbd888132647a945a61d9c7fe3997eeef Mon Sep 17 00:00:00 2001 From: Matej Smycka Date: Sun, 22 Mar 2026 21:03:07 +0100 Subject: [PATCH] osce: Clamp pitch embedding index to prevent OOB read The LACE and NoLACE feature networks access the pitch embedding table using periods[i_subframe] as a direct index without bounds checking. SILK's decode_pitch() clamps pitch lags to [min_lag, max_lag] where max_lag = PE_MAX_LAG_MS * Fs_kHz = 288 at 16kHz. If a model is trained with pitch_max < 288 (the Python training defaults use pitch_max=257), pitch lag values in the range [pitch_max+1, 288] would read past the end of the embedding weight table. Add IMIN/IMAX clamping to bound the pitch index to [0, *_PITCH_MAX] before the embedding lookup, consistent with how FARGAN handles pitch embedding access in fargan.c:53. The current shipped model (v1.6.1) uses pitch_max=300 which covers the full SILK pitch range, but the C code should defensively clamp regardless of model parameters. --- dnn/osce.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dnn/osce.c b/dnn/osce.c index 5f2752cab..193c4fccd 100644 --- a/dnn/osce.c +++ b/dnn/osce.c @@ -183,8 +183,9 @@ static void lace_feature_net( /* scaling and dimensionality reduction */ for (i_subframe = 0; i_subframe < 4; i_subframe ++) { + int pitch_index = IMAX(0, IMIN(periods[i_subframe], LACE_PITCH_MAX)); OPUS_COPY(input_buffer, features + i_subframe * LACE_NUM_FEATURES, LACE_NUM_FEATURES); - OPUS_COPY(input_buffer + LACE_NUM_FEATURES, hLACE->layers.lace_pitch_embedding.float_weights + periods[i_subframe] * LACE_PITCH_EMBEDDING_DIM, LACE_PITCH_EMBEDDING_DIM); + OPUS_COPY(input_buffer + LACE_NUM_FEATURES, hLACE->layers.lace_pitch_embedding.float_weights + pitch_index * LACE_PITCH_EMBEDDING_DIM, LACE_PITCH_EMBEDDING_DIM); OPUS_COPY(input_buffer + LACE_NUM_FEATURES + LACE_PITCH_EMBEDDING_DIM, numbits_embedded, 2 * LACE_NUMBITS_EMBEDDING_DIM); compute_generic_conv1d( @@ -454,8 +455,9 @@ static void nolace_feature_net( /* scaling and dimensionality reduction */ for (i_subframe = 0; i_subframe < 4; i_subframe ++) { + int pitch_index = IMAX(0, IMIN(periods[i_subframe], NOLACE_PITCH_MAX)); OPUS_COPY(input_buffer, features + i_subframe * NOLACE_NUM_FEATURES, NOLACE_NUM_FEATURES); - OPUS_COPY(input_buffer + NOLACE_NUM_FEATURES, hNoLACE->layers.nolace_pitch_embedding.float_weights + periods[i_subframe] * NOLACE_PITCH_EMBEDDING_DIM, NOLACE_PITCH_EMBEDDING_DIM); + OPUS_COPY(input_buffer + NOLACE_NUM_FEATURES, hNoLACE->layers.nolace_pitch_embedding.float_weights + pitch_index * NOLACE_PITCH_EMBEDDING_DIM, NOLACE_PITCH_EMBEDDING_DIM); OPUS_COPY(input_buffer + NOLACE_NUM_FEATURES + NOLACE_PITCH_EMBEDDING_DIM, numbits_embedded, 2 * NOLACE_NUMBITS_EMBEDDING_DIM); compute_generic_conv1d(