DragonFlyBSD Kernel Audit
DF-1163 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/dev/drm/amd/amdgpu/gfx_v7_0.c b/sys/dev/drm/amd/amdgpu/gfx_v7_0.c
--- a/sys/dev/drm/amd/amdgpu/gfx_v7_0.c
+++ b/sys/dev/drm/amdgpu/gfx_v7_0.c
@@ -3792,6 +3792,7 @@
 	int me, i, max_me = 4;
 	u32 bo_offset = 0;
 	u32 table_offset, table_size;
+	u32 fw_dwords, cp_dwords;
 
 	if (adev->asic_type == CHIP_KAVERI)
 		max_me = 5;
@@ -3801,6 +3802,7 @@
 
 	/* write the cp table buffer */
 	dst_ptr = adev->gfx.rlc.cp_table_ptr;
+	cp_dwords = adev->gfx.rlc.cp_table_size / 4;
 	for (me = 0; me < max_me; me++) {
 		if (me == 0) {
 			const struct gfx_firmware_header_v1_0 *hdr =
@@ -3808,6 +3810,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) {
@@ -3816,6 +3819,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) {
@@ -3824,6 +3828,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) {
@@ -3832,6 +3837,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 {
@@ -3840,10 +3846,24 @@
 			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 / heap info-leak) or write past the cp_table BO (OOB write).
+		 */
+		if (!table_size || table_size > fw_dwords ||
+		    table_offset > fw_dwords - table_size ||
+		    table_size > cp_dwords ||
+		    bo_offset > cp_dwords - 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]));