driver inclusion category: feature 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: Weili Qian qianweili@huawei.com Signed-off-by: JiangShui Yang yangjiangshui@h-partners.com --- drivers/misc/uacce/uacce.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-)
diff --git a/drivers/misc/uacce/uacce.c b/drivers/misc/uacce/uacce.c index bb4af54c0386..d00a5d98980e 100644 --- a/drivers/misc/uacce/uacce.c +++ b/drivers/misc/uacce/uacce.c @@ -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(¤t->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 */