@@ -302,78 +302,178 @@ static int wrap_180(int a)
302302 return a ;
303303}
304304
305- static int tdfb_init_coef (struct processing_module * mod , int source_nch ,
306- int sink_nch )
305+ static int tdfb_check_blob_size (struct comp_dev * dev , size_t size )
307306{
307+ if (size < sizeof (struct sof_tdfb_config ) || size > SOF_TDFB_MAX_SIZE ) {
308+ comp_err (dev , "invalid configuration blob, size %zu" , size );
309+ return - EINVAL ;
310+ }
311+
312+ return 0 ;
313+ }
314+
315+ /* Walk the TDFB blob and verify that every FIR section and trailing array
316+ * fits exactly inside the IPC payload. The walk mirrors tdfb_init_coef()
317+ * but stays bounded so a malformed blob cannot push tdfb_filter_seek()
318+ * past the buffer end at setup time. The channel-vs-stream relationships
319+ * are also checked here so a mismatching blob cannot replace the working
320+ * configuration during streaming.
321+ */
322+ static int tdfb_validate_config (struct comp_dev * dev ,
323+ struct sof_tdfb_config * config ,
324+ size_t config_size )
325+ {
326+ struct processing_module * mod = comp_mod (dev );
308327 struct tdfb_comp_data * cd = module_get_private_data (mod );
309328 struct sof_fir_coef_data * coef_data ;
310- struct sof_tdfb_config * config = cd -> config ;
311- struct comp_dev * dev = mod -> dev ;
312- int16_t * output_channel_mix_beam_off = NULL ;
313- int16_t * coefp ;
314- int size_sum = 0 ;
315- int min_delta_idx ; /* Index to beam angle with smallest delta vs. target */
316- int min_delta ; /* Smallest angle difference found in degrees */
317- int max_ch ;
318- int num_filters ;
319- int target_az ; /* Target azimuth angle in degrees */
320- int delta ; /* Target minus found angle in degrees absolute value */
321- int idx ;
322- int s ;
329+ int16_t * input_channel_select ;
330+ uint8_t * p ;
331+ uint8_t * end ;
332+ size_t remaining ;
333+ size_t step ;
334+ size_t expected ;
335+ size_t total_filters ;
336+ int16_t max_ch ;
323337 int i ;
324338
325- /* Sanity checks */
326- if ( config -> size != cd -> config_size ) {
327- comp_err ( dev , "Incorrect configuration blob size" );
339+ if (( size_t ) config -> size != config_size ) {
340+ comp_err ( dev , "blob size %zu / header size %u mismatch" ,
341+ config_size , config -> size );
328342 return - EINVAL ;
329343 }
330-
331- if (config -> num_output_channels > PLATFORM_MAX_CHANNELS ||
332- !config -> num_output_channels ) {
333- comp_err (dev , "invalid num_output_channels %d" ,
344+ if (!config -> num_output_channels ||
345+ config -> num_output_channels > PLATFORM_MAX_CHANNELS ) {
346+ comp_err (dev , "invalid num_output_channels %u" ,
334347 config -> num_output_channels );
335348 return - EINVAL ;
336349 }
337-
338- if (config -> num_output_channels != sink_nch ) {
339- comp_err (dev , "stream output channels count %d does not match configuration %d" ,
340- sink_nch , config -> num_output_channels );
350+ if (!config -> num_filters || config -> num_filters > SOF_TDFB_FIR_MAX_COUNT ) {
351+ comp_err (dev , "invalid num_filters %u" , config -> num_filters );
341352 return - EINVAL ;
342353 }
343-
344- if (config -> num_filters > SOF_TDFB_FIR_MAX_COUNT ) {
345- comp_err (dev , "invalid num_filters %d" ,
346- config -> num_filters );
347- return - EINVAL ;
348- }
349-
350- /* In SOF v1.6 - 1.8 based beamformer topologies the multiple angles, mic locations,
351- * and beam on/off switch were not defined. A most basic supported blob has num_angles
352- * equal to 1. Mic locations data is optional.
353- */
354- if (config -> num_angles == 0 || config -> num_angles > SOF_TDFB_MAX_ANGLES ) {
355- comp_err (dev , "invalid num_angles %d" ,
356- config -> num_angles );
354+ if (!config -> num_angles || config -> num_angles > SOF_TDFB_MAX_ANGLES ) {
355+ comp_err (dev , "invalid num_angles %u" , config -> num_angles );
357356 return - EINVAL ;
358357 }
359-
360358 if (!config -> angle_enum_mult ) {
361359 comp_err (dev , "invalid angle_enum_mult" );
362360 return - EINVAL ;
363361 }
364-
365362 if (config -> beam_off_defined > 1 ) {
366- comp_err (dev , "invalid beam_off_defined %d " ,
363+ comp_err (dev , "invalid beam_off_defined %u " ,
367364 config -> beam_off_defined );
368365 return - EINVAL ;
369366 }
370-
371367 if (config -> num_mic_locations > SOF_TDFB_MAX_MICROPHONES ) {
372- comp_err (dev , "invalid num_mic_locations %d " ,
368+ comp_err (dev , "invalid num_mic_locations %u " ,
373369 config -> num_mic_locations );
374370 return - EINVAL ;
375371 }
376372
373+ total_filters = (size_t )config -> num_filters *
374+ ((size_t )config -> num_angles + config -> beam_off_defined );
375+
376+ p = (uint8_t * )& config -> data [0 ];
377+ end = (uint8_t * )config + config_size ;
378+ for (i = 0 ; i < total_filters ; i ++ ) {
379+ remaining = end - p ;
380+ if (remaining < sizeof (struct sof_fir_coef_data )) {
381+ comp_err (dev , "FIR %d header out of bounds" , i );
382+ return - EINVAL ;
383+ }
384+ coef_data = (struct sof_fir_coef_data * )p ;
385+ if (fir_delay_size (coef_data ) <= 0 ) {
386+ comp_err (dev , "FIR %d invalid length %u" ,
387+ i , coef_data -> length );
388+ return - EINVAL ;
389+ }
390+ step = sizeof (struct sof_fir_coef_data ) +
391+ (size_t )coef_data -> length * sizeof (int16_t );
392+ if (step > remaining ) {
393+ comp_err (dev , "FIR %d coefs out of bounds" , i );
394+ return - EINVAL ;
395+ }
396+ p += step ;
397+ }
398+
399+ /* p now points at input_channel_select[]. The remaining bytes must
400+ * hold exactly: 3 per-filter int16 arrays (input_channel_select,
401+ * output_channel_mix, output_stream_mix), an optional beam-off output
402+ * channel mix, num_angles angle entries and num_mic_locations
403+ * microphone entries.
404+ */
405+ input_channel_select = (int16_t * )p ;
406+ expected = ((size_t )config -> num_filters * 3 +
407+ (size_t )config -> beam_off_defined * config -> num_filters ) *
408+ sizeof (int16_t ) +
409+ (size_t )config -> num_angles * sizeof (struct sof_tdfb_angle ) +
410+ (size_t )config -> num_mic_locations *
411+ sizeof (struct sof_tdfb_mic_location );
412+ if ((size_t )(end - p ) != expected ) {
413+ comp_err (dev , "blob trailer size mismatch" );
414+ return - EINVAL ;
415+ }
416+
417+ /* The blob must match the running stream. Skip these checks when no
418+ * stream is bound yet (cached channel counts are zero before prepare).
419+ */
420+ if (cd -> sink_channels && config -> num_output_channels != cd -> sink_channels ) {
421+ comp_err (dev , "blob num_output_channels %u does not match sink %d" ,
422+ config -> num_output_channels , cd -> sink_channels );
423+ return - EINVAL ;
424+ }
425+ if (cd -> source_channels ) {
426+ max_ch = 0 ;
427+ for (i = 0 ; i < config -> num_filters ; i ++ ) {
428+ if (input_channel_select [i ] < 0 ) {
429+ comp_err (dev , "invalid channel select for filter %d" , i );
430+ return - EINVAL ;
431+ }
432+ if (input_channel_select [i ] > max_ch )
433+ max_ch = input_channel_select [i ];
434+ }
435+ if (max_ch + 1 > cd -> source_channels ) {
436+ comp_err (dev , "blob needs %d source channels, stream has %d" ,
437+ max_ch + 1 , cd -> source_channels );
438+ return - EINVAL ;
439+ }
440+ }
441+
442+ return 0 ;
443+ }
444+
445+ static int tdfb_validator (struct comp_dev * dev , void * new_data , uint32_t new_data_size )
446+ {
447+ int ret ;
448+
449+ ret = tdfb_check_blob_size (dev , new_data_size );
450+ if (ret < 0 )
451+ return ret ;
452+
453+ return tdfb_validate_config (dev , new_data , new_data_size );
454+ }
455+
456+ static int tdfb_init_coef (struct processing_module * mod )
457+ {
458+ struct tdfb_comp_data * cd = module_get_private_data (mod );
459+ struct sof_fir_coef_data * coef_data ;
460+ struct sof_tdfb_config * config = cd -> config ;
461+ struct comp_dev * dev = mod -> dev ;
462+ int16_t * output_channel_mix_beam_off = NULL ;
463+ int16_t * coefp ;
464+ int size_sum = 0 ;
465+ int min_delta_idx ; /* Index to beam angle with smallest delta vs. target */
466+ int min_delta ; /* Smallest angle difference found in degrees */
467+ int num_filters ;
468+ int target_az ; /* Target azimuth angle in degrees */
469+ int delta ; /* Target minus found angle in degrees absolute value */
470+ int idx ;
471+ int i ;
472+
473+ /* Blob layout, bounds, size and stream channel relationships are
474+ * pre-validated by tdfb_validator() / tdfb_validate_config().
475+ */
476+
377477 /* Skip filter coefficients */
378478 num_filters = config -> num_filters * (config -> num_angles + config -> beam_off_defined );
379479 coefp = tdfb_filter_seek (config , num_filters );
@@ -386,8 +486,8 @@ static int tdfb_init_coef(struct processing_module *mod, int source_nch,
386486 cd -> output_stream_mix = coefp ;
387487 coefp += config -> num_filters ;
388488
389- /* Check if there's beam-off configured, then get pointers to beam angles data
390- * and microphone locations. Finally check that size matches .
489+ /* Check if there's beam-off configured, then get pointers to beam angles
490+ * data and microphone locations.
391491 */
392492 if (config -> beam_off_defined ) {
393493 output_channel_mix_beam_off = coefp ;
@@ -396,11 +496,6 @@ static int tdfb_init_coef(struct processing_module *mod, int source_nch,
396496 cd -> filter_angles = (struct sof_tdfb_angle * )coefp ;
397497 cd -> mic_locations = (struct sof_tdfb_mic_location * )
398498 (& cd -> filter_angles [config -> num_angles ]);
399- if ((uint8_t * )& cd -> mic_locations [config -> num_mic_locations ] !=
400- (uint8_t * )config + config -> size ) {
401- comp_err (dev , "invalid config size" );
402- return - EINVAL ;
403- }
404499
405500 /* Skip to requested coefficient set */
406501 min_delta = 360 ;
@@ -437,47 +532,16 @@ static int tdfb_init_coef(struct processing_module *mod, int source_nch,
437532 /* Seek to proper filter for requested angle or beam off configuration */
438533 coefp = tdfb_filter_seek (config , idx );
439534
440- /* Initialize filter bank */
535+ /* Initialize filter bank. FIR header bounds and length validity were
536+ * already checked when the blob entered the component.
537+ */
441538 for (i = 0 ; i < config -> num_filters ; i ++ ) {
442- /* Get delay line size */
443539 coef_data = (struct sof_fir_coef_data * )coefp ;
444- s = fir_delay_size (coef_data );
445- if (s > 0 ) {
446- size_sum += s ;
447- } else {
448- comp_err (dev , "FIR length %d is invalid" ,
449- coef_data -> length );
450- return - EINVAL ;
451- }
452-
453- /* Initialize coefficients for FIR filter and find next
454- * filter.
455- */
540+ size_sum += fir_delay_size (coef_data );
456541 fir_init_coef (& cd -> fir [i ], coef_data );
457542 coefp = coef_data -> coef + coef_data -> length ;
458543 }
459544
460- /* Find max used input channel */
461- max_ch = 0 ;
462- for (i = 0 ; i < config -> num_filters ; i ++ ) {
463- if (cd -> input_channel_select [i ] > max_ch )
464- max_ch = cd -> input_channel_select [i ];
465-
466- if (cd -> input_channel_select [i ] < 0 ) {
467- comp_err (dev , "invalid channel select for filter %d" , i );
468- return - EINVAL ;
469- }
470- }
471-
472- /* The stream must contain at least the number of channels that is
473- * used for filters input.
474- */
475- if (max_ch + 1 > source_nch ) {
476- comp_err (dev , "stream input channels count %d is not sufficient for configuration %d" ,
477- source_nch , max_ch + 1 );
478- return - EINVAL ;
479- }
480-
481545 return size_sum ;
482546}
483547
@@ -513,7 +577,7 @@ static int tdfb_setup(struct processing_module *mod, int source_nch, int sink_nc
513577 }
514578
515579 /* Set coefficients for each channel from coefficient blob */
516- delay_size = tdfb_init_coef (mod , source_nch , sink_nch );
580+ delay_size = tdfb_init_coef (mod );
517581 if (delay_size < 0 )
518582 return delay_size ; /* Contains error code */
519583
@@ -658,11 +722,8 @@ static int tdfb_process(struct processing_module *mod,
658722 /* Check for changed configuration */
659723 if (comp_is_new_data_blob_available (cd -> model_handler )) {
660724 cd -> config = comp_get_data_blob (cd -> model_handler , & cd -> config_size , NULL );
661- if (!cd -> config || cd -> config_size < sizeof (* cd -> config ) ||
662- cd -> config_size > SOF_TDFB_MAX_SIZE ) {
663- comp_err (dev , "invalid configuration blob, size %zu" , cd -> config_size );
725+ if (!cd -> config || tdfb_check_blob_size (dev , cd -> config_size ) < 0 )
664726 return - EINVAL ;
665- }
666727 ret = tdfb_setup (mod , audio_stream_get_channels (source ),
667728 audio_stream_get_channels (sink ),
668729 audio_stream_get_frm_fmt (source ));
@@ -754,15 +815,26 @@ static int tdfb_prepare(struct processing_module *mod,
754815 sink_channels = audio_stream_get_channels (& sinkb -> stream );
755816 rate = audio_stream_get_rate (& sourceb -> stream );
756817
818+ /* Cache stream channel counts for the blob validator before any
819+ * validate_config() call runs.
820+ */
821+ cd -> source_channels = source_channels ;
822+ cd -> sink_channels = sink_channels ;
823+
757824 /* Initialize filter */
758825 cd -> config = comp_get_data_blob (cd -> model_handler , & cd -> config_size , NULL );
759- if (!cd -> config || cd -> config_size < sizeof (* cd -> config ) ||
760- cd -> config_size > SOF_TDFB_MAX_SIZE ) {
761- comp_err (dev , "invalid configuration blob, size %zu" , cd -> config_size );
826+ if (!cd -> config || tdfb_check_blob_size (dev , cd -> config_size ) < 0 ) {
762827 ret = - EINVAL ;
763828 goto out ;
764829 }
765830
831+ /* The initial blob from topology has not been seen by the IPC-time
832+ * validator yet, so check the layout here before tdfb_setup() walks it.
833+ */
834+ ret = tdfb_validate_config (dev , cd -> config , cd -> config_size );
835+ if (ret < 0 )
836+ goto out ;
837+
766838 ret = tdfb_setup (mod , source_channels , sink_channels , frame_fmt );
767839 if (ret < 0 ) {
768840 comp_err (dev , "error: tdfb_setup failed." );
@@ -797,6 +869,11 @@ static int tdfb_prepare(struct processing_module *mod,
797869 if (ret < 0 )
798870 comp_set_state (dev , COMP_TRIGGER_RESET );
799871
872+ /* Reject malformed blobs at IPC time so a bad run-time update cannot
873+ * replace the working configuration.
874+ */
875+ comp_data_blob_set_validator (cd -> model_handler , tdfb_validator );
876+
800877 return ret ;
801878}
802879
@@ -807,6 +884,11 @@ static int tdfb_reset(struct processing_module *mod)
807884
808885 comp_dbg (mod -> dev , "entry" );
809886
887+ comp_data_blob_set_validator (cd -> model_handler , NULL );
888+
889+ cd -> source_channels = 0 ;
890+ cd -> sink_channels = 0 ;
891+
810892 tdfb_free_delaylines (mod );
811893
812894 cd -> tdfb_func = NULL ;
0 commit comments