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_size; 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_size = size; if (rdev->bios == NULL) { iounmap(bios); return false; @@ -96,6 +97,7 @@ return false; } rdev->bios = kzalloc(size, GFP_KERNEL); + rdev->bios_size = 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_size = 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_size = 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_size = vhdr->ImageLength; memcpy(rdev->bios, &vbios->VbiosContent, vhdr->ImageLength); ret = !!rdev->bios; diff --git a/sys/dev/drm/radeon/rv6xx_dpm.c b/sys/dev/drm/radeon/rv6xx_dpm.c --- a/sys/dev/drm/radeon/rv6xx_dpm.c +++ b/sys/dev/drm/radeon/rv6xx_dpm.c @@ -1893,7 +1893,20 @@ if (!rdev->pm.dpm.ps) return -ENOMEM; + /* data_offset and the per-state u16/u8 offsets below all come from + * the VBIOS and were not validated, enabling OOB heap reads from a + * crafted image. */ + if (data_offset == 0 || data_offset >= rdev->bios_size) + goto inval; + for (i = 0; i < power_info->pplib.ucNumStates; i++) { + u32 state_ofs, nonclk_ofs, clk_ofs; + state_ofs = data_offset + + le16_to_cpu(power_info->pplib.usStateArrayOffset) + + (u32)i * power_info->pplib.ucStateEntrySize; + if (state_ofs >= rdev->bios_size || + state_ofs + sizeof(union pplib_power_state) > rdev->bios_size) + goto inval; power_state = (union pplib_power_state *) (mode_info->atom_context->bios + data_offset + le16_to_cpu(power_info->pplib.usStateArrayOffset) + @@ -1903,6 +1916,13 @@ le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset) + (power_state->v1.ucNonClockStateIndex * power_info->pplib.ucNonClockSize)); + nonclk_ofs = data_offset + + le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset) + + (u32)power_state->v1.ucNonClockStateIndex * + power_info->pplib.ucNonClockSize; + if (nonclk_ofs >= rdev->bios_size || + nonclk_ofs + sizeof(*non_clock_info) > rdev->bios_size) + goto inval; if (power_info->pplib.ucStateEntrySize - 1) { u8 *idx; ps = kzalloc(sizeof(struct rv6xx_ps), GFP_KERNEL); @@ -1914,11 +1934,21 @@ rv6xx_parse_pplib_non_clock_info(rdev, &rdev->pm.dpm.ps[i], non_clock_info); idx = (u8 *)&power_state->v1.ucClockStateIndices[0]; + if ((u8 *)idx + (power_info->pplib.ucStateEntrySize - 1) > + rdev->bios + rdev->bios_size) + goto inval; for (j = 0; j < (power_info->pplib.ucStateEntrySize - 1); j++) { clock_info = (union pplib_clock_info *) (mode_info->atom_context->bios + data_offset + le16_to_cpu(power_info->pplib.usClockInfoArrayOffset) + (idx[j] * power_info->pplib.ucClockInfoSize)); + clk_ofs = data_offset + + le16_to_cpu(power_info->pplib.usClockInfoArrayOffset) + + (u32)idx[j] * + power_info->pplib.ucClockInfoSize; + if (clk_ofs >= rdev->bios_size || + clk_ofs + sizeof(*clock_info) > rdev->bios_size) + goto inval; rv6xx_parse_pplib_clock_info(rdev, &rdev->pm.dpm.ps[i], j, clock_info); @@ -1927,6 +1957,12 @@ } rdev->pm.dpm.num_ps = power_info->pplib.ucNumStates; return 0; + +inval: + DRM_ERROR("rv6xx: VBIOS power table offset out of bounds\n"); + kfree(rdev->pm.dpm.ps); + rdev->pm.dpm.ps = NULL; + return -EINVAL; } int rv6xx_dpm_init(struct radeon_device *rdev)