diff --git a/sys/dev/drm/radeon/radeon.h b/sys/dev/drm/radeon/radeon.h --- a/sys/dev/drm/radeon/radeon.h +++ b/sys/dev/drm/radeon/radeon.h @@ -2331,6 +2331,7 @@ int disp_priority; /* BIOS */ uint8_t *bios; + uint32_t bios_length; /* DF-1251: allocation size of *bios, in bytes */ bool is_atom_bios; uint16_t bios_header_start; struct radeon_bo *stolen_vga_memory; diff --git a/sys/dev/drm/radeon/radeon_bios.c b/sys/dev/drm/radeon/radeon_bios.c --- a/sys/dev/drm/radeon/radeon_bios.c +++ b/sys/dev/drm/radeon/radeon_bios.c @@ -66,6 +66,7 @@ return false; } rdev->bios = kmalloc(size, M_DRM, M_WAITOK); + rdev->bios_length = size; if (rdev->bios == NULL) { iounmap(bios); return false; @@ -96,6 +97,7 @@ return false; } rdev->bios = kzalloc(size, GFP_KERNEL); + rdev->bios_length = size; if (rdev->bios == NULL) { vga_pci_unmap_bios(vga_dev, bios); return false; @@ -151,6 +153,7 @@ } rdev->bios = kmemdup(bios, size, GFP_KERNEL); + rdev->bios_length = size; pmap_unmapdev((vm_offset_t)bios, 256 * 1024); pci_write_config(bsddev, PCIR_BIOS, saved_rom_bar, 4); @@ -267,6 +270,7 @@ return false; rdev->bios = kmalloc(size, M_DRM, M_WAITOK); + rdev->bios_length = size; if (!rdev->bios) { DRM_ERROR("Unable to allocate bios\n"); return false; @@ -715,6 +719,7 @@ } rdev->bios = kmalloc(vhdr->ImageLength, M_DRM, M_WAITOK); + rdev->bios_length = vhdr->ImageLength; memcpy(rdev->bios, &vbios->VbiosContent, vhdr->ImageLength); ret = !!rdev->bios; diff --git a/sys/dev/drm/radeon/radeon_combios.c b/sys/dev/drm/radeon/radeon_combios.c --- a/sys/dev/drm/radeon/radeon_combios.c +++ b/sys/dev/drm/radeon/radeon_combios.c @@ -3007,7 +3007,12 @@ struct radeon_device *rdev = dev->dev_private; if (offset) { - while (RBIOS16(offset)) { + /* DF-1251: bound the walk against the BIOS allocation; a malicious + * VBIOS table with no in-bounds terminator otherwise walks RBIOS* + * past rdev->bios and feeds heap-sourced addr/val into WREG32. */ + while (rdev->bios_length != 0 && + (uint32_t)offset + 10 <= rdev->bios_length && + RBIOS16(offset)) { uint16_t cmd = ((RBIOS16(offset) & 0xe000) >> 13); uint32_t addr = (RBIOS16(offset) & 0x1fff); uint32_t val, and_mask, or_mask; @@ -3086,7 +3091,10 @@ struct radeon_device *rdev = dev->dev_private; if (offset) { - while (RBIOS8(offset)) { + /* DF-1251: bound the walk against the BIOS allocation. */ + while (rdev->bios_length != 0 && + (uint32_t)offset + 5 <= rdev->bios_length && + RBIOS8(offset)) { uint8_t cmd = ((RBIOS8(offset) & 0xc0) >> 6); uint8_t addr = (RBIOS8(offset) & 0x3f); uint32_t val, shift, tmp; @@ -3178,7 +3186,10 @@ uint32_t tmp; if (offset) { - uint8_t val = RBIOS8(offset); + uint8_t val = + (rdev->bios_length != 0 && + (uint32_t)offset < rdev->bios_length) ? RBIOS8(offset) : 0xff; + /* DF-1251: bound the walk against the BIOS allocation. */ while (val != 0xff) { offset++;