[FFmpeg-devel] [PATCH 2/3] lavf/webm_chunk: Fix NULL dereference

Andreas Rheinhardt andreas.rheinhardt at gmail.com
Fri Jul 12 15:07:00 EEST 2019


Paul B Mahol:
> On 7/12/19, Andreas Rheinhardt <andreas.rheinhardt at gmail.com> wrote:
>> Andreas Rheinhardt:
>>> Andreas Rheinhardt:
>>>> Andreas Rheinhardt:
>>>>> Andreas Rheinhardt:
>>>>>> The earlier version of the webm_chunk muxer had several bugs:
>>>>>>
>>>>>> 1. If the first packet of an audio stream didn't have a PTS of zero,
>>>>>> then no chunk will be started before a packet is delivered to the
>>>>>> underlying Matroska/WebM muxer, i.e. the AVFormatContext used to write
>>>>>> these packets had a NULL as AVIOContext for output. This is behind the
>>>>>> crash in ticket #5752.
>>>>>>
>>>>>> 2. If an error happens during writing a packet, the underlyimg
>>>>>> Matroska/WebM muxer context is freed. This leads to a use-after-free
>>>>>> coupled with a double-free in webm_chunk_write_trailer (which supposes
>>>>>> that the underlying AVFormatContext is still valid).
>>>>>>
>>>>>> 3. Even when no error occurs at all, webm_chunk_write_trailer is still
>>>>>> buggy: After the underlying Matroska/WebM muxer has written its
>>>>>> trailer,
>>>>>> ending the chunk implicitly flushes it again which is illegal at this
>>>>>> point.
>>>>>>
>>>>>> These bugs have been fixed.
>>>>>>
>>>>>> Fixes #5752.
>>>>>>
>>>>>> Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt at gmail.com>
>>>>>> ---
>>>>>>  libavformat/webm_chunk.c | 44 +++++++++++++++++++++-------------------
>>>>>>  1 file changed, 23 insertions(+), 21 deletions(-)
>>>>>>
>>>>>> diff --git a/libavformat/webm_chunk.c b/libavformat/webm_chunk.c
>>>>>> index 2c99753b5b..391fee721a 100644
>>>>>> --- a/libavformat/webm_chunk.c
>>>>>> +++ b/libavformat/webm_chunk.c
>>>>>> @@ -168,7 +168,7 @@ static int chunk_start(AVFormatContext *s)
>>>>>>      return 0;
>>>>>>  }
>>>>>>
>>>>>> -static int chunk_end(AVFormatContext *s)
>>>>>> +static int chunk_end(AVFormatContext *s, int flush)
>>>>>>  {
>>>>>>      WebMChunkContext *wc = s->priv_data;
>>>>>>      AVFormatContext *oc = wc->avf;
>>>>>> @@ -179,11 +179,14 @@ static int chunk_end(AVFormatContext *s)
>>>>>>      char filename[MAX_FILENAME_SIZE];
>>>>>>      AVDictionary *options = NULL;
>>>>>>
>>>>>> -    if (wc->chunk_start_index == wc->chunk_index)
>>>>>> +    if (!oc->pb)
>>>>>>          return 0;
>>>>>> -    // Flush the cluster in WebM muxer.
>>>>>> -    oc->oformat->write_packet(oc, NULL);
>>>>>> +
>>>>>> +    if (flush)
>>>>>> +        // Flush the cluster in WebM muxer.
>>>>>> +        oc->oformat->write_packet(oc, NULL);
>>>>>>      buffer_size = avio_close_dyn_buf(oc->pb, &buffer);
>>>>>> +    oc->pb = NULL;
>>>>>>      ret = get_chunk_filename(s, 0, filename);
>>>>>>      if (ret < 0)
>>>>>>          goto fail;
>>>>>> @@ -194,7 +197,6 @@ static int chunk_end(AVFormatContext *s)
>>>>>>          goto fail;
>>>>>>      avio_write(pb, buffer, buffer_size);
>>>>>>      ff_format_io_close(s, &pb);
>>>>>> -    oc->pb = NULL;
>>>>>>  fail:
>>>>>>      av_dict_free(&options);
>>>>>>      av_free(buffer);
>>>>>> @@ -216,27 +218,19 @@ static int
>>>>>> webm_chunk_write_packet(AVFormatContext *s, AVPacket *pkt)
>>>>>>      }
>>>>>>
>>>>>>      // For video, a new chunk is started only on key frames. For
>>>>>> audio, a new
>>>>>> -    // chunk is started based on chunk_duration.
>>>>>> -    if ((st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
>>>>>> +    // chunk is started based on chunk_duration. Also, a new chunk is
>>>>>> started
>>>>>> +    // unconditionally if there is no currently open chunk.
>>>>>> +    if (!oc->pb || (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
>>>>>>           (pkt->flags & AV_PKT_FLAG_KEY)) ||
>>>>>>          (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
>>>>>> -         (pkt->pts == 0 || wc->duration_written >=
>>>>>> wc->chunk_duration))) {
>>>>>> +         wc->duration_written >= wc->chunk_duration)) {
>>>>>>          wc->duration_written = 0;
>>>>>> -        if ((ret = chunk_end(s)) < 0 || (ret = chunk_start(s)) < 0) {
>>>>>> -            goto fail;
>>>>>> +        if ((ret = chunk_end(s, 1)) < 0 || (ret = chunk_start(s)) < 0)
>>>>>> {
>>>>>> +            return ret;
>>>>>>          }
>>>>>>      }
>>>>>>
>>>>>>      ret = oc->oformat->write_packet(oc, pkt);
>>>>>> -    if (ret < 0)
>>>>>> -        goto fail;
>>>>>> -
>>>>>> -fail:
>>>>>> -    if (ret < 0) {
>>>>>> -        oc->streams = NULL;
>>>>>> -        oc->nb_streams = 0;
>>>>>> -        avformat_free_context(oc);
>>>>>> -    }
>>>>>>
>>>>>>      return ret;
>>>>>>  }
>>>>>> @@ -245,12 +239,20 @@ static int
>>>>>> webm_chunk_write_trailer(AVFormatContext *s)
>>>>>>  {
>>>>>>      WebMChunkContext *wc = s->priv_data;
>>>>>>      AVFormatContext *oc = wc->avf;
>>>>>> +    int ret;
>>>>>> +
>>>>>> +    if (!oc->pb) {
>>>>>> +        ret = chunk_start(s);
>>>>>> +        if (ret < 0)
>>>>>> +            goto fail;
>>>>>> +    }
>>>>>>      oc->oformat->write_trailer(oc);
>>>>>> -    chunk_end(s);
>>>>>> +    ret = chunk_end(s, 0);
>>>>>> +fail:
>>>>>>      oc->streams = NULL;
>>>>>>      oc->nb_streams = 0;
>>>>>>      avformat_free_context(oc);
>>>>>> -    return 0;
>>>>>> +    return ret;
>>>>>>  }
>>>>>>
>>>>>>  #define OFFSET(x) offsetof(WebMChunkContext, x)
>>>>>>
>>>>>>
>>>>> Ping for the last two patches of this patchset (i.e. this one and
>>>>> https://ffmpeg.org/pipermail/ffmpeg-devel/2019-April/242902.html)
>>>>>
>>>>> - Andreas
>>>>>
>>>> And another ping for these two patches.
>>>>
>>>> - Andreas
>>>>
>>>
>>> Ping #3.
>>>
>>> - Andreas
>>>
>> Ping.
> 
> Will apply first patch from this thread.
> Is this OK?
> 
If the first patch you are referring to is
https://ffmpeg.org/pipermail/ffmpeg-devel/2019-April/242903.html then
it is OK.

- Andreas


More information about the ffmpeg-devel mailing list