From nobody Fri Jun 19 07:45:24 2026 Received: from mail-pl1-f169.google.com (mail-pl1-f169.google.com [209.85.214.169]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E042B340A7D for ; Sun, 26 Apr 2026 10:38:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.169 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777199917; cv=none; b=Qay88cKW8U55T+y6c6xcpH07Jg8fr3y7aQySQ84ZmFJ++lBQSh9q/d/Jow90+sp8KITWrqGMjbudmTrrKfnnzydwMtVINq6eIc7QAtUlno1GUR2tYKij7kDEfa5vFDCjX9DERrzTrGp2uw7rsNSb91ynJ5PcZWTG3xSLKot2NqI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777199917; c=relaxed/simple; bh=YblxSBPeDfoMHUeHxWelrTFqkbtN6u0kW+K6Md/Hw/Q=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=qHtIwHVsODtsaCHMMAReIWX8QwFJ12/AxL1QvORG0NVusf+GgLKB5MsDVlq04CqPrG48NgD80rTpzjbkoA/Mbzhy8jcnmFlyXDLvkMCqIIHMz/5E5JYVYZQQ3IZX/6UudzPiRb+9NBoSPvwCcEtkwbZytvPMflWb1gRdwT857gk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=jWMp06bs; arc=none smtp.client-ip=209.85.214.169 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="jWMp06bs" Received: by mail-pl1-f169.google.com with SMTP id d9443c01a7336-2ad617d5b80so54079605ad.1 for ; Sun, 26 Apr 2026 03:38:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1777199915; x=1777804715; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=gRIYnBpWA24QfNeeW5zuWFSB7co7+n6A75Ah4Y5MXZE=; b=jWMp06bsYNjIdACCof6fRpdbHnFMe8vQz1MajhDAaZNMaYI7k5tpvPFAD4CyKqZxy+ PONNIHqg8za/evzRKRLii6ieg8F3oVfH/YYsNrsgE5n94IvcZ2NV8HuP0rhjudKo7VTu 7m5d29+9dXUuMgsiNTFHrsDThVGqpSJRpjRvSDVqAKmJHicgeVtbKAEfS3ARFDcuGS/h cPRZkCk60P7djAu3oNAbVDjIRexZisvF8Bv6MJbS3Ub0tjs9/rOUWuKBmlTQLhz263ga cV1Zt0GgAgqNBCbiWQ0XxKjhwMzfbM22PCy0H8+IcHpSpBDT1nv/kEVActdEqbZHNcrV 5y1g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777199915; x=1777804715; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=gRIYnBpWA24QfNeeW5zuWFSB7co7+n6A75Ah4Y5MXZE=; b=jc1SYKgVbjcVPd/RXSTUyRTa3Gh1E1qUT+Gfy0cM+XdEtUS1zROPMGSGgSrxoejUPR /pNgxQjPaj5SwQIwyZ3UfXMXISQLxGlOMvtfbzm2NP1QNtDV3hckyQT8gkTtESvr88pR LOpVpiw5f199ozTGqzxTDW+avlhoM1X1Yd3TvTSNCIrWsLe9UJN4IV5BxcG42/QUxpc8 R24r1L056P/9KyDl73a3W9Wa9EFCX03+Y8l6YHwExUzQFnFGXOGuDy3gRSgXWy/JaEhi u/BRQ2JdW62zWNzfMwoc+dGJ1BEIFPJvjPUj1GkJbbkZZLfXaKuG+zHa86NxynkauSSX R3PQ== X-Forwarded-Encrypted: i=1; AFNElJ9ZG6bPpMss3gIcMAaD0gFZNq+nbbG8YGD1vuCFrCKCr7hTUmwShFBFkAUKB7SIJHPzqsPtqybp2jAo+CU=@vger.kernel.org X-Gm-Message-State: AOJu0Yy+fTeVoWqlOXWEQt2Uboj830IZai9EFjqzOQdc6ohY0Q+tNBpw 2oEAXlXMnzJvFL+9C8dc/tFu0N4umYWnHPI5DS+B/HI/EOorxrhJ1UtmSPL5Mw== X-Gm-Gg: AeBDieuR+GEJg50fN+KZ1KCP/mH6aydqi3I9N0mpy4/kO9UYMtdvsOrXkDmhzlUPa60 Pv1JE87wrQ1lxY4PJ9+co9b2UPCLacNyfKLo+25hLz3ey0UkkHdsAl2GSUv8zPdUSwf7/PfvIW2 XGepI+9QO8hA+qtcnkorfjW4SN+/x9Tn4vt0wMw8d2pSjisDyeqNJU5q5DDIbMLP3f4IpTro6p1 IYm9aWsgGw2C13we353zSZk0cRpJo2zPlfuO3xLJvdfU1Mr9JfNwQTITbSZO9e7Zb17t6DiYHnL 8I6nlUE19SCHMhrwsRWcfKDqnI/P+hRkc4Zz7J5VmUGtxbb6/qq6tybx9/y9JpZshYVjOZp7zPg dyEvu+3kK36bvHftuHafr8OjnlxZax1YEklVI/176PDJRaaH7ehSW3YDINQChk0jZLFQjNj9V++ tuk4PjjKoIGR9474DvKq0+k7AwgA== X-Received: by 2002:a17:902:f54b:b0:2b0:c90f:449c with SMTP id d9443c01a7336-2b5f9ea99fbmr405337915ad.9.1777199915152; Sun, 26 Apr 2026 03:38:35 -0700 (PDT) Received: from gye-SER8.. ([1.243.227.27]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2b5fab208d4sm296020455ad.55.2026.04.26.03.38.33 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 26 Apr 2026 03:38:34 -0700 (PDT) From: gyeyoung baek To: Tomeu Vizoso , Oded Gabbay , dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org Cc: gyeyoung baek Subject: [PATCH] accel/rocket: Fix drm_mm UAF on close vs in-flight job Date: Sun, 26 Apr 2026 19:37:56 +0900 Message-ID: <20260426103758.1373137-1-gye976@gmail.com> X-Mailer: git-send-email 2.43.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The drm_mm and its mutex live in rocket_file_priv, which rocket_postclose() frees on fd close. But a BO held by an in-flight job needs to access them later -- when its destructor runs from the drm_sched free_job worker after the NPU IRQ -- and that access hits freed memory. BUG: KASAN: slab-use-after-free in __mutex_trylock_common+0x90/0x1e8 Workqueue: .npu drm_sched_free_job_work [gpu_sched] Call trace: __mutex_lock rocket_gem_bo_free rocket_job_cleanup rocket_job_free drm_sched_free_job_work [gpu_sched] Move drm_mm and the mutex out of rocket_file_priv into the kref-managed rocket_iommu_domain (renamed to rocket_vm). Their lifetime now follows the vm: while any job references the vm, the address-space state stays alive. Fixes: ed98261b4168 ("accel/rocket: Add a new driver for Rockchip's NPU") Signed-off-by: gyeyoung baek --- drivers/accel/rocket/rocket_drv.c | 74 +++++++++++++++---------------- drivers/accel/rocket/rocket_drv.h | 13 +++--- drivers/accel/rocket/rocket_gem.c | 29 ++++++------ drivers/accel/rocket/rocket_gem.h | 4 +- drivers/accel/rocket/rocket_job.c | 6 +-- drivers/accel/rocket/rocket_job.h | 2 +- 6 files changed, 63 insertions(+), 65 deletions(-) diff --git a/drivers/accel/rocket/rocket_drv.c b/drivers/accel/rocket/rocke= t_drv.c index 8bbbce594..bddcfc0ff 100644 --- a/drivers/accel/rocket/rocket_drv.c +++ b/drivers/accel/rocket/rocket_drv.c @@ -26,46 +26,54 @@ static struct platform_device *drm_dev; static struct rocket_device *rdev; =20 static void -rocket_iommu_domain_destroy(struct kref *kref) +rocket_vm_destroy(struct kref *kref) { - struct rocket_iommu_domain *domain =3D container_of(kref, struct rocket_i= ommu_domain, kref); + struct rocket_vm *vm =3D container_of(kref, struct rocket_vm, kref); =20 - iommu_domain_free(domain->domain); - domain->domain =3D NULL; - kfree(domain); + drm_mm_takedown(&vm->mm); + mutex_destroy(&vm->lock); + iommu_domain_free(vm->domain); + vm->domain =3D NULL; + kfree(vm); } =20 -static struct rocket_iommu_domain* -rocket_iommu_domain_create(struct device *dev) +static struct rocket_vm * +rocket_vm_create(struct device *dev) { - struct rocket_iommu_domain *domain =3D kmalloc_obj(*domain); + struct rocket_vm *vm =3D kmalloc_obj(*vm); + u64 start, end; void *err; =20 - if (!domain) + if (!vm) return ERR_PTR(-ENOMEM); =20 - domain->domain =3D iommu_paging_domain_alloc(dev); - if (IS_ERR(domain->domain)) { - err =3D ERR_CAST(domain->domain); - kfree(domain); + vm->domain =3D iommu_paging_domain_alloc(dev); + if (IS_ERR(vm->domain)) { + err =3D ERR_CAST(vm->domain); + kfree(vm); return err; } - kref_init(&domain->kref); =20 - return domain; + start =3D vm->domain->geometry.aperture_start; + end =3D vm->domain->geometry.aperture_end; + drm_mm_init(&vm->mm, start, end - start + 1); + mutex_init(&vm->lock); + kref_init(&vm->kref); + + return vm; } =20 -struct rocket_iommu_domain * -rocket_iommu_domain_get(struct rocket_file_priv *rocket_priv) +struct rocket_vm * +rocket_vm_get(struct rocket_file_priv *rocket_priv) { - kref_get(&rocket_priv->domain->kref); - return rocket_priv->domain; + kref_get(&rocket_priv->vm->kref); + return rocket_priv->vm; } =20 void -rocket_iommu_domain_put(struct rocket_iommu_domain *domain) +rocket_vm_put(struct rocket_vm *vm) { - kref_put(&domain->kref, rocket_iommu_domain_destroy); + kref_put(&vm->kref, rocket_vm_destroy); } =20 static int @@ -73,7 +81,6 @@ rocket_open(struct drm_device *dev, struct drm_file *file) { struct rocket_device *rdev =3D to_rocket_device(dev); struct rocket_file_priv *rocket_priv; - u64 start, end; int ret; =20 if (!try_module_get(THIS_MODULE)) @@ -86,29 +93,22 @@ rocket_open(struct drm_device *dev, struct drm_file *fi= le) } =20 rocket_priv->rdev =3D rdev; - rocket_priv->domain =3D rocket_iommu_domain_create(rdev->cores[0].dev); - if (IS_ERR(rocket_priv->domain)) { - ret =3D PTR_ERR(rocket_priv->domain); + rocket_priv->vm =3D rocket_vm_create(rdev->cores[0].dev); + if (IS_ERR(rocket_priv->vm)) { + ret =3D PTR_ERR(rocket_priv->vm); goto err_free; } =20 file->driver_priv =3D rocket_priv; =20 - start =3D rocket_priv->domain->domain->geometry.aperture_start; - end =3D rocket_priv->domain->domain->geometry.aperture_end; - drm_mm_init(&rocket_priv->mm, start, end - start + 1); - mutex_init(&rocket_priv->mm_lock); - ret =3D rocket_job_open(rocket_priv); if (ret) - goto err_mm_takedown; + goto err_vm_put; =20 return 0; =20 -err_mm_takedown: - mutex_destroy(&rocket_priv->mm_lock); - drm_mm_takedown(&rocket_priv->mm); - rocket_iommu_domain_put(rocket_priv->domain); +err_vm_put: + rocket_vm_put(rocket_priv->vm); err_free: kfree(rocket_priv); err_put_mod: @@ -122,9 +122,7 @@ rocket_postclose(struct drm_device *dev, struct drm_fil= e *file) struct rocket_file_priv *rocket_priv =3D file->driver_priv; =20 rocket_job_close(rocket_priv); - mutex_destroy(&rocket_priv->mm_lock); - drm_mm_takedown(&rocket_priv->mm); - rocket_iommu_domain_put(rocket_priv->domain); + rocket_vm_put(rocket_priv->vm); kfree(rocket_priv); module_put(THIS_MODULE); } diff --git a/drivers/accel/rocket/rocket_drv.h b/drivers/accel/rocket/rocke= t_drv.h index 2c673bb99..2754f46f1 100644 --- a/drivers/accel/rocket/rocket_drv.h +++ b/drivers/accel/rocket/rocket_drv.h @@ -11,22 +11,23 @@ =20 extern const struct dev_pm_ops rocket_pm_ops; =20 -struct rocket_iommu_domain { +struct rocket_vm { struct iommu_domain *domain; + struct drm_mm mm; + /* protects @mm */ + struct mutex lock; struct kref kref; }; =20 struct rocket_file_priv { struct rocket_device *rdev; =20 - struct rocket_iommu_domain *domain; - struct drm_mm mm; - struct mutex mm_lock; + struct rocket_vm *vm; =20 struct drm_sched_entity sched_entity; }; =20 -struct rocket_iommu_domain *rocket_iommu_domain_get(struct rocket_file_pri= v *rocket_priv); -void rocket_iommu_domain_put(struct rocket_iommu_domain *domain); +struct rocket_vm *rocket_vm_get(struct rocket_file_priv *rocket_priv); +void rocket_vm_put(struct rocket_vm *vm); =20 #endif diff --git a/drivers/accel/rocket/rocket_gem.c b/drivers/accel/rocket/rocke= t_gem.c index b6a385d2e..7f64134fd 100644 --- a/drivers/accel/rocket/rocket_gem.c +++ b/drivers/accel/rocket/rocket_gem.c @@ -14,20 +14,20 @@ static void rocket_gem_bo_free(struct drm_gem_object *obj) { struct rocket_gem_object *bo =3D to_rocket_bo(obj); - struct rocket_file_priv *rocket_priv =3D bo->driver_priv; + struct rocket_vm *vm =3D bo->vm; size_t unmapped; =20 drm_WARN_ON(obj->dev, refcount_read(&bo->base.pages_use_count) > 1); =20 - unmapped =3D iommu_unmap(bo->domain->domain, bo->mm.start, bo->size); + unmapped =3D iommu_unmap(vm->domain, bo->mm.start, bo->size); drm_WARN_ON(obj->dev, unmapped !=3D bo->size); =20 - mutex_lock(&rocket_priv->mm_lock); + mutex_lock(&vm->lock); drm_mm_remove_node(&bo->mm); - mutex_unlock(&rocket_priv->mm_lock); + mutex_unlock(&vm->lock); =20 - rocket_iommu_domain_put(bo->domain); - bo->domain =3D NULL; + rocket_vm_put(vm); + bo->vm =3D NULL; =20 drm_gem_shmem_free(&bo->base); } @@ -64,6 +64,7 @@ int rocket_ioctl_create_bo(struct drm_device *dev, void *= data, struct drm_file * struct drm_gem_shmem_object *shmem_obj; struct rocket_gem_object *rkt_obj; struct drm_gem_object *gem_obj; + struct rocket_vm *vm; struct sg_table *sgt; int ret; =20 @@ -74,8 +75,8 @@ int rocket_ioctl_create_bo(struct drm_device *dev, void *= data, struct drm_file * gem_obj =3D &shmem_obj->base; rkt_obj =3D to_rocket_bo(gem_obj); =20 - rkt_obj->driver_priv =3D rocket_priv; - rkt_obj->domain =3D rocket_iommu_domain_get(rocket_priv); + vm =3D rocket_vm_get(rocket_priv); + rkt_obj->vm =3D vm; rkt_obj->size =3D args->size; rkt_obj->offset =3D 0; =20 @@ -90,13 +91,13 @@ int rocket_ioctl_create_bo(struct drm_device *dev, void= *data, struct drm_file * goto err; } =20 - mutex_lock(&rocket_priv->mm_lock); - ret =3D drm_mm_insert_node_generic(&rocket_priv->mm, &rkt_obj->mm, + mutex_lock(&vm->lock); + ret =3D drm_mm_insert_node_generic(&vm->mm, &rkt_obj->mm, rkt_obj->size, PAGE_SIZE, 0, 0); - mutex_unlock(&rocket_priv->mm_lock); + mutex_unlock(&vm->lock); =20 - ret =3D iommu_map_sgtable(rocket_priv->domain->domain, + ret =3D iommu_map_sgtable(vm->domain, rkt_obj->mm.start, shmem_obj->sgt, IOMMU_READ | IOMMU_WRITE); @@ -115,9 +116,9 @@ int rocket_ioctl_create_bo(struct drm_device *dev, void= *data, struct drm_file * return 0; =20 err_remove_node: - mutex_lock(&rocket_priv->mm_lock); + mutex_lock(&vm->lock); drm_mm_remove_node(&rkt_obj->mm); - mutex_unlock(&rocket_priv->mm_lock); + mutex_unlock(&vm->lock); =20 err: drm_gem_shmem_object_free(gem_obj); diff --git a/drivers/accel/rocket/rocket_gem.h b/drivers/accel/rocket/rocke= t_gem.h index 240430334..e1fbbd8cf 100644 --- a/drivers/accel/rocket/rocket_gem.h +++ b/drivers/accel/rocket/rocket_gem.h @@ -9,9 +9,7 @@ struct rocket_gem_object { struct drm_gem_shmem_object base; =20 - struct rocket_file_priv *driver_priv; - - struct rocket_iommu_domain *domain; + struct rocket_vm *vm; struct drm_mm_node mm; size_t size; u32 offset; diff --git a/drivers/accel/rocket/rocket_job.c b/drivers/accel/rocket/rocke= t_job.c index 2f1861f96..7695fca02 100644 --- a/drivers/accel/rocket/rocket_job.c +++ b/drivers/accel/rocket/rocket_job.c @@ -233,7 +233,7 @@ static void rocket_job_cleanup(struct kref *ref) refcount); unsigned int i; =20 - rocket_iommu_domain_put(job->domain); + rocket_vm_put(job->vm); =20 dma_fence_put(job->done_fence); dma_fence_put(job->inference_done_fence); @@ -314,7 +314,7 @@ static struct dma_fence *rocket_job_run(struct drm_sche= d_job *sched_job) if (ret < 0) return fence; =20 - ret =3D iommu_attach_group(job->domain->domain, core->iommu_group); + ret =3D iommu_attach_group(job->vm->domain, core->iommu_group); if (ret < 0) return fence; =20 @@ -573,7 +573,7 @@ static int rocket_ioctl_submit_job(struct drm_device *d= ev, struct drm_file *file =20 rjob->out_bo_count =3D job->out_bo_handle_count; =20 - rjob->domain =3D rocket_iommu_domain_get(file_priv); + rjob->vm =3D rocket_vm_get(file_priv); =20 ret =3D rocket_job_push(rjob); if (ret) diff --git a/drivers/accel/rocket/rocket_job.h b/drivers/accel/rocket/rocke= t_job.h index 4ae00feec..9373c3d02 100644 --- a/drivers/accel/rocket/rocket_job.h +++ b/drivers/accel/rocket/rocket_job.h @@ -36,7 +36,7 @@ struct rocket_job { /* Fence to be signaled by IRQ handler when the job is complete. */ struct dma_fence *done_fence; =20 - struct rocket_iommu_domain *domain; + struct rocket_vm *vm; =20 struct kref refcount; }; --=20 2.43.0