Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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 <xiang.liu@amd.com>
Reviewed-by: Tao Zhou <tao.zhou1@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>

authored by

Xiang Liu and committed by
Alex Deucher
b8939bd7 d42d3012

+27 -9
+27 -9
drivers/gpu/drm/amd/amdgpu/amdgpu_cper.c
··· 32 32 static const guid_t CRASHDUMP = AMD_CRASHDUMP; 33 33 static const guid_t RUNTIME = AMD_GPU_NONSTANDARD_ERROR; 34 34 35 + #define CPER_SIGNATURE_SZ (sizeof(((struct cper_hdr *)0)->signature)) 36 + 35 37 static void __inc_entry_length(struct cper_hdr *hdr, uint32_t size) 36 38 { 37 39 hdr->record_length += size; ··· 427 425 428 426 static bool amdgpu_cper_is_hdr(struct amdgpu_ring *ring, u64 pos) 429 427 { 430 - struct cper_hdr *chdr; 428 + char signature[CPER_SIGNATURE_SZ]; 431 429 432 - chdr = (struct cper_hdr *)&(ring->ring[pos]); 433 - return strcmp(chdr->signature, "CPER") ? false : true; 430 + if ((pos << 2) >= ring->ring_size) 431 + return false; 432 + 433 + if ((pos << 2) + CPER_SIGNATURE_SZ <= ring->ring_size) { 434 + memcpy(signature, &ring->ring[pos], CPER_SIGNATURE_SZ); 435 + } else { 436 + u32 chunk = ring->ring_size - (pos << 2); 437 + 438 + memcpy(signature, &ring->ring[pos], chunk); 439 + memcpy(signature + chunk, ring->ring, CPER_SIGNATURE_SZ - chunk); 440 + } 441 + 442 + return !memcmp(signature, "CPER", CPER_SIGNATURE_SZ); 434 443 } 435 444 436 445 static u32 amdgpu_cper_ring_get_ent_sz(struct amdgpu_ring *ring, u64 pos) 437 446 { 438 - struct cper_hdr *chdr; 447 + struct cper_hdr chdr; 439 448 u64 p; 440 449 u32 chunk, rec_len = 0; 441 450 442 - chdr = (struct cper_hdr *)&(ring->ring[pos]); 443 451 chunk = ring->ring_size - (pos << 2); 444 452 445 - if (!strcmp(chdr->signature, "CPER")) { 446 - rec_len = chdr->record_length; 453 + if (amdgpu_cper_is_hdr(ring, pos)) { 454 + if (chunk >= sizeof(chdr)) { 455 + memcpy(&chdr, &ring->ring[pos], sizeof(chdr)); 456 + } else { 457 + memcpy(&chdr, &ring->ring[pos], chunk); 458 + memcpy((u8 *)&chdr + chunk, ring->ring, sizeof(chdr) - chunk); 459 + } 460 + 461 + rec_len = chdr.record_length; 447 462 goto calc; 448 463 } 449 464 ··· 469 450 goto calc; 470 451 471 452 for (p = pos + 1; p <= ring->buf_mask; p++) { 472 - chdr = (struct cper_hdr *)&(ring->ring[p]); 473 - if (!strcmp(chdr->signature, "CPER")) { 453 + if (amdgpu_cper_is_hdr(ring, p)) { 474 454 rec_len = (p - pos) << 2; 475 455 goto calc; 476 456 }