diff --git a/sys/dev/drm/amd/amdgpu/amdgpu_ucode.c b/sys/dev/drm/amd/amdgpu/amdgpu_ucode.c --- a/sys/dev/drm/amd/amdgpu/amdgpu_ucode.c +++ b/sys/dev/drm/amd/amdgpu/amdgpu_ucode.c @@ -334,6 +334,17 @@ cp_hdr = (const struct gfx_firmware_header_v1_0 *)ucode->fw->data; dmcu_hdr = (const struct dmcu_firmware_header_v1_0 *)ucode->fw->data; + const struct firmware *fw = ucode->fw; + uint32_t fw_size = fw->datasize; + uint32_t arr_off = le32_to_cpu(header->ucode_array_offset_bytes); + uint32_t ucode_size_bytes = le32_to_cpu(header->ucode_size_bytes); + + /* DF-1947: the firmware source window for every memcpy below is + * fw->data[arr_off .. arr_off+ucode_size). Validate it once before + * any branch uses it. */ + if (arr_off > fw_size || ucode_size_bytes > fw_size - arr_off) + return -EINVAL; + if (adev->firmware.load_type != AMDGPU_FW_LOAD_PSP || (ucode->ucode_id != AMDGPU_UCODE_ID_CP_MEC1 && ucode->ucode_id != AMDGPU_UCODE_ID_CP_MEC2 && @@ -344,40 +355,52 @@ ucode->ucode_id != AMDGPU_UCODE_ID_RLC_RESTORE_LIST_SRM_MEM && ucode->ucode_id != AMDGPU_UCODE_ID_DMCU_ERAM && ucode->ucode_id != AMDGPU_UCODE_ID_DMCU_INTV)) { - ucode->ucode_size = le32_to_cpu(header->ucode_size_bytes); + ucode->ucode_size = ucode_size_bytes; - memcpy(ucode->kaddr, (void *)((uint8_t *)ucode->fw->data + - le32_to_cpu(header->ucode_array_offset_bytes)), + memcpy(ucode->kaddr, (void *)((uint8_t *)fw->data + arr_off), ucode->ucode_size); } else if (ucode->ucode_id == AMDGPU_UCODE_ID_CP_MEC1 || ucode->ucode_id == AMDGPU_UCODE_ID_CP_MEC2) { - ucode->ucode_size = le32_to_cpu(header->ucode_size_bytes) - - le32_to_cpu(cp_hdr->jt_size) * 4; + uint32_t jt_size = le32_to_cpu(cp_hdr->jt_size); + /* DF-1947: guard the uint32 subtraction against underflow. */ + if (jt_size > UINT32_MAX / 4 || jt_size * 4 > ucode_size_bytes) + return -EINVAL; + ucode->ucode_size = ucode_size_bytes - jt_size * 4; - memcpy(ucode->kaddr, (void *)((uint8_t *)ucode->fw->data + - le32_to_cpu(header->ucode_array_offset_bytes)), + memcpy(ucode->kaddr, (void *)((uint8_t *)fw->data + arr_off), ucode->ucode_size); } else if (ucode->ucode_id == AMDGPU_UCODE_ID_CP_MEC1_JT || ucode->ucode_id == AMDGPU_UCODE_ID_CP_MEC2_JT) { - ucode->ucode_size = le32_to_cpu(cp_hdr->jt_size) * 4; + uint32_t jt_size = le32_to_cpu(cp_hdr->jt_size); + uint32_t jt_off = le32_to_cpu(cp_hdr->jt_offset); + if (jt_size > UINT32_MAX / 4 || jt_off > UINT32_MAX / 4) + return -EINVAL; + ucode->ucode_size = jt_size * 4; + if (arr_off > fw_size || + jt_off * 4 > fw_size - arr_off || + ucode->ucode_size > (fw_size - arr_off) - jt_off * 4) + return -EINVAL; - memcpy(ucode->kaddr, (void *)((uint8_t *)ucode->fw->data + - le32_to_cpu(header->ucode_array_offset_bytes) + - le32_to_cpu(cp_hdr->jt_offset) * 4), + memcpy(ucode->kaddr, (void *)((uint8_t *)fw->data + + arr_off + jt_off * 4), ucode->ucode_size); } else if (ucode->ucode_id == AMDGPU_UCODE_ID_DMCU_ERAM) { - ucode->ucode_size = le32_to_cpu(header->ucode_size_bytes) - - le32_to_cpu(dmcu_hdr->intv_size_bytes); + uint32_t intv_size = le32_to_cpu(dmcu_hdr->intv_size_bytes); + /* DF-1947: guard the uint32 subtraction against underflow. */ + if (intv_size > ucode_size_bytes) + return -EINVAL; + ucode->ucode_size = ucode_size_bytes - intv_size; - memcpy(ucode->kaddr, (void *)((uint8_t *)ucode->fw->data + - le32_to_cpu(header->ucode_array_offset_bytes)), + memcpy(ucode->kaddr, (void *)((uint8_t *)fw->data + arr_off), ucode->ucode_size); } else if (ucode->ucode_id == AMDGPU_UCODE_ID_DMCU_INTV) { - ucode->ucode_size = le32_to_cpu(dmcu_hdr->intv_size_bytes); + uint32_t intv_off = le32_to_cpu(dmcu_hdr->intv_offset_bytes); + uint32_t intv_size = le32_to_cpu(dmcu_hdr->intv_size_bytes); + if (intv_off > fw_size || intv_size > fw_size - intv_off) + return -EINVAL; + ucode->ucode_size = intv_size; - memcpy(ucode->kaddr, (void *)((uint8_t *)ucode->fw->data + - le32_to_cpu(header->ucode_array_offset_bytes) + - le32_to_cpu(dmcu_hdr->intv_offset_bytes)), + memcpy(ucode->kaddr, (void *)((uint8_t *)fw->data + intv_off), ucode->ucode_size); } else if (ucode->ucode_id == AMDGPU_UCODE_ID_RLC_RESTORE_LIST_CNTL) { ucode->ucode_size = adev->gfx.rlc.save_restore_list_cntl_size_bytes; @@ -407,15 +430,31 @@ if (NULL == ucode->fw) return 0; - comm_hdr = (const struct common_firmware_header *)ucode->fw->data; - header = (const struct gfx_firmware_header_v1_0 *)ucode->fw->data; + const struct firmware *fw = ucode->fw; + uint32_t fw_size = fw->datasize; + uint32_t jt_size, jt_off, arr_off, ucode_size_bytes; + + comm_hdr = (const struct common_firmware_header *)fw->data; + header = (const struct gfx_firmware_header_v1_0 *)fw->data; + jt_size = le32_to_cpu(header->jt_size); + jt_off = le32_to_cpu(header->jt_offset); + arr_off = le32_to_cpu(comm_hdr->ucode_array_offset_bytes); + ucode_size_bytes = le32_to_cpu(comm_hdr->ucode_size_bytes); + + /* DF-1947: jt_size*4 must not wrap, must fit in source window, and + * the source window itself must lie in fw->data. */ + if (jt_size > UINT32_MAX / 4 || jt_off > UINT32_MAX / 4) + return -EINVAL; + if (arr_off > fw_size || ucode_size_bytes > fw_size - arr_off) + return -EINVAL; + if (jt_off * 4 > ucode_size_bytes || + jt_size * 4 > ucode_size_bytes - jt_off * 4) + return -EINVAL; + dst_addr = ucode->kaddr + - ALIGN(le32_to_cpu(comm_hdr->ucode_size_bytes), - PAGE_SIZE); - src_addr = (uint8_t *)ucode->fw->data + - le32_to_cpu(comm_hdr->ucode_array_offset_bytes) + - (le32_to_cpu(header->jt_offset) * 4); - memcpy(dst_addr, src_addr, le32_to_cpu(header->jt_size) * 4); + ALIGN(ucode_size_bytes, PAGE_SIZE); + src_addr = (uint8_t *)fw->data + arr_off + jt_off * 4; + memcpy(dst_addr, src_addr, jt_size * 4); return 0; }