DragonFlyBSD Kernel Audit
DF-1177 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/dev/drm/amd/amdgpu/gfx_v9_0.c b/sys/dev/drm/amd/amdgpu/gfx_v9_0.c
--- a/sys/dev/drm/amd/amdgpu/gfx_v9_0.c
+++ b/sys/dev/drm/amd/amdgpu/gfx_v9_0.c
@@ -1109,13 +1109,45 @@
 			table_offset = le32_to_cpu(hdr->jt_offset);
 			table_size = le32_to_cpu(hdr->jt_size);
 		} else  if (me == 4) {
-			const struct gfx_firmware_header_v1_0 *hdr =
-				(const struct gfx_firmware_header_v1_0 *)adev->gfx.mec2_fw->data;
+			const struct gfx_firmware_header_v1_0 *hdr;
+			/* DF-1177: mec2_fw is optional -- init_microcode:744 sets it to
+			 * NULL when request_firmware fails.  Skip the me==4 iteration
+			 * in that case instead of dereferencing NULL here. */
+			if (adev->gfx.mec2_fw == NULL)
+				continue;
+			hdr = (const struct gfx_firmware_header_v1_0 *)
+				adev->gfx.mec2_fw->data;
 			fw_data = (const __le32 *)
 				(adev->gfx.mec2_fw->data +
 				 le32_to_cpu(hdr->header.ucode_array_offset_bytes));
 			table_offset = le32_to_cpu(hdr->jt_offset);
 			table_size = le32_to_cpu(hdr->jt_size);
+		} else {
+			continue;
+		}
+
+		/* DF-1177: validate jt_size against the remaining cp_table
+		 * capacity (16896 dwords minus the running offset) and the
+		 * firmware jt_offset+table_size against the firmware data size.
+		 * Without these checks a large jt_size writes past the cp_table
+		 * BO and reads past the firmware buffer. */
+		{
+			const struct firmware *_fw_me =
+				(me == 0) ? adev->gfx.ce_fw   :
+				(me == 1) ? adev->gfx.pfp_fw  :
+				(me == 2) ? adev->gfx.me_fw   :
+				(me == 3) ? adev->gfx.mec_fw  :
+						    adev->gfx.mec2_fw;
+			size_t _fw_ds = (_fw_me ? _fw_me->datasize : 0);
+			if (_fw_me == NULL ||
+			    table_size > 16896 - bo_offset ||
+			    (size_t)table_offset > _fw_ds ||
+			    (size_t)table_size > (_fw_ds - table_offset) / sizeof(uint32_t)) {
+				dev_warn(adev->dev,
+					 "rv_init_cp_jump_table: skipping me=%d jt_offset=%u jt_size=%u fw_ds=%zu\n",
+					 me, table_offset, table_size, _fw_ds);
+				continue;
+			}
 		}
 
 		for (i = 0; i < table_size; i ++) {