Commit 06b2ad1e authored by Weili Qian's avatar Weili Qian Committed by JiangShui
Browse files

uacce: fix NULL pointer when unbind device

driver inclusion
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/I867RA


CVE: NA

----------------------------------------------------------------------

When the user space process and 'unbind' device operations are
performed at the same time, the 'unbind' operation may be completed
before the memory is unmap(uacce_vma_close()). Since 'uacce->parent'
is set to NULL in uacce_remove(), 'pdev->driver' will trigger panic
in uacce_free_dma_buffers(). Therefore, the memory release process
is added to uacce_remove(), and uacce->mutex ensures that the memory
is released only once.

Signed-off-by: default avatarWeili Qian <qianweili@huawei.com>
Signed-off-by: default avatarJiangShui Yang <yangjiangshui@h-partners.com>
parent aa609da5
Loading
Loading
Loading
Loading
+18 −9
Original line number Diff line number Diff line
@@ -199,11 +199,9 @@ static void uacce_free_dma_buffers(struct uacce_queue *q)
	struct device *pdev = q->uacce->parent;
	int i = 0;

	if (module_refcount(pdev->driver->owner) > 0)
		module_put(pdev->driver->owner);

	if (!qfr->dma_list)
		return;

	while (i < qfr->dma_list[0].total_num) {
		WARN_ON(!qfr->dma_list[i].size || !qfr->dma_list[i].dma);
		dev_dbg(pdev, "free dma qfr (index = %d)\n", i);
@@ -368,18 +366,19 @@ static int uacce_fops_open(struct inode *inode, struct file *filep)
static int uacce_fops_release(struct inode *inode, struct file *filep)
{
	struct uacce_queue *q = filep->private_data;
	struct uacce_qfile_region *ss = q->qfrs[UACCE_QFRT_SS];
	struct uacce_device *uacce = q->uacce;
	struct uacce_qfile_region *ss;

	mutex_lock(&uacce->mutex);
	uacce_put_queue(q);
	uacce_unbind_queue(q);
	list_del(&q->list);
	mutex_unlock(&uacce->mutex);
	ss = q->qfrs[UACCE_QFRT_SS];
	if (ss && ss != &noiommu_ss_default_qfr) {
		uacce_free_dma_buffers(q);
		kfree(ss);
	}
	list_del(&q->list);
	mutex_unlock(&uacce->mutex);
	kfree(q);

	return 0;
@@ -403,14 +402,22 @@ static void uacce_vma_close(struct vm_area_struct *vma)

	if (qfr->type == UACCE_QFRT_SS &&
	    atomic_read(&current->active_mm->mm_users) > 0) {
		/*
		 * uacce_vma_close() and uacce_remove() may be executed concurrently.
		 * To avoid accessing the same address at the same time, takes the uacce->mutex.
		 */
		mutex_lock(&uacce->mutex);
		if ((q->state == UACCE_Q_STARTED) && uacce->ops->stop_queue)
			uacce->ops->stop_queue(q);
		uacce_free_dma_buffers(q);
		kfree(qfr);
		q->qfrs[vma->vm_pgoff] = NULL;
	} else if (qfr->type != UACCE_QFRT_SS) {
		mutex_unlock(&uacce->mutex);
		kfree(qfr);
	} else if (qfr->type != UACCE_QFRT_SS) {
		mutex_lock(&q->mutex);
		q->qfrs[vma->vm_pgoff] = NULL;
		mutex_unlock(&q->mutex);
		kfree(qfr);
	}
}

@@ -522,7 +529,6 @@ static int uacce_alloc_dma_buffers(struct uacce_queue *q,
	if (!slice)
		return -ENOMEM;

	(void)try_module_get(pdev->driver->owner);
	qfr->dma_list = slice;
	for (i = 0; i < ss_num; i++) {
		if (start + max_size > vma->vm_end)
@@ -1047,6 +1053,7 @@ void uacce_remove(struct uacce_device *uacce)
	mutex_lock(&uacce->mutex);
	/* ensure no open queue remains */
	list_for_each_entry_safe(q, next_q, &uacce->queues, list) {
		struct uacce_qfile_region *ss = q->qfrs[UACCE_QFRT_SS];
		/*
		 * Taking q->mutex ensures that fops do not use the defunct
		 * uacce->ops after the queue is disabled.
@@ -1061,6 +1068,8 @@ void uacce_remove(struct uacce_device *uacce)
		 * access the mmaped area while parent device is already removed
		 */
		unmap_mapping_range(q->mapping, 0, 0, 1);
		if (ss && ss != &noiommu_ss_default_qfr)
			uacce_free_dma_buffers(q);
	}

	/* disable sva now since no opened queues */