From 0b04e68ed62633149ecaeb8951ed9a09137f1c58 Mon Sep 17 00:00:00 2001 From: Awss Al-Mubarak Date: Sat, 17 Oct 2026 19:47:39 +0100 Subject: [PATCH 1/3] fbcon: Don't draw the fullscreen logo if it fails to load fb_set_logo_from_file() returns void and leaves the caller's struct fb_image untouched when the logo file is missing or can't be parsed. fb_show_logo_line() then reads image.data[0] and draws from uninitialised stack memory, which oopses while the framebuffer is registered and panics the boot. A TGA header with a width or height of 0 does the same, because kmalloc_array() returns ZERO_SIZE_PTR, which passes the NULL check. When the file does load, the pixel buffer is freed before fb_set_logo_from_file() returns, so the logo is drawn from freed memory. With init_on_free=1 it comes out black. Return an error from fb_set_logo_from_file() and skip drawing when it fails, reject a zero width or height, and free the pixel buffer once the logo has been drawn. Fixes: 19e0758ee533 ("Fullscreen Splash Images") Link: https://github.com/raspberrypi/linux/issues/7086 Signed-off-by: Awss Al-Mubarak --- drivers/video/fbdev/core/fb_logo.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/drivers/video/fbdev/core/fb_logo.c b/drivers/video/fbdev/core/fb_logo.c index 5666928844953e..9c6c125909c289 100644 --- a/drivers/video/fbdev/core/fb_logo.c +++ b/drivers/video/fbdev/core/fb_logo.c @@ -344,8 +344,8 @@ static bool fb_palette_contains_entry(struct image_palette *palette, int num_exi return false; // No duplicate found } -static void fb_set_logo_from_file(struct fb_info *info, const char *filepath, - struct fb_image *image, u32 *palette) +static int fb_set_logo_from_file(struct fb_info *info, const char *filepath, + struct fb_image *image, u32 *palette) { int current_rows = 0, palette_index = 0, actual_row, skip_x = 0, skip_y = 0, ret; unsigned char *read_logo = NULL, *header; @@ -366,6 +366,7 @@ static void fb_set_logo_from_file(struct fb_info *info, const char *filepath, } len = fw->size; file_content = fw->data; + ret = -EINVAL; if (!len) { pr_err("Error: logo TGA file is empty. Not drawing fullscreen logo.\n"); @@ -391,14 +392,18 @@ static void fb_set_logo_from_file(struct fb_info *info, const char *filepath, header[2], header[16]); goto cleanup; } + if (!width || !height) { + pr_err("Invalid logo file: TGA image is %ldx%ld\n", width, height); + goto cleanup; + } // Skip header + ID field current_ptr = file_content + 18 + header[0]; read_logo = kmalloc_array(width, height, GFP_KERNEL); - if (!read_logo) + if (!read_logo) { + ret = -ENOMEM; goto cleanup; - - image->data = read_logo; + } // TGA pixels are stored bottom-to-top by default, unless bit 5 of // image_descriptor is set @@ -473,9 +478,14 @@ static void fb_set_logo_from_file(struct fb_info *info, const char *filepath, if (image->width < info->var.xres) image->dx = (info->var.xres - image->width) / 2; + image->data = read_logo; + release_firmware(fw); + return 0; + cleanup: kfree(read_logo); release_firmware(fw); + return ret; } @@ -493,8 +503,9 @@ static int fb_show_logo_line(struct fb_info *info, int rotate, return 0; if (fullscreen_logo_enabled) { - fb_set_logo_from_file(info, fullscreen_logo_path, - &image, palette); + if (fb_set_logo_from_file(info, fullscreen_logo_path, + &image, palette)) + return 0; } else { image.depth = 8; image.data = logo->data; @@ -580,6 +591,8 @@ static int fb_show_logo_line(struct fb_info *info, int rotate, kfree(palette); if (saved_pseudo_palette != NULL) info->pseudo_palette = saved_pseudo_palette; + if (fullscreen_logo_enabled) + kfree(image.data); kfree(logo_new); kfree(logo_rotate); return image.dy + logo->height; From d2ab21c8e909a9552f8f477e2963687fc1a7a252 Mon Sep 17 00:00:00 2001 From: Awss Al-Mubarak Date: Sat, 17 Oct 2026 19:47:39 +0100 Subject: [PATCH 2/3] fbcon: Restore the pseudo palette after drawing the fullscreen logo fb_set_logo_from_file() allocates a palette for the fullscreen logo, copies the console colours into it and installs it as info->pseudo_palette for good. The driver's own palette is never put back and the new one is never freed, so it leaks once the framebuffer is unregistered, for example when simplefb is unbound. The copy also reads 32 entries from the driver's pseudo palette, which has 16 in simplefb. Allocate the palette in fb_show_logo_line(), install it only while the logo is drawn, then restore the driver's palette and free it, as the built-in logo path already does. The logo only uses entries from 32 up, so the console colours no longer need copying. Fixes: 19e0758ee533 ("Fullscreen Splash Images") Signed-off-by: Awss Al-Mubarak --- drivers/video/fbdev/core/fb_logo.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/video/fbdev/core/fb_logo.c b/drivers/video/fbdev/core/fb_logo.c index 9c6c125909c289..60203c0b6bda48 100644 --- a/drivers/video/fbdev/core/fb_logo.c +++ b/drivers/video/fbdev/core/fb_logo.c @@ -459,13 +459,7 @@ static int fb_set_logo_from_file(struct fb_info *info, const char *filepath, pr_err("Palette overflow. Entries clipped\n"); // Set logo palette - palette = kzalloc(256 * 4, GFP_KERNEL); - if (!palette) - goto cleanup; fb_set_logo_RGB_palette(info, &image_palette, palette, current_rows); - if (info->pseudo_palette) - memcpy(palette, info->pseudo_palette, 32 * sizeof(uint32_t)); - info->pseudo_palette = palette; image->width = min_t(unsigned int, width, info->var.xres); image->height = min_t(unsigned int, height, info->var.yres); @@ -503,9 +497,18 @@ static int fb_show_logo_line(struct fb_info *info, int rotate, return 0; if (fullscreen_logo_enabled) { + palette = kzalloc(256 * 4, GFP_KERNEL); + if (!palette) + return 0; + if (fb_set_logo_from_file(info, fullscreen_logo_path, - &image, palette)) + &image, palette)) { + kfree(palette); return 0; + } + + saved_pseudo_palette = info->pseudo_palette; + info->pseudo_palette = palette; } else { image.depth = 8; image.data = logo->data; From 841bda77c71fd22a92244e4f78ea21858139fffb Mon Sep 17 00:00:00 2001 From: Awss Al-Mubarak Date: Sat, 17 Oct 2026 19:47:40 +0100 Subject: [PATCH 3/3] fbcon: Don't overrun the fullscreen logo palette The colour table in fb_set_logo_from_file() holds 224 colours. Past that, new colours aren't stored, but current_rows is still incremented for every pixel whose colour isn't in the table. Later searches then read past the end of the table on the stack, and fb_set_logo_RGB_palette() writes current_rows entries into a palette that only has room for 224. KASAN reports both for a logo with 225 colours, and with 300 colours the search runs off the stack. Only count the colours that are stored, and print the overflow message when a colour had to be clipped. Clipped colours still map to the first entry. The message no longer appears for a logo with exactly 224 colours. Fixes: 19e0758ee533 ("Fullscreen Splash Images") Signed-off-by: Awss Al-Mubarak --- drivers/video/fbdev/core/fb_logo.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/video/fbdev/core/fb_logo.c b/drivers/video/fbdev/core/fb_logo.c index 60203c0b6bda48..66ebd2b1152709 100644 --- a/drivers/video/fbdev/core/fb_logo.c +++ b/drivers/video/fbdev/core/fb_logo.c @@ -354,7 +354,7 @@ static int fb_set_logo_from_file(struct fb_info *info, const char *filepath, struct image_palette image_palette; const char *current_ptr, *end_ptr; long width = 0, height = 0; - bool top_to_bottom; + bool top_to_bottom, clipped = false; u8 B, G, R; u8 entry[3]; ssize_t len; @@ -444,8 +444,10 @@ static int fb_set_logo_from_file(struct fb_info *info, const char *filepath, image_palette.colors[current_rows][k] = entry[k]; palette_index = current_rows; + current_rows++; + } else { + clipped = true; } - current_rows++; } actual_row = top_to_bottom ? i : (height - 1 - i); @@ -455,7 +457,7 @@ static int fb_set_logo_from_file(struct fb_info *info, const char *filepath, current_ptr += skip_x * 3 * 2; } - if (current_rows >= 224) + if (clipped) pr_err("Palette overflow. Entries clipped\n"); // Set logo palette