Opened 15 months ago

Last modified 15 months ago

#10474 new defect

Crash when vstack is used on two yuv420p streams with height not divisible by 2

Reported by: André Martins Owned by:
Priority: normal Component: avfilter
Version: git-master Keywords:
Cc: André Martins Blocked By:
Blocking: Reproduced by developer: no
Analyzed by developer: no

Description

Summary of the bug:
Out-of-bounds write (which often causes a crash) when vstack is used on two yuv420p streams with height not divisible by 2. Apparently, this only happens when the width is bigger than 64 (no errors for 64, errors for 65 and 66).

How to reproduce:

$ valgrind ./ffmpeg_g -v 9 -loglevel 99 -t 1 -f lavfi -i "testsrc=s=100x55,format=yuv420p[a]; testsrc=s=100x77,format=yuv420p[b]; [a][b]vstack" -f null /dev/null
==21919== Memcheck, a memory error detector
==21919== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==21919== Using Valgrind-3.16.1 and LibVEX; rerun with -h for copyright info
==21919== Command: ./ffmpeg_g -v 9 -loglevel 99 -t 1 -f lavfi -i testsrc=s=100x55,format=yuv420p[a];\ testsrc=s=100x77,format=yuv420p[b];\ [a][b]vstack -f null /dev/null
==21919== 
ffmpeg version git-2023-07-16-36f4e6f8f4c Copyright (c) 2000-2023 the FFmpeg developers
  built with gcc 10 (Debian 10.2.1-6)
  configuration: --fatal-warnings --enable-gpl --enable-version3 --enable-nonfree --enable-libvpx --enable-libopus --enable-libx264 --enable-openssl --assert-level=1 --disable-doc
  libavutil      58. 14.100 / 58. 14.100
  libavcodec     60. 22.100 / 60. 22.100
  libavformat    60. 10.100 / 60. 10.100
  libavdevice    60.  2.101 / 60.  2.101
  libavfilter     9.  8.102 /  9.  8.102
  libswscale      7.  3.100 /  7.  3.100
  libswresample   4. 11.100 /  4. 11.100
  libpostproc    57.  2.100 / 57.  2.100
