ksmbd: add chann_lock to protect ksmbd_chann_list xarray

ksmbd_chann_list xarray lacks synchronization, allowing use-after-free in
multi-channel sessions (between lookup_chann_list() and ksmbd_chann_del).

Adds rw_semaphore chann_lock to struct ksmbd_session and protects
all xa_load/xa_store/xa_erase accesses.

Cc: stable@vger.kernel.org
Reported-by: Igor Stepansky <igor.stepansky@orca.security>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
Namjae Jeon
2026-02-09 10:43:19 +09:00
committed by Steve French
parent 164cacd0ba
commit 4f3a06cc57
3 changed files with 17 additions and 1 deletions

View File

@@ -244,12 +244,14 @@ static void free_channel_list(struct ksmbd_session *sess)
struct channel *chann;
unsigned long index;
down_write(&sess->chann_lock);
xa_for_each(&sess->ksmbd_chann_list, index, chann) {
xa_erase(&sess->ksmbd_chann_list, index);
kfree(chann);
}
xa_destroy(&sess->ksmbd_chann_list);
up_write(&sess->chann_lock);
}
static void __session_rpc_close(struct ksmbd_session *sess,
@@ -434,7 +436,9 @@ static int ksmbd_chann_del(struct ksmbd_conn *conn, struct ksmbd_session *sess)
{
struct channel *chann;
down_write(&sess->chann_lock);
chann = xa_erase(&sess->ksmbd_chann_list, (long)conn);
up_write(&sess->chann_lock);
if (!chann)
return -ENOENT;
@@ -668,6 +672,7 @@ static struct ksmbd_session *__session_create(int protocol)
rwlock_init(&sess->tree_conns_lock);
atomic_set(&sess->refcnt, 2);
init_rwsem(&sess->rpc_lock);
init_rwsem(&sess->chann_lock);
ret = __init_smb2_session(sess);
if (ret)

View File

@@ -48,6 +48,7 @@ struct ksmbd_session {
char sess_key[CIFS_KEY_SIZE];
struct hlist_node hlist;
struct rw_semaphore chann_lock;
struct xarray ksmbd_chann_list;
struct xarray tree_conns;
struct ida tree_conn_ida;

View File

@@ -80,7 +80,13 @@ static inline bool check_session_id(struct ksmbd_conn *conn, u64 id)
struct channel *lookup_chann_list(struct ksmbd_session *sess, struct ksmbd_conn *conn)
{
return xa_load(&sess->ksmbd_chann_list, (long)conn);
struct channel *chann;
down_read(&sess->chann_lock);
chann = xa_load(&sess->ksmbd_chann_list, (long)conn);
up_read(&sess->chann_lock);
return chann;
}
/**
@@ -1559,8 +1565,10 @@ binding_session:
return -ENOMEM;
chann->conn = conn;
down_write(&sess->chann_lock);
old = xa_store(&sess->ksmbd_chann_list, (long)conn, chann,
KSMBD_DEFAULT_GFP);
up_write(&sess->chann_lock);
if (xa_is_err(old)) {
kfree(chann);
return xa_err(old);
@@ -1652,8 +1660,10 @@ binding_session:
return -ENOMEM;
chann->conn = conn;
down_write(&sess->chann_lock);
old = xa_store(&sess->ksmbd_chann_list, (long)conn,
chann, KSMBD_DEFAULT_GFP);
up_write(&sess->chann_lock);
if (xa_is_err(old)) {
kfree(chann);
return xa_err(old);