DF-1250 / fix.diff
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-1250/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 @@ -371,7 +371,19 @@ return false; raw = rdev->bios + edid_info; + /* DF-1250: raw[0x7e] is attacker-controlled (malicious VBIOS). Make + * sure the EDID block stays inside the BIOS allocation before we + * dereference raw[0x7e] and memcpy it out; otherwise a small (e.g. + * 512-byte) BIOS slab with raw[0x7e]=0xff leaks ~32 KB of adjacent + * kernel heap via the memcpy below. */ + if (rdev->bios_length == 0 || + (uint64_t)edid_info + 0x7f > rdev->bios_length) + return false; size = EDID_LENGTH * (raw[0x7e] + 1); + if ((uint64_t)edid_info + size > rdev->bios_length) { + dev_warn(rdev->dev, "hardcoded EDID table overruns BIOS image\n"); + return false; + } edid = kmalloc(size, M_DRM, M_WAITOK); if (edid == NULL) return false; |