[FFmpeg-devel] [PATCH 5/6] avcodec/rv34, mpegvideo: Fix segfault upon frame size change error
Andreas Rheinhardt
andreas.rheinhardt at outlook.com
Mon Apr 5 04:44:33 EEST 2021
The RealVideo 3.0 and 4.0 decoders call ff_mpv_common_init() only during
their init function and not during decode_frame(); when the size of the
frame changes, they call ff_mpv_common_frame_size_change(). Yet upon
error, said function calls ff_mpv_common_end() which frees the whole
MpegEncContext and not only those parts that
ff_mpv_common_frame_size_change() reinits. As a result, the context will
never be usable again; worse, because decode_frame() contains no check
for whether the context is initialized or not, it is presumed that it is
initialized, leading to segfaults. Basically the same happens if
rv34_decoder_realloc() fails.
This commit fixes this by only resetting the parts that
ff_mpv_common_frame_size_change() changes upon error and by actually
checking whether the context is in need of reinitialization in
ff_rv34_decode_frame().
Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt at outlook.com>
---
I actually don't like that we have two flags that indicate whether
a MpegEncContext is usable or not; how about we always call
ff_mpv_common_init() during init (and never lateron) and make it
unconditionally allocate the stuff that does not depend upon resolution
etc. and add a parameter to said function to also allocate the latter.
The decode_frame functions would then be modified to always use
ff_mpv_common_frame_size_change().
ff_rv34_decode_update_thread_context() currently checks twice whether
the source MpegEncContext is initialized; the second is trivially true,
because of the first check. But the first is also always true now,
because ff_mpv_common_end() is only called when closing the decoder
(and said check also made no sense before this patch because a
noninitialized MpegEncContext would have led to a segfault pretty soon).
Should this be changed to check for context_reinit instead?
libavcodec/mpegvideo.c | 6 ++++--
libavcodec/rv34.c | 13 +++++++++----
2 files changed, 13 insertions(+), 6 deletions(-)
diff --git a/libavcodec/mpegvideo.c b/libavcodec/mpegvideo.c
index 7eddbdcc37..5de0719f83 100644
--- a/libavcodec/mpegvideo.c
+++ b/libavcodec/mpegvideo.c
@@ -555,7 +555,6 @@ int ff_mpeg_update_thread_context(AVCodecContext *dst,
}
if (s->height != s1->height || s->width != s1->width || s->context_reinit) {
- s->context_reinit = 0;
s->height = s1->height;
s->width = s1->width;
if ((ret = ff_mpv_common_frame_size_change(s)) < 0)
@@ -1099,10 +1098,12 @@ int ff_mpv_common_frame_size_change(MpegEncContext *s)
if (err < 0)
goto fail;
}
+ s->context_reinit = 0;
return 0;
fail:
- ff_mpv_common_end(s);
+ free_context_frame(s);
+ s->context_reinit = 1;
return err;
}
@@ -1149,6 +1150,7 @@ void ff_mpv_common_end(MpegEncContext *s)
av_frame_free(&s->new_picture.f);
s->context_initialized = 0;
+ s->context_reinit = 0;
s->last_picture_ptr =
s->next_picture_ptr =
s->current_picture_ptr = NULL;
diff --git a/libavcodec/rv34.c b/libavcodec/rv34.c
index 7e5bfe0e22..99e580a09a 100644
--- a/libavcodec/rv34.c
+++ b/libavcodec/rv34.c
@@ -1383,6 +1383,7 @@ static int rv34_decoder_alloc(RV34DecContext *r)
if (!(r->cbp_chroma && r->cbp_luma && r->deblock_coefs &&
r->intra_types_hist && r->mb_type)) {
+ r->s.context_reinit = 1;
rv34_decoder_free(r);
return AVERROR(ENOMEM);
}
@@ -1530,7 +1531,7 @@ int ff_rv34_decode_update_thread_context(AVCodecContext *dst, const AVCodecConte
if (dst == src || !s1->context_initialized)
return 0;
- if (s->height != s1->height || s->width != s1->width) {
+ if (s->height != s1->height || s->width != s1->width || s->context_reinit) {
s->height = s1->height;
s->width = s1->width;
if ((err = ff_mpv_common_frame_size_change(s)) < 0)
@@ -1667,11 +1668,12 @@ int ff_rv34_decode_frame(AVCodecContext *avctx,
if (s->mb_num_left > 0 && s->current_picture_ptr) {
av_log(avctx, AV_LOG_ERROR, "New frame but still %d MB left.\n",
s->mb_num_left);
- ff_er_frame_end(&s->er);
+ if (!s->context_reinit)
+ ff_er_frame_end(&s->er);
ff_mpv_frame_end(s);
}
- if (s->width != si.width || s->height != si.height) {
+ if (s->width != si.width || s->height != si.height || s->context_reinit) {
int err;
av_log(s->avctx, AV_LOG_WARNING, "Changing dimensions to %dx%d\n",
@@ -1689,7 +1691,6 @@ int ff_rv34_decode_frame(AVCodecContext *avctx,
err = ff_set_dimensions(s->avctx, s->width, s->height);
if (err < 0)
return err;
-
if ((err = ff_mpv_common_frame_size_change(s)) < 0)
return err;
if ((err = rv34_decoder_realloc(r)) < 0)
@@ -1744,6 +1745,10 @@ int ff_rv34_decode_frame(AVCodecContext *avctx,
}
s->mb_x = s->mb_y = 0;
ff_thread_finish_setup(s->avctx);
+ } else if (s->context_reinit) {
+ av_log(s->avctx, AV_LOG_ERROR, "Decoder needs full frames to "
+ "reinitialize (start MB is %d).\n", si.start);
+ return AVERROR_INVALIDDATA;
} else if (HAVE_THREADS &&
(s->avctx->active_thread_type & FF_THREAD_FRAME)) {
av_log(s->avctx, AV_LOG_ERROR, "Decoder needs full frames in frame "
--
2.27.0
More information about the ffmpeg-devel
mailing list