Vulnerability: track_header in libavformat/vividas.c in FFmpeg 4.3.1 has an out-of-bounds write because of incorrect extradata packing.
Vulnerability ID: CVE-2020-35964
CWE: CWE-787
Severity score: 2.9
Project: https://github.com/FFmpeg/FFmpeg
Published: 2021-01-03
File: libavformat/vividas.c
Commit message: avformat/vividas: improve extradata packing checks in track_header()

Fixes: out of array accesses
Fixes: 26622/clusterfuzz-testcase-minimized-ffmpeg_dem_VIVIDAS_fuzzer-6581200338288640

Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
Commit author: Michael Niedermayer

Vulnerable version (before fix):
  * @sa http://wiki.multimedia.cx/index.php?title=Vividas_VIV  */  #include "libavutil/intreadwrite.h" #include "avio_internal.h" #include "avformat.h" static int track_header(VividasDemuxContext *viv, AVFormatContext *s,  uint8_t *          if (avio_tell(pb) < off) {             int num_data;             int xd_size = 0;             int data_len[256];             int offset = 1;             uint8_t *p; static int track_header(VividasDemuxContext *viv, AVFormatContext *s,  uint8_t *                     return AVERROR_INVALIDDATA;                 }                 data_len[j] = len;                 xd_size += len;             }              ret = ff_alloc_extradata(st->codecpar, 64 + xd_size + xd_size / 255);             if (ret < 0)                 return ret;  static int track_header(VividasDemuxContext *viv, AVFormatContext *s,  uint8_t *              for (j = 0; j < num_data - 1; j++) {                 unsigned delta = av_xiphlacing(&p[offset], data_len[j]);                 if (delta > data_len[j]) {                     return AVERROR_INVALIDDATA;                 }                 offset += delta;             }  static int track_header(VividasDemuxContext *viv, AVFormatContext *s,  uint8_t *                     av_freep(&st->codecpar->extradata);                     break;                 }                 offset += data_len[j];             } 

Fixed version (after fix):
  * @sa http://wiki.multimedia.cx/index.php?title=Vividas_VIV  */  #include "libavutil/avassert.h" #include "libavutil/intreadwrite.h" #include "avio_internal.h" #include "avformat.h" static int track_header(VividasDemuxContext *viv, AVFormatContext *s,  uint8_t *          if (avio_tell(pb) < off) {             int num_data;             int xd_size = 1;             int data_len[256];             int offset = 1;             uint8_t *p; static int track_header(VividasDemuxContext *viv, AVFormatContext *s,  uint8_t *                     return AVERROR_INVALIDDATA;                 }                 data_len[j] = len;                 xd_size += len + 1 + len/255;             }              ret = ff_alloc_extradata(st->codecpar, xd_size);             if (ret < 0)                 return ret;  static int track_header(VividasDemuxContext *viv, AVFormatContext *s,  uint8_t *              for (j = 0; j < num_data - 1; j++) {                 unsigned delta = av_xiphlacing(&p[offset], data_len[j]);                 av_assert0(delta <= xd_size - offset);                 offset += delta;             }  static int track_header(VividasDemuxContext *viv, AVFormatContext *s,  uint8_t *                     av_freep(&st->codecpar->extradata);                     break;                 }                 av_assert0(data_len[j] <= xd_size - offset);                 offset += data_len[j];             } 

Patch:
patch: @@ -28,6 +28,7 @@
  * @sa http://wiki.multimedia.cx/index.php?title=Vividas_VIV
  */
 
+#include "libavutil/avassert.h"
 #include "libavutil/intreadwrite.h"
 #include "avio_internal.h"
 #include "avformat.h"
@@ -379,7 +380,7 @@ static int track_header(VividasDemuxContext *viv, AVFormatContext *s,  uint8_t *
 
         if (avio_tell(pb) < off) {
             int num_data;
-            int xd_size = 0;
+            int xd_size = 1;
             int data_len[256];
             int offset = 1;
             uint8_t *p;
@@ -393,10 +394,10 @@ static int track_header(VividasDemuxContext *viv, AVFormatContext *s,  uint8_t *
                     return AVERROR_INVALIDDATA;
                 }
                 data_len[j] = len;
-                xd_size += len;
+                xd_size += len + 1 + len/255;
             }
 
-            ret = ff_alloc_extradata(st->codecpar, 64 + xd_size + xd_size / 255);
+            ret = ff_alloc_extradata(st->codecpar, xd_size);
             if (ret < 0)
                 return ret;
 
@@ -405,9 +406,7 @@ static int track_header(VividasDemuxContext *viv, AVFormatContext *s,  uint8_t *
 
             for (j = 0; j < num_data - 1; j++) {
                 unsigned delta = av_xiphlacing(&p[offset], data_len[j]);
-                if (delta > data_len[j]) {
-                    return AVERROR_INVALIDDATA;
-                }
+                av_assert0(delta <= xd_size - offset);
                 offset += delta;
             }
 
@@ -418,6 +417,7 @@ static int track_header(VividasDemuxContext *viv, AVFormatContext *s,  uint8_t *
                     av_freep(&st->codecpar->extradata);
                     break;
                 }
+                av_assert0(data_len[j] <= xd_size - offset);
                 offset += data_len[j];
             }
path: libavformat/vividas.c