Skip to content

Commit d48f6fd

Browse files
committed
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. (It still goes quite badly wrong if the framebuffer isn't 16bpp) Signed-off-by: Dave Stevenson <[email protected]>
1 parent 0c7075b commit d48f6fd

File tree

1 file changed

+87
-78
lines changed

1 file changed

+87
-78
lines changed

drivers/video/fbdev/core/fb_logo.c

Lines changed: 87 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -339,8 +339,8 @@ static void fb_set_logo_from_file(struct fb_info *info, const char *filepath,
339339
{
340340
int current_rows = 0, palette_index = 0, actual_row, skip_x = 0, skip_y = 0, ret;
341341
unsigned char *read_logo = NULL, *header;
342-
const char *file_content = NULL;
343-
const struct firmware *fw;
342+
const char *file_content;
343+
const struct firmware *fw = NULL;
344344
struct image_palette image_palette;
345345
const char *current_ptr, *end_ptr;
346346
long width = 0, height = 0;
@@ -349,98 +349,108 @@ static void fb_set_logo_from_file(struct fb_info *info, const char *filepath,
349349
u8 entry[3];
350350
ssize_t len;
351351

352-
ret = request_firmware(&fw, filepath, info->device);
352+
ret = firmware_request_nowarn(&fw, filepath, info->device);
353353
if (ret) {
354354
pr_info("Failed to load logo file '%s': %d\n", filepath, ret);
355355
goto cleanup;
356356
}
357357
len = fw->size;
358358
file_content = fw->data;
359359

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];
360+
if (!len) {
361+
pr_err("Error: logo TGA file is empty. Not drawing fullscreen logo.\n");
362+
goto cleanup;
363+
}
382364

383-
read_logo = kmalloc_array(width, height, GFP_KERNEL);
384-
if (!read_logo)
385-
goto cleanup;
365+
current_ptr = file_content;
366+
end_ptr = file_content + len;
367+
if (len < 18) {
368+
pr_err("Invalid logo file: TGA file too small for header\n");
369+
goto cleanup;
370+
}
371+
header = (unsigned char *)file_content;
386372

387-
image->data = read_logo;
373+
// Skip color map info (bytes 3-7)
374+
// Skip image origin (bytes 8-11)
375+
width = header[12] | (header[13] << 8);
376+
height = header[14] | (header[15] << 8);
388377

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;
378+
// Only supports uncompressed true-color images (type 2) with 24-bit depth
379+
if (header[2] != 2 || header[16] != 24) {
380+
pr_err("Unsupported TGA logo format: Type=%d, Depth=%d (only support Type=2, Depth=24)\n",
381+
header[2], header[16]);
382+
goto cleanup;
383+
}
384+
// Skip header + ID field
385+
current_ptr = file_content + 18 + header[0];
394386

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)) {
387+
read_logo = kmalloc_array(width, height, GFP_KERNEL);
388+
if (!read_logo)
389+
goto cleanup;
390+
391+
image->data = read_logo;
392+
393+
// TGA pixels are stored bottom-to-top by default, unless bit 5 of
394+
// image_descriptor is set
395+
top_to_bottom = (header[17] & 0x20) != 0;
396+
skip_x = 0;
397+
skip_y = 0;
398+
399+
if (width > info->var.xres) {
400+
pr_info("Logo is larger than screen (%lu vs %u), clipping horizontally\n",
401+
width, info->var.xres);
402+
skip_x = (width - info->var.xres) / 2;
403+
}
404+
if (height > info->var.yres) {
405+
pr_info("Logo is larger than screen (%lu vs %u), clipping vertically\n",
406+
height, info->var.yres);
407+
skip_y = (height - info->var.yres) / 2;
408+
}
409+
current_ptr += skip_y * width * 3 + skip_x * 3;
410+
// Parse pixel data (BGR format in TGA)
411+
for (int i = 0; i < height - 2 * skip_y; i++) {
412+
for (int j = 0; j < width - 2 * skip_x; j++) {
413+
if (current_ptr + 3 > end_ptr) {
414+
pr_info("TGA: Unexpected end of file\n");
415+
goto cleanup;
416+
}
417+
B = (unsigned char)*current_ptr++;
418+
G = (unsigned char)*current_ptr++;
419+
R = (unsigned char)*current_ptr++;
420+
entry[0] = R;
421+
entry[1] = G;
422+
entry[2] = B;
423+
palette_index = 0;
424+
425+
if (!fb_palette_contains_entry(&image_palette, current_rows,
426+
entry, 3, &palette_index)) {
427+
if (current_rows < 224) {
421428
for (int k = 0; k < 3; k++)
422-
image_palette.colors[current_rows][k] = entry[k];
429+
image_palette.colors[current_rows][k] =
430+
entry[k];
423431
palette_index = current_rows;
424-
current_rows++;
425432
}
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;
433+
current_rows++;
430434
}
431-
current_ptr += skip_x * 3 * 2;
435+
actual_row = top_to_bottom ? i : (height - 1 - i);
436+
437+
read_logo[actual_row * (width - 2 * skip_x) + j] =
438+
palette_index + 32;
432439
}
440+
current_ptr += skip_x * 3 * 2;
441+
}
433442

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;
443+
if (current_rows >= 224)
444+
pr_err("Palette overflow. Entries clipped\n");
440445

441-
} else {
442-
pr_err("Error: logo TGA file is empty. Not drawing fullscreen logo.\n");
443-
}
446+
// Set logo palette
447+
palette = kzalloc(256 * 4, GFP_KERNEL);
448+
if (!palette)
449+
goto cleanup;
450+
fb_set_logo_RGB_palette(&image_palette, palette, current_rows);
451+
if (info->pseudo_palette)
452+
memcpy(palette, info->pseudo_palette, 32 * sizeof(uint16_t));
453+
info->pseudo_palette = palette;
444454

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

456466
cleanup:
457467
kfree(read_logo);
458-
if (file_content)
459-
kvfree(file_content);
468+
release_firmware(fw);
460469
}
461470

462471

0 commit comments

Comments
 (0)