-
Notifications
You must be signed in to change notification settings - Fork 349
Audio: Fix pointer arithmetic mistake in ASRC initialize #3254
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -193,7 +193,8 @@ static const struct asrc_filter_params c_filter_params[CR_NUM] = { | |
| /* | ||
| * Initialises the pointers to the buffers and zeroes their content | ||
| */ | ||
| static enum asrc_error_code initialise_buffer(struct asrc_farrow *src_obj); | ||
| static enum asrc_error_code initialise_buffer(struct comp_dev *dev, | ||
| struct asrc_farrow *src_obj); | ||
|
|
||
| /* | ||
| * Initialise the pointers to the filters, set the number of filters | ||
|
|
@@ -212,15 +213,14 @@ static enum asrc_error_code initialise_filter(struct comp_dev *dev, | |
| * ------------------------------|-------------------------------------| | ||
| * 0x0000 |asrc_farrow src_obj | | ||
| * ------------------------------|-------------------------------------| | ||
| * &src_obj + sizeof(src_obj) |int_x *buffer_pointer[num_channels] | | ||
| * | | | ||
| * &src_obj + 1 |int32 impulse_response[filter_length]| | ||
| * ------------------------------|-------------------------------------| | ||
| * &impulse_response[0] + |int_x *buffer_pointer[num_channels] | | ||
| * filter_length | | | ||
| * ------------------------------|-------------------------------------| | ||
| * &buffer_pointer[0] |int_x ring_buffer[num_channels | | ||
| * &buffer_pointer[0] + | int_x ring_buffer[num_channels * | | ||
| * + num_channels*sizeof(int_x *)| *buffer_size] | | ||
| * ------------------------------|---------------------------------_---| | ||
| * &ring_buffer[0] |int32 impulse_response[filter_length]| | ||
| * + num_channels*buffer_size | | | ||
| * *sizeof(int_x) | | | ||
| * ------------------------------|-------------------------------------| | ||
| * | ||
| * Info: | ||
| * | ||
|
|
@@ -237,8 +237,6 @@ enum asrc_error_code asrc_get_required_size(struct comp_dev *dev, | |
| int num_channels, | ||
| int bit_depth) | ||
| { | ||
| int filter_length = 128; | ||
| int buffer_length = 256; | ||
| int size; | ||
|
|
||
| /* check for parameter errors */ | ||
|
|
@@ -273,14 +271,17 @@ enum asrc_error_code asrc_get_required_size(struct comp_dev *dev, | |
|
|
||
| /* accumulate the size */ | ||
| size = sizeof(struct asrc_farrow); | ||
| size += sizeof(int32_t *) * num_channels; /* pointers the the buffers */ | ||
| /* size of the ring buffers */ | ||
| size += buffer_length * num_channels * (bit_depth / 8); | ||
|
|
||
| /* size of the impulse response */ | ||
| size += filter_length * sizeof(int32_t); | ||
| size += ASRC_MAX_FILTER_LENGTH * sizeof(int32_t); | ||
|
|
||
| *required_size = size; | ||
| /* size of pointers to the buffers */ | ||
| size += sizeof(int32_t *) * num_channels; | ||
|
|
||
| /* size of the ring buffers */ | ||
| size += ASRC_MAX_BUFFER_LENGTH * num_channels * (bit_depth / 8); | ||
|
|
||
| *required_size = size; | ||
| return ASRC_EC_OK; | ||
| } | ||
|
|
||
|
|
@@ -366,18 +367,10 @@ enum asrc_error_code asrc_initialise(struct comp_dev *dev, | |
| } | ||
|
|
||
| /* | ||
| * The pointer to the internal ring buffer pointers is | ||
| * pointing to the memory subsequently to the memory where the | ||
| * src_obj lies. Only one of the buffers is initialised, | ||
| * depending on the specified bit depth. | ||
| * Set the pointer for the impulse response. It is just after | ||
| * src_obj in memory. | ||
| */ | ||
| if (src_obj->bit_depth == 32) { | ||
| src_obj->ring_buffers32 = (int32_t **)(src_obj + 1); | ||
| src_obj->ring_buffers16 = NULL; | ||
| } else if (src_obj->bit_depth == 16) { | ||
| src_obj->ring_buffers16 = (int16_t **)(src_obj + 1); | ||
| src_obj->ring_buffers32 = NULL; | ||
| } | ||
| src_obj->impulse_response = (int32_t *)(src_obj + 1); | ||
|
|
||
| /* | ||
| * Load the filter coefficients and parameters. This function | ||
|
|
@@ -392,35 +385,30 @@ enum asrc_error_code asrc_initialise(struct comp_dev *dev, | |
| return error_code; | ||
| } | ||
|
|
||
| /* | ||
| * The pointer to the internal ring buffer pointers is | ||
| * after impulse_response. Only one of the buffers is initialised, | ||
| * depending on the specified bit depth. | ||
| */ | ||
| if (src_obj->bit_depth == 32) { | ||
| src_obj->ring_buffers16 = NULL; | ||
| src_obj->ring_buffers32 = (int32_t **)(src_obj->impulse_response + | ||
| src_obj->filter_length); | ||
| } else if (src_obj->bit_depth == 16) { | ||
| src_obj->ring_buffers32 = NULL; | ||
| src_obj->ring_buffers16 = (int16_t **)(src_obj->impulse_response + | ||
| src_obj->filter_length); | ||
| } | ||
|
|
||
| /* set the channel pointers and fill buffers with zeros */ | ||
| error_code = initialise_buffer(src_obj); | ||
| error_code = initialise_buffer(dev, src_obj); | ||
|
|
||
| /* check for errors */ | ||
| if (error_code != ASRC_EC_OK) { | ||
| comp_err(dev, "asrc_initialise(), failed buffer initialise"); | ||
| return error_code; | ||
| } | ||
|
|
||
| /* | ||
| * Set the pointer for the impulse response. | ||
| * &m_impulse_response[0] = ring_buffers_x | ||
| * + num_channels * (1 + buffer_length); | ||
| * Here ring_buffer_x already points to the memory just behind | ||
| * the src_obj. The offset results from the number of pointers | ||
| * pointing to each channel and each channels buffer data. | ||
| */ | ||
| if (src_obj->bit_depth == 32) { | ||
| src_obj->impulse_response = | ||
| (int32_t *)(src_obj->ring_buffers32 + | ||
| src_obj->num_channels * | ||
| (1 + src_obj->buffer_length)); | ||
| } else if (src_obj->bit_depth == 16) { | ||
| src_obj->impulse_response = | ||
| (int32_t *)(src_obj->ring_buffers16 + | ||
| src_obj->num_channels * | ||
| (1 + src_obj->buffer_length / 2)); | ||
| } | ||
|
|
||
| /* return ok, if everything worked out */ | ||
| src_obj->is_initialised = true; | ||
| return ASRC_EC_OK; | ||
|
|
@@ -486,7 +474,7 @@ enum asrc_error_code asrc_set_fs_ratio(struct comp_dev *dev, | |
| } | ||
|
|
||
| /* Set the channel pointers and zero the buffers */ | ||
| error_code = initialise_buffer(src_obj); | ||
| error_code = initialise_buffer(dev, src_obj); | ||
| /* check for errors */ | ||
| if (error_code != ASRC_EC_OK) { | ||
| comp_err(dev, "asrc_set_fs_ratio(), failed buffer initialise"); | ||
|
|
@@ -551,52 +539,51 @@ enum asrc_error_code asrc_set_output_format(struct comp_dev *dev, | |
| /* | ||
| * BUFFER FUNCTIONS | ||
| */ | ||
| static enum asrc_error_code initialise_buffer(struct asrc_farrow *src_obj) | ||
| static enum asrc_error_code initialise_buffer(struct comp_dev *dev, | ||
| struct asrc_farrow *src_obj) | ||
| { | ||
| uint8_t *buffer; | ||
| int32_t *start_32; | ||
| int16_t *start_16; | ||
| int ch; | ||
| int n; | ||
|
|
||
| /* | ||
| * base_address points to the first address subsequently to the | ||
| * memory where the pointers to each ring buffer are stored. | ||
| */ | ||
| if (src_obj->bit_depth == 32) | ||
| buffer = (uint8_t *)(src_obj->ring_buffers32 + | ||
| src_obj->num_channels); | ||
| else if (src_obj->bit_depth == 16) | ||
| buffer = (uint8_t *)(src_obj->ring_buffers16 + | ||
| src_obj->num_channels); | ||
|
|
||
| /* | ||
| * set buffer_length to filter_length * 2 to compensate for | ||
| * Set buffer_length to filter_length * 2 to compensate for | ||
| * missing element wise wrap around while loading but allowing | ||
| * aligned loads. | ||
| */ | ||
| src_obj->buffer_length = src_obj->filter_length * 2; | ||
| if (src_obj->buffer_length > ASRC_MAX_BUFFER_LENGTH) { | ||
| comp_err(dev, "initialise_buffer(), buffer_length %d exceeds max.", | ||
| src_obj->buffer_length); | ||
| return ASRC_EC_INVALID_BUFFER_LENGTH; | ||
| } | ||
|
|
||
| src_obj->buffer_write_position = src_obj->filter_length; | ||
|
|
||
| /* set the base addresses for every channel and initialise the | ||
| * buffers to zero | ||
| /* | ||
| * Initialize the dynamically allocated 2D array and clear the | ||
singalsu marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| * buffers to zero. | ||
| */ | ||
| if (src_obj->bit_depth == 32) { | ||
| for (ch = 0; ch < src_obj->num_channels; ch++) { | ||
| src_obj->ring_buffers32[ch] = ((int32_t *)buffer) + | ||
| start_32 = (int32_t *)(src_obj->ring_buffers32 + | ||
| src_obj->num_channels); | ||
| for (ch = 0; ch < src_obj->num_channels; ch++) | ||
| src_obj->ring_buffers32[ch] = start_32 + | ||
| ch * src_obj->buffer_length; | ||
|
|
||
| /* initialise to zero */ | ||
| for (n = 0; n < src_obj->buffer_length; n++) | ||
| src_obj->ring_buffers32[ch][n] = 0; | ||
| } | ||
| } else if (src_obj->bit_depth == 16) { | ||
| for (ch = 0; ch < src_obj->num_channels; ch++) { | ||
| src_obj->ring_buffers16[ch] = ((int16_t *)buffer) + | ||
| /* initialise to zero */ | ||
| memset(start_32, 0, src_obj->num_channels * | ||
| src_obj->buffer_length * sizeof(int32_t)); | ||
| } else { | ||
| start_16 = (int16_t *)(src_obj->ring_buffers16 + | ||
| src_obj->num_channels); | ||
| for (ch = 0; ch < src_obj->num_channels; ch++) | ||
| src_obj->ring_buffers16[ch] = start_16 + | ||
| ch * src_obj->buffer_length; | ||
|
|
||
| /* initialise to zero */ | ||
| for (n = 0; n < src_obj->buffer_length; n++) | ||
| src_obj->ring_buffers16[ch][n] = 0; | ||
| } | ||
| /* initialise to zero */ | ||
| memset(start_16, 0, src_obj->num_channels * | ||
| src_obj->buffer_length * sizeof(int16_t)); | ||
| } | ||
|
||
|
|
||
| return ASRC_EC_OK; | ||
|
|
@@ -764,6 +751,13 @@ static enum asrc_error_code initialise_filter(struct comp_dev *dev, | |
| return ASRC_EC_INVALID_SAMPLE_RATE; | ||
| } | ||
|
|
||
| /* Check that filter length does not exceed allocated */ | ||
| if (src_obj->filter_length > ASRC_MAX_FILTER_LENGTH) { | ||
| comp_err(dev, "initialise_filter(), filter_length %d exceeds max", | ||
| src_obj->filter_length); | ||
| return ASRC_EC_INVALID_FILTER_LENGTH; | ||
| } | ||
|
|
||
| /* The function pointer is set according to the number of polyphase | ||
| * filters | ||
| */ | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wait, how does this work? There is a bit of a reserved region of size filter_length before the first of the buffers? Also, 2D array is incompatible with (int32_t **)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I moved the filter coefficients array to be before buffers32/16. This way I don't need to take into account the variable word length of buffers when computing next array location. The buffers32/16 is the last part of the allocated blob.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I understand a dynamic size 2D array needs to use (int32_t **) pointer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dynamic sized 2D array isn't a continuous memory region unlike statically allocated 2D arrays. Just to be aware of that again.
Of course, when the lower size varies you sure can only go the array-of-pointers with each pointer being an individual array way, and if you want allocate the individual arrays continuously in a big chunk -- but you still need a separate memory region to hold the pointers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes I know. The array of pointers to each row data allows that. However in this case the memory is allocated continuously for simplicity. I'm not aware of ways to do this in C in much cleaner way. We discussed the possibility to fix array first dimension to SOF max channels for simpler initialization but it would consume more RAM.
Now the channel # form the array rows and the channel samples are the columns for "src_obj->ring_buffers32[ch][frame]". The memory region to hold the pointers to column index zeros is in the beginning of the memory area for this "dynamic 2D array". This can be seen in function initialise_buffer().
Actually now I think there's a mistake in asrc_get_required_size(). When computing "size += ASRC_MAX_BUFFER_LENGTH * num_channels * (bit_depth / 8);" the array of pointers to colums data is missing. I think if should be num_channels * sizeof(int32 **). The bug has not triggered because the used filters are significantly shorter than MAX. Do you agree?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, no mistake, there's the "size += sizeof(int32_t *) * num_channels;". It holds the pointers in beginning of ring_buffers32/16 the pointers to columns data. So this proposal should be OK.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd say the array of pointers should have MAX, but null out any channels between the actual available ones and the max. So for stereo, ring_buffers32[0] and ring_buffers32[1] are valid, while ring_buffers32[2] through ring_buffers32[7] are reliably null.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would change the initialization and consume more allocated RAM. I don't think the stream channels count could change between prepare() and copy(). So such channels could increase should not happen. Also the channel count for ASRC is initialized only in prepare(), it's not monitored/updated in copy(). Access to more than initialized would require cd->asrc_obj corruption.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changing an allocation from 8 to 32 bytes? FWIW I don't think there are blocks in the heap smaller than 32 bytes in most memory configurations anyway. So for the array of pointers you can go ahead an allocate with [MAX] anyway, to protect against mistakes that escape review. Not gonna block on this though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The ASRC object allocation goes to 2 kB or 4 kB pool usually. This is relatively small in that amount yes. I'll do the next version with channels max fixed length for pointers if keep the channel specific buffers. If channel interleaved (as SRC), no need for such.