[FFmpeg-devel] [PATCH 1/3] avformat/wavenc: Fix leak and segfault on reallocation error

Andreas Rheinhardt andreas.rheinhardt at gmail.com
Thu Feb 25 23:02:29 EET 2021


Andreas Rheinhardt:
> Up until now, the wav muxer used a reallocation of the form ptr =
> av_realloc(ptr, size); that leaks upon error. Furthermore, if a
> failed reallocation happened when writing the trailer, a segfault
> would occur due to avio_write(NULL, size) because the muxer only
> prints an error message upon allocation error, but does not return
> the error.
> 
> Moreover setting the pointer to the buffer to NULL on error seems to
> be done on purpose in order to record that an error has occured so that
> outputting the peak values is no longer attempted. This behaviour has
> been retained by simply disabling whether peak data should be written
> if an error occurs.
> 
> Finally, the reallocation is now done once per peak block and not once
> per peak block per channel; it is also done with av_fast_realloc and not
> with a linear size increase.
> 
> Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt at gmail.com>
> ---
>  libavformat/wavenc.c | 53 ++++++++++++++++++++++++--------------------
>  1 file changed, 29 insertions(+), 24 deletions(-)
> 
> diff --git a/libavformat/wavenc.c b/libavformat/wavenc.c
> index 1027f107ee..9ef72f6e1c 100644
> --- a/libavformat/wavenc.c
> +++ b/libavformat/wavenc.c
> @@ -50,8 +50,6 @@
>  #define RF64_NEVER  0
>  #define RF64_ALWAYS 1
>  
> -#define PEAK_BUFFER_SIZE   1024
> -
>  typedef enum {
>      PEAK_OFF = 0,
>      PEAK_ON,
> @@ -72,8 +70,9 @@ typedef struct WAVMuxContext {
>      int64_t maxpts;
>      int16_t *peak_maxpos, *peak_maxneg;
>      uint32_t peak_num_frames;
> -    uint32_t peak_outbuf_size;
> +    unsigned peak_outbuf_size;
>      uint32_t peak_outbuf_bytes;
> +    unsigned size_increment;
>      uint8_t *peak_output;
>      int last_duration;
>      int write_bext;
> @@ -172,15 +171,15 @@ static av_cold int peak_init_writer(AVFormatContext *s)
>                 "Writing 16 bit peak for 8 bit audio does not make sense\n");
>          return AVERROR(EINVAL);
>      }
> +    if (par->channels > INT_MAX / (wav->peak_bps * wav->peak_ppv))
> +        return AVERROR(ERANGE);
> +    wav->size_increment = par->channels * wav->peak_bps * wav->peak_ppv;
>  
>      wav->peak_maxpos = av_mallocz_array(par->channels, sizeof(*wav->peak_maxpos));
>      wav->peak_maxneg = av_mallocz_array(par->channels, sizeof(*wav->peak_maxneg));
> -    wav->peak_output = av_malloc(PEAK_BUFFER_SIZE);
> -    if (!wav->peak_maxpos || !wav->peak_maxneg || !wav->peak_output)
> +    if (!wav->peak_maxpos || !wav->peak_maxneg)
>          goto nomem;
>  
> -    wav->peak_outbuf_size = PEAK_BUFFER_SIZE;
> -
>      return 0;
>  
>  nomem:
> @@ -188,14 +187,24 @@ nomem:
>      return AVERROR(ENOMEM);
>  }
>  
> -static void peak_write_frame(AVFormatContext *s)
> +static int peak_write_frame(AVFormatContext *s)
>  {
>      WAVMuxContext *wav = s->priv_data;
>      AVCodecParameters *par = s->streams[0]->codecpar;
> +    unsigned new_size = wav->peak_outbuf_bytes + wav->size_increment;
> +    uint8_t *tmp;
>      int c;
>  
> -    if (!wav->peak_output)
> -        return;
> +    if (new_size > INT_MAX) {
> +        wav->write_peak = PEAK_OFF;
> +        return AVERROR(ERANGE);
> +    }
> +    tmp = av_fast_realloc(wav->peak_output, &wav->peak_outbuf_size, new_size);
> +    if (!tmp) {
> +        wav->write_peak = PEAK_OFF;
> +        return AVERROR(ENOMEM);
> +    }
> +    wav->peak_output = tmp;
>  
>      for (c = 0; c < par->channels; c++) {
>          wav->peak_maxneg[c] = -wav->peak_maxneg[c];
> @@ -209,17 +218,6 @@ static void peak_write_frame(AVFormatContext *s)
>              wav->peak_maxpos[c] =
>                  FFMAX(wav->peak_maxpos[c], wav->peak_maxneg[c]);
>  
> -        if (wav->peak_outbuf_size - wav->peak_outbuf_bytes <
> -            wav->peak_format * wav->peak_ppv) {
> -            wav->peak_outbuf_size += PEAK_BUFFER_SIZE;
> -            wav->peak_output = av_realloc(wav->peak_output,
> -                                          wav->peak_outbuf_size);
> -            if (!wav->peak_output) {
> -                av_log(s, AV_LOG_ERROR, "No memory for peak data\n");
> -                return;
> -            }
> -        }
> -
>          if (wav->peak_format == PEAK_FORMAT_UINT8) {
>              wav->peak_output[wav->peak_outbuf_bytes++] =
>                  wav->peak_maxpos[c];
> @@ -241,6 +239,8 @@ static void peak_write_frame(AVFormatContext *s)
>          wav->peak_maxneg[c] = 0;
>      }
>      wav->peak_num_frames++;
> +
> +    return 0;
>  }
>  
>  static int peak_write_chunk(AVFormatContext *s)
> @@ -254,8 +254,11 @@ static int peak_write_chunk(AVFormatContext *s)
>      char timestamp[28];
>  
>      /* Peak frame of incomplete block at end */
> -    if (wav->peak_block_pos)
> -        peak_write_frame(s);
> +    if (wav->peak_block_pos) {
> +        int ret = peak_write_frame(s);
> +        if (ret < 0)
> +            return ret;
> +    }
>  
>      memset(timestamp, 0, sizeof(timestamp));
>      if (!(s->flags & AVFMT_FLAG_BITEXACT)) {
> @@ -386,7 +389,9 @@ static int wav_write_packet(AVFormatContext *s, AVPacket *pkt)
>              if (++c == s->streams[0]->codecpar->channels) {
>                  c = 0;
>                  if (++wav->peak_block_pos == wav->peak_block_size) {
> -                    peak_write_frame(s);
> +                    int ret = peak_write_frame(s);
> +                    if (ret < 0)
> +                        return ret;
>                      wav->peak_block_pos = 0;
>                  }
>              }
> 
Will apply this patchset tomorrow unless there are objections.

- Andreas


More information about the ffmpeg-devel mailing list