flxdec: add some write bounds checking

Without checking the bounds of the frame we are writing into, we can
write off the end of the destination buffer.

https://scarybeastsecurity.blogspot.dk/2016/11/0day-exploit-advancing-exploitation.html

https://bugzilla.gnome.org/show_bug.cgi?id=774834
This commit is contained in:
Matthew Waters 2016-11-22 19:05:00 +11:00
parent 45843ab9a2
commit bf43f44fcf

View File

@ -74,9 +74,9 @@ static gboolean gst_flxdec_src_query_handler (GstPad * pad, GstObject * parent,
GstQuery * query); GstQuery * query);
static void flx_decode_color (GstFlxDec *, guchar *, guchar *, gint); static void flx_decode_color (GstFlxDec *, guchar *, guchar *, gint);
static void flx_decode_brun (GstFlxDec *, guchar *, guchar *); static gboolean flx_decode_brun (GstFlxDec *, guchar *, guchar *);
static void flx_decode_delta_fli (GstFlxDec *, guchar *, guchar *); static gboolean flx_decode_delta_fli (GstFlxDec *, guchar *, guchar *);
static void flx_decode_delta_flc (GstFlxDec *, guchar *, guchar *); static gboolean flx_decode_delta_flc (GstFlxDec *, guchar *, guchar *);
#define rndalign(off) ((off) + ((off) & 1)) #define rndalign(off) ((off) + ((off) & 1))
@ -203,13 +203,14 @@ gst_flxdec_sink_event_handler (GstPad * pad, GstObject * parent,
return ret; return ret;
} }
static void static gboolean
flx_decode_chunks (GstFlxDec * flxdec, gulong count, guchar * data, flx_decode_chunks (GstFlxDec * flxdec, gulong count, guchar * data,
guchar * dest) guchar * dest)
{ {
FlxFrameChunk *hdr; FlxFrameChunk *hdr;
gboolean ret = TRUE;
g_return_if_fail (data != NULL); g_return_val_if_fail (data != NULL, FALSE);
while (count--) { while (count--) {
hdr = (FlxFrameChunk *) data; hdr = (FlxFrameChunk *) data;
@ -228,17 +229,17 @@ flx_decode_chunks (GstFlxDec * flxdec, gulong count, guchar * data,
break; break;
case FLX_BRUN: case FLX_BRUN:
flx_decode_brun (flxdec, data, dest); ret = flx_decode_brun (flxdec, data, dest);
data += rndalign (hdr->size) - FlxFrameChunkSize; data += rndalign (hdr->size) - FlxFrameChunkSize;
break; break;
case FLX_LC: case FLX_LC:
flx_decode_delta_fli (flxdec, data, dest); ret = flx_decode_delta_fli (flxdec, data, dest);
data += rndalign (hdr->size) - FlxFrameChunkSize; data += rndalign (hdr->size) - FlxFrameChunkSize;
break; break;
case FLX_SS2: case FLX_SS2:
flx_decode_delta_flc (flxdec, data, dest); ret = flx_decode_delta_flc (flxdec, data, dest);
data += rndalign (hdr->size) - FlxFrameChunkSize; data += rndalign (hdr->size) - FlxFrameChunkSize;
break; break;
@ -256,7 +257,12 @@ flx_decode_chunks (GstFlxDec * flxdec, gulong count, guchar * data,
data += rndalign (hdr->size) - FlxFrameChunkSize; data += rndalign (hdr->size) - FlxFrameChunkSize;
break; break;
} }
if (!ret)
break;
} }
return ret;
} }
@ -289,13 +295,13 @@ flx_decode_color (GstFlxDec * flxdec, guchar * data, guchar * dest, gint scale)
} }
} }
static void static gboolean
flx_decode_brun (GstFlxDec * flxdec, guchar * data, guchar * dest) flx_decode_brun (GstFlxDec * flxdec, guchar * data, guchar * dest)
{ {
gulong count, lines, row; gulong count, lines, row;
guchar x; guchar x;
g_return_if_fail (flxdec != NULL); g_return_val_if_fail (flxdec != NULL, FALSE);
lines = flxdec->hdr.height; lines = flxdec->hdr.height;
while (lines--) { while (lines--) {
@ -313,12 +319,21 @@ flx_decode_brun (GstFlxDec * flxdec, guchar * data, guchar * dest)
if (count > 0x7f) { if (count > 0x7f) {
/* literal run */ /* literal run */
count = 0x100 - count; count = 0x100 - count;
if ((glong) row - count < 0) {
GST_ERROR_OBJECT (flxdec, "Invalid BRUN packet detected.");
return FALSE;
}
row -= count; row -= count;
while (count--) while (count--)
*dest++ = *data++; *dest++ = *data++;
} else { } else {
if ((glong) row - count < 0) {
GST_ERROR_OBJECT (flxdec, "Invalid BRUN packet detected.");
return FALSE;
}
/* replicate run */ /* replicate run */
row -= count; row -= count;
x = *data++; x = *data++;
@ -328,22 +343,28 @@ flx_decode_brun (GstFlxDec * flxdec, guchar * data, guchar * dest)
} }
} }
} }
return TRUE;
} }
static void static gboolean
flx_decode_delta_fli (GstFlxDec * flxdec, guchar * data, guchar * dest) flx_decode_delta_fli (GstFlxDec * flxdec, guchar * data, guchar * dest)
{ {
gulong count, packets, lines, start_line; gulong count, packets, lines, start_line;
guchar *start_p, x; guchar *start_p, x;
g_return_if_fail (flxdec != NULL); g_return_val_if_fail (flxdec != NULL, FALSE);
g_return_if_fail (flxdec->delta_data != NULL); g_return_val_if_fail (flxdec->delta_data != NULL, FALSE);
/* use last frame for delta */ /* use last frame for delta */
memcpy (dest, flxdec->delta_data, flxdec->size); memcpy (dest, flxdec->delta_data, flxdec->size);
start_line = (data[0] + (data[1] << 8)); start_line = (data[0] + (data[1] << 8));
lines = (data[2] + (data[3] << 8)); lines = (data[2] + (data[3] << 8));
if (start_line + lines > flxdec->hdr.height) {
GST_ERROR_OBJECT (flxdec, "Invalid FLI packet detected. too many lines.");
return FALSE;
}
data += 4; data += 4;
/* start position of delta */ /* start position of delta */
@ -356,7 +377,8 @@ flx_decode_delta_fli (GstFlxDec * flxdec, guchar * data, guchar * dest)
while (packets--) { while (packets--) {
/* skip count */ /* skip count */
dest += *data++; guchar skip = *data++;
dest += skip;
/* RLE count */ /* RLE count */
count = *data++; count = *data++;
@ -364,12 +386,24 @@ flx_decode_delta_fli (GstFlxDec * flxdec, guchar * data, guchar * dest)
if (count > 0x7f) { if (count > 0x7f) {
/* literal run */ /* literal run */
count = 0x100 - count; count = 0x100 - count;
x = *data++;
if (skip + count > flxdec->hdr.width) {
GST_ERROR_OBJECT (flxdec, "Invalid FLI packet detected. "
"line too long.");
return FALSE;
}
x = *data++;
while (count--) while (count--)
*dest++ = x; *dest++ = x;
} else { } else {
if (skip + count > flxdec->hdr.width) {
GST_ERROR_OBJECT (flxdec, "Invalid FLI packet detected. "
"line too long.");
return FALSE;
}
/* replicate run */ /* replicate run */
while (count--) while (count--)
*dest++ = *data++; *dest++ = *data++;
@ -378,21 +412,27 @@ flx_decode_delta_fli (GstFlxDec * flxdec, guchar * data, guchar * dest)
start_p += flxdec->hdr.width; start_p += flxdec->hdr.width;
dest = start_p; dest = start_p;
} }
return TRUE;
} }
static void static gboolean
flx_decode_delta_flc (GstFlxDec * flxdec, guchar * data, guchar * dest) flx_decode_delta_flc (GstFlxDec * flxdec, guchar * data, guchar * dest)
{ {
gulong count, lines, start_l, opcode; gulong count, lines, start_l, opcode;
guchar *start_p; guchar *start_p;
g_return_if_fail (flxdec != NULL); g_return_val_if_fail (flxdec != NULL, FALSE);
g_return_if_fail (flxdec->delta_data != NULL); g_return_val_if_fail (flxdec->delta_data != NULL, FALSE);
/* use last frame for delta */ /* use last frame for delta */
memcpy (dest, flxdec->delta_data, flxdec->size); memcpy (dest, flxdec->delta_data, flxdec->size);
lines = (data[0] + (data[1] << 8)); lines = (data[0] + (data[1] << 8));
if (lines > flxdec->hdr.height) {
GST_ERROR_OBJECT (flxdec, "Invalid FLC packet detected. too many lines.");
return FALSE;
}
data += 2; data += 2;
start_p = dest; start_p = dest;
@ -405,9 +445,15 @@ flx_decode_delta_flc (GstFlxDec * flxdec, guchar * data, guchar * dest)
while ((opcode = (data[0] + (data[1] << 8))) & 0xc000) { while ((opcode = (data[0] + (data[1] << 8))) & 0xc000) {
data += 2; data += 2;
if ((opcode & 0xc000) == 0xc000) { if ((opcode & 0xc000) == 0xc000) {
/* skip count */ /* line skip count */
start_l += (0x10000 - opcode); gulong skip = (0x10000 - opcode);
dest += flxdec->hdr.width * (0x10000 - opcode); if (skip > flxdec->hdr.height) {
GST_ERROR_OBJECT (flxdec, "Invalid FLC packet detected. "
"skip line count too big.");
return FALSE;
}
start_l += skip;
dest += flxdec->hdr.width * skip;
} else { } else {
/* last pixel */ /* last pixel */
dest += flxdec->hdr.width; dest += flxdec->hdr.width;
@ -419,7 +465,8 @@ flx_decode_delta_flc (GstFlxDec * flxdec, guchar * data, guchar * dest)
/* last opcode is the packet count */ /* last opcode is the packet count */
while (opcode--) { while (opcode--) {
/* skip count */ /* skip count */
dest += *data++; guchar skip = *data++;
dest += skip;
/* RLE count */ /* RLE count */
count = *data++; count = *data++;
@ -427,12 +474,25 @@ flx_decode_delta_flc (GstFlxDec * flxdec, guchar * data, guchar * dest)
if (count > 0x7f) { if (count > 0x7f) {
/* replicate word run */ /* replicate word run */
count = 0x100 - count; count = 0x100 - count;
if (skip + count > flxdec->hdr.width) {
GST_ERROR_OBJECT (flxdec, "Invalid FLC packet detected. "
"line too long.");
return FALSE;
}
while (count--) { while (count--) {
*dest++ = data[0]; *dest++ = data[0];
*dest++ = data[1]; *dest++ = data[1];
} }
data += 2; data += 2;
} else { } else {
if (skip + count > flxdec->hdr.width) {
GST_ERROR_OBJECT (flxdec, "Invalid FLC packet detected. "
"line too long.");
return FALSE;
}
/* literal word run */ /* literal word run */
while (count--) { while (count--) {
*dest++ = *data++; *dest++ = *data++;
@ -442,6 +502,8 @@ flx_decode_delta_flc (GstFlxDec * flxdec, guchar * data, guchar * dest)
} }
lines--; lines--;
} }
return TRUE;
} }
static GstFlowReturn static GstFlowReturn
@ -571,9 +633,13 @@ gst_flxdec_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
out = gst_buffer_new_and_alloc (flxdec->size * 4); out = gst_buffer_new_and_alloc (flxdec->size * 4);
/* decode chunks */ /* decode chunks */
flx_decode_chunks (flxdec, if (!flx_decode_chunks (flxdec,
((FlxFrameType *) chunk)->chunks, ((FlxFrameType *) chunk)->chunks,
chunk + FlxFrameTypeSize, flxdec->frame_data); chunk + FlxFrameTypeSize, flxdec->frame_data)) {
GST_ELEMENT_ERROR (flxdec, STREAM, DECODE,
("%s", "Could not decode chunk"), NULL);
return GST_FLOW_ERROR;
}
/* save copy of the current frame for possible delta. */ /* save copy of the current frame for possible delta. */
memcpy (flxdec->delta_data, flxdec->frame_data, flxdec->size); memcpy (flxdec->delta_data, flxdec->frame_data, flxdec->size);