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)
