summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2013-04-12 17:25:51 +0200
committerwm4 <wm4@nowhere>2013-04-20 23:28:22 +0200
commit9f21c81633504696fc68a8ca94465460c54c5243 (patch)
tree5207cc97fb52796556132edfee5f1e351c9aaa86
parentc951010a261c4d615a7083f91e95771d8e98a8cd (diff)
downloadmpv-9f21c81633504696fc68a8ca94465460c54c5243.tar.bz2
mpv-9f21c81633504696fc68a8ca94465460c54c5243.tar.xz
demux_mkv: use a bounded buffer for block data
Should help avoiding out-of-bounds reads.
-rw-r--r--demux/demux_mkv.c213
-rw-r--r--demux/ebml.c25
-rw-r--r--demux/ebml.h4
3 files changed, 122 insertions, 120 deletions
diff --git a/demux/demux_mkv.c b/demux/demux_mkv.c
index b117aba90a..a96e69d811 100644
--- a/demux/demux_mkv.c
+++ b/demux/demux_mkv.c
@@ -249,27 +249,27 @@ static int aac_get_sample_rate_index(uint32_t sample_rate)
return i;
}
-static void demux_mkv_decode(mkv_track_t *track, uint8_t *src,
- uint8_t **dest, uint32_t *size, uint32_t type)
+static bstr demux_mkv_decode(mkv_track_t *track, bstr data, uint32_t type)
{
+ uint8_t *src = data.start;
uint8_t *orig_src = src;
-
- *dest = src;
+ uint8_t *dest = src;
+ uint32_t size = data.len;
for (int i = 0; i < track->num_encodings; i++) {
struct mkv_content_encoding *enc = track->encodings + i;
if (!(enc->scope & type))
continue;
- if (src != *dest && src != orig_src)
+ if (src != dest && src != orig_src)
talloc_free(src);
- src = *dest; // output from last iteration is new source
+ src = dest; // output from last iteration is new source
if (enc->comp_algo == 0) {
#if CONFIG_ZLIB
/* zlib encoded track */
- if (*size == 0)
+ if (size == 0)
continue;
z_stream zstream;
@@ -283,21 +283,21 @@ static void demux_mkv_decode(mkv_track_t *track, uint8_t *src,
goto error;
}
zstream.next_in = (Bytef *) src;
- zstream.avail_in = *size;
+ zstream.avail_in = size;
- *dest = NULL;
- zstream.avail_out = *size;
+ dest = NULL;
+ zstream.avail_out = size;
int result;
do {
- *size += 4000;
- *dest = talloc_realloc_size(NULL, *dest, *size);
- zstream.next_out = (Bytef *) (*dest + zstream.total_out);
+ size += 4000;
+ dest = talloc_realloc_size(NULL, dest, size);
+ zstream.next_out = (Bytef *) (dest + zstream.total_out);
result = inflate(&zstream, Z_NO_FLUSH);
if (result != Z_OK && result != Z_STREAM_END) {
mp_tmsg(MSGT_DEMUX, MSGL_WARN,
"[mkv] zlib decompression failed.\n");
- talloc_free(*dest);
- *dest = NULL;
+ talloc_free(dest);
+ dest = NULL;
inflateEnd(&zstream);
goto error;
}
@@ -305,46 +305,47 @@ static void demux_mkv_decode(mkv_track_t *track, uint8_t *src,
} while (zstream.avail_out == 4000 && zstream.avail_in != 0
&& result != Z_STREAM_END);
- *size = zstream.total_out;
+ size = zstream.total_out;
inflateEnd(&zstream);
#endif
} else if (enc->comp_algo == 2) {
/* lzo encoded track */
int out_avail;
- int dstlen = *size * 3;
+ int dstlen = size * 3;
- *dest = NULL;
+ dest = NULL;
while (1) {
- int srclen = *size;
- *dest = talloc_realloc_size(NULL, *dest,
- dstlen + AV_LZO_OUTPUT_PADDING);
+ int srclen = size;
+ dest = talloc_realloc_size(NULL, dest,
+ dstlen + AV_LZO_OUTPUT_PADDING);
out_avail = dstlen;
- int result = av_lzo1x_decode(*dest, &out_avail, src, &srclen);
+ int result = av_lzo1x_decode(dest, &out_avail, src, &srclen);
if (result == 0)
break;
if (!(result & AV_LZO_OUTPUT_FULL)) {
mp_tmsg(MSGT_DEMUX, MSGL_WARN,
"[mkv] lzo decompression failed.\n");
- talloc_free(*dest);
- *dest = NULL;
+ talloc_free(dest);
+ dest = NULL;
goto error;
}
mp_msg(MSGT_DEMUX, MSGL_DBG2,
"[mkv] lzo decompression buffer too small.\n");
dstlen *= 2;
}
- *size = dstlen - out_avail;
+ size = dstlen - out_avail;
} else if (enc->comp_algo == 3) {
- *dest = talloc_size(NULL, *size + enc->comp_settings_len);
- memcpy(*dest, enc->comp_settings, enc->comp_settings_len);
- memcpy(*dest + enc->comp_settings_len, src, *size);
- *size += enc->comp_settings_len;
+ dest = talloc_size(NULL, size + enc->comp_settings_len);
+ memcpy(dest, enc->comp_settings, enc->comp_settings_len);
+ memcpy(dest + enc->comp_settings_len, src, size);
+ size += enc->comp_settings_len;
}
}
error:
- if (src != *dest && src != orig_src)
+ if (src != dest && src != orig_src)
talloc_free(src);
+ return (bstr){dest, size};
}
@@ -1616,19 +1617,17 @@ static int demux_mkv_open_sub(demuxer_t *demuxer, mkv_track_t *track,
int sid)
{
if (track->subtitle_type) {
- int size;
- uint8_t *buffer;
+ bstr in = (bstr){track->private_data, track->private_size};
sh_sub_t *sh = new_sh_sub(demuxer, sid);
sh->gsh->demuxer_id = track->tnum;
track->sh_sub = sh;
sh->type = track->subtitle_type;
- size = track->private_size;
- demux_mkv_decode(track, track->private_data, &buffer, &size, 2);
- if (buffer && buffer != track->private_data) {
+ bstr buffer = demux_mkv_decode(track, in, 2);
+ if (buffer.start && buffer.start != track->private_data) {
talloc_free(track->private_data);
- talloc_steal(track, buffer);
- track->private_data = buffer;
- track->private_size = size;
+ talloc_steal(track, buffer.start);
+ track->private_data = buffer.start;
+ track->private_size = buffer.len;
}
sh->extradata = malloc(track->private_size);
memcpy(sh->extradata, track->private_data, track->private_size);
@@ -1784,8 +1783,19 @@ static int demux_mkv_open(demuxer_t *demuxer)
return DEMUXER_TYPE_MATROSKA;
}
-static int demux_mkv_read_block_lacing(uint8_t *buffer, uint64_t *size,
- uint8_t *laces,
+static bool bstr_read_u8(bstr *buffer, uint8_t *out_u8)
+{
+ if (buffer->len > 0) {
+ *out_u8 = buffer->start[0];
+ buffer->len -= 1;
+ buffer->start += 1;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+static int demux_mkv_read_block_lacing(bstr *buffer, uint8_t *laces,
uint32_t lace_size[MAX_NUM_LACES])
{
uint32_t total = 0;
@@ -1793,75 +1803,62 @@ static int demux_mkv_read_block_lacing(uint8_t *buffer, uint64_t *size,
int i;
/* lacing flags */
- if (*size < 1)
+ if (!bstr_read_u8(buffer, &flags))
goto error;
- flags = *buffer++;
- (*size)--;
switch ((flags & 0x06) >> 1) {
case 0: /* no lacing */
*laces = 1;
- lace_size[0] = *size;
+ lace_size[0] = buffer->len;
break;
case 1: /* xiph lacing */
case 2: /* fixed-size lacing */
case 3: /* EBML lacing */
- if (*size < 1)
+ if (!bstr_read_u8(buffer, laces))
goto error;
- *laces = *buffer++;
- (*size)--;
(*laces)++;
switch ((flags & 0x06) >> 1) {
case 1: /* xiph lacing */
for (i = 0; i < *laces - 1; i++) {
+ uint8_t t;
lace_size[i] = 0;
do {
- if (!*size)
+ if (!bstr_read_u8(buffer, &t))
goto error;
- lace_size[i] += *buffer;
- (*size)--;
- } while (*buffer++ == 0xFF);
- if (lace_size[i] > *size - total || total > *size)
+ lace_size[i] += t;
+ } while (t == 0xFF);
+ if (lace_size[i] > buffer->len - total || total > buffer->len)
goto error;
total += lace_size[i];
}
- lace_size[i] = *size - total;
+ lace_size[i] = buffer->len - total;
break;
case 2: /* fixed-size lacing */
for (i = 0; i < *laces; i++)
- lace_size[i] = *size / *laces;
+ lace_size[i] = buffer->len / *laces;
break;
case 3:; /* EBML lacing */
- int l;
- uint64_t num = ebml_read_vlen_uint(buffer, &l);
+ uint64_t num = ebml_read_vlen_uint(buffer);
if (num == EBML_UINT_INVALID)
goto error;
- buffer += l;
- if (*size < l)
- goto error;
- *size -= l;
- if (num > *size)
+ if (num > buffer->len)
goto error;
total = lace_size[0] = num;
for (i = 1; i < *laces - 1; i++) {
- int64_t snum = ebml_read_vlen_int(buffer, &l);
+ int64_t snum = ebml_read_vlen_int(buffer);
if (snum == EBML_INT_INVALID)
goto error;
- buffer += l;
- if (*size < l)
- goto error;
- *size -= l;
lace_size[i] = lace_size[i - 1] + snum;
- if (lace_size[i] > *size - total || total > *size)
+ if (lace_size[i] > buffer->len - total || total > buffer->len)
goto error;
total += lace_size[i];
}
- lace_size[i] = *size - total;
+ lace_size[i] = buffer->len - total;
break;
}
break;
@@ -1922,14 +1919,14 @@ static double real_fix_timestamp(unsigned char *buf, unsigned int timestamp, uns
}
static void handle_realvideo(demuxer_t *demuxer, mkv_track_t *track,
- uint8_t *buffer, uint32_t size, bool keyframe)
+ bstr data, bool keyframe)
{
mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv;
demux_packet_t *dp;
uint32_t timestamp = mkv_d->last_pts * 1000;
- dp = new_demux_packet(size);
- memcpy(dp->buffer, buffer, size);
+ dp = new_demux_packet(data.len);
+ memcpy(dp->buffer, data.start, data.len);
if (mkv_d->v_skip_to_keyframe) {
dp->pts = mkv_d->last_pts;
@@ -1948,7 +1945,7 @@ static void handle_realvideo(demuxer_t *demuxer, mkv_track_t *track,
}
static void handle_realaudio(demuxer_t *demuxer, mkv_track_t *track,
- uint8_t *buffer, uint32_t size, bool keyframe)
+ bstr data, bool keyframe)
{
mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv;
int sps = track->sub_packet_size;
@@ -1956,6 +1953,8 @@ static void handle_realaudio(demuxer_t *demuxer, mkv_track_t *track,
int cfs = track->coded_framesize;
int w = track->audiopk_size;
int spc = track->sub_packet_cnt;
+ uint8_t *buffer = data.start;
+ uint32_t size = data.len;
demux_packet_t *dp;
int x;
@@ -2054,19 +2053,19 @@ static void handle_realaudio(demuxer_t *demuxer, mkv_track_t *track,
}
struct block_info {
- uint64_t length;
uint64_t duration;
bool simple, keyframe;
uint64_t timecode;
mkv_track_t *track;
- uint8_t *data;
+ bstr data;
void *alloc;
};
static void free_block(struct block_info *block)
{
free(block->alloc);
- block->data = block->alloc = NULL;
+ block->alloc = NULL;
+ block->data = (bstr){0};
}
static void index_block(demuxer_t *demuxer, struct block_info *block)
@@ -2082,29 +2081,34 @@ static int read_block(demuxer_t *demuxer, struct block_info *block)
{
mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv;
stream_t *s = demuxer->stream;
- int tmp, num;
+ int num;
int16_t time;
+ uint64_t length;
int res = -1;
free_block(block);
- block->length = ebml_read_length(s, NULL);
- if (block->length < 6 || block->length > 500000000)
+ length = ebml_read_length(s, NULL);
+ if (length > 500000000)
goto exit;
- block->data = block->alloc = malloc(block->length + AV_LZO_INPUT_PADDING);
+ block->alloc = malloc(length + AV_LZO_INPUT_PADDING);
+ block->data = (bstr){block->alloc, length};
demuxer->filepos = stream_tell(s);
- if (stream_read(s, block->data, block->length) != (int) block->length)
+ if (stream_read(s, block->data.start, block->data.len) != block->data.len)
goto exit;
// Parse header of the Block element
/* first byte(s): track num */
- num = ebml_read_vlen_uint(block->data, &tmp);
- block->data += tmp;
+ num = ebml_read_vlen_uint(&block->data);
+ if (num == EBML_UINT_INVALID)
+ goto exit;
/* time (relative to cluster time) */
- time = block->data[0] << 8 | block->data[1];
- block->data += 2;
- block->length -= tmp + 2;
+ if (block->data.len < 3)
+ goto exit;
+ time = block->data.start[0] << 8 | block->data.start[1];
+ block->data.start += 2;
+ block->data.len -= 2;
if (block->simple)
- block->keyframe = block->data[0] & 0x80;
+ block->keyframe = block->data.start[0] & 0x80;
block->timecode = time * mkv_d->tc_scale + mkv_d->cluster_tc;
for (int i = 0; i < mkv_d->num_tracks; i++) {
if (mkv_d->tracks[i]->tnum == num) {
@@ -2128,22 +2132,18 @@ static int handle_block(demuxer_t *demuxer, struct block_info *block_info)
{
mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv;
demux_stream_t *ds = NULL;
- uint64_t old_length;
uint8_t laces;
int i, use_this_block = 1;
double current_pts;
- uint8_t *block = block_info->data;
- uint64_t length = block_info->length;
+ bstr data = block_info->data;
bool keyframe = block_info->keyframe;
uint64_t block_duration = block_info->duration;
uint64_t tc = block_info->timecode;
mkv_track_t *track = block_info->track;
uint32_t lace_size[MAX_NUM_LACES];
- old_length = length;
- if (demux_mkv_read_block_lacing(block, &length, &laces, lace_size))
+ if (demux_mkv_read_block_lacing(&data, &laces, lace_size))
return 0;
- block += old_length - length;
current_pts = tc / 1e9;
@@ -2160,12 +2160,12 @@ static int handle_block(demuxer_t *demuxer, struct block_info *block_info)
sh_audio_t *sh = (sh_audio_t *) ds->sh;
if (block_duration != 0) {
- sh->i_bps = length * 1e9 / block_duration;
+ sh->i_bps = data.len * 1e9 / block_duration;
track->fix_i_bps = 0;
} else if (track->qt_last_a_pts == 0.0)
track->qt_last_a_pts = current_pts;
else if (track->qt_last_a_pts != current_pts) {
- sh->i_bps = length / (current_pts - track->qt_last_a_pts);
+ sh->i_bps = data.len / (current_pts - track->qt_last_a_pts);
track->fix_i_bps = 0;
}
}
@@ -2196,22 +2196,19 @@ static int handle_block(demuxer_t *demuxer, struct block_info *block_info)
mkv_d->last_filepos = demuxer->filepos;
for (i = 0; i < laces; i++) {
+ bstr block = bstr_splice(data, 0, lace_size[i]);
if (ds == demuxer->video && track->realmedia)
- handle_realvideo(demuxer, track, block, lace_size[i],
- keyframe);
+ handle_realvideo(demuxer, track, block, keyframe);
else if (ds == demuxer->audio && track->realmedia)
- handle_realaudio(demuxer, track, block, lace_size[i],
- keyframe);
+ handle_realaudio(demuxer, track, block, keyframe);
else {
- int size = lace_size[i];
demux_packet_t *dp;
- uint8_t *buffer;
- demux_mkv_decode(track, block, &buffer, &size, 1);
- if (buffer) {
- dp = new_demux_packet(size);
- memcpy(dp->buffer, buffer, size);
- if (buffer != block)
- talloc_free(buffer);
+ bstr buffer = demux_mkv_decode(track, block, 1);
+ if (buffer.start) {
+ dp = new_demux_packet(buffer.len);
+ memcpy(dp->buffer, buffer.start, buffer.len);
+ if (buffer.start != block.start)
+ talloc_free(buffer.start);
dp->keyframe = keyframe;
/* If default_duration is 0, assume no pts value is known
* for packets after the first one (rather than all pts
@@ -2223,7 +2220,7 @@ static int handle_block(demuxer_t *demuxer, struct block_info *block_info)
ds_add_packet(ds, dp);
}
}
- block += lace_size[i];
+ data = bstr_cut(data, lace_size[i]);
}
if (ds == demuxer->video) {
@@ -2278,7 +2275,7 @@ static int read_block_group(demuxer_t *demuxer, int64_t end,
}
}
- return block->data ? 1 : 0;
+ return block->data.start ? 1 : 0;
error:
free_block(block);
diff --git a/demux/ebml.c b/demux/ebml.c
index 9dbc2c22f7..52332cd0c5 100644
--- a/demux/ebml.c
+++ b/demux/ebml.c
@@ -64,44 +64,49 @@ uint32_t ebml_read_id(stream_t *s, int *length)
/*
* Read a variable length unsigned int.
*/
-uint64_t ebml_read_vlen_uint(uint8_t *buffer, int *length)
+uint64_t ebml_read_vlen_uint(bstr *buffer)
{
int i, j, num_ffs = 0, len_mask = 0x80;
uint64_t num;
- for (i = 0, num = *buffer++; i < 8 && !(num & len_mask); i++)
+ if (buffer->len == 0)
+ return EBML_UINT_INVALID;
+
+ for (i = 0, num = buffer->start[0]; i < 8 && !(num & len_mask); i++)
len_mask >>= 1;
if (i >= 8)
return EBML_UINT_INVALID;
j = i + 1;
- if (length)
- *length = j;
if ((int) (num &= (len_mask - 1)) == len_mask - 1)
num_ffs++;
- while (i--) {
- num = (num << 8) | *buffer++;
+ if (j > buffer->len)
+ return EBML_UINT_INVALID;
+ for (int n = 0; n < i; n++) {
+ num = (num << 8) | buffer->start[n + 1];
if ((num & 0xFF) == 0xFF)
num_ffs++;
}
if (j == num_ffs)
return EBML_UINT_INVALID;
+ buffer->start += j;
+ buffer->len -= j;
return num;
}
/*
* Read a variable length signed int.
*/
-int64_t ebml_read_vlen_int(uint8_t *buffer, int *length)
+int64_t ebml_read_vlen_int(bstr *buffer)
{
uint64_t unum;
int l;
/* read as unsigned number first */
- unum = ebml_read_vlen_uint(buffer, &l);
+ size_t len = buffer->len;
+ unum = ebml_read_vlen_uint(buffer);
if (unum == EBML_UINT_INVALID)
return EBML_INT_INVALID;
- if (length)
- *length = l;
+ l = len - buffer->len;
return unum - ((1 << ((7 * l) - 1)) - 1);
}
diff --git a/demux/ebml.h b/demux/ebml.h
index be8573817a..3f8bad0f92 100644
--- a/demux/ebml.h
+++ b/demux/ebml.h
@@ -90,8 +90,8 @@ struct ebml_parse_ctx {
uint32_t ebml_read_id (stream_t *s, int *length);
-uint64_t ebml_read_vlen_uint (uint8_t *buffer, int *length);
-int64_t ebml_read_vlen_int (uint8_t *buffer, int *length);
+uint64_t ebml_read_vlen_uint (bstr *buffer);
+int64_t ebml_read_vlen_int (bstr *buffer);
uint64_t ebml_read_length (stream_t *s, int *length);
uint64_t ebml_read_uint (stream_t *s, uint64_t *length);
int64_t ebml_read_int (stream_t *s, uint64_t *length);