intel_bios_reader: Sanitize input to ensure all data blocks are within bounds

Running intel_bios_reader upon itself causes the reader to crash and
burn. It obviously finds a VBT signature inside the binary, but then
does not rigorously check that all data blocks are valid before
dereferencing them.

Reported-by: Emanuel Bronshtein
Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=45205
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
This commit is contained in:
Chris Wilson 2012-01-25 10:11:49 +00:00
parent 93a65895bb
commit 1ffe6b0ee8

View File

@ -74,7 +74,7 @@ static int tv_present;
static int lvds_present; static int lvds_present;
static int panel_type; static int panel_type;
static struct bdb_block *find_section(int section_id) static struct bdb_block *find_section(int section_id, int length)
{ {
struct bdb_block *block; struct bdb_block *block;
unsigned char *base = (unsigned char *)bdb; unsigned char *base = (unsigned char *)bdb;
@ -85,6 +85,8 @@ static struct bdb_block *find_section(int section_id)
/* skip to first section */ /* skip to first section */
idx += bdb->header_size; idx += bdb->header_size;
total = bdb->bdb_size; total = bdb->bdb_size;
if (total > length)
total = length;
block = malloc(sizeof(*block)); block = malloc(sizeof(*block));
if (!block) { if (!block) {
@ -93,30 +95,32 @@ static struct bdb_block *find_section(int section_id)
} }
/* walk the sections looking for section_id */ /* walk the sections looking for section_id */
while (idx < total) { while (idx + 3 < total) {
current_id = *(base + idx); current_id = *(base + idx);
idx++; current_size = *(uint16_t *)(base + idx + 1);
current_size = *((uint16_t *) (base + idx)); if (idx + current_size > total)
idx += 2; return NULL;
if (current_id == section_id) { if (current_id == section_id) {
block->id = current_id; block->id = current_id;
block->size = current_size; block->size = current_size;
block->data = base + idx; block->data = base + idx + 3;
return block; return block;
} }
idx += current_size;
idx += current_size + 3;
} }
free(block); free(block);
return NULL; return NULL;
} }
static void dump_general_features(void) static void dump_general_features(int length)
{ {
struct bdb_general_features *features; struct bdb_general_features *features;
struct bdb_block *block; struct bdb_block *block;
block = find_section(BDB_GENERAL_FEATURES); block = find_section(BDB_GENERAL_FEATURES, length);
if (!block) if (!block)
return; return;
@ -172,13 +176,13 @@ static void dump_general_features(void)
free(block); free(block);
} }
static void dump_backlight_info(void) static void dump_backlight_info(int length)
{ {
struct bdb_block *block; struct bdb_block *block;
struct bdb_lvds_backlight *backlight; struct bdb_lvds_backlight *backlight;
struct blc_struct *blc; struct blc_struct *blc;
block = find_section(BDB_LVDS_BACKLIGHT); block = find_section(BDB_LVDS_BACKLIGHT, length);
if (!block) if (!block)
return; return;
@ -341,7 +345,7 @@ static void dump_child_device(struct child_device_config *child)
} }
} }
static void dump_general_definitions(void) static void dump_general_definitions(int length)
{ {
struct bdb_block *block; struct bdb_block *block;
struct bdb_general_definitions *defs; struct bdb_general_definitions *defs;
@ -349,7 +353,7 @@ static void dump_general_definitions(void)
int i; int i;
int child_device_num; int child_device_num;
block = find_section(BDB_GENERAL_DEFINITIONS); block = find_section(BDB_GENERAL_DEFINITIONS, length);
if (!block) if (!block)
return; return;
@ -373,14 +377,14 @@ static void dump_general_definitions(void)
free(block); free(block);
} }
static void dump_child_devices(void) static void dump_child_devices(int length)
{ {
struct bdb_block *block; struct bdb_block *block;
struct bdb_child_devices *child_devs; struct bdb_child_devices *child_devs;
struct child_device_config *child; struct child_device_config *child;
int i; int i;
block = find_section(BDB_CHILD_DEVICE_TABLE); block = find_section(BDB_CHILD_DEVICE_TABLE, length);
if (!block) { if (!block) {
printf("No child device table found\n"); printf("No child device table found\n");
return; return;
@ -408,12 +412,12 @@ static void dump_child_devices(void)
free(block); free(block);
} }
static void dump_lvds_options(void) static void dump_lvds_options(int length)
{ {
struct bdb_block *block; struct bdb_block *block;
struct bdb_lvds_options *options; struct bdb_lvds_options *options;
block = find_section(BDB_LVDS_OPTIONS); block = find_section(BDB_LVDS_OPTIONS, length);
if (!block) { if (!block) {
printf("No LVDS options block\n"); printf("No LVDS options block\n");
return; return;
@ -437,7 +441,7 @@ static void dump_lvds_options(void)
free(block); free(block);
} }
static void dump_lvds_ptr_data(void) static void dump_lvds_ptr_data(int length)
{ {
struct bdb_block *block; struct bdb_block *block;
struct bdb_lvds_lfp_data *lvds_data; struct bdb_lvds_lfp_data *lvds_data;
@ -446,14 +450,14 @@ static void dump_lvds_ptr_data(void)
struct bdb_lvds_lfp_data_entry *entry; struct bdb_lvds_lfp_data_entry *entry;
int lfp_data_size; int lfp_data_size;
block = find_section(BDB_LVDS_LFP_DATA_PTRS); block = find_section(BDB_LVDS_LFP_DATA_PTRS, length);
if (!block) { if (!block) {
printf("No LFP data pointers block\n"); printf("No LFP data pointers block\n");
return; return;
} }
ptrs = block->data; ptrs = block->data;
block = find_section(BDB_LVDS_LFP_DATA); block = find_section(BDB_LVDS_LFP_DATA, length);
if (!block) { if (!block) {
printf("No LVDS data block\n"); printf("No LVDS data block\n");
return; return;
@ -476,7 +480,7 @@ static void dump_lvds_ptr_data(void)
free(block); free(block);
} }
static void dump_lvds_data(void) static void dump_lvds_data(int length)
{ {
struct bdb_block *block; struct bdb_block *block;
struct bdb_lvds_lfp_data *lvds_data; struct bdb_lvds_lfp_data *lvds_data;
@ -488,7 +492,7 @@ static void dump_lvds_data(void)
float clock; float clock;
int lfp_data_size, dvo_offset; int lfp_data_size, dvo_offset;
block = find_section(BDB_LVDS_LFP_DATA_PTRS); block = find_section(BDB_LVDS_LFP_DATA_PTRS, length);
if (!block) { if (!block) {
printf("No LVDS ptr block\n"); printf("No LVDS ptr block\n");
return; return;
@ -500,7 +504,7 @@ static void dump_lvds_data(void)
ptrs->ptr[0].dvo_timing_offset - ptrs->ptr[0].fp_timing_offset; ptrs->ptr[0].dvo_timing_offset - ptrs->ptr[0].fp_timing_offset;
free(block); free(block);
block = find_section(BDB_LVDS_LFP_DATA); block = find_section(BDB_LVDS_LFP_DATA, length);
if (!block) { if (!block) {
printf("No LVDS data block\n"); printf("No LVDS data block\n");
return; return;
@ -559,12 +563,12 @@ static void dump_lvds_data(void)
free(block); free(block);
} }
static void dump_driver_feature(void) static void dump_driver_feature(int length)
{ {
struct bdb_block *block; struct bdb_block *block;
struct bdb_driver_feature *feature; struct bdb_driver_feature *feature;
block = find_section(BDB_DRIVER_FEATURES); block = find_section(BDB_DRIVER_FEATURES, length);
if (!block) { if (!block) {
printf("No Driver feature data block\n"); printf("No Driver feature data block\n");
return; return;
@ -635,13 +639,13 @@ static void dump_driver_feature(void)
free(block); free(block);
} }
static void dump_edp(void) static void dump_edp(int length)
{ {
struct bdb_block *block; struct bdb_block *block;
struct bdb_edp *edp; struct bdb_edp *edp;
int bpp; int bpp;
block = find_section(BDB_EDP); block = find_section(BDB_EDP, length);
if (!block) { if (!block) {
printf("No EDP data block\n"); printf("No EDP data block\n");
return; return;
@ -751,13 +755,13 @@ print_detail_timing_data(struct lvds_dvo_timing2 *dvo_timing)
printf("\tclock: %d\n", dvo_timing->clock * 10); printf("\tclock: %d\n", dvo_timing->clock * 10);
} }
static void dump_sdvo_panel_dtds(void) static void dump_sdvo_panel_dtds(int length)
{ {
struct bdb_block *block; struct bdb_block *block;
struct lvds_dvo_timing2 *dvo_timing; struct lvds_dvo_timing2 *dvo_timing;
int n, count; int n, count;
block = find_section(BDB_SDVO_PANEL_DTDS); block = find_section(BDB_SDVO_PANEL_DTDS, length);
if (!block) { if (!block) {
printf("No SDVO panel dtds block\n"); printf("No SDVO panel dtds block\n");
return; return;
@ -774,12 +778,12 @@ static void dump_sdvo_panel_dtds(void)
free(block); free(block);
} }
static void dump_sdvo_lvds_options(void) static void dump_sdvo_lvds_options(int length)
{ {
struct bdb_block *block; struct bdb_block *block;
struct bdb_sdvo_lvds_options *options; struct bdb_sdvo_lvds_options *options;
block = find_section(BDB_SDVO_LVDS_OPTIONS); block = find_section(BDB_SDVO_LVDS_OPTIONS, length);
if (!block) { if (!block) {
printf("No SDVO LVDS options block\n"); printf("No SDVO LVDS options block\n");
return; return;
@ -899,6 +903,11 @@ int main(int argc, char **argv)
printf("VBT vers: %d.%d\n", vbt->version / 100, vbt->version % 100); printf("VBT vers: %d.%d\n", vbt->version / 100, vbt->version % 100);
bdb_off = vbt_off + vbt->bdb_offset; bdb_off = vbt_off + vbt->bdb_offset;
if (bdb_off >= finfo.st_size - sizeof(struct bdb_header)) {
printf("Invalid VBT found, BDB points beyond end of data block\n");
return 1;
}
bdb = (struct bdb_header *)(VBIOS + bdb_off); bdb = (struct bdb_header *)(VBIOS + bdb_off);
strncpy(signature, (char *)bdb->signature, 16); strncpy(signature, (char *)bdb->signature, 16);
signature[16] = 0; signature[16] = 0;
@ -907,7 +916,7 @@ int main(int argc, char **argv)
printf("Available sections: "); printf("Available sections: ");
for (i = 0; i < 256; i++) { for (i = 0; i < 256; i++) {
block = find_section(i); block = find_section(i, finfo.st_size);
if (!block) if (!block)
continue; continue;
printf("%d ", i); printf("%d ", i);
@ -920,19 +929,19 @@ int main(int argc, char **argv)
if (devid == -1) if (devid == -1)
printf("Warning: could not find PCI device ID!\n"); printf("Warning: could not find PCI device ID!\n");
dump_general_features(); dump_general_features(finfo.st_size);
dump_general_definitions(); dump_general_definitions(finfo.st_size);
dump_child_devices(); dump_child_devices(finfo.st_size);
dump_lvds_options(); dump_lvds_options(finfo.st_size);
dump_lvds_data(); dump_lvds_data(finfo.st_size);
dump_lvds_ptr_data(); dump_lvds_ptr_data(finfo.st_size);
dump_backlight_info(); dump_backlight_info(finfo.st_size);
dump_sdvo_lvds_options(); dump_sdvo_lvds_options(finfo.st_size);
dump_sdvo_panel_dtds(); dump_sdvo_panel_dtds(finfo.st_size);
dump_driver_feature(); dump_driver_feature(finfo.st_size);
dump_edp(); dump_edp(finfo.st_size);
return 0; return 0;
} }