
From: Namjae Jeon <linkinjeon@kernel.org> mainline inclusion from mainline-v6.15-rc1 commit fa4cdb8cbca7d6cb6aa13e4d8d83d1103f6345db category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/IC1QSM CVE: CVE-2025-22040 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=... -------------------------------- There is a race condition between session setup and ksmbd_sessions_deregister. The session can be freed before the connection is added to channel list of session. This patch check reference count of session before freeing it. Cc: stable@vger.kernel.org Reported-by: Sean Heelan <seanheelan@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org> Signed-off-by: Steve French <stfrench@microsoft.com> Signed-off-by: Wang Zhaolong <wangzhaolong1@huawei.com> --- fs/smb/server/auth.c | 4 ++-- fs/smb/server/mgmt/user_session.c | 14 ++++++++------ fs/smb/server/smb2pdu.c | 7 ++++--- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/fs/smb/server/auth.c b/fs/smb/server/auth.c index 58380a986af5..6af666749b8f 100644 --- a/fs/smb/server/auth.c +++ b/fs/smb/server/auth.c @@ -1010,13 +1010,13 @@ static int ksmbd_get_encryption_key(struct ksmbd_work *work, __u64 ses_id, if (!sess) return -EINVAL; ses_enc_key = enc ? sess->smb3encryptionkey : sess->smb3decryptionkey; - if (enc) - ksmbd_user_session_get(sess); memcpy(key, ses_enc_key, SMB3_ENC_DEC_KEY_SIZE); + if (!enc) + ksmbd_user_session_put(sess); return 0; } static inline void smb2_sg_set_buf(struct scatterlist *sg, const void *buf, diff --git a/fs/smb/server/mgmt/user_session.c b/fs/smb/server/mgmt/user_session.c index e1aabe889c61..50239175e7b4 100644 --- a/fs/smb/server/mgmt/user_session.c +++ b/fs/smb/server/mgmt/user_session.c @@ -178,11 +178,11 @@ static void ksmbd_expire_session(struct ksmbd_conn *conn) struct ksmbd_session *sess; down_write(&sessions_table_lock); down_write(&conn->session_lock); xa_for_each(&conn->sessions, id, sess) { - if (atomic_read(&sess->refcnt) == 0 && + if (atomic_read(&sess->refcnt) <= 1 && (sess->state != SMB2_SESSION_VALID || time_after(jiffies, sess->last_active + SMB2_SESSION_TIMEOUT))) { xa_erase(&conn->sessions, sess->id); hash_del(&sess->hlist); @@ -230,11 +230,12 @@ void ksmbd_sessions_deregister(struct ksmbd_conn *conn) xa_empty(&sess->ksmbd_chann_list)) { hash_del(&sess->hlist); down_write(&conn->session_lock); xa_erase(&conn->sessions, sess->id); up_write(&conn->session_lock); - ksmbd_session_destroy(sess); + if (atomic_dec_and_test(&sess->refcnt)) + ksmbd_session_destroy(sess); } } } down_write(&conn->session_lock); @@ -249,11 +250,12 @@ void ksmbd_sessions_deregister(struct ksmbd_conn *conn) ksmbd_chann_del(conn, sess); if (xa_empty(&sess->ksmbd_chann_list)) { xa_erase(&conn->sessions, sess->id); hash_del(&sess->hlist); - ksmbd_session_destroy(sess); + if (atomic_dec_and_test(&sess->refcnt)) + ksmbd_session_destroy(sess); } } up_write(&conn->session_lock); up_write(&sessions_table_lock); } @@ -309,12 +311,12 @@ void ksmbd_user_session_put(struct ksmbd_session *sess) if (!sess) return; if (atomic_read(&sess->refcnt) <= 0) WARN_ON(1); - else - atomic_dec(&sess->refcnt); + else if (atomic_dec_and_test(&sess->refcnt)) + ksmbd_session_destroy(sess); } struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn, u64 sess_id) { @@ -415,11 +417,11 @@ static struct ksmbd_session *__session_create(int protocol) xa_init(&sess->tree_conns); xa_init(&sess->ksmbd_chann_list); xa_init(&sess->rpc_handle_list); sess->sequence_number = 1; rwlock_init(&sess->tree_conns_lock); - atomic_set(&sess->refcnt, 1); + atomic_set(&sess->refcnt, 2); ret = __init_smb2_session(sess); if (ret) goto error; diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index b5ce09b10e3b..2074d977d5c9 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -2227,17 +2227,18 @@ int smb2_session_logoff(struct ksmbd_work *work) rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; smb2_set_err_rsp(work); return -ENOENT; } - ksmbd_destroy_file_table(&sess->file_table); down_write(&conn->session_lock); sess->state = SMB2_SESSION_EXPIRED; up_write(&conn->session_lock); - ksmbd_free_user(sess->user); - sess->user = NULL; + if (sess->user) { + ksmbd_free_user(sess->user); + sess->user = NULL; + } ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_NEGOTIATE); rsp->StructureSize = cpu_to_le16(4); err = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_logoff_rsp)); if (err) { -- 2.34.3