Skip to content

Commit 47aeaa7

Browse files
6by9popcornmix
authored andcommitted
fbcon: Add defensive coding to logo loader
There were various points where the loader was using uninitialised data, had the potential to run off the end of an array, or was handling core functions incorrectly. Fix these up. Also handle 24bpp and 32bpp framebuffers. Signed-off-by: Dave Stevenson <[email protected]>
1 parent 425c51d commit 47aeaa7

File tree

1 file changed

+105
-86
lines changed

1 file changed

+105
-86
lines changed

drivers/video/fbdev/core/fb_logo.c

Lines changed: 105 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -92,19 +92,29 @@ static void fb_set_logo_truepalette(struct fb_info *info,
9292
}
9393
}
9494

95-
static void fb_set_logo_RGB_palette(struct image_palette *palette,
96-
u32 *palette_to_write, int current_rows)
95+
static void fb_set_logo_RGB_palette(struct fb_info *info,
96+
struct image_palette *pal_in,
97+
u32 *pal_out, int current_rows)
9798
{
9899
// Set the kernel palette from an array of RGB values
99-
uint32_t color_code;
100+
static const unsigned char mask[] = {
101+
0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff
102+
};
103+
unsigned char redmask, greenmask, bluemask;
104+
int redshift, greenshift, blueshift;
100105
int i;
101106

102-
// Color format is RGB565, remove LSB 3 bits, and move to correct position
107+
redmask = mask[info->var.red.length < 8 ? info->var.red.length : 8];
108+
greenmask = mask[info->var.green.length < 8 ? info->var.green.length : 8];
109+
bluemask = mask[info->var.blue.length < 8 ? info->var.blue.length : 8];
110+
redshift = info->var.red.offset - (8 - info->var.red.length);
111+
greenshift = info->var.green.offset - (8 - info->var.green.length);
112+
blueshift = info->var.blue.offset - (8 - info->var.blue.length);
113+
103114
for (i = 0; i < current_rows; i++) {
104-
color_code = ((((uint16_t)palette->colors[i][0]) >> 3) << 11) |
105-
((((uint16_t)palette->colors[i][1]) >> 2) << 5) |
106-
(((uint16_t)palette->colors[i][2]) >> 3);
107-
palette_to_write[i+32] = color_code;
115+
pal_out[i + 32] = (safe_shift((pal_in->colors[i][0] & redmask), redshift) |
116+
safe_shift((pal_in->colors[i][1] & greenmask), greenshift) |
117+
safe_shift((pal_in->colors[i][2] & bluemask), blueshift));
108118
}
109119
}
110120

