From d695605c5d91b951a9c37f64a843e92caaa178e9 Mon Sep 17 00:00:00 2001
From: Robin Watts <robin.watts@artifex.com>
Date: Tue, 21 Jan 2014 18:28:21 +0000
Subject: Bug 694900: Avoid valgrind problems when cmap tables fill up.

The test file on this bug:

  de53b4bd41191f02d01a3c39b4880fa8_asan_heap-oob_caba3c_9561_7427.pdf

includes a corrupt CMAP. When this is read into memory it produces
a CMAP where the table gets too large. This produces lots of warnings
from 'add_table', but the calls to add_table all assume that the
process completed fine, resulting in range entries being added
that point to nonexistent values.

The fix is to make add_table return a bool to indicate success or
failure, and to only add range entries if the add_table succeeds.

Thanks to Mateusz Jurczyk and Gynvael Coldwind of the Google Security
Team for providing the example files.
---
 source/pdf/pdf-cmap.c | 56 ++++++++++++++++++++++++++++++++++++---------------
 1 file changed, 40 insertions(+), 16 deletions(-)

(limited to 'source')

diff --git a/source/pdf/pdf-cmap.c b/source/pdf/pdf-cmap.c
index a1f0b0b0..025d4659 100644
--- a/source/pdf/pdf-cmap.c
+++ b/source/pdf/pdf-cmap.c
@@ -180,13 +180,13 @@ pdf_add_codespace(fz_context *ctx, pdf_cmap *cmap, int low, int high, int n)
 /*
  * Add an integer to the table.
  */
-static void
+static int
 add_table(fz_context *ctx, pdf_cmap *cmap, int value)
 {
 	if (cmap->tlen >= USHRT_MAX + 1)
 	{
 		fz_warn(ctx, "cmap table is full; ignoring additional entries");
-		return;
+		return 1;
 	}
 	if (cmap->tlen + 1 > cmap->tcap)
 	{
@@ -195,6 +195,7 @@ add_table(fz_context *ctx, pdf_cmap *cmap, int value)
 		cmap->tcap = new_cap;
 	}
 	cmap->table[cmap->tlen++] = value;
+	return 0;
 }
 
 /*
@@ -242,9 +243,13 @@ pdf_map_range_to_table(fz_context *ctx, pdf_cmap *cmap, int low, int *table, int
 		fz_warn(ctx, "cannot map range to table; table is full");
 	else
 	{
+		int fail = 0;
 		for (i = 0; i < len; i++)
-			add_table(ctx, cmap, table[i]);
-		add_range(ctx, cmap, low, high, PDF_CMAP_TABLE, offset);
+			fail |= add_table(ctx, cmap, table[i]);
+		if (!fail)
+			add_range(ctx, cmap, low, high, PDF_CMAP_TABLE, offset);
+		else
+			cmap->tlen = offset;
 	}
 }
 
@@ -289,11 +294,15 @@ pdf_map_one_to_many(fz_context *ctx, pdf_cmap *cmap, int low, int *values, int l
 		fz_warn(ctx, "cannot map one to many; table is full");
 	else
 	{
+		int fail;
 		offset = cmap->tlen;
-		add_table(ctx, cmap, len);
+		fail = add_table(ctx, cmap, len);
 		for (i = 0; i < len; i++)
-			add_table(ctx, cmap, values[i]);
-		add_range(ctx, cmap, low, low, PDF_CMAP_MULTI, offset);
+			fail |= add_table(ctx, cmap, values[i]);
+		if (!fail)
+			add_range(ctx, cmap, low, low, PDF_CMAP_MULTI, offset);
+		else
+			cmap->tlen = offset;
 	}
 }
 
@@ -352,8 +361,10 @@ pdf_sort_cmap(fz_context *ctx, pdf_cmap *cmap)
 				/* LS -> L */
 				else if (pdf_range_flags(a) == PDF_CMAP_TABLE && pdf_range_flags(b) == PDF_CMAP_SINGLE && (pdf_range_high(b) - a->low <= 0x3fff))
 				{
-					pdf_range_set_high(a, pdf_range_high(b));
-					add_table(ctx, cmap, b->offset);
+					if (!add_table(ctx, cmap, b->offset))
+						pdf_range_set_high(a, pdf_range_high(b));
+					else
+						*(++a) = *b;
 				}
 
 				/* LR -> LR */
@@ -375,18 +386,31 @@ pdf_sort_cmap(fz_context *ctx, pdf_cmap *cmap)
 				/* SS -> L */
 				if (pdf_range_flags(a) == PDF_CMAP_SINGLE && pdf_range_flags(b) == PDF_CMAP_SINGLE)
 				{
-					pdf_range_set_flags(a, PDF_CMAP_TABLE);
-					pdf_range_set_high(a, pdf_range_high(b));
-					add_table(ctx, cmap, a->offset);
-					add_table(ctx, cmap, b->offset);
-					a->offset = cmap->tlen - 2;
+					int offset = cmap->tlen;
+					int fail = add_table(ctx, cmap, a->offset);
+					fail |= add_table(ctx, cmap, b->offset);
+					if (!fail)
+					{
+						pdf_range_set_flags(a, PDF_CMAP_TABLE);
+						pdf_range_set_high(a, pdf_range_high(b));
+						a->offset = cmap->tlen - 2;
+					} else {
+						cmap->tlen = offset;
+						*(++a) = *b;
+					}
 				}
 
 				/* LS -> L */
 				else if (pdf_range_flags(a) == PDF_CMAP_TABLE && pdf_range_flags(b) == PDF_CMAP_SINGLE && (pdf_range_high(b) - a->low <= 0x3fff))
 				{
-					pdf_range_set_high(a, pdf_range_high(b));
-					add_table(ctx, cmap, b->offset);
+					if (!add_table(ctx, cmap, b->offset))
+					{
+						pdf_range_set_high(a, pdf_range_high(b));
+					}
+					else
+					{
+						*(++a) = *b;
+					}
 				}
 
 				/* XX -> XX */
-- 
cgit v1.2.3