From 37251cef69aafe1e0477a34291a311a181d19ddb Mon Sep 17 00:00:00 2001 From: wm4 Date: Sun, 29 Jun 2014 23:22:07 +0200 Subject: 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 --- demux/demux_mkv.c | 160 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 102 insertions(+), 58 deletions(-) (limited to 'demux/demux_mkv.c') 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 = { -- cgit v1.2.3