From: Namjae Jeon linkinjeon@kernel.org
stable inclusion from stable-v6.6.57 commit 5511999e9615e4318e9142d23b29bd1597befc08 category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/IB0ENJ CVE: CVE-2024-50086
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?h=t...
--------------------------------
commit 7aa8804c0b67b3cb263a472d17f2cb50d7f1a930 upstream.
There is racy issue between smb2 session log off and smb2 session setup. It will cause user-after-free from session log off. This add session_lock when setting SMB2_SESSION_EXPIRED and referece count to session struct not to free session while it is being used.
Cc: stable@vger.kernel.org # v5.15+ Reported-by: zdi-disclosures@trendmicro.com # ZDI-CAN-25282 Signed-off-by: Namjae Jeon linkinjeon@kernel.org Signed-off-by: Steve French stfrench@microsoft.com Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org Conflicts: fs/ksmbd/mgmt/user_session.c fs/ksmbd/mgmt/user_session.h fs/ksmbd/server.c fs/ksmbd/smb2pdu.c fs/smb/server/mgmt/user_session.c fs/smb/server/user_session.h fs/smb/server/server.c fs/smb/server/smb2pdu.c [Conflicts due to ksmbd rename to smb/server ] Signed-off-by: Long Li leo.lilong@huawei.com --- fs/ksmbd/mgmt/user_session.c | 24 ++++++++++++++++++++---- fs/ksmbd/mgmt/user_session.h | 3 +++ fs/ksmbd/server.c | 2 ++ fs/ksmbd/smb2pdu.c | 8 +++++++- 4 files changed, 32 insertions(+), 5 deletions(-)
diff --git a/fs/ksmbd/mgmt/user_session.c b/fs/ksmbd/mgmt/user_session.c index ee121d1168ce..aba451fda8fa 100644 --- a/fs/ksmbd/mgmt/user_session.c +++ b/fs/ksmbd/mgmt/user_session.c @@ -183,9 +183,10 @@ static void ksmbd_expire_session(struct ksmbd_conn *conn)
down_write(&conn->session_lock); xa_for_each(&conn->sessions, id, sess) { - if (sess->state != SMB2_SESSION_VALID || + if (atomic_read(&sess->refcnt) == 0 && + (sess->state != SMB2_SESSION_VALID || time_after(jiffies, - sess->last_active + SMB2_SESSION_TIMEOUT)) { + sess->last_active + SMB2_SESSION_TIMEOUT))) { xa_erase(&conn->sessions, sess->id); hash_del(&sess->hlist); ksmbd_session_destroy(sess); @@ -275,8 +276,6 @@ struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id)
down_read(&sessions_table_lock); sess = __session_lookup(id); - if (sess) - sess->last_active = jiffies; up_read(&sessions_table_lock);
return sess; @@ -295,6 +294,22 @@ struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn, return sess; }
+void ksmbd_user_session_get(struct ksmbd_session *sess) +{ + atomic_inc(&sess->refcnt); +} + +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); +} + struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn, u64 sess_id) { @@ -362,6 +377,7 @@ static struct ksmbd_session *__session_create(int protocol) xa_init(&sess->ksmbd_chann_list); INIT_LIST_HEAD(&sess->rpc_handle_list); sess->sequence_number = 1; + atomic_set(&sess->refcnt, 1);
ret = __init_smb2_session(sess); if (ret) diff --git a/fs/ksmbd/mgmt/user_session.h b/fs/ksmbd/mgmt/user_session.h index 51f38e5b61ab..4dcbc1f235f0 100644 --- a/fs/ksmbd/mgmt/user_session.h +++ b/fs/ksmbd/mgmt/user_session.h @@ -60,6 +60,7 @@ struct ksmbd_session {
struct ksmbd_file_table file_table; unsigned long last_active; + atomic_t refcnt; };
static inline int test_session_flag(struct ksmbd_session *sess, int bit) @@ -100,4 +101,6 @@ void ksmbd_release_tree_conn_id(struct ksmbd_session *sess, int id); int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name); void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id); int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id); +void ksmbd_user_session_get(struct ksmbd_session *sess); +void ksmbd_user_session_put(struct ksmbd_session *sess); #endif /* __USER_SESSION_MANAGEMENT_H__ */ diff --git a/fs/ksmbd/server.c b/fs/ksmbd/server.c index b353b3f91ce2..daeda36a5eb1 100644 --- a/fs/ksmbd/server.c +++ b/fs/ksmbd/server.c @@ -239,6 +239,8 @@ static void __handle_ksmbd_work(struct ksmbd_work *work, return;
send: + if (work->sess) + ksmbd_user_session_put(work->sess); smb3_preauth_hash_rsp(work); if (work->sess && work->sess->enc && work->encrypted && conn->ops->encrypt_resp) { diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 20f97b37e7c6..829bb46f1f17 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -609,8 +609,10 @@ int smb2_check_user_session(struct ksmbd_work *work)
/* Check for validity of user session */ work->sess = ksmbd_session_lookup_all(conn, sess_id); - if (work->sess) + if (work->sess) { + ksmbd_user_session_get(work->sess); return 1; + } ksmbd_debug(SMB, "Invalid user session, Uid %llu\n", sess_id); return -ENOENT; } @@ -1760,6 +1762,7 @@ int smb2_sess_setup(struct ksmbd_work *work) }
conn->binding = true; + ksmbd_user_session_get(sess); } else if ((conn->dialect < SMB30_PROT_ID || server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) && (req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) { @@ -1786,6 +1789,7 @@ int smb2_sess_setup(struct ksmbd_work *work) }
conn->binding = false; + ksmbd_user_session_get(sess); } work->sess = sess;
@@ -2181,7 +2185,9 @@ int smb2_session_logoff(struct ksmbd_work *work) }
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;