Browse Source

avcodec/rv34, mpegvideo: Fix segfault upon frame size change error

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().

Reviewed-by: Michael Niedermayer <michael@niedermayer.cc>
Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
(cherry picked from commit 9abda1365c)
tags/n4.4
Andreas Rheinhardt 4 years ago
parent
commit
b0997b8526
2 changed files with 13 additions and 6 deletions
  1. +4
    -2
      libavcodec/mpegvideo.c
  2. +9
    -4
      libavcodec/rv34.c

+ 4
- 2
libavcodec/mpegvideo.c View File

@@ -555,7 +555,6 @@ int ff_mpeg_update_thread_context(AVCodecContext *dst,
} }


if (s->height != s1->height || s->width != s1->width || s->context_reinit) { if (s->height != s1->height || s->width != s1->width || s->context_reinit) {
s->context_reinit = 0;
s->height = s1->height; s->height = s1->height;
s->width = s1->width; s->width = s1->width;
if ((ret = ff_mpv_common_frame_size_change(s)) < 0) 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) if (err < 0)
goto fail; goto fail;
} }
s->context_reinit = 0;


return 0; return 0;
fail: fail:
ff_mpv_common_end(s);
free_context_frame(s);
s->context_reinit = 1;
return err; return err;
} }


@@ -1149,6 +1150,7 @@ void ff_mpv_common_end(MpegEncContext *s)
av_frame_free(&s->new_picture.f); av_frame_free(&s->new_picture.f);


s->context_initialized = 0; s->context_initialized = 0;
s->context_reinit = 0;
s->last_picture_ptr = s->last_picture_ptr =
s->next_picture_ptr = s->next_picture_ptr =
s->current_picture_ptr = NULL; s->current_picture_ptr = NULL;


+ 9
- 4
libavcodec/rv34.c View File

@@ -1383,6 +1383,7 @@ static int rv34_decoder_alloc(RV34DecContext *r)


if (!(r->cbp_chroma && r->cbp_luma && r->deblock_coefs && if (!(r->cbp_chroma && r->cbp_luma && r->deblock_coefs &&
r->intra_types_hist && r->mb_type)) { r->intra_types_hist && r->mb_type)) {
r->s.context_reinit = 1;
rv34_decoder_free(r); rv34_decoder_free(r);
return AVERROR(ENOMEM); return AVERROR(ENOMEM);
} }
@@ -1530,7 +1531,7 @@ int ff_rv34_decode_update_thread_context(AVCodecContext *dst, const AVCodecConte
if (dst == src || !s1->context_initialized) if (dst == src || !s1->context_initialized)
return 0; 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->height = s1->height;
s->width = s1->width; s->width = s1->width;
if ((err = ff_mpv_common_frame_size_change(s)) < 0) 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) { if (s->mb_num_left > 0 && s->current_picture_ptr) {
av_log(avctx, AV_LOG_ERROR, "New frame but still %d MB left.\n", av_log(avctx, AV_LOG_ERROR, "New frame but still %d MB left.\n",
s->mb_num_left); 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); 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; int err;


av_log(s->avctx, AV_LOG_WARNING, "Changing dimensions to %dx%d\n", 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); err = ff_set_dimensions(s->avctx, s->width, s->height);
if (err < 0) if (err < 0)
return err; return err;

if ((err = ff_mpv_common_frame_size_change(s)) < 0) if ((err = ff_mpv_common_frame_size_change(s)) < 0)
return err; return err;
if ((err = rv34_decoder_realloc(r)) < 0) 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; s->mb_x = s->mb_y = 0;
ff_thread_finish_setup(s->avctx); 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 && } else if (HAVE_THREADS &&
(s->avctx->active_thread_type & FF_THREAD_FRAME)) { (s->avctx->active_thread_type & FF_THREAD_FRAME)) {
av_log(s->avctx, AV_LOG_ERROR, "Decoder needs full frames in frame " av_log(s->avctx, AV_LOG_ERROR, "Decoder needs full frames in frame "


Loading…
Cancel
Save