qtdemux: guard against bogus atom sizes and short reads

Check the possibly 64-bit atom size more carefully before casting it
to an int and passing it to gst_pad_pull_range(), otherwise we might
end up pulling 0 bytes, getting an empty buffer as requested and
dereferencing not available data whilst thinking we actually asked
for and got 0x1000000000000 bytes. Similar fix for push mode operation
where neededbytes ends up being 0 bytes, which makes us assert. Fixes
crash with broken or fuzzed file (NB #122378).
This commit is contained in:
Tim-Philipp Müller 2009-06-29 18:58:33 +01:00
parent c730912f67
commit 405aae4568

View file

@ -62,6 +62,9 @@
# include <zlib.h> # include <zlib.h>
#endif #endif
/* max. size considered 'sane' for non-mdat atoms */
#define QTDEMUX_MAX_ATOM_SIZE (25*1024*1024)
GST_DEBUG_CATEGORY (qtdemux_debug); GST_DEBUG_CATEGORY (qtdemux_debug);
typedef struct _QtNode QtNode; typedef struct _QtNode QtNode;
@ -404,6 +407,37 @@ gst_qtdemux_dispose (GObject * object)
G_OBJECT_CLASS (parent_class)->dispose (object); G_OBJECT_CLASS (parent_class)->dispose (object);
} }
static GstFlowReturn
gst_qtdemux_pull_atom (GstQTDemux * qtdemux, guint64 offset, guint64 size,
GstBuffer ** buf)
{
GstFlowReturn flow;
/* Sanity check: catch bogus sizes (fuzzed/broken files) */
if (G_UNLIKELY (size > QTDEMUX_MAX_ATOM_SIZE)) {
GST_ELEMENT_ERROR (qtdemux, STREAM, DECODE,
(_("This file is invalid and cannot be played.")),
("atom has bogus size %" G_GUINT64_FORMAT, size));
return GST_FLOW_ERROR;
}
flow = gst_pad_pull_range (qtdemux->sinkpad, offset, size, buf);
if (G_UNLIKELY (flow != GST_FLOW_OK))
return flow;
/* Catch short reads - we don't want any partial atoms */
if (G_UNLIKELY (GST_BUFFER_SIZE (*buf) < size)) {
GST_WARNING_OBJECT (qtdemux, "short read: %u < %" G_GUINT64_FORMAT,
GST_BUFFER_SIZE (*buf), size);
gst_buffer_unref (*buf);
*buf = NULL;
return GST_FLOW_UNEXPECTED;
}
return flow;
}
#if 0 #if 0
static gboolean static gboolean
gst_qtdemux_src_convert (GstPad * pad, GstFormat src_format, gint64 src_value, gst_qtdemux_src_convert (GstPad * pad, GstFormat src_format, gint64 src_value,
@ -1393,7 +1427,7 @@ extract_initial_length_and_fourcc (guint8 * data, guint64 * plength,
static GstFlowReturn static GstFlowReturn
gst_qtdemux_loop_state_header (GstQTDemux * qtdemux) gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
{ {
guint64 length; guint64 length = 0;
guint32 fourcc; guint32 fourcc;
GstBuffer *buf = NULL; GstBuffer *buf = NULL;
GstFlowReturn ret = GST_FLOW_OK; GstFlowReturn ret = GST_FLOW_OK;
@ -1402,7 +1436,8 @@ gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, 16, &buf); ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, 16, &buf);
if (G_UNLIKELY (ret != GST_FLOW_OK)) if (G_UNLIKELY (ret != GST_FLOW_OK))
goto beach; goto beach;
extract_initial_length_and_fourcc (GST_BUFFER_DATA (buf), &length, &fourcc); if (G_LIKELY (GST_BUFFER_SIZE (buf) == 16))
extract_initial_length_and_fourcc (GST_BUFFER_DATA (buf), &length, &fourcc);
gst_buffer_unref (buf); gst_buffer_unref (buf);
if (G_UNLIKELY (length == 0)) { if (G_UNLIKELY (length == 0)) {
@ -1490,7 +1525,7 @@ gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
GstBuffer *ftyp; GstBuffer *ftyp;
/* extract major brand; might come in handy for ISO vs QT issues */ /* extract major brand; might come in handy for ISO vs QT issues */
ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, length, &ftyp); ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &ftyp);
if (ret != GST_FLOW_OK) if (ret != GST_FLOW_OK)
goto beach; goto beach;
qtdemux->offset += length; qtdemux->offset += length;
@ -1511,7 +1546,7 @@ gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
"unknown %08x '%" GST_FOURCC_FORMAT "' of size %" G_GUINT64_FORMAT "unknown %08x '%" GST_FOURCC_FORMAT "' of size %" G_GUINT64_FORMAT
" at %" G_GUINT64_FORMAT, fourcc, GST_FOURCC_ARGS (fourcc), length, " at %" G_GUINT64_FORMAT, fourcc, GST_FOURCC_ARGS (fourcc), length,
cur_offset); cur_offset);
ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, length, &unknown); ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &unknown);
if (ret != GST_FLOW_OK) if (ret != GST_FLOW_OK)
goto beach; goto beach;
GST_MEMDUMP ("Unknown tag", GST_BUFFER_DATA (unknown), GST_MEMDUMP ("Unknown tag", GST_BUFFER_DATA (unknown),
@ -2222,7 +2257,7 @@ gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux)
GST_LOG_OBJECT (qtdemux, "reading %d bytes @ %" G_GUINT64_FORMAT, size, GST_LOG_OBJECT (qtdemux, "reading %d bytes @ %" G_GUINT64_FORMAT, size,
offset); offset);
ret = gst_pad_pull_range (qtdemux->sinkpad, offset, size, &buf); ret = gst_qtdemux_pull_atom (qtdemux, offset, size, &buf);
if (G_UNLIKELY (ret != GST_FLOW_OK)) if (G_UNLIKELY (ret != GST_FLOW_OK))
goto beach; goto beach;
@ -2521,9 +2556,8 @@ gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf)
/* get fourcc/length, set neededbytes */ /* get fourcc/length, set neededbytes */
extract_initial_length_and_fourcc ((guint8 *) data, &size, &fourcc); extract_initial_length_and_fourcc ((guint8 *) data, &size, &fourcc);
GST_DEBUG_OBJECT (demux, GST_DEBUG_OBJECT (demux, "Peeking found [%" GST_FOURCC_FORMAT "] "
"Peeking found [%" GST_FOURCC_FORMAT "] size: %u", "size: %" G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), size);
GST_FOURCC_ARGS (fourcc), (guint) size);
if (size == 0) { if (size == 0) {
GST_ELEMENT_ERROR (demux, STREAM, DECODE, GST_ELEMENT_ERROR (demux, STREAM, DECODE,
(_("This file is invalid and cannot be played.")), (_("This file is invalid and cannot be played.")),
@ -2544,6 +2578,13 @@ gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf)
demux->neededbytes = size; demux->neededbytes = size;
demux->mdatoffset = demux->offset; demux->mdatoffset = demux->offset;
} }
} else if (G_UNLIKELY (size > QTDEMUX_MAX_ATOM_SIZE)) {
GST_ELEMENT_ERROR (demux, STREAM, DECODE,
(_("This file is invalid and cannot be played.")),
("atom %" GST_FOURCC_FORMAT " has bogus size %" G_GUINT64_FORMAT,
GST_FOURCC_ARGS (fourcc), size));
ret = GST_FLOW_ERROR;
break;
} else { } else {
demux->neededbytes = size; demux->neededbytes = size;
demux->state = QTDEMUX_STATE_HEADER; demux->state = QTDEMUX_STATE_HEADER;