[PATCH] drm/nouveau/svm: drain fault handler before freeing svmm

Zhenhao Wan <[email protected]>
Newsgroups org.freedesktop.lists.nouveau,org.freedesktop.lists.dri-devel,org.kernel.vger.linux-kernel,org.kernel.vger.stable
Message-ID <[email protected]>
The SVM fault handler nouveau_svm_fault() looks up each fault's
nouveau_svmm from the per-device instance list under svm->mutex, caches
it in the fault array, then drops svm->mutex and dereferences the svmm
across blocking faults (mmu_interval_notifier_insert(), hmm_range_fault()),
taking svmm->mutex and touching svmm->vmm.

nouveau_svmm has no reference of its own. It is freed on DRM file close,
via nouveau_svmm_fini() -> mmu_notifier_put(), which frees asynchronously
through call_srcu() without waiting for the handler; svm->mutex is not
held during the dereference, and mmget_not_zero() pins the mm, not the
svmm. A fault handled concurrently with a close can therefore dereference
a freed svmm. flush_work() on the fault buffer otherwise runs only at
device removal and suspend, never on the per-client close path.

The instance is already unlinked earlier in the same close, by
nouveau_svmm_part(), so no new fault can resolve to this svmm. Drain the
fault handler in nouveau_svmm_fini() before the free, so any handler that
cached the pointer has completed. nouveau_cli_fini() already flushes
cli->work here, and device-scope teardown already uses the same idiom.

Fixes: eeaf06ac1a55 ("drm/nouveau/svm: initial support for shared virtual memory")
Reported-by: Yuhao Jiang <[email protected]>
Assisted-by: Claude:claude-opus-5
Cc: [email protected]
Signed-off-by: Zhenhao Wan <[email protected]>
---
 drivers/gpu/drm/nouveau/nouveau_svm.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/drivers/gpu/drm/nouveau/nouveau_svm.c b/drivers/gpu/drm/nouveau/nouveau_svm.c
index dcc92131488e..01191b126be5 100644
--- a/drivers/gpu/drm/nouveau/nouveau_svm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_svm.c
@@ -304,6 +304,17 @@ nouveau_svmm_fini(struct nouveau_svmm **psvmm)
 {
 	struct nouveau_svmm *svmm = *psvmm;
 	if (svmm) {
+		struct nouveau_svm *svm = svmm->vmm->cli->drm->svm;
+
+		/* The fault handler caches svmm pointers looked up under
+		 * svm->mutex and then dereferences them after dropping it,
+		 * across blocking faults.  The instance has already been
+		 * unlinked (nouveau_svmm_part), so no new fault can resolve to
+		 * this svmm; drain the handler to release any in-flight
+		 * reference before the svmm is freed.
+		 */
+		flush_work(&svm->buffer[0].work);
+
 		mutex_lock(&svmm->mutex);
 		svmm->vmm = NULL;
 		mutex_unlock(&svmm->mutex);

---
base-commit: db2ddb87143519e20a95aa36c60b36107b736a58
change-id: 20260813-nouveau-svm-svmm-uaf-bbcf36265aaa

Best regards,
--  
Zhenhao Wan <[email protected]>
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.