Re: [FFmpeg-devel] [PATCH 3/4] wavpack: fully support stream parameter changes

2020-04-05 Thread David Bryant
On 4/5/20 1:32 PM, Anton Khirnov wrote:
> Fix invalid memory access on DSD streams with changing channel count.
> ---
>  libavcodec/wavpack.c | 122 +++
>  1 file changed, 90 insertions(+), 32 deletions(-)
>
> diff --git a/libavcodec/wavpack.c b/libavcodec/wavpack.c
> index b27262b94e..9cc4104dd0 100644
> --- a/libavcodec/wavpack.c
> +++ b/libavcodec/wavpack.c
> @@ -20,6 +20,7 @@
>   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 
> USA
>   */
>  
> +#include "libavutil/buffer.h"
>  #include "libavutil/channel_layout.h"
>  
>  #define BITSTREAM_READER_LE
> @@ -109,7 +110,10 @@ typedef struct WavpackContext {
>  AVFrame *frame;
>  ThreadFrame curr_frame, prev_frame;
>  Modulation modulation;
> +
> +AVBufferRef *dsd_ref;
>  DSDContext *dsdctx;
> +int dsd_channels;
>  } WavpackContext;
>  
>  #define LEVEL_DECAY(a)  (((a) + 0x80) >> 8)
> @@ -978,6 +982,32 @@ static av_cold int wv_alloc_frame_context(WavpackContext 
> *c)
>  return 0;
>  }
>  
> +static int wv_dsd_reset(WavpackContext *s, int channels)
> +{
> +int i;
> +
> +s->dsdctx = NULL;
> +s->dsd_channels = 0;
> +av_buffer_unref(&s->dsd_ref);
> +
> +if (!channels)
> +return 0;
> +
> +if (channels > INT_MAX / sizeof(*s->dsdctx))
> +return AVERROR(EINVAL);
> +
> +s->dsd_ref = av_buffer_allocz(channels * sizeof(*s->dsdctx));
> +if (!s->dsd_ref)
> +return AVERROR(ENOMEM);
> +s->dsdctx = (DSDContext*)s->dsd_ref->data;
> +s->dsd_channels = channels;
> +
> +for (i = 0; i < channels; i++)
> +memset(s->dsdctx[i].buf, 0x69, sizeof(s->dsdctx[i].buf));
> +
> +return 0;
> +}
> +
>  #if HAVE_THREADS
>  static int init_thread_copy(AVCodecContext *avctx)
>  {
> @@ -1008,6 +1038,17 @@ static int update_thread_context(AVCodecContext *dst, 
> const AVCodecContext *src)
>  return ret;
>  }
>  
> +av_buffer_unref(&fdst->dsd_ref);
> +fdst->dsdctx = NULL;
> +fdst->dsd_channels = 0;
> +if (fsrc->dsd_ref) {
> +fdst->dsd_ref = av_buffer_ref(fsrc->dsd_ref);
> +if (!fdst->dsd_ref)
> +return AVERROR(ENOMEM);
> +fdst->dsdctx = (DSDContext*)fdst->dsd_ref->data;
> +fdst->dsd_channels = fsrc->dsd_channels;
> +}
> +
>  return 0;
>  }
>  #endif
> @@ -1025,15 +1066,9 @@ static av_cold int wavpack_decode_init(AVCodecContext 
> *avctx)
>  s->curr_frame.f = av_frame_alloc();
>  s->prev_frame.f = av_frame_alloc();
>  
> -// the DSD to PCM context is shared (and used serially) between all 
> decoding threads
> -s->dsdctx = av_calloc(avctx->channels, sizeof(DSDContext));
> -
> -if (!s->curr_frame.f || !s->prev_frame.f || !s->dsdctx)
> +if (!s->curr_frame.f || !s->prev_frame.f)
>  return AVERROR(ENOMEM);
>  
> -for (int i = 0; i < avctx->channels; i++)
> -memset(s->dsdctx[i].buf, 0x69, sizeof(s->dsdctx[i].buf));
> -
>  ff_init_dsd_data();
>  
>  return 0;
> @@ -1053,8 +1088,7 @@ static av_cold int wavpack_decode_end(AVCodecContext 
> *avctx)
>  ff_thread_release_buffer(avctx, &s->prev_frame);
>  av_frame_free(&s->prev_frame.f);
>  
> -if (!avctx->internal->is_copy)
> -av_freep(&s->dsdctx);
> +av_buffer_unref(&s->dsd_ref);
>  
>  return 0;
>  }
> @@ -1065,6 +1099,7 @@ static int wavpack_decode_block(AVCodecContext *avctx, 
> int block_no,
>  WavpackContext *wc = avctx->priv_data;
>  WavpackFrameContext *s;
>  GetByteContext gb;
> +enum AVSampleFormat sample_fmt;
>  void *samples_l = NULL, *samples_r = NULL;
>  int ret;
>  int got_terms   = 0, got_weights = 0, got_samples = 0,
> @@ -1102,7 +1137,15 @@ static int wavpack_decode_block(AVCodecContext *avctx, 
> int block_no,
>  return AVERROR_INVALIDDATA;
>  }
>  s->frame_flags = bytestream2_get_le32(&gb);
> -bpp= av_get_bytes_per_sample(avctx->sample_fmt);
> +
> +if (s->frame_flags & (WV_FLOAT_DATA | WV_DSD_DATA))
> +sample_fmt = AV_SAMPLE_FMT_FLTP;
> +else if ((s->frame_flags & 0x03) <= 1)
> +sample_fmt = AV_SAMPLE_FMT_S16P;
> +else
> +sample_fmt  = AV_SAMPLE_FMT_S32P;
> +
> +bpp= av_get_bytes_per_sample(sample_fmt);
>  orig_bpp   = ((s->frame_flags & 0x03) + 1) << 3;
>  multiblock = (s->frame_flags & WV_SINGLE_BLOCK) != WV_SINGLE_BLOCK;
>  
> @@ -1436,11 +1479,11 @@ static int wavpack_decode_block(AVCodecContext 
> *avctx, int block_no,
>  av_log(avctx, AV_LOG_ERROR, "Hybrid config not found\n");
>  return AVERROR_INVALIDDATA;
>  }
> -if (!got_float && avctx->sample_fmt == AV_SAMPLE_FMT_FLTP) {
> +if (!got_float && sample_fmt == AV_SAMPLE_FMT_FLTP) {
>  av_log(avctx, AV_LOG_ERROR, "Float information not found\n");
>  return AVERROR_INVALIDDATA;
>  }
> -if (s->got_extra_bits && avctx->sample_fmt !=

[FFmpeg-devel] [PATCH 3/4] wavpack: fully support stream parameter changes

2020-04-05 Thread Anton Khirnov
Fix invalid memory access on DSD streams with changing channel count.
---
 libavcodec/wavpack.c | 122 +++
 1 file changed, 90 insertions(+), 32 deletions(-)

diff --git a/libavcodec/wavpack.c b/libavcodec/wavpack.c
index b27262b94e..9cc4104dd0 100644
--- a/libavcodec/wavpack.c
+++ b/libavcodec/wavpack.c
@@ -20,6 +20,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+#include "libavutil/buffer.h"
 #include "libavutil/channel_layout.h"
 
 #define BITSTREAM_READER_LE
@@ -109,7 +110,10 @@ typedef struct WavpackContext {
 AVFrame *frame;
 ThreadFrame curr_frame, prev_frame;
 Modulation modulation;
+
+AVBufferRef *dsd_ref;
 DSDContext *dsdctx;
+int dsd_channels;
 } WavpackContext;
 
 #define LEVEL_DECAY(a)  (((a) + 0x80) >> 8)
@@ -978,6 +982,32 @@ static av_cold int wv_alloc_frame_context(WavpackContext 
*c)
 return 0;
 }
 
+static int wv_dsd_reset(WavpackContext *s, int channels)
+{
+int i;
+
+s->dsdctx = NULL;
+s->dsd_channels = 0;
+av_buffer_unref(&s->dsd_ref);
+
+if (!channels)
+return 0;
+
+if (channels > INT_MAX / sizeof(*s->dsdctx))
+return AVERROR(EINVAL);
+
+s->dsd_ref = av_buffer_allocz(channels * sizeof(*s->dsdctx));
+if (!s->dsd_ref)
+return AVERROR(ENOMEM);
+s->dsdctx = (DSDContext*)s->dsd_ref->data;
+s->dsd_channels = channels;
+
+for (i = 0; i < channels; i++)
+memset(s->dsdctx[i].buf, 0x69, sizeof(s->dsdctx[i].buf));
+
+return 0;
+}
+
 #if HAVE_THREADS
 static int init_thread_copy(AVCodecContext *avctx)
 {
@@ -1008,6 +1038,17 @@ static int update_thread_context(AVCodecContext *dst, 
const AVCodecContext *src)
 return ret;
 }
 
+av_buffer_unref(&fdst->dsd_ref);
+fdst->dsdctx = NULL;
+fdst->dsd_channels = 0;
+if (fsrc->dsd_ref) {
+fdst->dsd_ref = av_buffer_ref(fsrc->dsd_ref);
+if (!fdst->dsd_ref)
+return AVERROR(ENOMEM);
+fdst->dsdctx = (DSDContext*)fdst->dsd_ref->data;
+fdst->dsd_channels = fsrc->dsd_channels;
+}
+
 return 0;
 }
 #endif
@@ -1025,15 +1066,9 @@ static av_cold int wavpack_decode_init(AVCodecContext 
*avctx)
 s->curr_frame.f = av_frame_alloc();
 s->prev_frame.f = av_frame_alloc();
 
-// the DSD to PCM context is shared (and used serially) between all 
decoding threads
-s->dsdctx = av_calloc(avctx->channels, sizeof(DSDContext));
-
-if (!s->curr_frame.f || !s->prev_frame.f || !s->dsdctx)
+if (!s->curr_frame.f || !s->prev_frame.f)
 return AVERROR(ENOMEM);
 
-for (int i = 0; i < avctx->channels; i++)
-memset(s->dsdctx[i].buf, 0x69, sizeof(s->dsdctx[i].buf));
-
 ff_init_dsd_data();
 
 return 0;
@@ -1053,8 +1088,7 @@ static av_cold int wavpack_decode_end(AVCodecContext 
*avctx)
 ff_thread_release_buffer(avctx, &s->prev_frame);
 av_frame_free(&s->prev_frame.f);
 
-if (!avctx->internal->is_copy)
-av_freep(&s->dsdctx);
+av_buffer_unref(&s->dsd_ref);
 
 return 0;
 }
