metadata_iterators.c: Limit padding size
Without this fix, its possible for libFLAC to create an oversized
padding metadata block when:
a) it merges existing padding blocks
or
b) it expands padding block during metadata changes
resulting in a corrupt FLAC file.
Patch-from: lvqcl <lvqcl.mail@gmail.com>
diff --git a/src/libFLAC/metadata_iterators.c b/src/libFLAC/metadata_iterators.c
index 1acb19c..0e2edd5 100644
--- a/src/libFLAC/metadata_iterators.c
+++ b/src/libFLAC/metadata_iterators.c
@@ -1090,7 +1090,7 @@
{
if(node->data->type == FLAC__METADATA_TYPE_PADDING && 0 != node->next && node->next->data->type == FLAC__METADATA_TYPE_PADDING) {
const unsigned growth = FLAC__STREAM_METADATA_HEADER_LENGTH + node->next->data->length;
- node->data->length += growth;
+ node->data->length += growth; /* new block size can be greater than max metadata block size, but it'll be fixed later in chain_prepare_for_write_() */
chain_delete_node_(chain, node->next);
return true;
@@ -1157,6 +1157,22 @@
}
}
+ /* check sizes of all metadata blocks; reduce padding size if necessary */
+ {
+ FLAC__Metadata_Node *node;
+ for (node = chain->head; node; node = node->next) {
+ if(node->data->length >= (1u << FLAC__STREAM_METADATA_LENGTH_LEN)) {
+ if(node->data->type == FLAC__METADATA_TYPE_PADDING) {
+ node->data->length = (1u << FLAC__STREAM_METADATA_LENGTH_LEN) - 1;
+ current_length = chain_calculate_length_(chain);
+ } else {
+ chain->status = FLAC__METADATA_CHAIN_STATUS_BAD_METADATA;
+ return 0;
+ }
+ }
+ }
+ }
+
return current_length;
}
@@ -1608,27 +1624,73 @@
* but doesn't actually alter the chain. Make sure to update the logic
* here if chain_prepare_for_write_() changes.
*/
- const FLAC__off_t current_length = chain_calculate_length_(chain);
+ FLAC__off_t current_length;
+ FLAC__Metadata_Node *node;
FLAC__ASSERT(0 != chain);
- if(use_padding) {
- /* if the metadata shrank and the last block is padding, we just extend the last padding block */
- if(current_length < chain->initial_length && chain->tail->data->type == FLAC__METADATA_TYPE_PADDING)
- return false;
- /* if the metadata shrank more than 4 bytes then there's room to add another padding block */
- else if(current_length + (FLAC__off_t)FLAC__STREAM_METADATA_HEADER_LENGTH <= chain->initial_length)
- return false;
- /* if the metadata grew but the last block is padding, try cutting the padding to restore the original length so we don't have to rewrite the whole file */
- else if(current_length > chain->initial_length) {
- const FLAC__off_t delta = current_length - chain->initial_length;
- if(chain->tail->data->type == FLAC__METADATA_TYPE_PADDING) {
- /* if the delta is exactly the size of the last padding block, remove the padding block */
- if((FLAC__off_t)chain->tail->data->length + (FLAC__off_t)FLAC__STREAM_METADATA_HEADER_LENGTH == delta)
- return false;
- /* if there is at least 'delta' bytes of padding, trim the padding down */
- else if((FLAC__off_t)chain->tail->data->length >= delta)
- return false;
+ current_length = chain_calculate_length_(chain);
+
+ for (node = chain->head; node; node = node->next) {
+ if(node != chain->tail || !use_padding) {
+ if(node->data->length >= (1u << FLAC__STREAM_METADATA_LENGTH_LEN)) {
+ if(node->data->type == FLAC__METADATA_TYPE_PADDING) {
+ current_length -= node->data->length;
+ current_length += (1u << FLAC__STREAM_METADATA_LENGTH_LEN) - 1;
+ } else {
+ return false /* the return value doesn't matter */;
+ }
+ }
+ } else {
+ /* use_padding == true, node == chain->tail */
+ unsigned block_len = node->data->length;
+
+ /* if the metadata shrank and the last block is padding, we just extend the last padding block */
+ if(current_length < chain->initial_length && node->data->type == FLAC__METADATA_TYPE_PADDING) {
+ const FLAC__off_t delta = chain->initial_length - current_length;
+ block_len += delta;
+ current_length += delta;
+ /* test extended block */
+ if(block_len >= (1u << FLAC__STREAM_METADATA_LENGTH_LEN)) {
+ current_length -= block_len;
+ current_length += (1u << FLAC__STREAM_METADATA_LENGTH_LEN) - 1;
+ }
+ }
+ /* if the metadata shrank more than 4 bytes then there's room to add another padding block */
+ else if(current_length + (FLAC__off_t)FLAC__STREAM_METADATA_HEADER_LENGTH <= chain->initial_length) {
+ /* test current last block */
+ if(block_len >= (1u << FLAC__STREAM_METADATA_LENGTH_LEN)) {
+ current_length -= block_len;
+ current_length += (1u << FLAC__STREAM_METADATA_LENGTH_LEN) - 1;
+ }
+ block_len = chain->initial_length - (FLAC__STREAM_METADATA_HEADER_LENGTH + current_length);
+ current_length += FLAC__STREAM_METADATA_HEADER_LENGTH + block_len;
+ /* test added block */
+ if(block_len >= (1u << FLAC__STREAM_METADATA_LENGTH_LEN)) {
+ current_length -= block_len;
+ current_length += (1u << FLAC__STREAM_METADATA_LENGTH_LEN) - 1;
+ }
+ }
+ /* if the metadata grew but the last block is padding, try cutting the padding to restore the original length so we don't have to rewrite the whole file */
+ else if(current_length > chain->initial_length) {
+ const FLAC__off_t delta = current_length - chain->initial_length;
+ if(node->data->type == FLAC__METADATA_TYPE_PADDING) {
+ /* if the delta is exactly the size of the last padding block, remove the padding block */
+ if((FLAC__off_t)node->data->length + (FLAC__off_t)FLAC__STREAM_METADATA_HEADER_LENGTH == delta) {
+ block_len = 0;
+ current_length -= FLAC__STREAM_METADATA_HEADER_LENGTH + node->data->length;
+ }
+ /* if there is at least 'delta' bytes of padding, trim the padding down */
+ else if((FLAC__off_t)node->data->length >= delta) {
+ block_len -= delta;
+ current_length -= delta;
+ /* test shrinked block */
+ if(block_len >= (1u << FLAC__STREAM_METADATA_LENGTH_LEN)) {
+ current_length -= block_len;
+ current_length += (1u << FLAC__STREAM_METADATA_LENGTH_LEN) - 1;
+ }
+ }
+ }
}
}
}