]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
drm/amd/ras: Fix CPER ring debugfs read overflow
authorXiang Liu <xiang.liu@amd.com>
Thu, 7 May 2026 12:56:15 +0000 (20:56 +0800)
committerAlex Deucher <alexander.deucher@amd.com>
Mon, 11 May 2026 21:54:28 +0000 (17:54 -0400)
The legacy CPER debugfs reader can reach the payload path without a
valid pointer snapshot. The remaining user byte count is also treated as
the ring occupancy in dwords, so reads past the header can copy more than
requested.

Take the CPER lock before sampling pointers. Resample rptr/wptr for
payload reads, bound the payload copy by available dwords and the
remaining user size, and advance the file position for each dword copied.

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>
(cherry picked from commit 1e40ef87ffdc291e05ccdade8b9170cc9c1c4249)

drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c

index 66e8a2f7afcf6f16b487e6ada4fb2118044b5689..d6bee5c30073dcda7c1b821922a3575c0e5f49cd 100644 (file)
@@ -552,8 +552,9 @@ static ssize_t amdgpu_debugfs_ring_read(struct file *f, char __user *buf,
                                        size_t size, loff_t *pos)
 {
        struct amdgpu_ring *ring = file_inode(f)->i_private;
-       uint32_t value, result, early[3];
+       u32 value, result, early[3] = { 0 };
        uint64_t p;
+       u32 avail_dw, start_dw, read_dw;
        loff_t i;
        int r;
 
@@ -565,10 +566,10 @@ static ssize_t amdgpu_debugfs_ring_read(struct file *f, char __user *buf,
 
        result = 0;
 
-       if (*pos < 12) {
-               if (ring->funcs->type == AMDGPU_RING_TYPE_CPER)
-                       mutex_lock(&ring->adev->cper.ring_lock);
+       if (ring->funcs->type == AMDGPU_RING_TYPE_CPER)
+               mutex_lock(&ring->adev->cper.ring_lock);
 
+       if (*pos < 12) {
                early[0] = amdgpu_ring_get_rptr(ring) & ring->buf_mask;
                early[1] = amdgpu_ring_get_wptr(ring) & ring->buf_mask;
                early[2] = ring->wptr & ring->buf_mask;
@@ -600,13 +601,24 @@ static ssize_t amdgpu_debugfs_ring_read(struct file *f, char __user *buf,
                        *pos += 4;
                }
        } else {
+               early[0] = amdgpu_ring_get_rptr(ring) & ring->buf_mask;
+               early[1] = amdgpu_ring_get_wptr(ring) & ring->buf_mask;
+
                p = early[0];
                if (early[0] <= early[1])
-                       size = (early[1] - early[0]);
+                       avail_dw = early[1] - early[0];
                else
-                       size = ring->ring_size - (early[0] - early[1]);
+                       avail_dw = ring->buf_mask + 1 - (early[0] - early[1]);
 
-               while (size) {
+               start_dw = (*pos > 12) ? ((*pos - 12) >> 2) : 0;
+               if (start_dw >= avail_dw)
+                       goto out;
+
+               p = (p + start_dw) & ring->ptr_mask;
+               avail_dw -= start_dw;
+               read_dw = min_t(u32, avail_dw, size >> 2);
+
+               while (read_dw) {
                        if (p == early[1])
                                goto out;
 
@@ -619,9 +631,10 @@ static ssize_t amdgpu_debugfs_ring_read(struct file *f, char __user *buf,
 
                        buf += 4;
                        result += 4;
-                       size--;
+                       read_dw--;
                        p++;
                        p &= ring->ptr_mask;
+                       *pos += 4;
                }
        }