DF-1391 / fix.diff
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | 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) |