In grub-core/video/readers/jpeg.c, the height and width of a JPEG image don't have an upper limit for how big the JPEG image can be. In Coverity, this is getting flagged as an untrusted loop bound. This issue can also seen in PNG and TGA format images as well but Coverity isn't flagging it. To prevent this, the constant IMAGE_HW_MAX_PX is being added to include/grub/bitmap.h, which has a value of 16384, to act as an artificial limit and restrict the height and width of images. This value was picked as it is double the current max resolution size, which is 8K. Fixes: CID 292450 Signed-off-by: Alec Brown <alec.r.brown@oracle.com> Reviewed-by: Darren Kenny <darren.kenny@oracle.com> Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
519 lines
13 KiB
C
519 lines
13 KiB
C
/*
|
|
* GRUB -- GRand Unified Bootloader
|
|
* Copyright (C) 2006,2007 Free Software Foundation, Inc.
|
|
*
|
|
* GRUB is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* GRUB is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <grub/bitmap.h>
|
|
#include <grub/types.h>
|
|
#include <grub/normal.h>
|
|
#include <grub/dl.h>
|
|
#include <grub/mm.h>
|
|
#include <grub/misc.h>
|
|
#include <grub/bufio.h>
|
|
|
|
GRUB_MOD_LICENSE ("GPLv3+");
|
|
|
|
/* Uncomment following define to enable TGA debug. */
|
|
//#define TGA_DEBUG
|
|
|
|
#define dump_int_field(x) grub_dprintf ("tga", #x " = %d (0x%04x)\n", x, x);
|
|
#if defined(TGA_DEBUG)
|
|
static grub_command_t cmd;
|
|
#endif
|
|
|
|
enum
|
|
{
|
|
GRUB_TGA_IMAGE_TYPE_NONE = 0,
|
|
GRUB_TGA_IMAGE_TYPE_UNCOMPRESSED_INDEXCOLOR = 1,
|
|
GRUB_TGA_IMAGE_TYPE_UNCOMPRESSED_TRUECOLOR = 2,
|
|
GRUB_TGA_IMAGE_TYPE_UNCOMPRESSED_BLACK_AND_WHITE = 3,
|
|
GRUB_TGA_IMAGE_TYPE_RLE_INDEXCOLOR = 9,
|
|
GRUB_TGA_IMAGE_TYPE_RLE_TRUECOLOR = 10,
|
|
GRUB_TGA_IMAGE_TYPE_RLE_BLACK_AND_WHITE = 11,
|
|
};
|
|
|
|
enum
|
|
{
|
|
GRUB_TGA_COLOR_MAP_TYPE_NONE = 0,
|
|
GRUB_TGA_COLOR_MAP_TYPE_INCLUDED = 1
|
|
};
|
|
|
|
enum
|
|
{
|
|
GRUB_TGA_IMAGE_ORIGIN_RIGHT = 0x10,
|
|
GRUB_TGA_IMAGE_ORIGIN_TOP = 0x20
|
|
};
|
|
|
|
struct grub_tga_header
|
|
{
|
|
grub_uint8_t id_length;
|
|
grub_uint8_t color_map_type;
|
|
grub_uint8_t image_type;
|
|
|
|
/* Color Map Specification. */
|
|
grub_uint16_t color_map_first_index;
|
|
grub_uint16_t color_map_length;
|
|
grub_uint8_t color_map_bpp;
|
|
|
|
/* Image Specification. */
|
|
grub_uint16_t image_x_origin;
|
|
grub_uint16_t image_y_origin;
|
|
grub_uint16_t image_width;
|
|
grub_uint16_t image_height;
|
|
grub_uint8_t image_bpp;
|
|
grub_uint8_t image_descriptor;
|
|
} GRUB_PACKED;
|
|
|
|
struct tga_data
|
|
{
|
|
struct grub_tga_header hdr;
|
|
int uses_rle;
|
|
int pktlen;
|
|
int bpp;
|
|
int is_rle;
|
|
grub_uint8_t pixel[4];
|
|
grub_uint8_t palette[256][3];
|
|
struct grub_video_bitmap *bitmap;
|
|
grub_file_t file;
|
|
unsigned image_width;
|
|
unsigned image_height;
|
|
};
|
|
|
|
static grub_err_t
|
|
fetch_pixel (struct tga_data *data)
|
|
{
|
|
if (!data->uses_rle)
|
|
{
|
|
if (grub_file_read (data->file, &data->pixel[0], data->bpp)
|
|
!= data->bpp)
|
|
return grub_errno;
|
|
return GRUB_ERR_NONE;
|
|
}
|
|
if (!data->pktlen)
|
|
{
|
|
grub_uint8_t type;
|
|
if (grub_file_read (data->file, &type, sizeof (type)) != sizeof(type))
|
|
return grub_errno;
|
|
data->is_rle = !!(type & 0x80);
|
|
data->pktlen = (type & 0x7f) + 1;
|
|
if (data->is_rle && grub_file_read (data->file, &data->pixel[0], data->bpp)
|
|
!= data->bpp)
|
|
return grub_errno;
|
|
}
|
|
if (!data->is_rle && grub_file_read (data->file, &data->pixel[0], data->bpp)
|
|
!= data->bpp)
|
|
return grub_errno;
|
|
data->pktlen--;
|
|
return GRUB_ERR_NONE;
|
|
}
|
|
|
|
static grub_err_t
|
|
tga_load_palette (struct tga_data *data)
|
|
{
|
|
grub_size_t len = grub_le_to_cpu32 (data->hdr.color_map_length) * 3;
|
|
|
|
if (len > sizeof (data->palette))
|
|
len = sizeof (data->palette);
|
|
|
|
if (grub_file_read (data->file, &data->palette, len)
|
|
!= (grub_ssize_t) len)
|
|
return grub_errno;
|
|
|
|
#ifndef GRUB_CPU_WORDS_BIGENDIAN
|
|
{
|
|
grub_size_t i;
|
|
for (i = 0; 3 * i < len; i++)
|
|
{
|
|
grub_uint8_t t;
|
|
t = data->palette[i][0];
|
|
data->palette[i][0] = data->palette[i][2];
|
|
data->palette[i][2] = t;
|
|
}
|
|
}
|
|
#endif
|
|
return GRUB_ERR_NONE;
|
|
}
|
|
|
|
static grub_err_t
|
|
tga_load_index_color (struct tga_data *data)
|
|
{
|
|
unsigned int x;
|
|
unsigned int y;
|
|
grub_uint8_t *ptr;
|
|
|
|
for (y = 0; y < data->image_height; y++)
|
|
{
|
|
ptr = data->bitmap->data;
|
|
if ((data->hdr.image_descriptor & GRUB_TGA_IMAGE_ORIGIN_TOP) != 0)
|
|
ptr += y * data->bitmap->mode_info.pitch;
|
|
else
|
|
ptr += (data->image_height - 1 - y) * data->bitmap->mode_info.pitch;
|
|
|
|
for (x = 0; x < data->image_width; x++)
|
|
{
|
|
grub_err_t err;
|
|
err = fetch_pixel (data);
|
|
if (err)
|
|
return err;
|
|
|
|
ptr[0] = data->palette[data->pixel[0]][0];
|
|
ptr[1] = data->palette[data->pixel[0]][1];
|
|
ptr[2] = data->palette[data->pixel[0]][2];
|
|
|
|
ptr += 3;
|
|
}
|
|
}
|
|
return GRUB_ERR_NONE;
|
|
}
|
|
|
|
static grub_err_t
|
|
tga_load_grayscale (struct tga_data *data)
|
|
{
|
|
unsigned int x;
|
|
unsigned int y;
|
|
grub_uint8_t *ptr;
|
|
|
|
for (y = 0; y < data->image_height; y++)
|
|
{
|
|
ptr = data->bitmap->data;
|
|
if ((data->hdr.image_descriptor & GRUB_TGA_IMAGE_ORIGIN_TOP) != 0)
|
|
ptr += y * data->bitmap->mode_info.pitch;
|
|
else
|
|
ptr += (data->image_height - 1 - y) * data->bitmap->mode_info.pitch;
|
|
|
|
for (x = 0; x < data->image_width; x++)
|
|
{
|
|
grub_err_t err;
|
|
err = fetch_pixel (data);
|
|
if (err)
|
|
return err;
|
|
|
|
ptr[0] = data->pixel[0];
|
|
ptr[1] = data->pixel[0];
|
|
ptr[2] = data->pixel[0];
|
|
|
|
ptr += 3;
|
|
}
|
|
}
|
|
return GRUB_ERR_NONE;
|
|
}
|
|
|
|
static grub_err_t
|
|
tga_load_truecolor_R8G8B8 (struct tga_data *data)
|
|
{
|
|
unsigned int x;
|
|
unsigned int y;
|
|
grub_uint8_t *ptr;
|
|
|
|
for (y = 0; y < data->image_height; y++)
|
|
{
|
|
ptr = data->bitmap->data;
|
|
if ((data->hdr.image_descriptor & GRUB_TGA_IMAGE_ORIGIN_TOP) != 0)
|
|
ptr += y * data->bitmap->mode_info.pitch;
|
|
else
|
|
ptr += (data->image_height - 1 - y) * data->bitmap->mode_info.pitch;
|
|
|
|
for (x = 0; x < data->image_width; x++)
|
|
{
|
|
grub_err_t err;
|
|
err = fetch_pixel (data);
|
|
if (err)
|
|
return err;
|
|
|
|
#ifdef GRUB_CPU_WORDS_BIGENDIAN
|
|
ptr[0] = data->pixel[0];
|
|
ptr[1] = data->pixel[1];
|
|
ptr[2] = data->pixel[2];
|
|
#else
|
|
ptr[0] = data->pixel[2];
|
|
ptr[1] = data->pixel[1];
|
|
ptr[2] = data->pixel[0];
|
|
#endif
|
|
ptr += 3;
|
|
}
|
|
}
|
|
return GRUB_ERR_NONE;
|
|
}
|
|
|
|
static grub_err_t
|
|
tga_load_truecolor_R8G8B8A8 (struct tga_data *data)
|
|
{
|
|
unsigned int x;
|
|
unsigned int y;
|
|
grub_uint8_t *ptr;
|
|
|
|
for (y = 0; y < data->image_height; y++)
|
|
{
|
|
ptr = data->bitmap->data;
|
|
if ((data->hdr.image_descriptor & GRUB_TGA_IMAGE_ORIGIN_TOP) != 0)
|
|
ptr += y * data->bitmap->mode_info.pitch;
|
|
else
|
|
ptr += (data->image_height - 1 - y) * data->bitmap->mode_info.pitch;
|
|
|
|
for (x = 0; x < data->image_width; x++)
|
|
{
|
|
grub_err_t err;
|
|
err = fetch_pixel (data);
|
|
if (err)
|
|
return err;
|
|
|
|
#ifdef GRUB_CPU_WORDS_BIGENDIAN
|
|
ptr[0] = data->pixel[0];
|
|
ptr[1] = data->pixel[1];
|
|
ptr[2] = data->pixel[2];
|
|
ptr[3] = data->pixel[3];
|
|
#else
|
|
ptr[0] = data->pixel[2];
|
|
ptr[1] = data->pixel[1];
|
|
ptr[2] = data->pixel[0];
|
|
ptr[3] = data->pixel[3];
|
|
#endif
|
|
|
|
ptr += 4;
|
|
}
|
|
}
|
|
return GRUB_ERR_NONE;
|
|
}
|
|
|
|
static grub_err_t
|
|
grub_video_reader_tga (struct grub_video_bitmap **bitmap,
|
|
const char *filename)
|
|
{
|
|
grub_ssize_t pos;
|
|
struct tga_data data;
|
|
|
|
grub_memset (&data, 0, sizeof (data));
|
|
|
|
data.file = grub_buffile_open (filename, GRUB_FILE_TYPE_PIXMAP, 0);
|
|
if (! data.file)
|
|
return grub_errno;
|
|
|
|
/* TGA Specification states that we SHOULD start by reading
|
|
ID from end of file, but we really don't care about that as we are
|
|
not going to support developer area & extensions at this point. */
|
|
|
|
/* Read TGA header from beginning of file. */
|
|
if (grub_file_read (data.file, &data.hdr, sizeof (data.hdr))
|
|
!= sizeof (data.hdr))
|
|
{
|
|
grub_file_close (data.file);
|
|
return grub_errno;
|
|
}
|
|
|
|
/* Skip ID field. */
|
|
pos = grub_file_tell (data.file);
|
|
pos += data.hdr.id_length;
|
|
grub_file_seek (data.file, pos);
|
|
if (grub_errno != GRUB_ERR_NONE)
|
|
{
|
|
grub_file_close (data.file);
|
|
return grub_errno;
|
|
}
|
|
|
|
grub_dprintf("tga", "tga: header\n");
|
|
dump_int_field(data.hdr.id_length);
|
|
dump_int_field(data.hdr.color_map_type);
|
|
dump_int_field(data.hdr.image_type);
|
|
dump_int_field(data.hdr.color_map_first_index);
|
|
dump_int_field(data.hdr.color_map_length);
|
|
dump_int_field(data.hdr.color_map_bpp);
|
|
dump_int_field(data.hdr.image_x_origin);
|
|
dump_int_field(data.hdr.image_y_origin);
|
|
dump_int_field(data.hdr.image_width);
|
|
dump_int_field(data.hdr.image_height);
|
|
dump_int_field(data.hdr.image_bpp);
|
|
dump_int_field(data.hdr.image_descriptor);
|
|
|
|
data.image_width = grub_le_to_cpu16 (data.hdr.image_width);
|
|
data.image_height = grub_le_to_cpu16 (data.hdr.image_height);
|
|
|
|
grub_dprintf ("tga", "image height: %d\n", data.image_height);
|
|
grub_dprintf ("tga", "image width: %d\n", data.image_width);
|
|
|
|
/* Check image height and width are within restrictions. */
|
|
if ((data.image_height > IMAGE_HW_MAX_PX) || (data.image_width > IMAGE_HW_MAX_PX))
|
|
return grub_error (GRUB_ERR_BAD_FILE_TYPE, "tga: invalid image size");
|
|
|
|
/* Check that bitmap encoding is supported. */
|
|
switch (data.hdr.image_type)
|
|
{
|
|
case GRUB_TGA_IMAGE_TYPE_RLE_TRUECOLOR:
|
|
case GRUB_TGA_IMAGE_TYPE_RLE_BLACK_AND_WHITE:
|
|
case GRUB_TGA_IMAGE_TYPE_RLE_INDEXCOLOR:
|
|
data.uses_rle = 1;
|
|
break;
|
|
case GRUB_TGA_IMAGE_TYPE_UNCOMPRESSED_TRUECOLOR:
|
|
case GRUB_TGA_IMAGE_TYPE_UNCOMPRESSED_BLACK_AND_WHITE:
|
|
case GRUB_TGA_IMAGE_TYPE_UNCOMPRESSED_INDEXCOLOR:
|
|
data.uses_rle = 0;
|
|
break;
|
|
|
|
default:
|
|
grub_file_close (data.file);
|
|
return grub_error (GRUB_ERR_BAD_FILE_TYPE,
|
|
"unsupported bitmap format (unknown encoding %d)", data.hdr.image_type);
|
|
}
|
|
|
|
data.bpp = data.hdr.image_bpp / 8;
|
|
|
|
/* Check that bitmap depth is supported. */
|
|
switch (data.hdr.image_type)
|
|
{
|
|
case GRUB_TGA_IMAGE_TYPE_RLE_BLACK_AND_WHITE:
|
|
case GRUB_TGA_IMAGE_TYPE_UNCOMPRESSED_BLACK_AND_WHITE:
|
|
if (data.hdr.image_bpp != 8)
|
|
{
|
|
grub_file_close (data.file);
|
|
return grub_error (GRUB_ERR_BAD_FILE_TYPE,
|
|
"unsupported bitmap format (bpp=%d)",
|
|
data.hdr.image_bpp);
|
|
}
|
|
grub_video_bitmap_create (bitmap, data.image_width,
|
|
data.image_height,
|
|
GRUB_VIDEO_BLIT_FORMAT_RGB_888);
|
|
if (grub_errno != GRUB_ERR_NONE)
|
|
{
|
|
grub_file_close (data.file);
|
|
return grub_errno;
|
|
}
|
|
|
|
data.bitmap = *bitmap;
|
|
/* Load bitmap data. */
|
|
tga_load_grayscale (&data);
|
|
break;
|
|
|
|
case GRUB_TGA_IMAGE_TYPE_RLE_INDEXCOLOR:
|
|
case GRUB_TGA_IMAGE_TYPE_UNCOMPRESSED_INDEXCOLOR:
|
|
if (data.hdr.image_bpp != 8
|
|
|| data.hdr.color_map_bpp != 24
|
|
|| data.hdr.color_map_first_index != 0)
|
|
{
|
|
grub_file_close (data.file);
|
|
return grub_error (GRUB_ERR_BAD_FILE_TYPE,
|
|
"unsupported bitmap format (bpp=%d)",
|
|
data.hdr.image_bpp);
|
|
}
|
|
grub_video_bitmap_create (bitmap, data.image_width,
|
|
data.image_height,
|
|
GRUB_VIDEO_BLIT_FORMAT_RGB_888);
|
|
if (grub_errno != GRUB_ERR_NONE)
|
|
{
|
|
grub_file_close (data.file);
|
|
return grub_errno;
|
|
}
|
|
|
|
data.bitmap = *bitmap;
|
|
/* Load bitmap data. */
|
|
tga_load_palette (&data);
|
|
tga_load_index_color (&data);
|
|
break;
|
|
|
|
case GRUB_TGA_IMAGE_TYPE_RLE_TRUECOLOR:
|
|
case GRUB_TGA_IMAGE_TYPE_UNCOMPRESSED_TRUECOLOR:
|
|
switch (data.hdr.image_bpp)
|
|
{
|
|
case 24:
|
|
grub_video_bitmap_create (bitmap, data.image_width,
|
|
data.image_height,
|
|
GRUB_VIDEO_BLIT_FORMAT_RGB_888);
|
|
if (grub_errno != GRUB_ERR_NONE)
|
|
{
|
|
grub_file_close (data.file);
|
|
return grub_errno;
|
|
}
|
|
|
|
data.bitmap = *bitmap;
|
|
/* Load bitmap data. */
|
|
tga_load_truecolor_R8G8B8 (&data);
|
|
break;
|
|
|
|
case 32:
|
|
grub_video_bitmap_create (bitmap, data.image_width,
|
|
data.image_height,
|
|
GRUB_VIDEO_BLIT_FORMAT_RGBA_8888);
|
|
if (grub_errno != GRUB_ERR_NONE)
|
|
{
|
|
grub_file_close (data.file);
|
|
return grub_errno;
|
|
}
|
|
|
|
data.bitmap = *bitmap;
|
|
/* Load bitmap data. */
|
|
tga_load_truecolor_R8G8B8A8 (&data);
|
|
break;
|
|
|
|
default:
|
|
grub_file_close (data.file);
|
|
return grub_error (GRUB_ERR_BAD_FILE_TYPE,
|
|
"unsupported bitmap format (bpp=%d)",
|
|
data.hdr.image_bpp);
|
|
}
|
|
}
|
|
|
|
/* If there was a loading problem, destroy bitmap. */
|
|
if (grub_errno != GRUB_ERR_NONE)
|
|
{
|
|
grub_video_bitmap_destroy (*bitmap);
|
|
*bitmap = 0;
|
|
}
|
|
|
|
grub_file_close (data.file);
|
|
return grub_errno;
|
|
}
|
|
|
|
#if defined(TGA_DEBUG)
|
|
static grub_err_t
|
|
grub_cmd_tgatest (grub_command_t cmd_d __attribute__ ((unused)),
|
|
int argc, char **args)
|
|
{
|
|
struct grub_video_bitmap *bitmap = 0;
|
|
|
|
if (argc != 1)
|
|
return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
|
|
|
|
grub_video_reader_tga (&bitmap, args[0]);
|
|
if (grub_errno != GRUB_ERR_NONE)
|
|
return grub_errno;
|
|
|
|
grub_video_bitmap_destroy (bitmap);
|
|
|
|
return GRUB_ERR_NONE;
|
|
}
|
|
#endif
|
|
|
|
static struct grub_video_bitmap_reader tga_reader = {
|
|
.extension = ".tga",
|
|
.reader = grub_video_reader_tga,
|
|
.next = 0
|
|
};
|
|
|
|
GRUB_MOD_INIT(tga)
|
|
{
|
|
grub_video_bitmap_reader_register (&tga_reader);
|
|
#if defined(TGA_DEBUG)
|
|
cmd = grub_register_command ("tgatest", grub_cmd_tgatest,
|
|
"FILE", "Tests loading of TGA bitmap.");
|
|
#endif
|
|
}
|
|
|
|
GRUB_MOD_FINI(tga)
|
|
{
|
|
#if defined(TGA_DEBUG)
|
|
grub_unregister_command (cmd);
|
|
#endif
|
|
grub_video_bitmap_reader_unregister (&tga_reader);
|
|
}
|