Skip to content

Commit 746ca74

Browse files
committed
audio: tdfb: register IPC-time blob validator
Hook a tdfb blob validator into the model handler so a corrupted or mismatching run-time configuration update is rejected before it can replace the working blob. Capture then continues with the previously set filters instead of being interrupted by bad re-configuration. The TDFB blob is variable size and the per-filter walk in tdfb_init_coef() was not bounded against the IPC payload, so a bad length field could push tdfb_filter_seek() past the buffer. The validator now walks every FIR section and the trailing arrays with byte-bounded steps, requires the total layout to exactly match config->size, and rejects blobs whose num_output_channels or input_channel_select[] entries do not fit the channel counts of the running stream. The same walk is reused at prepare time on the initial topology blob. With ingress fully validated, the redundant sanity checks inside tdfb_init_coef() are dropped. Signed-off-by: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
1 parent 3bbc01e commit 746ca74

2 files changed

Lines changed: 179 additions & 95 deletions

File tree

src/audio/tdfb/tdfb.c

Lines changed: 177 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -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;

src/audio/tdfb/tdfb_comp.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ struct tdfb_comp_data {
8888
int16_t *output_stream_mix; /**< for each FIR define stream */
8989
int16_t az_value; /**< beam steer azimuth as in control enum */
9090
int16_t az_value_estimate; /**< beam steer azimuth as in control enum */
91+
int16_t source_channels; /**< source channel count cached for validator */
92+
int16_t sink_channels; /**< sink channel count cached for validator */
9193
size_t config_size; /**< size of the configuration blob */
9294
size_t fir_delay_size; /**< allocated size */
9395
unsigned int max_frames; /**< max frames to process */

0 commit comments

Comments
 (0)