]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
drm/amdgpu: bound SR-IOV RAS CPER dump parsing against used_size
authorChenglei Xie <Chenglei.Xie@amd.com>
Mon, 11 May 2026 19:24:29 +0000 (15:24 -0400)
committerAlex Deucher <alexander.deucher@amd.com>
Wed, 27 May 2026 14:48:27 +0000 (10:48 -0400)
The VF copies a PF-provided CPER telemetry blob and walks records using
cper_dump->count and each entry's record_length. count is u64 while the
loop used u32, so a large count could loop indefinitely. record_length was
not limited to the kmemdup'd region, so the first iteration could read far
past the allocation; record_length == 0 could spin forever on the same
entry. Together that allowed a malicious hypervisor to leak heap past the
blob into the CPER ring or hang the guest.

Require used_size to cover the fixed header before buf and stay within the
telemetry cap. Track remaining bytes in buf, cap iterations with u64 and
CPER_MAX_ALLOWED_COUNT, and reject record_length outside
[sizeof(cper_hdr), remaining] before writing to the ring.

Signed-off-by: Chenglei Xie <Chenglei.Xie@amd.com>
Reviewed-by: YiPeng Chai <YiPeng.Chai@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c

index 3fcbd722ee3b447f6705b145d535d791b24809a1..229eb6219de01080948cb729570adabee6d4dbd5 100644 (file)
@@ -1804,13 +1804,15 @@ amdgpu_virt_write_cpers_to_ring(struct amdgpu_device *adev,
        struct amd_sriov_ras_cper_dump *cper_dump = NULL;
        struct cper_hdr *entry = NULL;
        struct amdgpu_ring *ring = &adev->cper.ring_buf;
-       uint32_t checksum, used_size, i;
+       uint32_t checksum, used_size;
+       u64 remaining, cnt, i;
        int ret = 0;
 
        checksum = host_telemetry->header.checksum;
        used_size = host_telemetry->header.used_size;
 
-       if (used_size > (AMD_SRIOV_MSG_RAS_TELEMETRY_SIZE_KB_V1 << 10))
+       if (used_size < offsetof(struct amd_sriov_ras_cper_dump, buf) ||
+           used_size > (AMD_SRIOV_MSG_RAS_TELEMETRY_SIZE_KB_V1 << 10))
                return -EINVAL;
 
        cper_dump = kmemdup(&host_telemetry->body.cper_dump, used_size, GFP_KERNEL);
@@ -1835,11 +1837,19 @@ amdgpu_virt_write_cpers_to_ring(struct amdgpu_device *adev,
        }
 
        entry = (struct cper_hdr *)&cper_dump->buf[0];
+       remaining = (u64)used_size - offsetof(struct amd_sriov_ras_cper_dump, buf);
+       cnt = min_t(u64, cper_dump->count, CPER_MAX_ALLOWED_COUNT);
+
+       for (i = 0; i < cnt; i++) {
+               if (entry->record_length < sizeof(struct cper_hdr) ||
+                   entry->record_length > remaining) {
+                       ret = -EINVAL;
+                       goto out;
+               }
 
-       for (i = 0; i < cper_dump->count; i++) {
                amdgpu_cper_ring_write(ring, entry, entry->record_length);
-               entry = (struct cper_hdr *)((char *)entry +
-                                           entry->record_length);
+               remaining -= entry->record_length;
+               entry = (struct cper_hdr *)((char *)entry + entry->record_length);
        }
 
        if (cper_dump->overflow_count)