ksmbd: fix share_conf UAF in tree_conn disconnect

__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: b39a1833cc ("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>
This commit is contained in:
Nicholas Carlini
2026-03-09 08:29:49 +09:00
committed by Steve French
parent f338e77383
commit 5258572aa5

View File

@@ -102,8 +102,10 @@ out_error:
void ksmbd_tree_connect_put(struct ksmbd_tree_connect *tcon) 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); kfree(tcon);
}
} }
static int __ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, 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); ret = ksmbd_ipc_tree_disconnect_request(sess->id, tree_conn->id);
ksmbd_release_tree_conn_id(sess, 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); 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); kfree(tree_conn);
}
return ret; return ret;
} }