Commit message: Bug 694957: fix stack buffer overflow in xps_parse_color

xps_parse_color happily reads more than FZ_MAX_COLORS values out of a
ContextColor array which overflows the passed in samples array.
Limiting the number of allowed samples to FZ_MAX_COLORS and make sure
to use that constant for all callers fixes the problem.

Thanks to Jean-Jamil Khalifé for reporting and investigating the issue
and providing a sample exploit file.
CVE: CVE-2014-2013
CWE: CWE-119
Vulnerability description: Stack-based buffer overflow in the xps_parse_color function in xps/xps-common.c in MuPDF 1.3 and earlier allows remote attackers to execute arbitrary code via a large number of entries in the ContextColor value of the Fill attribute in a Path element.

Project: mupdf
File: xps-common.c

xps_parse_color(xps_document *doc, char *base_uri, char *string,
		fz_colorspace **csp, float *samples)
{
	fz_context *ctx = doc->ctx;
	char *p;
	int i, n;
	char buf[1024];
	char *profile;

	*csp = fz_device_rgb(ctx);

	samples[0] = 1;
	samples[1] = 0;
	samples[2] = 0;
	samples[3] = 0;

	if (string[0] == '#')
	{
		if (strlen(string) == 9)
		{
			samples[0] = unhex(string[1]) * 16 + unhex(string[2]);
			samples[1] = unhex(string[3]) * 16 + unhex(string[4]);
			samples[2] = unhex(string[5]) * 16 + unhex(string[6]);
			samples[3] = unhex(string[7]) * 16 + unhex(string[8]);
		}
		else
		{
			samples[0] = 255;
			samples[1] = unhex(string[1]) * 16 + unhex(string[2]);
			samples[2] = unhex(string[3]) * 16 + unhex(string[4]);
			samples[3] = unhex(string[5]) * 16 + unhex(string[6]);
		}

		samples[0] /= 255;
		samples[1] /= 255;
		samples[2] /= 255;
		samples[3] /= 255;
	}

	else if (string[0] == 's' && string[1] == 'c' && string[2] == '#')
	{
		if (count_commas(string) == 2)
			sscanf(string, "sc#%g,%g,%g", samples + 1, samples + 2, samples + 3);
		if (count_commas(string) == 3)
			sscanf(string, "sc#%g,%g,%g,%g", samples, samples + 1, samples + 2, samples + 3);
	}

	else if (strstr(string, "ContextColor ") == string)
	{
		/* Crack the string for profile name and sample values */
		fz_strlcpy(buf, string, sizeof buf);

		profile = strchr(buf, ' ');
		if (!profile)
		{
			fz_warn(ctx, "cannot find icc profile uri in '%s'", string);
			return;
		}

		*profile++ = 0;
		p = strchr(profile, ' ');
		if (!p)
		{
			fz_warn(ctx, "cannot find component values in '%s'", profile);
			return;
		}

		*p++ = 0;
		n = count_commas(p) + 1;
		if (n > FZ_MAX_COLORS)
		{
			fz_warn(ctx, "ignoring %d color components (max %d allowed)", n - FZ_MAX_COLORS, FZ_MAX_COLORS);
			n = FZ_MAX_COLORS;
		}
		i = 0;
		while (i < n)
		{
			samples[i++] = fz_atof(p);
			p = strchr(p, ',');
			if (!p)
				break;
			p ++;
			if (*p == ' ')
				p ++;
		}
		while (i < n)
		{
			samples[i++] = 0;
		}

		/* TODO: load ICC profile */
		switch (n)
		{
		case 2: *csp = fz_device_gray(ctx); break;
		case 4: *csp = fz_device_rgb(ctx); break;
		case 5: *csp = fz_device_cmyk(ctx); break;
		default: *csp = fz_device_gray(ctx); break;
		}
	}
}

Project: mupdf
File: xps-common.c

xps_set_color(xps_document *doc, fz_colorspace *colorspace, float *samples)
{
	int i;
	doc->colorspace = colorspace;
	for (i = 0; i < colorspace->n; i++)
		doc->color[i] = samples[i + 1];
	doc->alpha = samples[0] * doc->opacity[doc->opacity_top];
}

Project: mupdf
File: xps-glyphs.c

xps_insert_font(xps_document *doc, char *name, fz_font *font)
{
	xps_font_cache *cache = fz_malloc_struct(doc->ctx, xps_font_cache);
	cache->name = fz_strdup(doc->ctx, name);
	cache->font = fz_keep_font(doc->ctx, font);
	cache->next = doc->font_table;
	doc->font_table = cache;
}

Project: mupdf
File: xps-common.c

xps_parse_brush(xps_document *doc, const fz_matrix *ctm, const fz_rect *area, char *base_uri, xps_resource *dict, fz_xml *node)
{
	if (doc->cookie && doc->cookie->abort)
		return;
	/* SolidColorBrushes are handled in a special case and will never show up here */
	if (!strcmp(fz_xml_tag(node), "ImageBrush"))
		xps_parse_image_brush(doc, ctm, area, base_uri, dict, node);
	else if (!strcmp(fz_xml_tag(node), "VisualBrush"))
		xps_parse_visual_brush(doc, ctm, area, base_uri, dict, node);
	else if (!strcmp(fz_xml_tag(node), "LinearGradientBrush"))
		xps_parse_linear_gradient_brush(doc, ctm, area, base_uri, dict, node);
	else if (!strcmp(fz_xml_tag(node), "RadialGradientBrush"))
		xps_parse_radial_gradient_brush(doc, ctm, area, base_uri, dict, node);
	else
		fz_warn(doc->ctx, "unknown brush tag: %s", fz_xml_tag(node));
}