]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph-client.git/commitdiff
ksmbd: fix share_conf UAF in tree_conn disconnect
authorNicholas Carlini <nicholas@carlini.com>
Sun, 8 Mar 2026 23:29:49 +0000 (08:29 +0900)
committerSteve French <stfrench@microsoft.com>
Wed, 18 Mar 2026 02:45:29 +0000 (21:45 -0500)
__ksmbd_tree_conn_disconnect() drops the share_conf reference before
checking tree_conn->refcount. When someone uses SMB3 multichannel and
binds two connections to one session, a SESSION_LOGOFF on connection A
calls ksmbd_conn_wait_idle(conn) which only drains connection A's
request counter, not connection B's. This means there's a race condition:
requests already dispatched on connection B hold tree_conn references via
work->tcon. The disconnect path frees share_conf while those requests
are still walking work->tcon->share_conf, causing a use-after-free.

This fix combines the share_conf put with the tree_conn free so it
only happens when the last reference is dropped.

Fixes: b39a1833cc4a ("ksmbd: fix use-after-free in ksmbd_tree_connect_put under concurrency")
Signed-off-by: Nicholas Carlini <nicholas@carlini.com>
Acked-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/smb/server/mgmt/tree_connect.c

index a72d7e42a6c278b73c59428f6bc8c6184f1e3161..58e5b8592da46f9b29851a4cce6fe301d452ba7a 100644 (file)
@@ -102,8 +102,10 @@ out_error:
 
 void ksmbd_tree_connect_put(struct ksmbd_tree_connect *tcon)
 {
-       if (atomic_dec_and_test(&tcon->refcount))
+       if (atomic_dec_and_test(&tcon->refcount)) {
+               ksmbd_share_config_put(tcon->share_conf);
                kfree(tcon);
+       }
 }
 
 static int __ksmbd_tree_conn_disconnect(struct ksmbd_session *sess,
@@ -113,10 +115,11 @@ static int __ksmbd_tree_conn_disconnect(struct ksmbd_session *sess,
 
        ret = ksmbd_ipc_tree_disconnect_request(sess->id, tree_conn->id);
        ksmbd_release_tree_conn_id(sess, tree_conn->id);
-       ksmbd_share_config_put(tree_conn->share_conf);
        ksmbd_counter_dec(KSMBD_COUNTER_TREE_CONNS);
-       if (atomic_dec_and_test(&tree_conn->refcount))
+       if (atomic_dec_and_test(&tree_conn->refcount)) {
+               ksmbd_share_config_put(tree_conn->share_conf);
                kfree(tree_conn);
+       }
        return ret;
 }