#11617: heap-use-after-free in libavcodec/libaomenc.c found by AddressSanitizer
---------------------------------+--------------------------------------
             Reporter:  Ayose    |                     Type:  defect
               Status:  new      |                 Priority:  normal
            Component:  avcodec  |                  Version:  git-master
             Keywords:           |               Blocked By:
             Blocking:           |  Reproduced by developer:  0
Analyzed by developer:  0        |
---------------------------------+--------------------------------------
 ''AddressSanitizer'' found a `heap-use-after-free` in
 `libavcodec/libaomenc.c`.

 The problem is triggered with a command like the following one, but only
 if FFmpeg is compiled with ASAN (`./configure --toolchain=clang-asan`):

 {{{
 $ ffmpeg -f lavfi -i testsrc -to 1 -c:v libaom-av1 /tmp/0.webm
 }}}

 If FFmpeg is executed without ASAN, the previous command does not fail.


 === Steps to Reproduce

 This test is done against ab37c7e49ff98a8c32c90e6df49dd11145bc64bb, clang
 19, and libaom 3.12:

 {{{
 $ clang --version
 Debian clang version 19.1.7 (3)
 Target: x86_64-pc-linux-gnu
 Thread model: posix
 InstalledDir: /usr/lib/llvm-19/bin

 $ pkg-config --modversion aom
 3.12.1

 $ git log --format='%h %cs' -1
 ab37c7e49f 2025-05-30
 }}}

 FFmpeg is built with `--toolchain=clang-asan`:

 {{{
 $ git clone https://git.ffmpeg.org/ffmpeg.git ffmpeg

 $ cd ffmpeg

 $ ./configure \
     --toolchain=clang-asan \
     --enable-debug \
     --enable-libaom

 $ make -j16
 }}}

 Then, a `heap-use-after-free` is detected in
 `libavcodec/libaomenc.c:1028:28`:

 {{{
 $ ./ffmpeg_g -y -v warning -hide_banner -f lavfi -i testsrc -to 1 -c:v
 libaom-av1 /tmp/0.webm
 [libaom-av1 @ 0x519000002380] Neither bitrate nor constrained quality
 specified, using default CRF of 32
 =================================================================
 ==19780==ERROR: AddressSanitizer: heap-use-after-free on address
 0x509000007818 at pc 0x564397e7f93a bp 0x7f27e79ad430 sp 0x7f27e79ad428
 WRITE of size 8 at 0x509000007818 thread T10 (vf#0:0)
     #0 0x564397e7f939 in aom_init
 /tmp/ffmpeg/libavcodec/libaomenc.c:1028:28
     #1 0x564398f08f62 in avcodec_open2
 /tmp/ffmpeg/libavcodec/avcodec.c:336:19
     #2 0x564398070117 in enc_open /tmp/ffmpeg/fftools/ffmpeg_enc.c:337:16
     #3 0x5643980b7938 in enc_open
 /tmp/ffmpeg/fftools/ffmpeg_sched.c:1686:11
     #4 0x5643980b7938 in send_to_enc
 /tmp/ffmpeg/fftools/ffmpeg_sched.c:1806:19
     #5 0x564398086538 in fg_output_frame
 /tmp/ffmpeg/fftools/ffmpeg_filter.c:2359:15
     #6 0x564398084fba in fg_output_step
 /tmp/ffmpeg/fftools/ffmpeg_filter.c:2460:11
     #7 0x564398084fba in read_frames
 /tmp/ffmpeg/fftools/ffmpeg_filter.c:2521:23
     #8 0x56439807916e in filter_thread
 /tmp/ffmpeg/fftools/ffmpeg_filter.c:2962:15
     #9 0x5643980b85a9 in task_wrapper
 /tmp/ffmpeg/fftools/ffmpeg_sched.c:2534:11
     #10 0x564398013b3a in asan_thread_start(void*) asan_interceptors.cpp.o
     #11 0x7f27f5743b7a  (/lib/x86_64-linux-gnu/libc.so.6+0x92b7a)
 (BuildId: b46a78e7229ed6fe08549e2bc7ca64155cc5cf1e)
     #12 0x7f27f57c15ef in clone (/lib/x86_64-linux-gnu/libc.so.6+0x1105ef)
 (BuildId: b46a78e7229ed6fe08549e2bc7ca64155cc5cf1e)

 0x509000007818 is located 24 bytes inside of 40-byte region
 [0x509000007800,0x509000007828)
 freed by thread T10 (vf#0:0) here:
     #0 0x564398015f4a in free (/tmp/ffmpeg/ffmpeg_g+0x734f4a) (BuildId:
 fc49802141dc7a2a32e6338567bec09a75fe6f79)
     #1 0x56439996eb44 in av_packet_side_data_free
 /tmp/ffmpeg/libavcodec/packet.c:751:9

 previously allocated by thread T10 (vf#0:0) here:
     #0 0x564398016ceb in posix_memalign (/tmp/ffmpeg/ffmpeg_g+0x735ceb)
 (BuildId: fc49802141dc7a2a32e6338567bec09a75fe6f79)
     #1 0x56439b47a9cb in av_malloc /tmp/ffmpeg/libavutil/mem.c:107:9
     #2 0x56439b47a9cb in av_mallocz /tmp/ffmpeg/libavutil/mem.c:258:17
     #3 0x564399e2452d in av_cpb_properties_alloc
 /tmp/ffmpeg/libavcodec/utils.c:956:30
     #4 0x564397e7edf4 in aom_init
 /tmp/ffmpeg/libavcodec/libaomenc.c:992:17
     #5 0x564398f08f62 in avcodec_open2
 /tmp/ffmpeg/libavcodec/avcodec.c:336:19

 Thread T10 (vf#0:0) created by T0 here:
     #0 0x564397ffb6f5 in pthread_create (/tmp/ffmpeg/ffmpeg_g+0x71a6f5)
 (BuildId: fc49802141dc7a2a32e6338567bec09a75fe6f79)
     #1 0x5643980b2738 in task_start
 /tmp/ffmpeg/fftools/ffmpeg_sched.c:414:11
     #2 0x5643980e79f3 in transcode /tmp/ffmpeg/fftools/ffmpeg.c:881:11
     #3 0x5643980e79f3 in main /tmp/ffmpeg/fftools/ffmpeg.c:1009:11
     #4 0x7f27f56daca7  (/lib/x86_64-linux-gnu/libc.so.6+0x29ca7) (BuildId:
 b46a78e7229ed6fe08549e2bc7ca64155cc5cf1e)

 SUMMARY: AddressSanitizer: heap-use-after-free
 /tmp/ffmpeg/libavcodec/libaomenc.c:1028:28 in aom_init
 Shadow bytes around the buggy address:
   0x509000007580: fa fa fa fa fa fa fa fa 00 00 00 fa fa fa fa fa
   0x509000007600: fa fa fa fa fa fa fa fa 00 00 00 fa fa fa fa fa
   0x509000007680: fa fa fa fa fa fa fa fa 00 00 00 fa fa fa fa fa
   0x509000007700: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
   0x509000007780: 00 00 00 fa fa fa fa fa fa fa fa fa fa fa fa fa
 =>0x509000007800: fd fd fd[fd]fd fa fa fa fa fa fa fa fa fa fa fa
   0x509000007880: fd fd fd fa fa fa fa fa fa fa fa fa fa fa fa fa
   0x509000007900: fd fd fd fd fd fa fa fa fa fa fa fa fa fa fa fa
   0x509000007980: fa fa fa fa fa fa fa fa 00 00 00 fa fa fa fa fa
   0x509000007a00: fa fa fa fa fa fa fa fa 00 00 00 00 00 fa fa fa
   0x509000007a80: fa fa fa fa fa fa fa fa 00 00 00 fa fa fa fa fa
 Shadow byte legend (one shadow byte represents 8 application bytes):
   Addressable:           00
   Partially addressable: 01 02 03 04 05 06 07
   Heap left redzone:       fa
   Freed heap region:       fd
   Stack left redzone:      f1
   Stack mid redzone:       f2
   Stack right redzone:     f3
   Stack after return:      f5
   Stack use after scope:   f8
   Global redzone:          f9
   Global init order:       f6
   Poisoned by user:        f7
   Container overflow:      fc
   Array cookie:            ac
   Intra object redzone:    bb
   ASan internal:           fe
   Left alloca redzone:     ca
   Right alloca redzone:    cb
 ==19780==ABORTING
 }}}

 === Possible Cause

 In `libavcodec/libaomenc.c:1028:28`, the code tries to write to memory
 pointed by `cpb_props`:

 {{{#!c
 cpb_props->buffer_size = avctx->rc_buffer_size;
 }}}

 `cpb_props` is assigned in line `992`, before calling to
 `ff_dovi_configure`:

 {{{#!c
 cpb_props = ff_encode_add_cpb_side_data(avctx);
 if (!cpb_props)
     return AVERROR(ENOMEM);

 ctx->dovi.logctx = avctx;
 if ((res = ff_dovi_configure(&ctx->dovi, avctx)) < 0)
     return res;
 }}}

 
[https://git.ffmpeg.org/gitweb/ffmpeg.git/blob/ab37c7e49ff98a8c32c90e6df49dd11145bc64bb:/libavcodec/encode.c#l880
 `ff_encode_add_cpb_side_data`] returns a new array, and its address is
 stored in `avctx->coded_side_data`.
 
[https://git.ffmpeg.org/gitweb/ffmpeg.git/blob/ab37c7e49ff98a8c32c90e6df49dd11145bc64bb:/libavcodec/dovi_rpuenc.c#l241
 `ff_dovi_configure`] calls to
 
[https://git.ffmpeg.org/gitweb/ffmpeg.git/blob/ab37c7e49ff98a8c32c90e6df49dd11145bc64bb:/libavcodec/codec_par.c#l203
 `avcodec_parameters_to_context`], which replaces the memory of
 `coded_side_data`:

 {{{#!c
 int avcodec_parameters_to_context(AVCodecContext *codec,
                                   const AVCodecParameters *par)
 {
     // ...

     av_packet_side_data_free(&codec->coded_side_data,
 &codec->nb_coded_side_data);
     ret = codec_parameters_copy_side_data(&codec->coded_side_data,
 &codec->nb_coded_side_data,
                                           par->coded_side_data,
 par->nb_coded_side_data);

     // ...
 }
 }}}

 With GDB I can see that the memory stored in the `cpb_props` variable is
 freed by `av_free`:

 {{{
 $ gdb --args ./ffmpeg_g -y -v warning -hide_banner -f lavfi -i testsrc -to
 1 -c:v libaom-av1 /tmp/0.webm
 [...]

 (gdb) break libavcodec/libaomenc.c:993

 (gdb) commands
 >printf "cpb_props = %p\n", cpb_props
 >c
 >end

 (gdb) break libavcodec/packet.c:751

 (gdb) commands
 >printf "av_free: %p\n", sd[i].data
 >c
 >end

 (gdb) r
 [...]

 Thread 18 "vf#0:0" hit Breakpoint 1, aom_init (avctx=0x519000002880,
 iface=<optimized out>)
     at libavcodec/libaomenc.c:993
 993         if (!cpb_props)
 cpb_props = 0x509000007800

 Thread 18 "vf#0:0" hit Breakpoint 2, av_packet_side_data_free
 (psd=psd@entry=0x519000002b88,
     pnb_sd=pnb_sd@entry=0x519000002b90) at libavcodec/packet.c:751
 751             av_free(sd[i].data);
 av_free: 0x509000007800

 Thread 18 "vf#0:0" hit Breakpoint 2, av_packet_side_data_free
 (psd=psd@entry=0x51100000d9e0,
     pnb_sd=pnb_sd@entry=0x51100000d9e8) at libavcodec/packet.c:751
 751             av_free(sd[i].data);
 av_free: 0x509000007900

 =================================================================
 ==165799==ERROR: AddressSanitizer: heap-use-after-free on address
 0x509000007818 at pc 0x555555b3d998 bp 0x7fffdfe67090 sp 0x7fffdfe67088

 [...]
 }}}


 === Possible Fix

 I modified `libavcodec/libaomenc.c` to initialize `cpb_props` ''after''
 the call to `ff_dovi_configure`:

 {{{#!diff
 diff --git a/libavcodec/libaomenc.c b/libavcodec/libaomenc.c
 index 9a384fcc3..d2c47b43e 100644
 --- a/libavcodec/libaomenc.c
 +++ b/libavcodec/libaomenc.c
 @@ -989,14 +989,14 @@ static av_cold int aom_init(AVCodecContext *avctx,
      if (codec_caps & AOM_CODEC_CAP_HIGHBITDEPTH)
          ctx->rawimg.bit_depth = enccfg.g_bit_depth;

 -    cpb_props = ff_encode_add_cpb_side_data(avctx);
 -    if (!cpb_props)
 -        return AVERROR(ENOMEM);
 -
      ctx->dovi.logctx = avctx;
      if ((res = ff_dovi_configure(&ctx->dovi, avctx)) < 0)
          return res;

 +    cpb_props = ff_encode_add_cpb_side_data(avctx);
 +    if (!cpb_props)
 +        return AVERROR(ENOMEM);
 +
      if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) {
          const AVBitStreamFilter *filter =
 av_bsf_get_by_name("extract_extradata");
          int ret;
 }}}

 With the change, the command works with no issues:

 {{{
 $ ./ffmpeg_g -y -v warning -hide_banner -f lavfi -i testsrc -to 1 -c:v
 libaom-av1 /tmp/0.webm
 [libaom-av1 @ 0x519000002380] Neither bitrate nor constrained quality
 specified, using default CRF of 32

 $ ./ffprobe -hide_banner -i /tmp/0.webm
 Input #0, matroska,webm, from '/tmp/0.webm':
   Metadata:
     ENCODER         : Lavf62.0.102
   Duration: 00:00:01.00, start: 0.000000, bitrate: 33 kb/s
   Stream #0:0: Video: av1 (libaom-av1) (High), gbrp(pc,
 gbr/bt709/iec61966-2-1, progressive), 320x240 [SAR 1:1 DAR 4:3], 25 fps,
 25 tbr, 1k tbn
     Metadata:
       ENCODER         : Lavc62.3.101 libaom-av1
       DURATION        : 00:00:01.000000000

 $
 }}}

 Though I don't know if this change may break something else.
-- 
Ticket URL: <https://trac.ffmpeg.org/ticket/11617>
FFmpeg <https://ffmpeg.org>
FFmpeg issue tracker
_______________________________________________
FFmpeg-trac mailing list
FFmpeg-trac@avcodec.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-trac

To unsubscribe, visit link above, or email
ffmpeg-trac-requ...@ffmpeg.org with subject "unsubscribe".

Reply via email to