The branch, master has been updated via c05fc27dd33b361eb0105157ab7d3a01c2ffa782 (commit) from cdee519d40e61bd65ba5b3fbec00acd50a08d0d9 (commit)
- Log ----------------------------------------------------------------- commit c05fc27dd33b361eb0105157ab7d3a01c2ffa782 Author: Lynne <d...@lynne.ee> AuthorDate: Mon Aug 11 22:26:35 2025 +0900 Commit: michaelni <mich...@niedermayer.cc> CommitDate: Tue Aug 19 14:32:21 2025 +0000 aacdec_usac: use RefStruct to track unfinished extension buffers Extensions in AAC USAC can be stored across multiple frames (mainly to keep CBR compliance). This means that we need to reallocate a buffer when new data is received, accumulate the bitstream data, and so on until the end of extension flag is signalled and the extension can be decoded. This is made more complicated by the way in which the AAC channel layout switching is performed. After decades of evolution, our AAC decoder evolved to double-buffer its entire configuration. All changes are buffered, verified, and applied, on a per-frame basis if required, in often random order. Since we allocate the extension data on heap, this means that if configuration is applied, in order to avoid double-freeing, we have to keep track of what we've allocated. It should be noted that extensions which are spread in multiple frames are generally rare, so an optimization to introduce av_refstruct_realloc() wouldn't generally be useful across the codebase. Therefore, a copy is good enough for now. Thanks to Michael Niedermayer for additional fixing. Fixes: double free Fixes: 393523547/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_AAC_LATM_fuzzer-6740617236905984 Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg diff --git a/libavcodec/aac/aacdec.c b/libavcodec/aac/aacdec.c index 947079bc3d..9b42014ee8 100644 --- a/libavcodec/aac/aacdec.c +++ b/libavcodec/aac/aacdec.c @@ -62,6 +62,7 @@ #include "libavutil/opt.h" #include "libavutil/tx.h" #include "libavutil/version.h" +#include "libavutil/refstruct.h" /* * supported tools @@ -421,6 +422,26 @@ static uint64_t sniff_channel_order(uint8_t (*layout_map)[3], int tags) return layout; } +static void copy_oc(OutputConfiguration *dst, OutputConfiguration *src) +{ + int i; + + for (i = 0; i < src->usac.nb_elems; i++) { + AACUsacElemConfig *src_e = &src->usac.elems[i]; + AACUsacElemConfig *dst_e = &dst->usac.elems[i]; + /* dst_e->ext.pl_buf is guaranteed to be set to src_e->ext.pl_buf + * upon this function's return */ + av_refstruct_replace(&dst_e->ext.pl_buf, src_e->ext.pl_buf); + } + + /* Unref all additional buffers to close leaks */ + for (; i < dst->usac.nb_elems; i++) + av_refstruct_unref(&dst->usac.elems[i].ext.pl_buf); + + /* Set all other properties */ + *dst = *src; +} + /** * Save current output configuration if and only if it has been locked. */ @@ -429,7 +450,7 @@ static int push_output_configuration(AACDecContext *ac) int pushed = 0; if (ac->oc[1].status == OC_LOCKED || ac->oc[0].status == OC_NONE) { - ac->oc[0] = ac->oc[1]; + copy_oc(&ac->oc[0], &ac->oc[1]); pushed = 1; } ac->oc[1].status = OC_NONE; @@ -443,7 +464,8 @@ static int push_output_configuration(AACDecContext *ac) static void pop_output_configuration(AACDecContext *ac) { if (ac->oc[1].status != OC_LOCKED && ac->oc[0].status != OC_NONE) { - ac->oc[1] = ac->oc[0]; + copy_oc(&ac->oc[1], &ac->oc[0]); + ac->avctx->ch_layout = ac->oc[1].ch_layout; ff_aac_output_configure(ac, ac->oc[1].layout_map, ac->oc[1].layout_map_tags, ac->oc[1].status, 0); @@ -1110,7 +1132,7 @@ static av_cold int decode_close(AVCodecContext *avctx) AACUSACConfig *usac = &oc->usac; for (int j = 0; j < usac->nb_elems; j++) { AACUsacElemConfig *ec = &usac->elems[j]; - av_freep(&ec->ext.pl_data); + av_refstruct_unref(&ec->ext.pl_buf); } av_channel_layout_uninit(&ac->oc[i].ch_layout); diff --git a/libavcodec/aac/aacdec.h b/libavcodec/aac/aacdec.h index e5a79a7139..b3763fdccc 100644 --- a/libavcodec/aac/aacdec.h +++ b/libavcodec/aac/aacdec.h @@ -344,7 +344,7 @@ typedef struct AACUsacElemConfig { uint8_t payload_frag; uint32_t default_len; uint32_t pl_data_offset; - uint8_t *pl_data; + uint8_t *pl_buf; } ext; } AACUsacElemConfig; @@ -353,7 +353,7 @@ typedef struct AACUSACConfig { uint16_t core_frame_len; uint16_t stream_identifier; - AACUsacElemConfig elems[64]; + AACUsacElemConfig elems[MAX_ELEM_ID]; int nb_elems; struct { diff --git a/libavcodec/aac/aacdec_usac.c b/libavcodec/aac/aacdec_usac.c index e03e6e015f..c4b821bbba 100644 --- a/libavcodec/aac/aacdec_usac.c +++ b/libavcodec/aac/aacdec_usac.c @@ -24,12 +24,13 @@ #include "aacdec_ac.h" #include "libavcodec/aacsbr.h" - #include "libavcodec/aactab.h" -#include "libavutil/mem.h" #include "libavcodec/mpeg4audio.h" #include "libavcodec/unary.h" +#include "libavutil/mem.h" +#include "libavutil/refstruct.h" + /* Number of scalefactor bands per complex prediction band, equal to 2. */ #define SFB_PER_PRED_BAND 2 @@ -1574,7 +1575,6 @@ static int parse_audio_preroll(AACDecContext *ac, GetBitContext *gb) static int parse_ext_ele(AACDecContext *ac, AACUsacElemConfig *e, GetBitContext *gb) { - uint8_t *tmp; uint8_t pl_frag_start = 1; uint8_t pl_frag_end = 1; uint32_t len; @@ -1601,18 +1601,26 @@ static int parse_ext_ele(AACDecContext *ac, AACUsacElemConfig *e, if (pl_frag_start) e->ext.pl_data_offset = 0; - /* If an extension starts and ends this packet, we can directly use it */ + /* If an extension starts and ends this packet, we can directly use it below. + * Otherwise, we have to copy it to a buffer and accumulate it. */ if (!(pl_frag_start && pl_frag_end)) { - tmp = av_realloc(e->ext.pl_data, e->ext.pl_data_offset + len); - if (!tmp) { - av_free(e->ext.pl_data); + /* Reallocate the data */ + uint8_t *tmp_buf = av_refstruct_alloc_ext(e->ext.pl_data_offset + len, + AV_REFSTRUCT_FLAG_NO_ZEROING, + NULL, NULL); + if (!tmp_buf) return AVERROR(ENOMEM); - } - e->ext.pl_data = tmp; + + /* Copy the data over only if we had saved data to begin with */ + if (e->ext.pl_buf) + memcpy(tmp_buf, e->ext.pl_buf, e->ext.pl_data_offset); + + av_refstruct_unref(&e->ext.pl_buf); + e->ext.pl_buf = tmp_buf; /* Readout data to a buffer */ for (int i = 0; i < len; i++) - e->ext.pl_data[e->ext.pl_data_offset + i] = get_bits(gb, 8); + e->ext.pl_buf[e->ext.pl_data_offset + i] = get_bits(gb, 8); } e->ext.pl_data_offset += len; @@ -1624,7 +1632,7 @@ static int parse_ext_ele(AACDecContext *ac, AACUsacElemConfig *e, GetBitContext *gb2 = gb; GetBitContext gbc; if (!(pl_frag_start && pl_frag_end)) { - ret = init_get_bits8(&gbc, e->ext.pl_data, pl_len); + ret = init_get_bits8(&gbc, e->ext.pl_buf, pl_len); if (ret < 0) return ret; @@ -1642,7 +1650,7 @@ static int parse_ext_ele(AACDecContext *ac, AACUsacElemConfig *e, /* This should never happen */ av_assert0(0); } - av_freep(&e->ext.pl_data); + av_refstruct_unref(&e->ext.pl_buf); if (ret < 0) return ret; ----------------------------------------------------------------------- Summary of changes: libavcodec/aac/aacdec.c | 28 +++++++++++++++++++++++++--- libavcodec/aac/aacdec.h | 4 ++-- libavcodec/aac/aacdec_usac.c | 32 ++++++++++++++++++++------------ 3 files changed, 47 insertions(+), 17 deletions(-) hooks/post-receive -- _______________________________________________ ffmpeg-cvslog mailing list ffmpeg-cvslog@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-cvslog To unsubscribe, visit link above, or email ffmpeg-cvslog-requ...@ffmpeg.org with subject "unsubscribe".