⬢ DragonFlyBSD Kernel Audit
DF-4862 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/drm/amd/amdgpu/amdgpu_cgs.c
+++ b/sys/dev/drm/amd/amdgpu/amdgpu_cgs.c
@@ -212,6 +212,11 @@
 		struct amdgpu_firmware_info *ucode;
 
 		id = fw_type_convert(cgs_device, type);
+		if (id >= AMDGPU_UCODE_ID_MAXIMUM)
+			/* fw_type_convert's default is exactly the array size:
+			 * indexing ucode[id] reads one element past the end
+			 * (ucode[MAXIMUM].fw aliases firmware.fw_buf). */
+			return -EINVAL;
 		ucode = &adev->firmware.ucode[id];
 		if (ucode->fw == NULL)
 			return -EINVAL;
@@ -231,8 +236,19 @@
 		info->mc_addr = gpu_addr;
 		info->version = (uint16_t)le32_to_cpu(header->header.ucode_version);
 
-		if (CGS_UCODE_ID_CP_MEC == type)
+		if (CGS_UCODE_ID_CP_MEC == type) {
+			/* jt_offset is an unchecked header dword (up to ~4 GB
+			 * after <<2).  In a sane gfx header the JT payload
+			 * lives inside the declared ucode image, so the window
+			 * start must be <= ucode_size_bytes (== the staged copy
+			 * size here).  Unbounded, the SMU DMA engine
+			 * (PPSMC_MSG_LoadUcodes via the smu7 TOC,
+			 * entry->data_size_byte) reads far beyond the staged
+			 * fw_buf window. */
+			if (((uint64_t)le32_to_cpu(header->jt_offset) << 2) > data_size)
+				return -EINVAL;
 			info->image_size = le32_to_cpu(header->jt_offset) << 2;
+		}
 
 		info->fw_version = amdgpu_get_firmware_version(cgs_device, type);
 		info->feature_version = (uint16_t)le32_to_cpu(header->ucode_feature_version);