From de9f6bcdb44f0e410031002e2f103826ea6b6259 Mon Sep 17 00:00:00 2001 From: Jesse Zhang Date: Fri, 9 Oct 2026 13:21:33 +0800 Subject: [PATCH] drm/amdgpu: don't drop the runtime PM reference twice in amdgpu_gpr amdgpu_debugfs_gpr_read() drops its runtime PM reference right after reading the wave registers. When copying them to userspace fails, it jumps to the err label, which drops the reference a second time. A read into a bad buffer on Navi31 either steals a reference somebody else holds (power/runtime_usage goes from 1 to 0) or underflows the count: amdgpu 0000:03:00.0: Runtime PM usage count underflow! The registers are already in the kernel buffer at that point, so finish the hardware access first and copy the buffer with a single copy_to_user() instead of the put_user() loop with its own error path. Fixes: 9eee152aab56 ("drm/amdgpu/debugfs: fix ref count leak when pm_runtime_get_sync fails") Reviewed-by: Alex Deucher Signed-off-by: Jesse Zhang Signed-off-by: Alex Deucher (cherry picked from commit 31035bbd74f7c3e10c163db875b8e67f8b50ee6a) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c | 23 ++++----------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c index ab7ccfaea22a..4f9b72ac5698 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c @@ -1266,7 +1266,6 @@ static ssize_t amdgpu_debugfs_gpr_read(struct file *f, char __user *buf, { struct amdgpu_device *adev = f->f_inode->i_private; int r; - ssize_t result = 0; uint32_t offset, se, sh, cu, wave, simd, thread, bank, *data; if (size > 4096 || size & 3 || *pos & 3) @@ -1310,25 +1309,11 @@ static ssize_t amdgpu_debugfs_gpr_read(struct file *f, char __user *buf, mutex_unlock(&adev->grbm_idx_mutex); pm_runtime_put_autosuspend(adev_to_drm(adev)->dev); - - while (size) { - uint32_t value; - - value = data[result >> 2]; - r = put_user(value, (uint32_t *)buf); - if (r) { - amdgpu_virt_disable_access_debugfs(adev); - goto err; - } - - result += 4; - buf += 4; - size -= 4; - } - - kfree(data); amdgpu_virt_disable_access_debugfs(adev); - return result; + + r = copy_to_user(buf, data, size) ? -EFAULT : size; + kfree(data); + return r; err: pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);