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

Reply via email to