summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2014-06-29 23:22:07 +0200
committerwm4 <wm4@nowhere>2014-06-29 23:27:28 +0200
commit37251cef69aafe1e0477a34291a311a181d19ddb (patch)
tree8261c128046b8e793dc0276046a73843e5815672
parent16465840586e63df4ddd4f1b4707f357e1d788b4 (diff)
downloadmpv-37251cef69aafe1e0477a34291a311a181d19ddb.tar.bz2
mpv-37251cef69aafe1e0477a34291a311a181d19ddb.tar.xz
demux_mkv: add some overflow checks etc.
Some of these might be security relevant. The RealAudio code was especially bad. I'm not sure if all RealAudio stuff still plays correctly; I didn't have that many samples for testing. Some checks might be unnecessary or overcomplicated compared to the (obfuscated) nature of the code. CC: @mpv-player/stable
-rw-r--r--demux/demux_mkv.c160
-rw-r--r--demux/ebml.c10
2 files changed, 111 insertions, 59 deletions
diff --git a/demux/demux_mkv.c b/demux/demux_mkv.c
index 2c94eb6e33..ee355f258f 100644
--- a/demux/demux_mkv.c
+++ b/demux/demux_mkv.c
@@ -128,13 +128,13 @@ typedef struct mkv_track {
double ra_pts; /* previous audio timestamp */
/** realaudio descrambling */
- int sub_packet_size; ///< sub packet size, per stream
- int sub_packet_h; ///< number of coded frames per block
- int coded_framesize; ///< coded frame size, per stream
- int audiopk_size; ///< audio packet size
+ uint16_t sub_packet_size; ///< sub packet size, per stream
+ uint16_t sub_packet_h; ///< number of coded frames per block
+ uint32_t coded_framesize; ///< coded frame size, per stream
+ uint16_t audiopk_size; ///< audio packet size
unsigned char *audio_buf; ///< place to store reordered audio data
double *audio_timestamp; ///< timestamp for each audio packet
- int sub_packet_cnt; ///< number of subpacket already received
+ uint16_t sub_packet_cnt; ///< number of subpacket already received
int audio_filepos; ///< file position of first audio packet in block
/* generic content encoding support */
@@ -142,7 +142,7 @@ typedef struct mkv_track {
int num_encodings;
/* latest added index entry for this track */
- int last_index_entry;
+ size_t last_index_entry;
} mkv_track_t;
typedef struct mkv_index {
@@ -167,7 +167,7 @@ typedef struct mkv_demuxer {
uint64_t cluster_end;
mkv_index_t *indexes;
- int num_indexes;
+ size_t num_indexes;
bool index_complete;
uint64_t deferred_cues;
@@ -192,19 +192,6 @@ typedef struct mkv_demuxer {
// (Subtitle packets added before first A/V keyframe packet is found with seek.)
#define NUM_SUB_PREROLL_PACKETS 500
-/**
- * \brief ensures there is space for at least one additional element
- * \param array array to grow
- * \param nelem current number of elements in array
- * \param elsize size of one array element
- */
-static void *grow_array(void *array, int nelem, size_t elsize)
-{
- if (!(nelem & 31))
- array = realloc(array, (nelem + 32) * elsize);
- return array;
-}
-
#define AAC_SYNC_EXTENSION_TYPE 0x02b7
static int aac_get_sample_rate_index(uint32_t sample_rate)
{
@@ -258,6 +245,12 @@ static bstr demux_mkv_decode(struct mp_log *log, mkv_track_t *track,
zstream.avail_out = size;
int result;
do {
+ if (size >= INT_MAX - 4000) {
+ talloc_free(dest);
+ dest = NULL;
+ inflateEnd(&zstream);
+ goto error;
+ }
size += 4000;
dest = talloc_realloc_size(track->parser_tmp, dest, size);
zstream.next_out = (Bytef *) (dest + zstream.total_out);
@@ -279,6 +272,8 @@ static bstr demux_mkv_decode(struct mp_log *log, mkv_track_t *track,
} else if (enc->comp_algo == 2) {
/* lzo encoded track */
int out_avail;
+ if (size > INT_MAX / 3 + AV_LZO_OUTPUT_PADDING)
+ goto error;
int dstlen = size * 3;
dest = NULL;
@@ -297,6 +292,11 @@ static bstr demux_mkv_decode(struct mp_log *log, mkv_track_t *track,
goto error;
}
mp_dbg(log, "lzo decompression buffer too small.\n");
+ if (dstlen > INT_MAX / 2 + AV_LZO_OUTPUT_PADDING) {
+ talloc_free(dest);
+ dest = NULL;
+ goto error;
+ }
dstlen *= 2;
}
size = dstlen - out_avail;
@@ -311,6 +311,8 @@ static bstr demux_mkv_decode(struct mp_log *log, mkv_track_t *track,
error:
if (src != dest && src != orig_src)
talloc_free(src);
+ if (!size)
+ dest = NULL;
return (bstr){dest, size};
}
@@ -433,7 +435,7 @@ static void parse_trackencodings(struct demuxer *demuxer,
if (e.order >= ce[i].order)
break;
}
- ce = talloc_realloc_size(track, ce, (n_enc + 1) * sizeof(*ce));
+ ce = talloc_realloc(track, ce, mkv_content_encoding_t, n_enc + 1);
memmove(ce + i + 1, ce + i, (n_enc - i) * sizeof(*ce));
memcpy(ce + i, &e, sizeof(e));
}
@@ -525,8 +527,8 @@ static void parse_trackentry(struct demuxer *demuxer,
struct ebml_track_entry *entry)
{
mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv;
- struct mkv_track *track = talloc_zero_size(NULL, sizeof(*track));
- track->last_index_entry = -1;
+ struct mkv_track *track = talloc_zero(NULL, struct mkv_track);
+ track->last_index_entry = (size_t)-1;
track->parser_tmp = talloc_new(track);
track->tnum = entry->track_number;
@@ -584,7 +586,7 @@ static void parse_trackentry(struct demuxer *demuxer,
track->codec_id = "";
}
- if (entry->n_codec_private) {
+ if (entry->n_codec_private && entry->codec_private.len <= 0x1000000) {
int len = entry->codec_private.len;
track->private_data = talloc_size(track, len + AV_LZO_INPUT_PADDING);
memcpy(track->private_data, entry->codec_private.start, len);
@@ -640,8 +642,8 @@ static int demux_mkv_read_tracks(demuxer_t *demuxer)
if (ebml_read_element(s, &parse_ctx, &tracks, &ebml_tracks_desc) < 0)
return -1;
- mkv_d->tracks = talloc_size(mkv_d,
- tracks.n_track_entry * sizeof(*mkv_d->tracks));
+ mkv_d->tracks = talloc_zero_array(mkv_d, struct mkv_track*,
+ tracks.n_track_entry);
for (int i = 0; i < tracks.n_track_entry; i++) {
MP_VERBOSE(demuxer, "| + a track...\n");
parse_trackentry(demuxer, &tracks.track_entry[i]);
@@ -655,8 +657,8 @@ static void cue_index_add(demuxer_t *demuxer, int track_id, uint64_t filepos,
{
mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv;
- mkv_d->indexes = grow_array(mkv_d->indexes, mkv_d->num_indexes,
- sizeof(mkv_index_t));
+ MP_TARRAY_GROW(mkv_d, mkv_d->indexes, mkv_d->num_indexes);
+
mkv_d->indexes[mkv_d->num_indexes].tnum = track_id;
mkv_d->indexes[mkv_d->num_indexes].timecode = timecode;
mkv_d->indexes[mkv_d->num_indexes].filepos = filepos;
@@ -670,7 +672,7 @@ static void add_block_position(demuxer_t *demuxer, struct mkv_track *track,
if (mkv_d->index_complete || !track)
return;
- if (track->last_index_entry >= 0) {
+ if (track->last_index_entry != (size_t)-1) {
mkv_index_t *index = &mkv_d->indexes[track->last_index_entry];
// filepos is always the cluster position, which can contain multiple
// blocks with different timecodes - one is enough.
@@ -1241,6 +1243,11 @@ static int demux_mkv_open_video(demuxer_t *demuxer, mkv_track_t *track)
}
}
+ if (extradata_size > 0x1000000) {
+ MP_WARN(demuxer, "Invalid CodecPrivate\n");
+ return 1;
+ }
+
sh = new_sh_stream(demuxer, STREAM_VIDEO);
if (!sh)
return 1;
@@ -1465,6 +1472,8 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
if (track->private_size == 0 || track->ms_compat && !sh_a->wf->cbSize)
goto error;
if (!track->ms_compat) {
+ if (track->private_size > 0x1000000)
+ goto error;
sh_a->wf->cbSize = track->private_size;
sh_a->wf = talloc_realloc_size(sh_a, sh_a->wf,
sizeof(*sh_a->wf) + sh_a->wf->cbSize);
@@ -1499,10 +1508,18 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
src += RAPROPERTIES5_SIZE;
}
+ if (track->audiopk_size == 0 || track->sub_packet_size == 0 ||
+ track->sub_packet_h == 0 || track->coded_framesize == 0)
+ goto error;
+ if (track->coded_framesize > 0x40000000)
+ goto error;
+
src += 3;
if (version == 5)
src++;
codecdata_length = AV_RB32(src);
+ if (codecdata_length < 0 || codecdata_length > 0x1000000)
+ goto error;
src += 4;
sh_a->wf->cbSize = codecdata_length;
sh_a->wf = talloc_realloc_size(sh_a, sh_a->wf,
@@ -1527,9 +1544,9 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
sh_a->wf->nBlockAlign = track->coded_framesize;
audiobuf:
track->audio_buf =
- talloc_size(demuxer, track->sub_packet_h * track->audiopk_size);
+ talloc_array_size(track, track->sub_packet_h, track->audiopk_size);
track->audio_timestamp =
- talloc_size(demuxer, track->sub_packet_h * sizeof(double));
+ talloc_array(track, double, track->sub_packet_h);
break;
}
@@ -1631,6 +1648,9 @@ static int demux_mkv_open_sub(demuxer_t *demuxer, mkv_track_t *track)
}
}
+ if (track->private_size > 0x10000000)
+ return 1;
+
bstr in = (bstr){track->private_data, track->private_size};
struct sh_stream *sh = new_sh_stream(demuxer, STREAM_SUB);
if (!sh)
@@ -1989,46 +2009,63 @@ static void handle_realaudio(demuxer_t *demuxer, mkv_track_t *track,
bstr data, bool keyframe)
{
mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv;
- int sps = track->sub_packet_size;
- int sph = track->sub_packet_h;
- int cfs = track->coded_framesize;
- int w = track->audiopk_size;
- int spc = track->sub_packet_cnt;
+ uint16_t sps = track->sub_packet_size;
+ uint16_t sph = track->sub_packet_h;
+ uint32_t cfs = track->coded_framesize; // restricted to [1,0x40000000]
+ uint16_t w = track->audiopk_size;
+ uint16_t spc = track->sub_packet_cnt;
uint8_t *buffer = data.start;
uint32_t size = data.len;
demux_packet_t *dp;
+ // track->audio_buf allocation size
+ size_t audiobuf_size = sph * w;
+
if ((track->a_formattag == MP_FOURCC('2', '8', '_', '8'))
|| (track->a_formattag == MP_FOURCC('c', 'o', 'o', 'k'))
|| (track->a_formattag == MP_FOURCC('a', 't', 'r', 'c'))
- || (track->a_formattag == MP_FOURCC('s', 'i', 'p', 'r'))) {
-// if(!block_bref)
-// spc = track->sub_packet_cnt = 0;
+ || (track->a_formattag == MP_FOURCC('s', 'i', 'p', 'r')))
+ {
switch (track->a_formattag) {
case MP_FOURCC('2', '8', '_', '8'):
- for (int x = 0; x < sph / 2; x++)
- memcpy(track->audio_buf + x * 2 * w + spc * cfs,
- buffer + cfs * x, cfs);
+ for (int x = 0; x < sph / 2; x++) {
+ uint64_t dst_offset = x * 2 * w + spc * (uint64_t)cfs;
+ if (dst_offset + cfs > audiobuf_size)
+ goto error;
+ uint64_t src_offset = x * (uint64_t)cfs;
+ if (src_offset + cfs > size)
+ goto error;
+ memcpy(track->audio_buf + dst_offset, buffer + src_offset, cfs);
+ }
break;
case MP_FOURCC('c', 'o', 'o', 'k'):
case MP_FOURCC('a', 't', 'r', 'c'):
- for (int x = 0; x < w / sps; x++)
- memcpy(track->audio_buf +
- sps * (sph * x + ((sph + 1) / 2) * (spc & 1) +
- (spc >> 1)), buffer + sps * x, sps);
+ for (int x = 0; x < w / sps; x++) {
+ uint32_t dst_offset = sps * (sph * x + ((sph + 1) / 2) * (spc & 1)
+ + (spc >> 1));
+ if (dst_offset + sps > audiobuf_size)
+ goto error;
+ uint32_t src_offset = sps * x;
+ if (src_offset + sps > size)
+ goto error;
+ memcpy(track->audio_buf + dst_offset, buffer + src_offset, sps);
+ }
break;
case MP_FOURCC('s', 'i', 'p', 'r'):
+ if (spc * w + w > audiobuf_size || w > size)
+ goto error;
memcpy(track->audio_buf + spc * w, buffer, w);
if (spc == sph - 1) {
int n;
int bs = sph * w * 2 / 96; // nibbles per subpacket
// Perform reordering
for (n = 0; n < 38; n++) {
- int j;
- int i = bs * sipr_swaps[n][0];
- int o = bs * sipr_swaps[n][1];
- // swap nibbles of block 'i' with 'o' TODO: optimize
- for (j = 0; j < bs; j++) {
+ int i = bs * sipr_swaps[n][0]; // 77 max
+ int o = bs * sipr_swaps[n][1]; // 95 max
+ // swap nibbles of block 'i' with 'o'
+ for (int j = 0; j < bs; j++) {
+ if (i / 2 >= audiobuf_size || o / 2 >= audiobuf_size)
+ goto error;
int x = (i & 1) ?
(track->audio_buf[i >> 1] >> 4) :
(track->audio_buf[i >> 1] & 0x0F);
@@ -2060,8 +2097,11 @@ static void handle_realaudio(demuxer_t *demuxer, mkv_track_t *track,
if (track->sub_packet_cnt == 0)
track->audio_filepos = mkv_d->last_filepos;
if (++(track->sub_packet_cnt) == sph) {
- int apk_usize = track->stream->audio->wf->nBlockAlign;
track->sub_packet_cnt = 0;
+ // apk_usize has same range as coded_framesize in worst case
+ uint32_t apk_usize = track->stream->audio->wf->nBlockAlign;
+ if (apk_usize > audiobuf_size)
+ goto error;
// Release all the audio packets
for (int x = 0; x < sph * w / apk_usize; x++) {
dp = new_demux_packet_from(track->audio_buf + x * apk_usize,
@@ -2087,6 +2127,9 @@ static void handle_realaudio(demuxer_t *demuxer, mkv_track_t *track,
dp->keyframe = keyframe;
demuxer_add_packet(demuxer, track->stream, dp);
}
+ return;
+error:
+ MP_ERR(demuxer, "RealAudio decrypting error.\n");
}
static void mkv_seek_reset(demuxer_t *demuxer)
@@ -2154,6 +2197,9 @@ static int libav_parse_wavpack(mkv_track_t *track, uint8_t *src,
if (blocksize > srclen)
goto fail;
+ if (dstlen > 0x10000000 || blocksize > 0x10000000)
+ goto fail;
+
tmp = talloc_realloc(track->parser_tmp, dst, uint8_t,
dstlen + blocksize + 32);
if (!tmp)
@@ -2223,7 +2269,7 @@ static bool mkv_parse_packet(mkv_track_t *track, bstr *raw, bstr *out)
int len = av_parser_parse2(track->av_parser, track->av_parser_codec,
&data, &size, raw->start, raw->len,
AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
- if (len < 0)
+ if (len < 0 || len > 0x10000000)
return false;
*raw = bstr_cut(*raw, len);
if (size) {
@@ -2640,7 +2686,7 @@ static struct mkv_index *seek_with_cues(struct demuxer *demuxer, int seek_id,
min_diff = -min_diff;
min_diff = FFMAX(min_diff, 1);
- for (int i = 0; i < mkv_d->num_indexes; i++) {
+ for (size_t i = 0; i < mkv_d->num_indexes; i++) {
if (seek_id < 0 || mkv_d->indexes[i].tnum == seek_id) {
int64_t diff =
target_timecode -
@@ -2661,7 +2707,7 @@ static struct mkv_index *seek_with_cues(struct demuxer *demuxer, int seek_id,
uint64_t seek_pos = index->filepos;
if (flags & SEEK_SUBPREROLL) {
uint64_t prev_target = 0;
- for (int i = 0; i < mkv_d->num_indexes; i++) {
+ for (size_t i = 0; i < mkv_d->num_indexes; i++) {
if (seek_id < 0 || mkv_d->indexes[i].tnum == seek_id) {
uint64_t index_pos = mkv_d->indexes[i].filepos;
if (index_pos > prev_target && index_pos < seek_pos)
@@ -2745,7 +2791,6 @@ static void demux_mkv_seek(demuxer_t *demuxer, float rel_seek_secs, int flags)
stream_t *s = demuxer->stream;
uint64_t target_filepos;
mkv_index_t *index = NULL;
- int i;
read_deferred_cues(demuxer);
@@ -2759,7 +2804,7 @@ static void demux_mkv_seek(demuxer_t *demuxer, float rel_seek_secs, int flags)
stream_control(s, STREAM_CTRL_GET_SIZE, &size);
target_filepos = (uint64_t) (size * rel_seek_secs);
- for (i = 0; i < mkv_d->num_indexes; i++)
+ for (size_t i = 0; i < mkv_d->num_indexes; i++)
if (mkv_d->indexes[i].tnum == v_tnum)
if ((index == NULL)
|| ((mkv_d->indexes[i].filepos >= target_filepos)
@@ -2807,7 +2852,6 @@ static void mkv_free(struct demuxer *demuxer)
mkv_seek_reset(demuxer);
for (int i = 0; i < mkv_d->num_tracks; i++)
demux_mkv_free_trackentry(mkv_d->tracks[i]);
- free(mkv_d->indexes);
}
const demuxer_desc_t demuxer_desc_matroska = {
diff --git a/demux/ebml.c b/demux/ebml.c
index f420616aa9..1d0473a6e1 100644
--- a/demux/ebml.c
+++ b/demux/ebml.c
@@ -364,7 +364,7 @@ static void ebml_parse_element(struct ebml_parse_ctx *ctx, void *target,
char *s = target;
uint8_t *end = data + size;
uint8_t *p = data;
- int num_elems[MAX_EBML_SUBELEMENTS] = {};
+ int num_elems[MAX_EBML_SUBELEMENTS] = {0};
while (p < end) {
uint8_t *startp = p;
int len;
@@ -390,6 +390,10 @@ static void ebml_parse_element(struct ebml_parse_ctx *ctx, void *target,
if (type->fields[i].id == id) {
field_idx = i;
num_elems[i]++;
+ if (num_elems[i] >= 0x70000000) {
+ MP_ERR(ctx, "Too many EBML subelements.\n");
+ goto other_error;
+ }
break;
}
@@ -566,6 +570,10 @@ static void ebml_parse_element(struct ebml_parse_ctx *ctx, void *target,
case EBML_TYPE_STR:
case EBML_TYPE_BINARY:;
+ if (length > 0x80000000) {
+ MP_ERR(ctx, "Not reading overly long EBML element.\n");
+ break;
+ }
struct bstr *strptr;
GETPTR(strptr, struct bstr);
strptr->start = data;