| From 2e203a79b7d9af4029307c1a845b3c148d5f5e62 Mon Sep 17 00:00:00 2001 |
| From: Matthew Waters <matthew@centricular.com> |
| Date: Tue, 22 Nov 2016 19:05:00 +1100 |
| Subject: [PATCH 1/4] 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 |
| --- |
| gst/flx/gstflxdec.c | 116 +++++++++++++++++++++++++++++++++++++++++----------- |
| 1 file changed, 91 insertions(+), 25 deletions(-) |
| |
| diff --git a/gst/flx/gstflxdec.c b/gst/flx/gstflxdec.c |
| index 604be2f..d51a8e6 100644 |
| --- a/gst/flx/gstflxdec.c |
| +++ b/gst/flx/gstflxdec.c |
| @@ -74,9 +74,9 @@ static gboolean gst_flxdec_src_query_handler (GstPad * pad, GstObject * parent, |
| GstQuery * query); |
| |
| static void flx_decode_color (GstFlxDec *, guchar *, guchar *, gint); |
| -static void flx_decode_brun (GstFlxDec *, guchar *, guchar *); |
| -static void flx_decode_delta_fli (GstFlxDec *, guchar *, guchar *); |
| -static void flx_decode_delta_flc (GstFlxDec *, guchar *, guchar *); |
| +static gboolean flx_decode_brun (GstFlxDec *, guchar *, guchar *); |
| +static gboolean flx_decode_delta_fli (GstFlxDec *, guchar *, guchar *); |
| +static gboolean flx_decode_delta_flc (GstFlxDec *, guchar *, guchar *); |
| |
| #define rndalign(off) ((off) + ((off) & 1)) |
| |
| @@ -203,13 +203,14 @@ gst_flxdec_sink_event_handler (GstPad * pad, GstObject * parent, |
| return ret; |
| } |
| |
| -static void |
| +static gboolean |
| flx_decode_chunks (GstFlxDec * flxdec, gulong count, guchar * data, |
| guchar * dest) |
| { |
| FlxFrameChunk *hdr; |
| + gboolean ret = TRUE; |
| |
| - g_return_if_fail (data != NULL); |
| + g_return_val_if_fail (data != NULL, FALSE); |
| |
| while (count--) { |
| hdr = (FlxFrameChunk *) data; |
| @@ -228,17 +229,17 @@ flx_decode_chunks (GstFlxDec * flxdec, gulong count, guchar * data, |
| break; |
| |
| case FLX_BRUN: |
| - flx_decode_brun (flxdec, data, dest); |
| + ret = flx_decode_brun (flxdec, data, dest); |
| data += rndalign (hdr->size) - FlxFrameChunkSize; |
| break; |
| |
| case FLX_LC: |
| - flx_decode_delta_fli (flxdec, data, dest); |
| + ret = flx_decode_delta_fli (flxdec, data, dest); |
| data += rndalign (hdr->size) - FlxFrameChunkSize; |
| break; |
| |
| case FLX_SS2: |
| - flx_decode_delta_flc (flxdec, data, dest); |
| + ret = flx_decode_delta_flc (flxdec, data, dest); |
| data += rndalign (hdr->size) - FlxFrameChunkSize; |
| break; |
| |
| @@ -256,7 +257,12 @@ flx_decode_chunks (GstFlxDec * flxdec, gulong count, guchar * data, |
| data += rndalign (hdr->size) - FlxFrameChunkSize; |
| 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) |
| { |
| gulong count, lines, row; |
| guchar x; |
| |
| - g_return_if_fail (flxdec != NULL); |
| + g_return_val_if_fail (flxdec != NULL, FALSE); |
| |
| lines = flxdec->hdr.height; |
| while (lines--) { |
| @@ -313,12 +319,21 @@ flx_decode_brun (GstFlxDec * flxdec, guchar * data, guchar * dest) |
| if (count > 0x7f) { |
| /* literal run */ |
| count = 0x100 - count; |
| + if ((glong) row - count < 0) { |
| + GST_ERROR_OBJECT (flxdec, "Invalid BRUN packet detected."); |
| + return FALSE; |
| + } |
| row -= count; |
| |
| while (count--) |
| *dest++ = *data++; |
| |
| } else { |
| + if ((glong) row - count < 0) { |
| + GST_ERROR_OBJECT (flxdec, "Invalid BRUN packet detected."); |
| + return FALSE; |
| + } |
| + |
| /* replicate run */ |
| row -= count; |
| 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) |
| { |
| gulong count, packets, lines, start_line; |
| guchar *start_p, x; |
| |
| - g_return_if_fail (flxdec != NULL); |
| - g_return_if_fail (flxdec->delta_data != NULL); |
| + g_return_val_if_fail (flxdec != NULL, FALSE); |
| + g_return_val_if_fail (flxdec->delta_data != NULL, FALSE); |
| |
| /* use last frame for delta */ |
| memcpy (dest, flxdec->delta_data, flxdec->size); |
| |
| start_line = (data[0] + (data[1] << 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; |
| |
| /* start position of delta */ |
| @@ -356,7 +377,8 @@ flx_decode_delta_fli (GstFlxDec * flxdec, guchar * data, guchar * dest) |
| |
| while (packets--) { |
| /* skip count */ |
| - dest += *data++; |
| + guchar skip = *data++; |
| + dest += skip; |
| |
| /* RLE count */ |
| count = *data++; |
| @@ -364,12 +386,24 @@ flx_decode_delta_fli (GstFlxDec * flxdec, guchar * data, guchar * dest) |
| if (count > 0x7f) { |
| /* literal run */ |
| 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--) |
| *dest++ = x; |
| |
| } else { |
| + if (skip + count > flxdec->hdr.width) { |
| + GST_ERROR_OBJECT (flxdec, "Invalid FLI packet detected. " |
| + "line too long."); |
| + return FALSE; |
| + } |
| + |
| /* replicate run */ |
| while (count--) |
| *dest++ = *data++; |
| @@ -378,21 +412,27 @@ flx_decode_delta_fli (GstFlxDec * flxdec, guchar * data, guchar * dest) |
| start_p += flxdec->hdr.width; |
| dest = start_p; |
| } |
| + |
| + return TRUE; |
| } |
| |
| -static void |
| +static gboolean |
| flx_decode_delta_flc (GstFlxDec * flxdec, guchar * data, guchar * dest) |
| { |
| gulong count, lines, start_l, opcode; |
| guchar *start_p; |
| |
| - g_return_if_fail (flxdec != NULL); |
| - g_return_if_fail (flxdec->delta_data != NULL); |
| + g_return_val_if_fail (flxdec != NULL, FALSE); |
| + g_return_val_if_fail (flxdec->delta_data != NULL, FALSE); |
| |
| /* use last frame for delta */ |
| memcpy (dest, flxdec->delta_data, flxdec->size); |
| |
| 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; |
| |
| 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) { |
| data += 2; |
| if ((opcode & 0xc000) == 0xc000) { |
| - /* skip count */ |
| - start_l += (0x10000 - opcode); |
| - dest += flxdec->hdr.width * (0x10000 - opcode); |
| + /* line skip count */ |
| + gulong skip = (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 { |
| /* last pixel */ |
| dest += flxdec->hdr.width; |
| @@ -419,7 +465,8 @@ flx_decode_delta_flc (GstFlxDec * flxdec, guchar * data, guchar * dest) |
| /* last opcode is the packet count */ |
| while (opcode--) { |
| /* skip count */ |
| - dest += *data++; |
| + guchar skip = *data++; |
| + dest += skip; |
| |
| /* RLE count */ |
| count = *data++; |
| @@ -427,12 +474,25 @@ flx_decode_delta_flc (GstFlxDec * flxdec, guchar * data, guchar * dest) |
| if (count > 0x7f) { |
| /* replicate word run */ |
| count = 0x100 - count; |
| + |
| + if (skip + count > flxdec->hdr.width) { |
| + GST_ERROR_OBJECT (flxdec, "Invalid FLC packet detected. " |
| + "line too long."); |
| + return FALSE; |
| + } |
| + |
| while (count--) { |
| *dest++ = data[0]; |
| *dest++ = data[1]; |
| } |
| data += 2; |
| } else { |
| + if (skip + count > flxdec->hdr.width) { |
| + GST_ERROR_OBJECT (flxdec, "Invalid FLC packet detected. " |
| + "line too long."); |
| + return FALSE; |
| + } |
| + |
| /* literal word run */ |
| while (count--) { |
| *dest++ = *data++; |
| @@ -442,6 +502,8 @@ flx_decode_delta_flc (GstFlxDec * flxdec, guchar * data, guchar * dest) |
| } |
| lines--; |
| } |
| + |
| + return TRUE; |
| } |
| |
| static GstFlowReturn |
| @@ -571,9 +633,13 @@ gst_flxdec_chain (GstPad * pad, GstObject * parent, GstBuffer * buf) |
| out = gst_buffer_new_and_alloc (flxdec->size * 4); |
| |
| /* decode chunks */ |
| - flx_decode_chunks (flxdec, |
| - ((FlxFrameType *) chunk)->chunks, |
| - chunk + FlxFrameTypeSize, flxdec->frame_data); |
| + if (!flx_decode_chunks (flxdec, |
| + ((FlxFrameType *) chunk)->chunks, |
| + 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. */ |
| memcpy (flxdec->delta_data, flxdec->frame_data, flxdec->size); |
| -- |
| 2.10.2 |
| |