Commit 5258572a authored by Nicholas Carlini's avatar Nicholas Carlini Committed by Steve French
Browse files

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: b39a1833 ("ksmbd: fix use-after-free in ksmbd_tree_connect_put under concurrency")
Signed-off-by: default avatarNicholas Carlini <nicholas@carlini.com>
Acked-by: default avatarNamjae Jeon <linkinjeon@kernel.org>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent f338e773
Loading
Loading
Loading
Loading
+6 −3
Original line number Diff line number Diff line
@@ -102,9 +102,11 @@ ksmbd_tree_conn_connect(struct ksmbd_work *work, const char *share_name)

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,
					struct ksmbd_tree_connect *tree_conn)
@@ -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;
}