amdgpu_userq_signal_ioctl() retrieves the user queue via xa_load()
and then dereferences it in amdgpu_userq_fence_read_wptr(),
amdgpu_userq_fence_create(), and direct queue->last_fence accesses,
all before userq_mutex is acquired by amdgpu_userq_ensure_ev_fence().
A concurrent AMDGPU_USERQ_OP_FREE can destroy and free the queue
in this window, leading to a use-after-free.
This bug predates the queue-id wait ioctl changes and has been
present since the original signal/wait ioctl implementation.
Fix this by moving amdgpu_userq_ensure_ev_fence() before xa_load()
so that the queue lookup and all subsequent accesses are performed
under the userq_mutex that ensure_ev_fence acquires. Add the
necessary mutex_unlock() calls to the error paths between the moved
ensure_ev_fence and the existing unlock.
Fixes: a292fdecd728 ("drm/amdgpu: Implement userqueue signal/wait IOCTL")
Cc: [email protected]
Signed-off-by: Chenyuan Mi <[email protected]>
---
drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c | 17 +++++++++++------
1 file changed, 11 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c
b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c
index 1785ea7c18fe..7866f583eea4 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c
@@ -545,23 +545,28 @@ int amdgpu_userq_signal_ioctl(struct drm_device *dev,
void *data,
}
}
- /* Retrieve the user queue */
+ /* We are here means UQ is active, make sure the eviction fence is
valid */
+ amdgpu_userq_ensure_ev_fence(&fpriv->userq_mgr, &fpriv->evf_mgr);
+
+ /* Retrieve the user queue under userq_mutex (held by ensure_ev_fence)
*/
queue = xa_load(&userq_mgr->userq_xa, args->queue_id);
if (!queue) {
+ mutex_unlock(&userq_mgr->userq_mutex);
r = -ENOENT;
goto put_gobj_write;
}
r = amdgpu_userq_fence_read_wptr(adev, queue, &wptr);
- if (r)
+ if (r) {
+ mutex_unlock(&userq_mgr->userq_mutex);
goto put_gobj_write;
+ }
r = amdgpu_userq_fence_alloc(&userq_fence);
- if (r)
+ if (r) {
+ mutex_unlock(&userq_mgr->userq_mutex);
goto put_gobj_write;
-
- /* We are here means UQ is active, make sure the eviction fence is
valid */
- amdgpu_userq_ensure_ev_fence(&fpriv->userq_mgr, &fpriv->evf_mgr);
+ }
/* Create a new fence */
r = amdgpu_userq_fence_create(queue, userq_fence, wptr, &fence);
--
2.53.0