@@ -1065,6 +1099,7 @@ static int wavpack_decode_block(AVCodecContext *avctx, 
int block_no,
 WavpackContext *wc = avctx->priv_data;
 WavpackFrameContext *s;
 GetByteContext gb;
+enum AVSampleFormat sample_fmt;
 void *samples_l = NULL, *samples_r = NULL;
 int ret;
 int got_terms   = 0, got_weights = 0, got_samples = 0,
@@ -1102,7 +1137,15 @@ static int wavpack_decode_block(AVCodecContext *avctx, 
int block_no,
 return AVERROR_INVALIDDATA;
 }
 s->frame_flags = bytestream2_get_le32(&gb);
-bpp= av_get_bytes_per_sample(avctx->sample_fmt);
+
+if (s->frame_flags & (WV_FLOAT_DATA | WV_DSD_DATA))
+sample_fmt = AV_SAMPLE_FMT_FLTP;
+else if ((s->frame_flags & 0x03) <= 1)
+sample_fmt = AV_SAMPLE_FMT_S16P;
+else
+sample_fmt  = AV_SAMPLE_FMT_S32P;
+
+bpp= av_get_bytes_per_sample(sample_fmt);
 orig_bpp   = ((s->frame_flags & 0x03) + 1) << 3;
 multiblock = (s->frame_flags & WV_SINGLE_BLOCK) != WV_SINGLE_BLOCK;
 
@@ -1436,11 +1479,11 @@ static int wavpack_decode_block(AVCodecContext *avctx, 
int block_no,
 av_log(avctx, AV_LOG_ERROR, "Hybrid config not found\n");
 return AVERROR_INVALIDDATA;
 }
-if (!got_float && avctx->sample_fmt == AV_SAMPLE_FMT_FLTP) {
+if (!got_float && sample_fmt == AV_SAMPLE_FMT_FLTP) {
 av_log(avctx, AV_LOG_ERROR, "Float information not found\n");
 return AVERROR_INVALIDDATA;
 }
-if (s->got_extra_bits && avctx->sample_fmt != AV_SAMPLE_FMT_FLTP) {
+if (s->got_extra_bits && sample_fmt != AV_SAMPLE_FMT_FLTP) {
 const int size   = get_bits_left(&s->gb_extra_bits);
 const int wanted = s->samples * s->extra_bits << s->stereo_in;
 if (size < wanted) {
@@ -1462,27 +1505,54 @@ static int wavpack_decode_block(AVCodecContext