Splitting the commandline.
Reading option '-v' ... matched as option 'v' (set logging level) with argument '9'.
Reading option '-loglevel' ... matched as option 'loglevel' (set logging level) with argument '99'.
Reading option '-t' ... matched as option 't' (record or transcode "duration" seconds of audio/video) with argument '1'.
Reading option '-f' ... matched as option 'f' (force format) with argument 'lavfi'.
Reading option '-i' ... matched as input url with argument 'testsrc=s=100x55,format=yuv420p[a]; testsrc=s=100x77,format=yuv420p[b]; [a][b]vstack'.
Reading option '-f' ... matched as option 'f' (force format) with argument 'null'.
Reading option '/dev/null' ... matched as output url.
Finished splitting the commandline.
Parsing a group of options: global .
Applying option v (set logging level) with argument 9.
Successfully parsed a group of options.
Parsing a group of options: input url testsrc=s=100x55,format=yuv420p[a]; testsrc=s=100x77,format=yuv420p[b]; [a][b]vstack.
Applying option t (record or transcode "duration" seconds of audio/video) with argument 1.
Applying option f (force format) with argument lavfi.
Successfully parsed a group of options.
Opening an input file: testsrc=s=100x55,format=yuv420p[a]; testsrc=s=100x77,format=yuv420p[b]; [a][b]vstack.
[AVFilterGraph @ 0x57114c0] Setting 's' to value '100x55'
[AVFilterGraph @ 0x57114c0] Setting 'pix_fmts' to value 'yuv420p'
[AVFilterGraph @ 0x57114c0] Setting 's' to value '100x77'
[AVFilterGraph @ 0x57114c0] Setting 'pix_fmts' to value 'yuv420p'
detected 8 logical cores
[Parsed_testsrc_0 @ 0x57147c0] size:100x55 rate:25/1 duration:-1.000000 sar:1/1
[Parsed_testsrc_2 @ 0x5715a80] size:100x77 rate:25/1 duration:-1.000000 sar:1/1
[auto_scale_0 @ 0x571c140] w:iw h:ih flags:'' interl:0
[Parsed_format_1 @ 0x57153c0] auto-inserting filter 'auto_scale_0' between the filter 'Parsed_testsrc_0' and the filter 'Parsed_format_1'
[auto_scale_1 @ 0x5753fc0] w:iw h:ih flags:'' interl:0
[Parsed_format_3 @ 0x5716680] auto-inserting filter 'auto_scale_1' between the filter 'Parsed_testsrc_2' and the filter 'Parsed_format_3'
[AVFilterGraph @ 0x57114c0] query_formats: 6 queried, 3 merged, 2 already done, 0 delayed
[auto_scale_0 @ 0x571c140] w:100 h:55 fmt:rgb24 sar:1/1 -> w:100 h:55 fmt:yuv420p sar:1/1 flags:0x00000004
[auto_scale_1 @ 0x5753fc0] w:100 h:77 fmt:rgb24 sar:1/1 -> w:100 h:77 fmt:yuv420p sar:1/1 flags:0x00000004
[Parsed_vstack_4 @ 0x5716d40] [framesync @ 0x5716fa8] Selected 1/25 time base
[Parsed_vstack_4 @ 0x5716d40] [framesync @ 0x5716fa8] Sync level 1
==21919== Thread 2:
==21919== Invalid write of size 8
==21919==    at 0x483F849: memmove (vg_replace_strmem.c:1270)
==21919==    by 0x10A366D: image_copy_plane (imgutils.c:353)
==21919==    by 0x10A366D: av_image_copy_plane (imgutils.c:378)
==21919==    by 0x4661BE: process_slice (vf_stack.c:153)
==21919==    by 0x325808: worker_func (pthread.c:49)
==21919==    by 0x10BECAD: run_jobs (slicethread.c:65)
==21919==    by 0x10BECAD: thread_worker (slicethread.c:89)
==21919==    by 0x52E7EA6: start_thread (pthread_create.c:477)
==21919==    by 0x53FEA2E: clone (clone.S:95)
==21919==  Address 0x59d36a0 is 0 bytes after a block of size 4,256 alloc'd
==21919==    at 0x483AEB8: memalign (vg_replace_malloc.c:906)
==21919==    by 0x483AFCE: posix_memalign (vg_replace_malloc.c:1070)
==21919==    by 0x10A753E: av_malloc (mem.c:105)
==21919==    by 0x10920B5: av_buffer_alloc (buffer.c:82)
==21919==    by 0x109211F: av_buffer_allocz (buffer.c:95)
==21919==    by 0x1092814: pool_alloc_buffer (buffer.c:363)
==21919==    by 0x1092814: av_buffer_pool_get (buffer.c:401)
==21919==    by 0x321401: ff_frame_pool_get (framepool.c:217)
==21919==    by 0x4CC22A: ff_default_get_video_buffer2 (video.c:87)
==21919==    by 0x466049: process_frame (vf_stack.c:178)
==21919==    by 0x32247F: ff_framesync_activate (framesync.c:364)
==21919==    by 0x30EB04: ff_filter_activate (avfilter.c:1323)
==21919==    by 0x312284: get_frame_internal (buffersink.c:137)
==21919== 
==21919== Invalid write of size 2
==21919==    at 0x483F8A3: memmove (vg_replace_strmem.c:1270)
==21919==    by 0x10A366D: image_copy_plane (imgutils.c:353)
==21919==    by 0x10A366D: av_image_copy_plane (imgutils.c:378)
==21919==    by 0x4661BE: process_slice (vf_stack.c:153)
==21919==    by 0x325808: worker_func (pthread.c:49)
==21919==    by 0x10BECAD: run_jobs (slicethread.c:65)
==21919==    by 0x10BECAD: thread_worker (slicethread.c:89)
==21919==    by 0x52E7EA6: start_thread (pthread_create.c:477)
==21919==    by 0x53FEA2E: clone (clone.S:95)
==21919==  Address 0x59d36b0 is 16 bytes after a block of size 4,256 alloc'd
==21919==    at 0x483AEB8: memalign (vg_replace_malloc.c:906)
==21919==    by 0x483AFCE: posix_memalign (vg_replace_malloc.c:1070)
==21919==    by 0x10A753E: av_malloc (mem.c:105)
==21919==    by 0x10920B5: av_buffer_alloc (buffer.c:82)
==21919==    by 0x109211F: av_buffer_allocz (buffer.c:95)
==21919==    by 0x1092814: pool_alloc_buffer (buffer.c:363)
==21919==    by 0x1092814: av_buffer_pool_get (buffer.c:401)
==21919==    by 0x321401: ff_frame_pool_get (framepool.c:217)
==21919==    by 0x4CC22A: ff_default_get_video_buffer2 (video.c:87)
==21919==    by 0x466049: process_frame (vf_stack.c:178)
==21919==    by 0x32247F: ff_framesync_activate (framesync.c:364)
==21919==    by 0x30EB04: ff_filter_activate (avfilter.c:1323)
==21919==    by 0x312284: get_frame_internal (buffersink.c:137)
==21919== 
[lavfi @ 0x5710700] All info found
[lavfi @ 0x5710700] stream 0: start_time: 0 duration: NOPTS
[lavfi @ 0x5710700] format: start_time: 0 duration: NOPTS (estimate from bit rate) bitrate=0 kb/s
Input #0, lavfi, from 'testsrc=s=100x55,format=yuv420p[a]; testsrc=s=100x77,format=yuv420p[b]; [a][b]vstack':
  Duration: N/A, start: 0.000000, bitrate: N/A
  Stream #0:0, 1, 1/25: Video: wrapped_avframe, 1 reference frame, yuv420p, 100x132 [SAR 1:1 DAR 25:33], 0/1, 25 fps, 25 tbr, 25 tbn
