Commit 28cacaac authored by Sunil Khatri's avatar Sunil Khatri Committed by Alex Deucher
Browse files

drm/amdgpu/userq: defer queue publication until create completes



The userq create path publishes queues to global xarrays such as
userq_doorbell_xa and userq_xa before creation was fully complete.
Later on if create queue fails, teardown could free an already
visible queue, opening a UAF race with concurrent queue walkers.
Also calling amdgpu_userq_put in such cases complicates the cleanup.

Solution is to defer queue publication until create succeeds and no
partially initialized queue is exposed.

Signed-off-by: default avatarSunil Khatri <sunil.khatri@amd.com>
Reviewed-by: default avatarChristian König <christian.koenig@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent 421c0f19
Loading
Loading
Loading
Loading
+33 −33
Original line number Diff line number Diff line
@@ -758,7 +758,6 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args)
	const struct amdgpu_userq_funcs *uq_funcs;
	struct amdgpu_usermode_queue *queue;
	struct amdgpu_db_info db_info;
	char *queue_name;
	bool skip_map_queue;
	u32 qid;
	uint64_t index;
@@ -848,65 +847,66 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args)
		goto unlock;
	}

	/* drop this refcount during queue destroy */
	kref_init(&queue->refcount);

	/* Wait for mode-1 reset to complete */
	down_read(&adev->reset_domain->sem);
	r = xa_err(xa_store_irq(&adev->userq_doorbell_xa, index, queue, GFP_KERNEL));
	/* don't map the queue if scheduling is halted */
	if (adev->userq_halt_for_enforce_isolation &&
	    ((queue->queue_type == AMDGPU_HW_IP_GFX) ||
	     (queue->queue_type == AMDGPU_HW_IP_COMPUTE)))
		skip_map_queue = true;
	else
		skip_map_queue = false;
	if (!skip_map_queue) {
		r = amdgpu_userq_map_helper(queue);
		if (r) {
			drm_file_err(uq_mgr->file, "Failed to map Queue\n");
			uq_funcs->mqd_destroy(queue);
			amdgpu_userq_fence_driver_free(queue);
			kfree(queue);
		up_read(&adev->reset_domain->sem);
			goto unlock;
		}
	}

	/* drop this refcount during queue destroy */
	kref_init(&queue->refcount);

	/* Wait for mode-1 reset to complete */
	down_read(&adev->reset_domain->sem);
	r = xa_alloc(&uq_mgr->userq_xa, &qid, queue,
		     XA_LIMIT(1, AMDGPU_MAX_USERQ_COUNT), GFP_KERNEL);
	if (r) {
		drm_file_err(uq_mgr->file, "Failed to allocate a queue id\n");
		amdgpu_userq_fence_driver_free(queue);
		xa_erase_irq(&adev->userq_doorbell_xa, index);
		if (!skip_map_queue)
			amdgpu_userq_unmap_helper(queue);

		uq_funcs->mqd_destroy(queue);
		amdgpu_userq_fence_driver_free(queue);
		kfree(queue);
		r = -ENOMEM;
		up_read(&adev->reset_domain->sem);
		goto unlock;
	}
	up_read(&adev->reset_domain->sem);

	/* don't map the queue if scheduling is halted */
	if (adev->userq_halt_for_enforce_isolation &&
	    ((queue->queue_type == AMDGPU_HW_IP_GFX) ||
	     (queue->queue_type == AMDGPU_HW_IP_COMPUTE)))
		skip_map_queue = true;
	else
		skip_map_queue = false;
	if (!skip_map_queue) {
		r = amdgpu_userq_map_helper(queue);
	r = xa_err(xa_store_irq(&adev->userq_doorbell_xa, index, queue, GFP_KERNEL));
	if (r) {
			drm_file_err(uq_mgr->file, "Failed to map Queue\n");
			xa_erase_irq(&adev->userq_doorbell_xa, index);
		xa_erase(&uq_mgr->userq_xa, qid);
			amdgpu_userq_fence_driver_free(queue);
		if (!skip_map_queue)
			amdgpu_userq_unmap_helper(queue);

		uq_funcs->mqd_destroy(queue);
		amdgpu_userq_fence_driver_free(queue);
		kfree(queue);
		up_read(&adev->reset_domain->sem);
		goto unlock;
	}
	}

	queue_name = kasprintf(GFP_KERNEL, "queue-%d", qid);
	if (!queue_name) {
		r = -ENOMEM;
		goto unlock;
	}
	up_read(&adev->reset_domain->sem);

#if defined(CONFIG_DEBUG_FS)
	char queue_name[32];

	scnprintf(queue_name, sizeof(queue_name), "queue_%d", qid);
	/* Queue dentry per client to hold MQD information   */
	queue->debugfs_queue = debugfs_create_dir(queue_name, filp->debugfs_client);
	debugfs_create_file("mqd_info", 0444, queue->debugfs_queue, queue, &amdgpu_mqd_info_fops);
#endif
	amdgpu_userq_init_hang_detect_work(queue);
	kfree(queue_name);

	args->out.queue_id = qid;
	atomic_inc(&uq_mgr->userq_count[queue->queue_type]);