@@ -339,8 +349,8 @@ static void fb_set_logo_from_file(struct fb_info *info, const char *filepath,
339349
{
340350
int current_rows = 0, palette_index = 0, actual_row, skip_x = 0, skip_y = 0, ret;
341351
unsigned char *read_logo = NULL, *header;
342-
const char *file_content = NULL;
343-
const struct firmware *fw;
352+
const char *file_content;
353+
const struct firmware *fw = NULL;
344354
struct image_palette image_palette;
345355
const char *current_ptr, *end_ptr;
346356
long width = 0, height = 0;
@@ -349,98 +359,108 @@ static void fb_set_logo_from_file(struct fb_info *info, const char *filepath,
349359
u8 entry[3];
350360
ssize_t len;
351361

352-
ret = request_firmware(&fw, filepath, info->device);
362+
ret = firmware_request_nowarn(&fw, filepath, info->device);
353363
if (ret) {
354364
pr_info("Failed to load logo file '%s': %d\n", filepath, ret);
355365
goto cleanup;
356366
}
357367
len = fw->size;
358368
file_content = fw->data;
359369

360-
if (len > 0) {
361-
current_ptr = file_content;
362-
end_ptr = file_content + len;
363-
if (len < 18) {
364-
pr_err("Invalid logo file: TGA file too small for header\n");
365-
goto cleanup;
366-
}
367-
header = (unsigned char *)file_content;
368-
369-
// Skip color map info (bytes 3-7)
370-
// Skip image origin (bytes 8-11)
371-
width = header[12] | (header[13] << 8);
372-
height = header[14] | (header[15] << 8);
373-
374-
// Only supports uncompressed true-color images (type 2) with 24-bit depth
375-
if (header[2] != 2 || header[16] != 24) {
376-
pr_err("Unsupported TGA logo format: Type=%d, Depth=%d (only support Type=2, Depth=24)\n",
377-
header[2], header[16]);
378-
goto cleanup;
379-
}
380-
// Skip header + ID field
381-
current_ptr = file_content + 18 + header[0];
370+
if (!len) {
371+
pr_err("Error: logo TGA file is empty. Not drawing fullscreen logo.\n");
372+
goto cleanup;
373+
}
374+
375+
current_ptr = file_content;
376+
end_ptr = file_content + len;
377+
if (len < 18) {
378+
pr_err("Invalid logo file: TGA file too small for header\n");
379+
goto cleanup;
380+
}
381+
header = (unsigned char *)file_content;
382382

383-
read_logo = kmalloc_array(width, height, GFP_KERNEL);
384-
if (!read_logo)
385-
goto cleanup;
383+
// Skip color map info (bytes 3-7)
384+
// Skip image origin (bytes 8-11)
385+
width = header[12] | (header[13] << 8);
386+
height = header[14] | (header[15] << 8);
386387

387-
image->data = read_logo;
388+
// Only supports uncompressed true-color images (type 2) with 24-bit depth
389+
if (header[2] != 2 || header[16] != 24) {
390+
pr_err("Unsupported TGA logo format: Type=%d, Depth=%d (only support Type=2, Depth=24)\n",
391+
header[2], header[16]);
392+
goto cleanup;
393+
}
394+
// Skip header + ID field
395+
current_ptr = file_content + 18 + header[0];
388396

389-
// TGA pixels are stored bottom-to-top by default, unless bit 5 of
390-
// image_descriptor is set
391-
top_to_bottom = (header[17] & 0x20) != 0;
392-
skip_x = 0;
393-
skip_y = 0;
397+
read_logo = kmalloc_array(width, height, GFP_KERNEL);
398+
if (!read_logo)
399+
goto cleanup;
394400

395-
if (image->width > info->var.xres) {
396-
pr_info("Logo is larger than screen, clipping horizontally");
397-
skip_x = (image->width - info->var.xres) / 2;
398-
}
399-
if (image->height > info->var.yres) {
400-
pr_info("Logo is larger than screen, clipping vertically");
401-
skip_y = (image->height - info->var.yres) / 2;
402-
}
403-
current_ptr += skip_y * width * 3 + skip_x * 3;
404-
// Parse pixel data (BGR format in TGA)
405-
for (int i = 0; i < height - 2 * skip_y; i++) {
406-
for (int j = 0; j < width - 2 * skip_x; j++) {
407-
if (current_ptr + 3 > end_ptr) {
408-
pr_info("TGA: Unexpected end of file\n");
409-
goto cleanup;
410-
}
411-
B = (unsigned char)*current_ptr++;
412-
G = (unsigned char)*current_ptr++;
413-
R = (unsigned char)*current_ptr++;
414-
entry[0] = R;
415-
entry[1] = G;
416-
entry[2] = B;
417-
palette_index = 0;
418-
419-
if (!fb_palette_contains_entry(&image_palette, current_rows,
420-
entry, 3, &palette_index)) {
401+
image->data = read_logo;
402+
403+
// TGA pixels are stored bottom-to-top by default, unless bit 5 of
404+
// image_descriptor is set
405+
top_to_bottom = (header[17] & 0x20) != 0;
406+
skip_x = 0;
407+
skip_y = 0;
408+
409+
if (width > info->var.xres) {
410+
pr_info("Logo is larger than screen (%lu vs %u), clipping horizontally\n",
411+
width, info->var.xres);
412+
skip_x = (width - info->var.xres) / 2;
413+
}
414+
if (height > info->var.yres) {
415+
pr_info("Logo is larger than screen (%lu vs %u), clipping vertically\n",
416+
height, info->var.yres);
417+
skip_y = (height - info->var.yres) / 2;
418+
}
419+
current_ptr += skip_y * width * 3 + skip_x * 3;
420+
// Parse pixel data (BGR format in TGA)
421+
for (int i = 0; i < height - 2 * skip_y; i++) {
422+
for (int j = 0; j < width - 2 * skip_x; j++) {
423+
if (current_ptr + 3 > end_ptr) {
424+
pr_info("TGA: Unexpected end of file\n");
425+
goto cleanup;
426+
}
427+
B = (unsigned char)*current_ptr++;
428+
G = (unsigned char)*current_ptr++;
429+
R = (unsigned char)*current_ptr++;
430+
entry[0] = R;
431+
entry[1] = G;
432+
entry[2] = B;
433+
palette_index = 0;
434+
435+
if (!fb_palette_contains_entry(&image_palette, current_rows,
436+
entry, 3, &palette_index)) {
437+
if (current_rows < 224) {
421438
for (int k = 0; k < 3; k++)
422-
image_palette.colors[current_rows][k] = entry[k];
439+
image_palette.colors[current_rows][k] =
440+
entry[k];
423441
palette_index = current_rows;
424-
current_rows++;
425442
}
426-
actual_row = top_to_bottom ? i : (height - 1 - i);
427-
428-
read_logo[actual_row * (width - 2 * skip_x) + j] =
429-
palette_index + 32;
443+
current_rows++;
430444
}
431-
current_ptr += skip_x * 3 * 2;
445+
actual_row = top_to_bottom ? i : (height - 1 - i);
446+
447+
read_logo[actual_row * (width - 2 * skip_x) + j] =
448+
palette_index + 32;
432449
}
450+
current_ptr += skip_x * 3 * 2;
451+
}
433452

434-
// Set logo palette
435-
palette = kmalloc(256 * 4, GFP_KERNEL);
436-
if (palette == NULL)
437-
goto cleanup;
438-
fb_set_logo_RGB_palette(&image_palette, palette, current_rows);
439-
info->pseudo_palette = palette;
453+
if (current_rows >= 224)
454+
pr_err("Palette overflow. Entries clipped\n");
440455

441-
} else {
442-
pr_err("Error: logo TGA file is empty. Not drawing fullscreen logo.\n");
443-
}
456+
// Set logo palette
457+
palette = kzalloc(256 * 4, GFP_KERNEL);
458+
if (!palette)
459+
goto cleanup;
460+
fb_set_logo_RGB_palette(info, &image_palette, palette, current_rows);
461+
if (info->pseudo_palette)
462+
memcpy(palette, info->pseudo_palette, 32 * sizeof(uint32_t));
463+
info->pseudo_palette = palette;
444464

445465
image->width = min_t(unsigned int, width, info->var.xres);
446466
image->height = min_t(unsigned int, height, info->var.yres);
@@ -455,8 +475,7 @@ static void fb_set_logo_from_file(struct fb_info *info, const char *filepath,
455475

456476
cleanup:
457477
kfree(read_logo);
458-
if (file_content)
459-
kvfree(file_content);
478+
release_firmware(fw);
460479
}
461480

462481

0 commit comments

Comments
 (0)