Hi iceray-Li

duplicate, fixed by commit 0685766183 in 
https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24848 (not on master 12c589a37d 
yet).

thx

On Sun, Sep 27, 2026 at 04:07:34PM +0800, iceray-Li wrote:
> 
> Hello,
> 
> we would like to report an `AVERROR_BUG` leak from the decode API that we hit
> while fuzzing `tools/target_dec_fuzzer` built for H.264 on FFmpeg 9.0. There 
> is
> no sanitizer finding involved; the failure is FFmpeg's own sentinel
> `av_assert0(ret != AVERROR_BUG)` in the fuzz target firing because
> `avcodec_receive_frame()` returned `AVERROR_BUG`.
> 
> ## Summary
> 
> When the H.264 decoder outputs a frame in a pixel format whose components are
> 2 bytes wide (`AV_PIX_FMT_YUV444P10LE` in our reproducer) and the stream asks
> for a non-zero horizontal crop, `av_frame_apply_cropping()` fails an internal
> alignment assumption and returns `AVERROR_BUG`. `ff_decode_receive_frame()`
> propagates it to `avcodec_receive_frame()`, so a normal API call returns
> `AVERROR_BUG` to the application.
> 
> The condition is not exotic: for any planar format with `comp->step == 2`,
> `ff_ctz(crop_left * step) == ff_ctz(crop_left) + 1`, so the check
> `log2_crop_align < min_log2_align` in `libavutil/frame.c` is always true when
> `crop_left != 0`.
> 
> ## Details
> 
> ```c
> /* libavutil/frame.c : av_frame_apply_cropping() */
> if (!(flags & AV_FRAME_CROP_UNALIGNED)) {
>     int log2_crop_align = frame->crop_left ? ff_ctz(frame->crop_left) : 
> INT_MAX;
>     int min_log2_align = INT_MAX;
> 
>     for (int i = 0; frame->data[i]; i++) {
>         int log2_align = offsets[i] ? ff_ctz(offsets[i]) : INT_MAX;
>         min_log2_align = FFMIN(log2_align, min_log2_align);
>     }
> 
>     /* we assume, and it should always be true, that the data alignment is
>      * related to the cropping alignment by a constant power-of-2 factor */
>     if (log2_crop_align < min_log2_align)
>         return AVERROR_BUG;
> ```
> 
> Observed values at the failing call (gdb, `libavutil/frame.c`):
> 
> ```text
> frame->format           = AV_PIX_FMT_YUV444P10LE  (comp->step = 2)
> width x height          = 16 x 192
> crop_left/top/right/bot = 2 / 0 / 0 / 0
> linesize[0..2]          = 64 / 64 / 64
> offsets[i]              = crop_left * step = 4
> log2_crop_align         = ff_ctz(2) = 1
> min_log2_align          = ff_ctz(4) = 2
> -> return AVERROR_BUG
> ```
> 
> The value is then propagated without being converted:
> 
> ```text
> #0 ff_decode_receive_frame     libavcodec/decode.c:836   (goto fail after 
> apply_cropping)
> #1 avcodec_receive_frame_flags libavcodec/avcodec.c:716
> #2 avcodec_receive_frame       libavcodec/avcodec.c:722
> #3 audio_video_handler         tools/target_dec_fuzzer.c:97
> ```
> 
> Note that `apply_cropping` is enabled by default 
> (`libavcodec/options_table.h`:
> `apply_cropping` default `1`) and `AV_CODEC_FLAG_UNALIGNED` is not set by
> default, so ordinary applications take this path, not just the fuzzer.
> 
> ## Reproduction
> 
> - Project/version: FFmpeg 9.0 (`libavutil 61.1.100`)
> - OS: Linux x86_64, clang
> - Target: `tools/target_dec_fuzzer.c` built with `-DFFMPEG_DECODER=h264`
>   (unmodified upstream target)
> - Flags: `-fsanitize=address,undefined -fno-sanitize-recover=all 
> -fno-omit-frame-pointer -O2 -g`
> - Input: `ffmpeg_h264_apply_cropping_crop_left.poc` (314 bytes,
>   sha256 `0ded9dee0af564589805ac923f87cb8c92433c7318ac68a2e149aad9ef2d13c5`)
> 
> ```sh
> ./target_dec_fuzzer ffmpeg_h264_apply_cropping_crop_left.poc
> # -> Assertion ret != AVERROR_BUG failed at tools/target_dec_fuzzer.c:98
> # -> SIGABRT (exit 134)
> ```
> 
> Replayed on an untouched 9.0 tree; also re-checked that
> `libavutil/frame.c` and `libavcodec/h264_slice.c` are unchanged on current
> `master` (comments and code identical).
> 
> ## Impact
> 
> `AVERROR_BUG` is documented as an internal error that should never be returned
> by the API. Applications that treat it as fatal (as FFmpeg's own fuzz target
> does) abort when decoding media from an untrusted source — a denial of 
> service.
> 
> ## Suggested direction
> 
> The alignment assumption only holds when `comp->step == 1`. One minimal option
> is to skip the alignment adjustment (falling back to unaligned cropping, which
> is already a supported mode one branch above) instead of treating the
> situation as an internal error:
> 
> ```diff
>          /* we assume, and it should always be true, that the data alignment 
> is
>           * related to the cropping alignment by a constant power-of-2 factor 
> */
> -        if (log2_crop_align < min_log2_align)
> -            return AVERROR_BUG;
> +        if (log2_crop_align < min_log2_align)
> +            log2_crop_align = INT_MAX;   /* step > 1: fall back to unaligned 
> cropping */
> ```
> 
> We built and verified that change on FFmpeg 9.0 (`make -j16
> tools/target_dec_h264_fuzzer`): before the patch the PoC aborts with the
> assertion above, after the patch the same input prints `Execution successful`
> and exits 0. The patch is attached as
> `proposed-fix-frame-apply-cropping.patch` (a few lines, no behaviour change 
> for
> formats with single-byte components). We are happy to adapt it if you prefer 
> to
> fix it on the decoder side instead.
> 
> We can provide the PoC, the full sanitizer output, the gdb session log, and 
> the
> fuzzer build instructions.
> 
> Best regards,
> iceray-Li


>  frame.c |    8 ++++++--
>  1 file changed, 6 insertions(+), 2 deletions(-)
> 6640c44442fe51d9c491cb6627dce43cfd75c32a  
> /varproposed-fix-frame-apply-cropping.patch
> --- a/libavutil/frame.c
> +++ b/libavutil/frame.c
> @@ -804,9 +804,13 @@
>          }
>  
>          /* we assume, and it should always be true, that the data alignment 
> is
> -         * related to the cropping alignment by a constant power-of-2 factor 
> */
> +         * related to the cropping alignment by a constant power-of-2 factor.
> +         * That does not hold for pixel formats whose components are more 
> than
> +         * one byte wide, where offsets[i] == crop_left * comp->step.  In 
> that
> +         * case fall back to unaligned cropping instead of treating it as an
> +         * internal error. */
>          if (log2_crop_align < min_log2_align)
> -            return AVERROR_BUG;
> +            log2_crop_align = INT_MAX;
>  
>          if (min_log2_align < 5 && log2_crop_align != INT_MAX) {
>              frame->crop_left &= ~((1 << (5 + log2_crop_align - 
> min_log2_align)) - 1);


-- 
Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB

Democracy is the form of government in which you can choose your dictator

Attachment: signature.asc
Description: PGP signature

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to