Re: [PATCH 2/2] drm/amdgpu/userq: hold the doorbell xa lock during hang reset

[email protected]
Newsgroups org.freedesktop.lists.dri-devel
Message-ID <[email protected]>
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:

Pre-existing issues:
- [High] Unprotected xa_for_each iteration allows concurrent Use-After-Free.
- [High] Lockless and unprotected access to userq->last_fence can result in a Use-After-Free.
- [Medium] Unprotected state modification of queue->state causes a data race.
--

--- Patch [2]: [PATCH 2/2] drm/amdgpu/userq: hold the doorbell xa lock during hang reset ---
commit c3c720e398b80f11731a92d4798c53c135bb4886
Author: Junrui Luo <[email protected]>

drm/amdgpu/userq: hold the doorbell xa lock during hang reset

This commit fixes a use-after-free vulnerability in the AMDGPU driver.
It adds an xa_lock_irqsave() lock around an xa_for_each() loop in
mes_userq_detect_and_reset() to prevent the queue from being freed
while iterating.

> diff --git a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
> index 4e44a581a78a2..f4d12e4b2d483 100644
> --- a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
> +++ b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c

[ ... ]

> @@ -230,6 +230,13 @@ static int mes_userq_detect_and_reset(struct amdgpu_device *adev,
>  	if (r) {
>  		dev_err(adev->dev, "Failed to detect and reset queues, err (%d)\n", r);
>  	} else if (hung_db_num) {
> +		/*
> +		 * The doorbell xarray is device wide, so this walks queues
> +		 * owned by other drm_files too. Hold its lock: the free path
> +		 * erases the entry under the same lock strictly before it
> +		 * frees the queue, so an entry found here stays allocated.
> +		 */
> +		xa_lock_irqsave(&adev->userq_doorbell_xa, flags);
>  		xa_for_each(&adev->userq_doorbell_xa, queue_id, queue) {

[Severity: High]
This isn't a bug introduced by this patch, but since you are fixing the
xa_for_each() iteration here, I noticed several other functions iterate
adev->userq_doorbell_xa locklessly.

For example, in amdgpu_userq_stop_sched_for_enforce_isolation():

    xa_for_each(&adev->userq_doorbell_xa, queue_id, queue) {
        uqm = queue->userq_mgr;
        cancel_delayed_work_sync(&uqm->resume_work);
        mutex_lock(&uqm->userq_mutex);

Similar loops exist in amdgpu_userq_start_sched_for_enforce_isolation() and
amdgpu_userq_suspend().

The xa_for_each macro only uses rcu_read_lock() internally during the lookup.

Could an unprivileged user trigger amdgpu_userq_destroy() concurrently and
free the queue, causing a use-after-free when the loop body dereferences
queue->userq_mgr?

>  			if (queue->queue_type == queue_type) {
>  				for (i = 0; i < hung_db_num; i++) {
>  					if (queue->doorbell_index == db_array[i]) {
>  						queue->state = AMDGPU_USERQ_STATE_HUNG;

[Severity: Medium]
This is a pre-existing issue, but does assigning queue->state here without
holding queue->userq_mgr->userq_mutex cause a data race?

Concurrently, actions like amdgpu_userq_start_sched_for_enforce_isolation() or
PM resume can call amdgpu_userq_map_helper() under userq_mutex, which also
evaluates and sets queue->state.

Could this lead to lost updates where the AMDGPU_USERQ_STATE_HUNG state is
overwritten?

>  						found_hung_queue = true;
>  						atomic_inc(&adev->gpu_reset_counter);
>  						amdgpu_userq_fence_driver_force_completion(queue);

[Severity: High]
This is a pre-existing issue, but is it safe to call
amdgpu_userq_fence_driver_force_completion() here?

This function reads userq->last_fence without taking
queue->userq_mgr->userq_mutex or holding rcu_read_lock():

amdgpu_userq_fence_driver_force_completion()
    struct dma_fence *f = userq->last_fence;
    if (f) {
        struct amdgpu_userq_fence *fence = to_amdgpu_userq_fence(f);

Concurrently, amdgpu_userq_fence_init() (called under userq_mutex) updates
userq->last_fence and drops the old fence reference via dma_fence_put().

Since xa_lock_irqsave() maps to a sleeping lock on PREEMPT_RT kernels and
does not disable interrupts, the RCU grace period could end.

Could this allow the fence memory to be freed before to_amdgpu_userq_fence(f)
accesses f->ops, resulting in a use-after-free?

> -						drm_dev_wedged_event(adev_to_drm(adev), DRM_WEDGE_RECOVERY_NONE, NULL);
>  					}
>  				}
>  			}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.