DragonFlyBSD Kernel Audit
DF-1134 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/dev/drm/amd/amdgpu/gfx_v8_0.c b/sys/dev/drm/amd/amdgpu/gfx_v8_0.c
--- a/sys/dev/drm/amd/amdgpu/gfx_v8_0.c
+++ b/sys/dev/drm/amd/amdgpu/gfx_v8_0.c
@@ -1305,6 +1305,7 @@
 	int me, i, max_me = 4;
 	u32 bo_offset = 0;
 	u32 table_offset, table_size;
+	u32 fw_dwords;
 
 	if (adev->asic_type == CHIP_CARRIZO)
 		max_me = 5;
@@ -1318,6 +1319,7 @@
 			fw_data = (const __le32 *)
 				(adev->gfx.ce_fw->data +
 				 le32_to_cpu(hdr->header.ucode_array_offset_bytes));
+			fw_dwords = adev->gfx.ce_fw->datasize / 4;
 			table_offset = le32_to_cpu(hdr->jt_offset);
 			table_size = le32_to_cpu(hdr->jt_size);
 		} else if (me == 1) {
@@ -1326,6 +1328,7 @@
 			fw_data = (const __le32 *)
 				(adev->gfx.pfp_fw->data +
 				 le32_to_cpu(hdr->header.ucode_array_offset_bytes));
+			fw_dwords = adev->gfx.pfp_fw->datasize / 4;
 			table_offset = le32_to_cpu(hdr->jt_offset);
 			table_size = le32_to_cpu(hdr->jt_size);
 		} else if (me == 2) {
@@ -1334,6 +1337,7 @@
 			fw_data = (const __le32 *)
 				(adev->gfx.me_fw->data +
 				 le32_to_cpu(hdr->header.ucode_array_offset_bytes));
+			fw_dwords = adev->gfx.me_fw->datasize / 4;
 			table_offset = le32_to_cpu(hdr->jt_offset);
 			table_size = le32_to_cpu(hdr->jt_size);
 		} else if (me == 3) {
@@ -1342,6 +1346,7 @@
 			fw_data = (const __le32 *)
 				(adev->gfx.mec_fw->data +
 				 le32_to_cpu(hdr->header.ucode_array_offset_bytes));
+			fw_dwords = adev->gfx.mec_fw->datasize / 4;
 			table_offset = le32_to_cpu(hdr->jt_offset);
 			table_size = le32_to_cpu(hdr->jt_size);
 		} else  if (me == 4) {
@@ -1350,10 +1355,22 @@
 			fw_data = (const __le32 *)
 				(adev->gfx.mec2_fw->data +
 				 le32_to_cpu(hdr->header.ucode_array_offset_bytes));
+			fw_dwords = adev->gfx.mec2_fw->datasize / 4;
 			table_offset = le32_to_cpu(hdr->jt_offset);
 			table_size = le32_to_cpu(hdr->jt_size);
 		}
 
+		/* jt_offset/jt_size come straight from the firmware header
+		 * with no validation.  Reject values that would read past
+		 * the firmware blob (OOB read) or write past the cp_table BO
+		 * (OOB write). */
+		if (table_size > fw_dwords ||
+		    table_offset > fw_dwords - table_size ||
+		    table_size > adev->gfx.rlc.cp_table_size / 4 ||
+		    bo_offset > adev->gfx.rlc.cp_table_size / 4 - table_size) {
+			dev_warn(adev->dev, "invalid CP jump table offset/size, skipping\n");
+			continue;
+		}
 		for (i = 0; i < table_size; i ++) {
 			dst_ptr[bo_offset + i] =
 				cpu_to_le32(le32_to_cpu(fw_data[table_offset + i]));