matroskademux: not so fatal error handling

If some bits out of place in block(group) parsing, forego and move to next.
Also skip large blocks in pull mode, but need to give up in push mode.

Fixes #626463.
Improves #620790.
This commit is contained in:
Mark Nauwelaerts 2010-08-16 16:05:41 +02:00
parent 680eb51b68
commit 4d9c1e99f2

View file

@ -4654,8 +4654,9 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux,
while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
if (!is_simpleblock) { if (!is_simpleblock) {
if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) {
break; goto data_error;
}
} else { } else {
id = GST_MATROSKA_ID_SIMPLEBLOCK; id = GST_MATROSKA_ID_SIMPLEBLOCK;
} }
@ -4681,13 +4682,8 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux,
size = GST_BUFFER_SIZE (buf); size = GST_BUFFER_SIZE (buf);
/* first byte(s): blocknum */ /* first byte(s): blocknum */
if ((n = gst_matroska_ebmlnum_uint (data, size, &num)) < 0) { if ((n = gst_matroska_ebmlnum_uint (data, size, &num)) < 0)
GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL), ("Data error")); goto data_error;
gst_buffer_unref (buf);
buf = NULL;
ret = GST_FLOW_ERROR;
break;
}
data += n; data += n;
size -= n; size -= n;
@ -4695,15 +4691,16 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux,
stream_num = gst_matroska_demux_stream_from_num (demux, num); stream_num = gst_matroska_demux_stream_from_num (demux, num);
if (G_UNLIKELY (size < 3)) { if (G_UNLIKELY (size < 3)) {
GST_WARNING_OBJECT (demux, "Invalid size %u", size); GST_WARNING_OBJECT (demux, "Invalid size %u", size);
ret = GST_FLOW_ERROR; /* non-fatal, try next block(group) */
break; ret = GST_FLOW_OK;
goto done;
} else if (G_UNLIKELY (stream_num < 0 || } else if (G_UNLIKELY (stream_num < 0 ||
stream_num >= demux->num_streams)) { stream_num >= demux->num_streams)) {
/* let's not give up on a stray invalid track number */ /* let's not give up on a stray invalid track number */
GST_WARNING_OBJECT (demux, GST_WARNING_OBJECT (demux,
"Invalid stream %d for track number %" G_GUINT64_FORMAT "Invalid stream %d for track number %" G_GUINT64_FORMAT
"; ignoring block", stream_num, num); "; ignoring block", stream_num, num);
break; goto done;
} }
stream = g_ptr_array_index (demux->src, stream_num); stream = g_ptr_array_index (demux->src, stream_num);
@ -4729,12 +4726,8 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux,
case 0x1: /* xiph lacing */ case 0x1: /* xiph lacing */
case 0x2: /* fixed-size lacing */ case 0x2: /* fixed-size lacing */
case 0x3: /* EBML lacing */ case 0x3: /* EBML lacing */
if (size == 0) { if (size == 0)
GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL), goto invalid_lacing;
("Invalid lacing size"));
ret = GST_FLOW_ERROR;
break;
}
laces = GST_READ_UINT8 (data) + 1; laces = GST_READ_UINT8 (data) + 1;
data += 1; data += 1;
size -= 1; size -= 1;
@ -4746,12 +4739,8 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux,
for (n = 0; ret == GST_FLOW_OK && n < laces - 1; n++) { for (n = 0; ret == GST_FLOW_OK && n < laces - 1; n++) {
while (1) { while (1) {
if (size == 0) { if (size == 0)
GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL), goto invalid_lacing;
("Invalid lacing size"));
ret = GST_FLOW_ERROR;
break;
}
temp = GST_READ_UINT8 (data); temp = GST_READ_UINT8 (data);
lace_size[n] += temp; lace_size[n] += temp;
data += 1; data += 1;
@ -4773,12 +4762,8 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux,
case 0x3: /* EBML lacing */ { case 0x3: /* EBML lacing */ {
guint total; guint total;
if ((n = gst_matroska_ebmlnum_uint (data, size, &num)) < 0) { if ((n = gst_matroska_ebmlnum_uint (data, size, &num)) < 0)
GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL), goto data_error;
("Data error"));
ret = GST_FLOW_ERROR;
break;
}
data += n; data += n;
size -= n; size -= n;
total = lace_size[0] = num; total = lace_size[0] = num;
@ -4786,12 +4771,8 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux,
gint64 snum; gint64 snum;
gint r; gint r;
if ((r = gst_matroska_ebmlnum_sint (data, size, &snum)) < 0) { if ((r = gst_matroska_ebmlnum_sint (data, size, &snum)) < 0)
GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL), goto data_error;
("Data error"));
ret = GST_FLOW_ERROR;
break;
}
data += r; data += r;
size -= r; size -= r;
lace_size[n] = lace_size[n - 1] + snum; lace_size[n] = lace_size[n - 1] + snum;
@ -4900,6 +4881,10 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux,
break; break;
} }
/* reading a number or so could have failed */
if (ret != GST_FLOW_OK)
goto data_error;
if (ret == GST_FLOW_OK && readblock) { if (ret == GST_FLOW_OK && readblock) {
guint64 duration = 0; guint64 duration = 0;
gint64 lace_time = 0; gint64 lace_time = 0;
@ -5201,6 +5186,20 @@ eos:
ret = gst_matroska_demux_combine_flows (demux, stream, ret); ret = gst_matroska_demux_combine_flows (demux, stream, ret);
goto done; goto done;
} }
invalid_lacing:
{
GST_ELEMENT_WARNING (demux, STREAM, DEMUX, (NULL), ("Invalid lacing size"));
/* non-fatal, try next block(group) */
ret = GST_FLOW_OK;
goto done;
}
data_error:
{
GST_ELEMENT_WARNING (demux, STREAM, DEMUX, (NULL), ("Data error"));
/* non-fatal, try next block(group) */
ret = GST_FLOW_OK;
goto done;
}
} }
/* return FALSE if block(group) should be skipped (due to a seek) */ /* return FALSE if block(group) should be skipped (due to a seek) */
@ -5399,16 +5398,26 @@ gst_matroska_demux_parse_contents (GstMatroskaDemux * demux, GstEbmlRead * ebml)
return ret; return ret;
} }
#define GST_FLOW_OVERFLOW GST_FLOW_CUSTOM_ERROR
static inline GstFlowReturn static inline GstFlowReturn
gst_matroska_demux_check_read_size (GstMatroskaDemux * demux, guint64 bytes) gst_matroska_demux_check_read_size (GstMatroskaDemux * demux, guint64 bytes)
{ {
if (G_UNLIKELY (bytes > 10 * 1024 * 1024)) { if (G_UNLIKELY (bytes > 10 * 1024 * 1024)) {
/* only a few blocks are expected/allowed to be large, /* only a few blocks are expected/allowed to be large,
* and will be recursed into, whereas others will be read and must fit */ * and will be recursed into, whereas others will be read and must fit */
GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL), if (demux->streaming) {
("reading large block of size %" G_GUINT64_FORMAT " not supported; " /* fatal in streaming case, as we can't step over easily */
"file might be corrupt.", bytes)); GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL),
return GST_FLOW_ERROR; ("reading large block of size %" G_GUINT64_FORMAT " not supported; "
"file might be corrupt.", bytes));
return GST_FLOW_ERROR;
} else {
/* indicate higher level to quietly give up */
GST_DEBUG_OBJECT (demux,
"too large block of size %" G_GUINT64_FORMAT, bytes);
return GST_FLOW_ERROR;
}
} else { } else {
return GST_FLOW_OK; return GST_FLOW_OK;
} }
@ -5431,37 +5440,6 @@ gst_matroska_demux_check_parse_error (GstMatroskaDemux * demux)
} }
} }
/* initializes @ebml with @bytes from input stream at current offset.
* Returns UNEXPECTED if insufficient available,
* ERROR if too much was attempted to read. */
static inline GstFlowReturn
gst_matroska_demux_take (GstMatroskaDemux * demux, guint64 bytes,
GstEbmlRead * ebml)
{
GstBuffer *buffer = NULL;
GstFlowReturn ret = GST_FLOW_OK;
GST_LOG_OBJECT (demux, "taking %" G_GUINT64_FORMAT " bytes for parsing",
bytes);
ret = gst_matroska_demux_check_read_size (demux, bytes);
if (ret != GST_FLOW_OK)
goto exit;
if (demux->streaming) {
if (gst_adapter_available (demux->adapter) >= bytes)
buffer = gst_adapter_take_buffer (demux->adapter, bytes);
else
ret = GST_FLOW_UNEXPECTED;
} else
ret = gst_matroska_demux_peek_bytes (demux, demux->offset, bytes, &buffer,
NULL);
if (G_LIKELY (buffer)) {
gst_ebml_read_init (ebml, GST_ELEMENT_CAST (demux), buffer, demux->offset);
demux->offset += bytes;
}
exit:
return ret;
}
static inline GstFlowReturn static inline GstFlowReturn
gst_matroska_demux_flush (GstMatroskaDemux * demux, guint flush) gst_matroska_demux_flush (GstMatroskaDemux * demux, guint flush)
{ {
@ -5482,6 +5460,46 @@ gst_matroska_demux_flush (GstMatroskaDemux * demux, guint flush)
return GST_FLOW_OK; return GST_FLOW_OK;
} }
/* initializes @ebml with @bytes from input stream at current offset.
* Returns UNEXPECTED if insufficient available,
* ERROR if too much was attempted to read. */
static inline GstFlowReturn
gst_matroska_demux_take (GstMatroskaDemux * demux, guint64 bytes,
GstEbmlRead * ebml)
{
GstBuffer *buffer = NULL;
GstFlowReturn ret = GST_FLOW_OK;
GST_LOG_OBJECT (demux, "taking %" G_GUINT64_FORMAT " bytes for parsing",
bytes);
ret = gst_matroska_demux_check_read_size (demux, bytes);
if (G_UNLIKELY (ret != GST_FLOW_OK)) {
if (!demux->streaming) {
/* in pull mode, we can skip */
if ((ret = gst_matroska_demux_flush (demux, bytes)) == GST_FLOW_OK)
ret = GST_FLOW_OVERFLOW;
} else {
/* otherwise fatal */
ret = GST_FLOW_ERROR;
}
goto exit;
}
if (demux->streaming) {
if (gst_adapter_available (demux->adapter) >= bytes)
buffer = gst_adapter_take_buffer (demux->adapter, bytes);
else
ret = GST_FLOW_UNEXPECTED;
} else
ret = gst_matroska_demux_peek_bytes (demux, demux->offset, bytes, &buffer,
NULL);
if (G_LIKELY (buffer)) {
gst_ebml_read_init (ebml, GST_ELEMENT_CAST (demux), buffer, demux->offset);
demux->offset += bytes;
}
exit:
return ret;
}
static void static void
gst_matroska_demux_check_seekability (GstMatroskaDemux * demux) gst_matroska_demux_check_seekability (GstMatroskaDemux * demux)
{ {
@ -5567,8 +5585,12 @@ gst_matroska_demux_find_tracks (GstMatroskaDemux * demux)
#define GST_READ_CHECK(stmt) \ #define GST_READ_CHECK(stmt) \
G_STMT_START { \ G_STMT_START { \
if (G_UNLIKELY ((ret = (stmt)) != GST_FLOW_OK)) \ if (G_UNLIKELY ((ret = (stmt)) != GST_FLOW_OK)) { \
if (ret == GST_FLOW_OVERFLOW) { \
ret = GST_FLOW_OK; \
} \
goto read_error; \ goto read_error; \
} \
} G_STMT_END } G_STMT_END
static GstFlowReturn static GstFlowReturn
@ -5579,6 +5601,9 @@ gst_matroska_demux_parse_id (GstMatroskaDemux * demux, guint32 id,
GstFlowReturn ret = GST_FLOW_OK; GstFlowReturn ret = GST_FLOW_OK;
guint64 read; guint64 read;
GST_LOG_OBJECT (demux, "Parsing Element id 0x%x, "
"size %" G_GUINT64_FORMAT ", prefix %d", id, length, needed);
/* if we plan to read and parse this element, we need prefix (id + length) /* if we plan to read and parse this element, we need prefix (id + length)
* and the contents */ * and the contents */
/* mind about overflow wrap-around when dealing with undefined size */ /* mind about overflow wrap-around when dealing with undefined size */