Re: ksmbd: BUG_ON in locks_release_private() - file_lock destroyed while VFS blocked requests are still attached

Namjae Jeon <[email protected]>
Newsgroups org.kernel.vger.linux-cifs
Message-ID <CAKYAXd-B1mjT-JONqToLMeQKmV_GRdR1Pogi9VugyCADATpO3g@mail.gmail.com>
On Thu, Aug 13, 2026 at 1:09 AM Blue bird <[email protected]> wrote:
>
> Hello,
>
> I found a remotely triggerable kernel BUG() in ksmbd, reproducible on current
> mainline. An authenticated SMB3 client with access to a share can crash the
> kernel and permanently disable the SMB service in a few seconds of traffic.
> A related KASAN slab-use-after-free in smb2_lock() falls out of the same defect.
Can you check if an attached patch fixes this issue ?

Thanks!
0001-ksmbd-detach-blocked-lock-requests-before-freeing.patch (text/x-patch, 4.2 KB)
From db757617fc7c790bff59a01a78f51ae91efc42e7 Mon Sep 17 00:00:00 2001
From: Namjae Jeon <[email protected]>
Date: Fri, 14 Aug 2026 11:18:38 +0900
Subject: [PATCH] ksmbd: detach blocked lock requests before freeing

A file_lock retained by ksmbd for byte-range lock bookkeeping can still
be part of the VFS blocked-request graph. In particular, the VFS can
chain a new waiter below an already blocked request through
flc_blocked_requests. The ksmbd_file reference count does not cover that
graph.

Both __ksmbd_close_fd() and the cross-request unlock path free these
retained file_lock objects directly. If a dependent waiter is still
attached, locks_release_private() hits
BUG_ON(!list_empty(&flc->flc_blocked_requests)). The same lifetime
mismatch can leave a freed ksmbd_lock reachable through its request-local
llist.

Detach the file_lock from the blocked-request graph before freeing it in
the close, cross-request unlock, and rollback paths. locks_delete_block()
also wakes requests chained below the object. Remove llist when a
completed lock is published so a globally visible ksmbd_lock no longer
points into the submitting worker's stack.

Fixes: d63528eb0d43 ("ksmbd: free ksmbd_lock when file is closed")
Reported-by: Kyenghwan Hwang <[email protected]>
Signed-off-by: Namjae Jeon <[email protected]>
---
 fs/smb/server/smb2pdu.c   | 17 ++++++++++++-----
 fs/smb/server/vfs_cache.c |  8 +++++---
 2 files changed, 17 insertions(+), 8 deletions(-)

diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index 835d9e2aacc4..37cf1deed6d0 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -9169,6 +9169,12 @@ static void smb2_remove_blocked_lock(void **argv)
 	locks_wake_up(flock);
 }
 
+static void smb2_free_lock(struct file_lock *flock)
+{
+	ksmbd_vfs_posix_lock_unblock(flock);
+	locks_free_lock(flock);
+}
+
 static void smb2_free_blocked_lock(struct file_lock *flock)
 {
 	ksmbd_vfs_posix_lock_unblock(flock);
@@ -9355,14 +9361,14 @@ int smb2_lock(struct ksmbd_work *work)
 					    cmp_lock->end == smb_lock->end &&
 					    !lock_defer_pending(cmp_lock->fl)) {
 						nolock = 0;
-						list_del(&cmp_lock->flist);
-						list_del(&cmp_lock->clist);
+						list_del_init(&cmp_lock->flist);
+						list_del_init(&cmp_lock->clist);
 						cmp_lock->conn = NULL;
 						spin_unlock(&conn->llist_lock);
 						up_read(&conn_list_lock);
 
 						ksmbd_conn_put(conn);
-						locks_free_lock(cmp_lock->fl);
+						smb2_free_lock(cmp_lock->fl);
 						kfree(cmp_lock);
 						goto out_check_cl;
 					}
@@ -9527,7 +9533,8 @@ int smb2_lock(struct ksmbd_work *work)
 	/* publish only once the whole batch has committed */
 	if (!list_empty(&rollback_list)) {
 		spin_lock(&work->conn->llist_lock);
-		list_for_each_entry(smb_lock, &rollback_list, llist) {
+		list_for_each_entry_safe(smb_lock, tmp, &rollback_list, llist) {
+			list_del_init(&smb_lock->llist);
 			smb_lock->conn = ksmbd_conn_get(work->conn);
 			list_add_tail(&smb_lock->clist,
 				      &work->conn->lock_list);
@@ -9567,7 +9574,7 @@ int smb2_lock(struct ksmbd_work *work)
 		}
 
 		list_del(&smb_lock->llist);
-		locks_free_lock(smb_lock->fl);
+		smb2_free_lock(smb_lock->fl);
 		if (rlock)
 			locks_free_lock(rlock);
 		kfree(smb_lock);
diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c
index 90348a409162..972e8985a503 100644
--- a/fs/smb/server/vfs_cache.c
+++ b/fs/smb/server/vfs_cache.c
@@ -630,8 +630,9 @@ static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp)
 	if (!IS_ERR_OR_NULL(filp))
 		fput(filp);
 
-	/* because the reference count of fp is 0, it is guaranteed that
-	 * there are not accesses to fp->lock_list.
+	/*
+	 * The zero fp reference count serializes access to fp->lock_list, but
+	 * the VFS may still have blocked requests chained below these locks.
 	 */
 	list_for_each_entry_safe(smb_lock, tmp_lock, &fp->lock_list, flist) {
 		struct ksmbd_conn *conn = smb_lock->conn;
@@ -644,7 +645,8 @@ static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp)
 			ksmbd_conn_put(conn);
 		}
 
-		list_del(&smb_lock->flist);
+		list_del_init(&smb_lock->flist);
+		ksmbd_vfs_posix_lock_unblock(smb_lock->fl);
 		locks_free_lock(smb_lock->fl);
 		kfree(smb_lock);
 	}
-- 
2.34.1
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.