[PATCH v3 1/2] video: bmp: bound RLE8 decode writes to the framebuffer

Shahriyar Jalayeri <[email protected]>
Newsgroups gmane.comp.boot-loaders.u-boot
Message-ID <[email protected]>
video_display_rle8_bitmap() moves the framebuffer cursor fb up by a
full row plus a scanline on each End-Of-Line escape, with no
lower-bound check.  Repeated EOL escapes desynchronise fb from the
scanline index y: after height - 1 escapes y is back in range while fb
has drifted about one framebuffer below priv->fb, and the decoder
writes pixel data before the start of the framebuffer.  The DELTA
escape recomputes fb from an unchecked y as well.  A crafted RLE8
image displayed from the splash-screen or PXE-menu path can therefore
write out of bounds.

Check the cursor against [priv->fb, priv->fb + fb_size) before each
run and reject the image with -EINVAL if a write would fall outside
it.

Signed-off-by: Shahriyar Jalayeri <[email protected]>
Reviewed-by: Simon Glass <[email protected]>
---
 drivers/video/video_bmp.c | 28 +++++++++++++++++++++-------
 1 file changed, 21 insertions(+), 7 deletions(-)

diff --git a/drivers/video/video_bmp.c b/drivers/video/video_bmp.c
index 1f267d45812..c6710f898da 100644
--- a/drivers/video/video_bmp.c
+++ b/drivers/video/video_bmp.c
@@ -122,13 +122,15 @@ static void draw_encoded_bitmap(u8 **fbp, uint bpix, enum video_format eformat,
 	*fbp = fb;
 }
 
-static void video_display_rle8_bitmap(struct udevice *dev,
-				      struct bmp_image *bmp, uint bpix,
-				      struct bmp_color_table_entry *palette,
-				      uchar *fb, int x_off, int y_off,
-				      ulong width, ulong height)
+static int video_display_rle8_bitmap(struct udevice *dev,
+				     struct bmp_image *bmp, uint bpix,
+				     struct bmp_color_table_entry *palette,
+				     uchar *fb, int x_off, int y_off,
+				     ulong width, ulong height)
 {
 	struct video_priv *priv = dev_get_uclass_priv(dev);
+	uchar *fb_start = priv->fb;
+	uchar *fb_end = (uchar *)priv->fb + priv->fb_size;
 	uchar *bmap;
 	ulong cnt, runlen;
 	int x, y;
@@ -176,6 +178,9 @@ static void video_display_rle8_bitmap(struct udevice *dev,
 							cnt = width - x;
 						else
 							cnt = runlen;
+						if (fb < fb_start ||
+						    fb + cnt * bytes_per_pixel > fb_end)
+							return -EINVAL;
 						draw_unencoded_bitmap(
 							&fb, bpix, eformat,
 							bmap, palette, cnt);
@@ -202,6 +207,9 @@ static void video_display_rle8_bitmap(struct udevice *dev,
 						cnt = width - x;
 					else
 						cnt = runlen;
+					if (fb < fb_start ||
+					    fb + cnt * bytes_per_pixel > fb_end)
+						return -EINVAL;
 					draw_encoded_bitmap(&fb, bpix, eformat,
 							    palette, &bmap[1],
 							    cnt);
@@ -211,6 +219,8 @@ static void video_display_rle8_bitmap(struct udevice *dev,
 			bmap += 2;
 		}
 	}
+
+	return 0;
 }
 
 /**
@@ -267,6 +277,7 @@ int video_bmp_display(struct udevice *dev, ulong bmp_image, int x, int y,
 	enum video_format eformat;
 	struct bmp_color_table_entry *palette;
 	int hdr_size;
+	int ret;
 
 	if (!bmp || !(bmp->header.signature[0] == 'B' &&
 	    bmp->header.signature[1] == 'M')) {
@@ -337,8 +348,11 @@ int video_bmp_display(struct udevice *dev, ulong bmp_image, int x, int y,
 				&bmp->header.compression);
 			debug("compressed %d %d\n", compression, BMP_BI_RLE8);
 			if (compression == BMP_BI_RLE8) {
-				video_display_rle8_bitmap(dev, bmp, bpix, palette, fb,
-							  x, y, width, height);
+				ret = video_display_rle8_bitmap(dev, bmp, bpix,
+								palette, fb, x, y,
+								width, height);
+				if (ret)
+					return ret;
 				break;
 			}
 		}

-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.