Commit 838c660bb3 for ffmpeg
commit 838c660bb3cd66ba74981b453c14c79214f6ef92
Author: Michael Niedermayer <michael@niedermayer.cc>
Date: Tue Oct 20 16:48:54 2026 +0200
avcodec: release the hwaccel picture private data before its frame
The frame can hold the last reference to the frames context and with it to
the device context the hwaccel's free_frame_priv callback receives. vvc,
mpegvideo and the mjpeg close path released the frame first, and the av1,
vp8, vp9 and h264 picture replacement replaced it first; the other
decoders already release the private data first.
Found during triage/review of the security report AISLE-2026-R0043
diff --git a/libavcodec/av1dec.c b/libavcodec/av1dec.c
index 0fa6e9245b..315828e18a 100644
--- a/libavcodec/av1dec.c
+++ b/libavcodec/av1dec.c
@@ -749,10 +749,9 @@ static void av1_frame_replace(AV1Frame *dst, const AV1Frame *src)
dst->raw_frame_header = src->raw_frame_header;
- ff_progress_frame_replace(&dst->pf, &src->pf);
-
av_refstruct_replace(&dst->hwaccel_picture_private,
src->hwaccel_picture_private);
+ ff_progress_frame_replace(&dst->pf, &src->pf);
dst->spatial_id = src->spatial_id;
dst->temporal_id = src->temporal_id;
diff --git a/libavcodec/h264_picture.c b/libavcodec/h264_picture.c
index ce75b0cc89..8cb91e371c 100644
--- a/libavcodec/h264_picture.c
+++ b/libavcodec/h264_picture.c
@@ -71,9 +71,6 @@ static void h264_copy_picture_params(H264Picture *dst, const H264Picture *src)
av_refstruct_replace(&dst->ref_index[i], src->ref_index[i]);
}
- av_refstruct_replace(&dst->hwaccel_picture_private,
- src->hwaccel_picture_private);
-
av_refstruct_replace(&dst->decode_error_flags, src->decode_error_flags);
dst->qscale_table = src->qscale_table;
@@ -117,6 +114,8 @@ int ff_h264_ref_picture(H264Picture *dst, const H264Picture *src)
ret = ff_thread_ref_frame(&dst->tf, &src->tf);
if (ret < 0)
goto fail;
+ av_refstruct_replace(&dst->hwaccel_picture_private,
+ src->hwaccel_picture_private);
if (src->needs_fg) {
ret = av_frame_ref(dst->f_grain, src->f_grain);
@@ -144,6 +143,8 @@ int ff_h264_replace_picture(H264Picture *dst, const H264Picture *src)
av_assert0(src->tf.f == src->f);
dst->tf.f = dst->f;
+ av_refstruct_replace(&dst->hwaccel_picture_private,
+ src->hwaccel_picture_private);
ret = ff_thread_replace_frame(&dst->tf, &src->tf);
if (ret < 0)
goto fail;
@@ -159,6 +160,7 @@ int ff_h264_replace_picture(H264Picture *dst, const H264Picture *src)
return 0;
fail:
+ av_refstruct_unref(&dst->hwaccel_picture_private);
ff_h264_unref_picture(dst);
return ret;
}
diff --git a/libavcodec/hwaccel_internal.h b/libavcodec/hwaccel_internal.h
index ac860f8ab0..64c865e858 100644
--- a/libavcodec/hwaccel_internal.h
+++ b/libavcodec/hwaccel_internal.h
@@ -101,9 +101,9 @@ typedef struct FFHWAccel {
/**
* Size of per-frame hardware accelerator private data.
*
- * Private data is allocated with av_mallocz() before
- * AVCodecContext.get_buffer() and deallocated after
- * AVCodecContext.release_buffer().
+ * Private data is allocated with ff_hwaccel_frame_priv_alloc() and
+ * released before the frame, so free_frame_priv() can still use the
+ * frame's device context.
*/
int frame_priv_data_size;
diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c
index 431ae239b5..f50bb556eb 100644
--- a/libavcodec/mjpegdec.c
+++ b/libavcodec/mjpegdec.c
@@ -2953,6 +2953,7 @@ av_cold int ff_mjpeg_decode_end(AVCodecContext *avctx)
av_log(avctx, AV_LOG_INFO, "Single field\n");
}
+ av_refstruct_unref(&s->hwaccel_picture_private);
av_frame_free(&s->picture);
s->picture_ptr = NULL;
@@ -2977,7 +2978,6 @@ av_cold int ff_mjpeg_decode_end(AVCodecContext *avctx)
reset_icc_profile(s);
- av_refstruct_unref(&s->hwaccel_picture_private);
av_freep(&s->jls_state);
return 0;
diff --git a/libavcodec/mpegpicture.c b/libavcodec/mpegpicture.c
index 3f32fe8773..569243fde9 100644
--- a/libavcodec/mpegpicture.c
+++ b/libavcodec/mpegpicture.c
@@ -32,11 +32,10 @@ static void mpv_pic_reset(AVRefStructOpaque unused, void *obj)
{
MPVPicture *pic = obj;
+ av_refstruct_unref(&pic->hwaccel_picture_private);
av_frame_unref(pic->f);
ff_thread_progress_reset(&pic->progress);
- av_refstruct_unref(&pic->hwaccel_picture_private);
-
av_refstruct_unref(&pic->mbskip_table);
av_refstruct_unref(&pic->qscale_table_base);
av_refstruct_unref(&pic->mb_type_base);
diff --git a/libavcodec/vp8.c b/libavcodec/vp8.c
index 40564ec9f9..ba22a9ee38 100644
--- a/libavcodec/vp8.c
+++ b/libavcodec/vp8.c
@@ -2899,10 +2899,10 @@ av_cold int ff_vp8_decode_init(AVCodecContext *avctx)
#if HAVE_THREADS
static void vp8_replace_frame(VP8Frame *dst, const VP8Frame *src)
{
- ff_progress_frame_replace(&dst->tf, &src->tf);
- av_refstruct_replace(&dst->seg_map, src->seg_map);
av_refstruct_replace(&dst->hwaccel_picture_private,
src->hwaccel_picture_private);
+ ff_progress_frame_replace(&dst->tf, &src->tf);
+ av_refstruct_replace(&dst->seg_map, src->seg_map);
}
#define REBASE(pic) ((pic) ? (pic) - &s_src->frames[0] + &s->frames[0] : NULL)
diff --git a/libavcodec/vp9.c b/libavcodec/vp9.c
index dfd5da08ed..83d712bb25 100644
--- a/libavcodec/vp9.c
+++ b/libavcodec/vp9.c
@@ -151,6 +151,8 @@ static void vp9_frame_replace(VP9Frame *dst, const VP9Frame *src)
av_refstruct_replace(&dst->header_ref, src->header_ref);
dst->frame_header = src->frame_header;
+ av_refstruct_replace(&dst->hwaccel_picture_private,
+ src->hwaccel_picture_private);
ff_progress_frame_replace(&dst->tf, &src->tf);
av_refstruct_replace(&dst->extradata, src->extradata);
@@ -158,9 +160,6 @@ static void vp9_frame_replace(VP9Frame *dst, const VP9Frame *src)
dst->segmentation_map = src->segmentation_map;
dst->mv = src->mv;
dst->uses_2pass = src->uses_2pass;
-
- av_refstruct_replace(&dst->hwaccel_picture_private,
- src->hwaccel_picture_private);
}
static int update_size(AVCodecContext *avctx, int w, int h)
diff --git a/libavcodec/vvc/refs.c b/libavcodec/vvc/refs.c
index c50cd32f5c..4deb489b52 100644
--- a/libavcodec/vvc/refs.c
+++ b/libavcodec/vvc/refs.c
@@ -51,6 +51,7 @@ void ff_vvc_unref_frame(VVCFrameContext *fc, VVCFrame *frame, int flags)
if (!(frame->flags & ~VVC_FRAME_FLAG_CORRUPT))
frame->flags = 0;
if (!frame->flags) {
+ av_refstruct_unref(&frame->hwaccel_picture_private);
av_frame_unref(frame->frame);
if (frame->needs_fg) {
@@ -69,7 +70,6 @@ void ff_vvc_unref_frame(VVCFrameContext *fc, VVCFrame *frame, int flags)
av_refstruct_unref(&frame->rpl_tab);
frame->collocated_ref = NULL;
- av_refstruct_unref(&frame->hwaccel_picture_private);
}
}