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]> --- drivers/video/video_bmp.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/drivers/video/video_bmp.c b/drivers/video/video_bmp.c index 1f267d45812..d59ef430755 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; } /** @@ -337,8 +347,10 @@ 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); + if (video_display_rle8_bitmap(dev, bmp, bpix, + palette, fb, x, y, + width, height)) + return -EINVAL; break; } } -- 2.43.0
