From: Xiang Liu Date: Thu, 9 Apr 2026 09:10:21 +0000 (+0800) Subject: drm/amdgpu: fix CPER ring header parsing X-Git-Tag: v7.1-rc1~24^2~3^2~55 X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=b8939bd764c9c8bf6488dc0d71d9c718c25d8cfc;p=thirdparty%2Fkernel%2Fstable.git drm/amdgpu: fix CPER ring header parsing amdgpu_cper_ring_get_ent_sz() parses CPER headers directly from the circular ring buffer to determine the current entry size. When the ring is full and the write pointer lands near the end of the buffer, the header can wrap across the ring boundary. The existing code treats the 4-byte CPER signature as a C string and uses strcmp() on in-ring binary data, then reads record_length through a direct struct pointer cast. Both assumptions are unsafe for wrapped entries and can read past the end of the ring mapping. Fix the parser by comparing the signature as raw bytes and by copying the header into a local buffer before reading record_length, handling wraparound explicitly in both cases. This avoids out-of-bounds reads in amdgpu_cper_ring_get_ent_sz() when the CPER ring is full or the current entry starts at the tail of the ring. Signed-off-by: Xiang Liu Reviewed-by: Tao Zhou Signed-off-by: Alex Deucher --- diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.c index c72c345334d01..4e6e390854e6e 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.c @@ -32,6 +32,8 @@ static const guid_t BOOT = BOOT_TYPE; static const guid_t CRASHDUMP = AMD_CRASHDUMP; static const guid_t RUNTIME = AMD_GPU_NONSTANDARD_ERROR; +#define CPER_SIGNATURE_SZ (sizeof(((struct cper_hdr *)0)->signature)) + static void __inc_entry_length(struct cper_hdr *hdr, uint32_t size) { hdr->record_length += size; @@ -425,23 +427,40 @@ int amdgpu_cper_generate_ce_records(struct amdgpu_device *adev, static bool amdgpu_cper_is_hdr(struct amdgpu_ring *ring, u64 pos) { - struct cper_hdr *chdr; + char signature[CPER_SIGNATURE_SZ]; + + if ((pos << 2) >= ring->ring_size) + return false; - chdr = (struct cper_hdr *)&(ring->ring[pos]); - return strcmp(chdr->signature, "CPER") ? false : true; + if ((pos << 2) + CPER_SIGNATURE_SZ <= ring->ring_size) { + memcpy(signature, &ring->ring[pos], CPER_SIGNATURE_SZ); + } else { + u32 chunk = ring->ring_size - (pos << 2); + + memcpy(signature, &ring->ring[pos], chunk); + memcpy(signature + chunk, ring->ring, CPER_SIGNATURE_SZ - chunk); + } + + return !memcmp(signature, "CPER", CPER_SIGNATURE_SZ); } static u32 amdgpu_cper_ring_get_ent_sz(struct amdgpu_ring *ring, u64 pos) { - struct cper_hdr *chdr; + struct cper_hdr chdr; u64 p; u32 chunk, rec_len = 0; - chdr = (struct cper_hdr *)&(ring->ring[pos]); chunk = ring->ring_size - (pos << 2); - if (!strcmp(chdr->signature, "CPER")) { - rec_len = chdr->record_length; + if (amdgpu_cper_is_hdr(ring, pos)) { + if (chunk >= sizeof(chdr)) { + memcpy(&chdr, &ring->ring[pos], sizeof(chdr)); + } else { + memcpy(&chdr, &ring->ring[pos], chunk); + memcpy((u8 *)&chdr + chunk, ring->ring, sizeof(chdr) - chunk); + } + + rec_len = chdr.record_length; goto calc; } @@ -450,8 +469,7 @@ static u32 amdgpu_cper_ring_get_ent_sz(struct amdgpu_ring *ring, u64 pos) goto calc; for (p = pos + 1; p <= ring->buf_mask; p++) { - chdr = (struct cper_hdr *)&(ring->ring[p]); - if (!strcmp(chdr->signature, "CPER")) { + if (amdgpu_cper_is_hdr(ring, p)) { rec_len = (p - pos) << 2; goto calc; }