DragonFlyBSD Kernel Audit
DF-1176 / 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
@@ -688,13 +688,43 @@
 			le32_to_cpu(rlc_hdr->reg_list_format_separate_start);
 	adev->gfx.rlc.starting_offsets_start =
 			le32_to_cpu(rlc_hdr->starting_offsets_start);
+	/* DF-1176: reg_list_format_size_bytes and reg_list_size_bytes are
+	 * attacker-controlled u32 fields from the firmware header. Validate them
+	 * against fw->datasize and compute the allocation size in size_t to
+	 * prevent u32 wraparound -> undersized kmalloc -> heap overflow.
+	 * Also validate reg_list_format_array_offset_bytes and
+	 * reg_list_array_offset_bytes against fw->datasize to prevent OOB reads
+	 * past the firmware buffer. */
 	adev->gfx.rlc.reg_list_format_size_bytes =
 			le32_to_cpu(rlc_hdr->reg_list_format_size_bytes);
 	adev->gfx.rlc.reg_list_size_bytes =
 			le32_to_cpu(rlc_hdr->reg_list_size_bytes);
-	adev->gfx.rlc.register_list_format =
-			kmalloc(adev->gfx.rlc.reg_list_format_size_bytes +
-				adev->gfx.rlc.reg_list_size_bytes, M_DRM, GFP_KERNEL);
+
+	{
+		size_t rlf_sz = adev->gfx.rlc.reg_list_format_size_bytes;
+		size_t rl_sz  = adev->gfx.rlc.reg_list_size_bytes;
+		size_t total  = rlf_sz + rl_sz;
+		uint32_t rlf_off = le32_to_cpu(
+			rlc_hdr->reg_list_format_array_offset_bytes);
+		uint32_t rl_off  = le32_to_cpu(
+			rlc_hdr->reg_list_array_offset_bytes);
+		size_t fw_ds    = adev->gfx.rlc_fw->datasize;
+
+		/* size_t overflow check */
+		if (total < rlf_sz ||
+		    rlf_off > fw_ds || rlf_sz > fw_ds - rlf_off ||
+		    rl_off  > fw_ds || rl_sz  > fw_ds - rl_off) {
+			err = -EINVAL;
+			dev_err(adev->dev,
+				"invalid RLC reg_list sizes/off: fmt_sz=%u fmt_off=%u "
+				"list_sz=%u list_off=%u fw_ds=%zu\n",
+				adev->gfx.rlc.reg_list_format_size_bytes, rlf_off,
+				adev->gfx.rlc.reg_list_size_bytes, rl_off, fw_ds);
+			goto out;
+		}
+		adev->gfx.rlc.register_list_format =
+			kmalloc(total, M_DRM, GFP_KERNEL);
+	}
 	if (!adev->gfx.rlc.register_list_format) {
 		err = -ENOMEM;
 		goto out;