From 12668bb5636bfd651182db421658944042c79806 Mon Sep 17 00:00:00 2001
From: Federico Mena Quintero <federico@gnome.org>
Date: Fri, 24 Nov 2017 17:32:44 -0600
Subject: [PATCH 4/8] Aggregate commits for overflow checking and support for
 BMPv3 with bitmasks.

---
 gdk-pixbuf/io-bmp.c | 130 ++++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 95 insertions(+), 35 deletions(-)

diff --git a/gdk-pixbuf/io-bmp.c b/gdk-pixbuf/io-bmp.c
index fc0d88d..abbce6e 100644
--- a/gdk-pixbuf/io-bmp.c
+++ b/gdk-pixbuf/io-bmp.c
@@ -301,6 +301,18 @@ static gboolean DecodeHeader(unsigned char *BFH, unsigned char *BIH,
 		State->Header.height = lsb_32 (&BIH[8]);
 		State->Header.depth = lsb_16 (&BIH[14]);
 		State->Compressed = lsb_32 (&BIH[16]);
+	} else if (State->Header.size == 56) {
+                /* BMP v3 with RGBA bitmasks */
+		State->Header.width = lsb_32 (&BIH[4]);
+		State->Header.height = lsb_32 (&BIH[8]);
+		State->Header.depth = lsb_16 (&BIH[14]);
+		State->Compressed = lsb_32 (&BIH[16]);
+	} else if (State->Header.size == 52) {
+                /* BMP v3 with RGB bitmasks */
+		State->Header.width = lsb_32 (&BIH[4]);
+		State->Header.height = lsb_32 (&BIH[8]);
+		State->Header.depth = lsb_16 (&BIH[14]);
+		State->Compressed = lsb_32 (&BIH[16]);
 	} else if (State->Header.size == 40) {
                 /* BMP v3 */ 
 		State->Header.width = lsb_32 (&BIH[4]);
@@ -352,19 +364,18 @@ static gboolean DecodeHeader(unsigned char *BFH, unsigned char *BIH,
 	else
             State->Header.n_colors = (1 << State->Header.depth);
 	
-	if (State->Header.n_colors > (1 << State->Header.depth)) {
-		g_set_error_literal (error,
-                                     GDK_PIXBUF_ERROR,
-                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
-                                     _("BMP image has bogus header data"));
-		State->read_state = READ_STATE_ERROR;
-		return FALSE;
-	}
-
 	State->Type = State->Header.depth;	/* This may be less trivial someday */
 
 	/* Negative heights indicates bottom-down pixelorder */
 	if (State->Header.height < 0) {
+		if (State->Header.height == INT_MIN) {
+			g_set_error_literal (error,
+					     GDK_PIXBUF_ERROR,
+					     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+					     _("BMP image has bogus header data"));
+			State->read_state = READ_STATE_ERROR;
+			return FALSE;
+		}
 		State->Header.height = -State->Header.height;
 		State->Header.Negative = 1;
 	}
@@ -424,6 +435,10 @@ static gboolean DecodeHeader(unsigned char *BFH, unsigned char *BIH,
 		State->LineWidth = (State->LineWidth / 4) * 4 + 4;
 
 	if (State->pixbuf == NULL) {
+		guint64 len;
+		int rowstride;
+		gboolean has_alpha;
+
 		if (State->size_func) {
 			gint width = State->Header.width;
 			gint height = State->Header.height;
@@ -436,19 +451,45 @@ static gboolean DecodeHeader(unsigned char *BFH, unsigned char *BIH,
 			}
 		}
 
-		if (State->Type == 32 || 
-		    State->Compressed == BI_RLE4 || 
+		/* rowstride is always >= width, so do an early check for bogus header */
+		if (State->Header.width <= 0 ||
+		    State->Header.height <= 0 ||
+		    !g_uint64_checked_mul (&len, State->Header.width, State->Header.height) ||
+		    len > G_MAXINT) {
+			g_set_error_literal (error,
+                                             GDK_PIXBUF_ERROR,
+                                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                                             _("BMP image has bogus header data"));
+			State->read_state = READ_STATE_ERROR;
+			return FALSE;
+		}
+
+		if (State->Type == 32 ||
+		    State->Compressed == BI_RLE4 ||
 		    State->Compressed == BI_RLE8)
-			State->pixbuf =
-				gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8,
-					       (gint) State->Header.width,
-					       (gint) State->Header.height);
+			has_alpha = TRUE;
 		else
-			State->pixbuf =
-				gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8,
-					       (gint) State->Header.width,
-					       (gint) State->Header.height);
-		
+			has_alpha = FALSE;
+
+		rowstride = gdk_pixbuf_calculate_rowstride (GDK_COLORSPACE_RGB, has_alpha, 8,
+							    (gint) State->Header.width,
+							    (gint) State->Header.height);
+
+		if (rowstride <= 0 ||
+		    !g_uint64_checked_mul (&len, rowstride, State->Header.height) ||
+		    len > G_MAXINT) {
+			g_set_error_literal (error,
+                                             GDK_PIXBUF_ERROR,
+                                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                                             _("BMP image has bogus header data"));
+			State->read_state = READ_STATE_ERROR;
+			return FALSE;
+		}
+
+		State->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, has_alpha, 8,
+						(gint) State->Header.width,
+						(gint) State->Header.height);
+
 		if (State->pixbuf == NULL) {
 			g_set_error_literal (error,
                                              GDK_PIXBUF_ERROR,
@@ -456,8 +497,8 @@ static gboolean DecodeHeader(unsigned char *BFH, unsigned char *BIH,
                                              _("Not enough memory to load bitmap image"));
 			State->read_state = READ_STATE_ERROR;
 			return FALSE;
-			}
-		
+		}
+
 		if (State->prepared_func != NULL)
 			/* Notify the client that we are ready to go */
 			(*State->prepared_func) (State->pixbuf, NULL, State->user_data);
@@ -499,9 +540,10 @@ static gboolean DecodeHeader(unsigned char *BFH, unsigned char *BIH,
 			State->BufferSize = State->LineWidth;
 		}
 	} else if (State->Compressed == BI_BITFIELDS) {
-               if (State->Header.size == 108 || State->Header.size == 124) 
+               if (State->Header.size == 52  || State->Header.size == 56 ||
+                   State->Header.size == 108 || State->Header.size == 124)
                {
-			/* v4 and v5 have the bitmasks in the header */
+			/* extended v3, v4 and v5 have the bitmasks in the header */
 			if (!decode_bitmasks (&BIH[40], State, error)) {
 			       State->read_state = READ_STATE_ERROR;
 			       return FALSE;
@@ -611,8 +653,8 @@ decode_bitmasks (guchar *buf,
 	find_bits (State->g_mask, &State->g_shift, &State->g_bits);
 	find_bits (State->b_mask, &State->b_shift, &State->b_bits);
 
-        /* v4 and v5 have an alpha mask */
-        if (State->Header.size == 108 || State->Header.size == 124) {
+        /* extended v3, v4 and v5 have an alpha mask */
+        if (State->Header.size == 56 || State->Header.size == 108 || State->Header.size == 124) {
 	      buf += 4;
 	      State->a_mask = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
 	      find_bits (State->a_mask, &State->a_shift, &State->a_bits);
@@ -737,12 +779,10 @@ static gboolean gdk_pixbuf__bmp_image_stop_load(gpointer data, GError **error)
 		g_object_unref(context->pixbuf);
 
 	if (context->read_state == READ_STATE_HEADERS) {
-                if (error && *error == NULL) {
-                        g_set_error_literal (error,
-                                             GDK_PIXBUF_ERROR,
-                                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
-                                             _("Premature end-of-file encountered"));
-                }
+                g_set_error_literal (error,
+                                     GDK_PIXBUF_ERROR,
+                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                                     _("Premature end-of-file encountered"));
 		retval = FALSE;
 	}
 	
@@ -1318,6 +1358,7 @@ gdk_pixbuf__bmp_image_save_to_callback (GdkPixbufSaveFunc   save_func,
 					GError            **error)
 {
 	guint width, height, channel, size, stride, src_stride, x, y;
+	guint bf_size;
 	guchar BFH_BIH[54], *pixels, *buf, *src, *dst, *dst_line;
 	gboolean ret;
 
@@ -1326,14 +1367,33 @@ gdk_pixbuf__bmp_image_save_to_callback (GdkPixbufSaveFunc   save_func,
 	channel = gdk_pixbuf_get_n_channels (pixbuf);
 	pixels = gdk_pixbuf_get_pixels (pixbuf);
 	src_stride = gdk_pixbuf_get_rowstride (pixbuf);
-	stride = (width * 3 + 3) & ~3;
-	size = stride * height;
+
+	/* stride = (width * 3 + 3) & ~3 */
+	if (!g_uint_checked_mul (&stride, width, 3) ||
+	    !g_uint_checked_add (&stride, stride, 3)) {
+		g_set_error_literal (error, GDK_PIXBUF_ERROR,
+		                     GDK_PIXBUF_ERROR_FAILED,
+		                     _("Image is too wide for BMP format."));
+		return FALSE;
+	}
+
+	stride &= ~3;
+
+	/* size = stride * height
+	 * bf_size = size + 14 + 40 */
+	if (!g_uint_checked_mul (&size, stride, height) ||
+	    !g_uint_checked_add (&bf_size, size, 14 + 40)) {
+		g_set_error_literal (error, GDK_PIXBUF_ERROR,
+		                     GDK_PIXBUF_ERROR_FAILED,
+		                     _("Image is too wide for BMP format."));
+		return FALSE;
+	}
 
 	/* filling BFH */
 	dst = BFH_BIH;
 	*dst++ = 'B';			/* bfType */
 	*dst++ = 'M';
-	put32 (dst, size + 14 + 40);	/* bfSize */
+	put32 (dst, bf_size);	/* bfSize */
 	put32 (dst, 0);			/* bfReserved1 + bfReserved2 */
 	put32 (dst, 14 + 40);		/* bfOffBits */
 
-- 
2.16.1

