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]));