Successfully opened the file.
Parsing a group of options: output url /dev/null.
Applying option f (force format) with argument null.
Successfully parsed a group of options.
Opening an output file: /dev/null.
[out#0/null @ 0x59d7680] No explicit maps, mapping streams automatically...
[vost#0:0/wrapped_avframe @ 0x59d8780] Created video stream from input stream 0:0
Successfully opened the file.
Stream mapping:
  Stream #0:0 -> #0:0 (wrapped_avframe (native) -> wrapped_avframe (native))
Press [q] to stop, [?] for help
[graph 0 input from stream 0:0 @ 0x59f2e00] Setting 'video_size' to value '100x132'
[graph 0 input from stream 0:0 @ 0x59f2e00] Setting 'pix_fmt' to value '0'
[graph 0 input from stream 0:0 @ 0x59f2e00] Setting 'time_base' to value '1/25'
[graph 0 input from stream 0:0 @ 0x59f2e00] Setting 'pixel_aspect' to value '1/1'
[graph 0 input from stream 0:0 @ 0x59f2e00] Setting 'frame_rate' to value '25/1'
[graph 0 input from stream 0:0 @ 0x59f2e00] w:100 h:132 pixfmt:yuv420p tb:1/25 fr:25/1 sar:1/1
[AVFilterGraph @ 0x59e2e00] query_formats: 4 queried, 3 merged, 0 already done, 0 delayed
Output #0, null, to '/dev/null':
  Metadata:
    encoder         : Lavf60.10.100
  Stream #0:0, 0, 1/25: Video: wrapped_avframe, 1 reference frame, yuv420p(progressive), 100x132 (0x0) [SAR 1:1 DAR 25:33], 0/1, q=2-31, 200 kb/s, 25 fps, 25 tbn
    Metadata:
      encoder         : Lavc60.22.100 wrapped_avframe
frame=    0 fps=0.0 q=-0.0 size=       0kB time=00:00:00.00 bitrate=N/A speed=   0x    
frame=   15 fps=0.0 q=-0.0 size=N/A time=00:00:00.60 bitrate=N/A speed= 1.2x    
[out_0_0 @ 0x59f4940] EOF on sink link out_0_0:default.
No more output streams to write to, finishing.
[vist#0:0/wrapped_avframe @ 0x59d6940] Decoder thread received EOF packet
[vist#0:0/wrapped_avframe @ 0x59d6940] Decoder returned EOF, finishing
[vist#0:0/wrapped_avframe @ 0x59d6940] Terminating decoder thread
[out#0/null @ 0x59d7680] All streams finished
[out#0/null @ 0x59d7680] Terminating muxer thread
[out#0/null @ 0x59d7680] Output file #0 (/dev/null):
[out#0/null @ 0x59d7680]   Output stream #0:0 (video): 25 frames encoded; 25 packets muxed (12000 bytes); 
[out#0/null @ 0x59d7680]   Total: 25 packets (12000 bytes) muxed
[out#0/null @ 0x59d7680] video:12kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: unknown
frame=   25 fps=0.0 q=-0.0 Lsize=N/A time=00:00:00.96 bitrate=N/A speed=1.53x    
[in#0/lavfi @ 0x57104c0] Terminating demuxer thread
[in#0/lavfi @ 0x57104c0] Input file #0 (testsrc=s=100x55,format=yuv420p[a]; testsrc=s=100x77,format=yuv420p[b]; [a][b]vstack):
[in#0/lavfi @ 0x57104c0]   Input stream #0:0 (video): 28 packets read (13440 bytes); 26 frames decoded; 0 decode errors; 
[in#0/lavfi @ 0x57104c0]   Total: 28 packets (13440 bytes) demuxed
==21919== 
==21919== HEAP SUMMARY:
==21919==     in use at exit: 0 bytes in 0 blocks
==21919==   total heap usage: 4,457 allocs, 4,457 frees, 2,752,452 bytes allocated
==21919== 
==21919== All heap blocks were freed -- no leaks are possible
==21919== 
==21919== For lists of detected and suppressed errors, rerun with: -s
==21919== ERROR SUMMARY: 224 errors from 2 contexts (suppressed: 0 from 0)

Change History (1)

comment:1 by hungkuishing, 15 months ago

The crash is caused by chroma plane right shift.
for yuv420p,
the height of first input luma is 55, chroma height is obtained by AV_CEIL_RSHIFT which is 28;
the height of second input luma is 77, chroma height is 39.

the height of output luma is 132(55 + 77), then height of output chroma is 66 (132 >> 1)

66 < 67(28 + 39) so ffmpeg crashed...

The filter need check input parameters, especially height of input...

Note: See TracTickets for help on using tickets.