Blob Blame Raw
From 82b80dcb70a7ca8986125ff412bceddafc896842 Mon Sep 17 00:00:00 2001
From: Mike Frysinger <vapier@gentoo.org>
Date: Sat, 14 May 2016 02:13:15 -0400
Subject: [PATCH] gif: avoid out-of-bound reads of masks array #209

When given invalid inputs, we might be fed the EOF marker before it is
actually the EOF.  The gif logic assumes once it sees the EOF marker,
there won't be any more data, so it leaves the cur_bits index possibly
negative.  So when we get more data, we underflow the masks array.

Flag it so we don't try to output anything more.  The image is invalid,
so we shouldn't be truncating any valid inputs.

This fixes #209.
---
 src/gd_gif_out.c | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)
 
From 315dbfb0e75895e3ba84f649c491956e75f1106c Mon Sep 17 00:00:00 2001
From: "Christoph M. Becker" <cmbecker69@gmx.de>
Date: Tue, 19 Jul 2016 10:43:55 +0200
Subject: [PATCH] Add test case for issue #209

---
 tests/gif/.gitignore     |   1 +
 tests/gif/CMakeLists.txt |   1 +
 tests/gif/Makemodule.am  |   4 +++-
 tests/gif/bug00209.c     |  29 +++++++++++++++++++++++++++++
 tests/gif/bug00209.gd2   | Bin 0 -> 1050 bytes
 5 files changed, 34 insertions(+), 1 deletion(-)
 create mode 100644 tests/gif/bug00209.c
 create mode 100644 tests/gif/bug00209.gd2

diff --git a/src/gd_gif_out.c b/src/gd_gif_out.c
index 51ceb75..3099d49 100644
--- a/src/gd_gif_out.c
+++ b/src/gd_gif_out.c
@@ -1442,15 +1442,23 @@ static void compress(int init_bits, gdIOCtxPtr outfile, gdImagePtr im, GifCtx *c
  * code in turn.  When the buffer fills up empty it and start over.
  */
 
-static unsigned long masks[] = {
+static const unsigned long masks[] = {
 	0x0000, 0x0001, 0x0003, 0x0007, 0x000F,
 	0x001F, 0x003F, 0x007F, 0x00FF,
 	0x01FF, 0x03FF, 0x07FF, 0x0FFF,
 	0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF
 };
 
+/* Arbitrary value to mark output is done.  When we see EOFCode, then we don't
+ * expect to see any more data.  If we do (e.g. corrupt image inputs), cur_bits
+ * might be negative, so flag it to return early.
+ */
+#define CUR_BITS_FINISHED -1000
+
 static void output(code_int code, GifCtx *ctx)
 {
+	if (ctx->cur_bits == CUR_BITS_FINISHED)
+		return;
 	ctx->cur_accum &= masks[ctx->cur_bits];
 
 	if(ctx->cur_bits > 0) {
@@ -1492,6 +1500,8 @@ static void output(code_int code, GifCtx *ctx)
 			ctx->cur_accum >>= 8;
 			ctx->cur_bits -= 8;
 		}
+		/* Flag that it's done to prevent re-entry. */
+		ctx->cur_bits = CUR_BITS_FINISHED;
 
 		flush_char(ctx);
 	}

diff --git a/tests/gif/CMakeLists.txt b/tests/gif/CMakeLists.txt
index 92010c3..d26b1fe 100644
--- a/tests/gif/CMakeLists.txt
+++ b/tests/gif/CMakeLists.txt
@@ -7,6 +7,7 @@ LIST(APPEND TESTS_FILES
 	bug00060
 	bug00066
 	bug00181
+	bug00209
 	bug00227
 )
 
diff --git a/tests/gif/bug00209.c b/tests/gif/bug00209.c
new file mode 100644
index 0000000..6eafc32
--- /dev/null
+++ b/tests/gif/bug00209.c
@@ -0,0 +1,29 @@
+/* Test case for  <https://github.com/libgd/libgd/issues/209>. */
+
+#include "gd.h"
+#include "gdtest.h"
+
+int main()
+{
+    gdImagePtr im;
+    FILE *fp;
+
+    /* printf("start\n"); */
+
+    fp = gdTestFileOpen("gif/bug00209.gd2");
+    gdTestAssert(fp != NULL);
+    im = gdImageCreateFromGd2(fp);
+    gdTestAssert(im != NULL);
+    fclose(fp);
+    /* printf("loaded\n"); */
+
+    fp = gdTestTempFp();
+    gdTestAssert(fp != NULL);
+    gdImageGif(im, fp);
+    fclose(fp);
+    /* printf("saved\n"); */
+
+    gdImageDestroy(im);
+
+    return gdNumFailures();
+}