This patch set fix CVE-2024-50086.
Namjae Jeon (2): ksmbd: fix race condition between session lookup and expire ksmbd: fix user-after-free from session log off
fs/ksmbd/connection.c | 2 ++ fs/ksmbd/connection.h | 1 + fs/ksmbd/mgmt/user_session.c | 34 +++++++++++++++++++++++++++------- fs/ksmbd/mgmt/user_session.h | 3 +++ fs/ksmbd/server.c | 2 ++ fs/ksmbd/smb2pdu.c | 8 +++++++- 6 files changed, 42 insertions(+), 8 deletions(-)
From: Namjae Jeon linkinjeon@kernel.org
stable inclusion from stable-v5.15.144 commit c77fd3e25a51ac92b0f1b347a96eff6a0b4f066f 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...
--------------------------------
[ Upstream commit 53ff5cf89142b978b1a5ca8dc4d4425e6a09745f ]
Thread A + Thread B ksmbd_session_lookup | smb2_sess_setup sess = xa_load | | | xa_erase(&conn->sessions, sess->id); | | ksmbd_session_destroy(sess) --> kfree(sess) | // UAF! | sess->last_active = jiffies | +
This patch add rwsem to fix race condition between ksmbd_session_lookup and ksmbd_expire_session.
Reported-by: luosili rootlab@huawei.com 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/connection.h [Conflict due to not merge 62e6846ee3ba ("ksmbd: casefold utf-8 share names and fix ascii lowercase conversion")] Signed-off-by: Long Li leo.lilong@huawei.com --- fs/ksmbd/connection.c | 2 ++ fs/ksmbd/connection.h | 1 + fs/ksmbd/mgmt/user_session.c | 10 +++++++--- 3 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/fs/ksmbd/connection.c b/fs/ksmbd/connection.c index cb9ce1dd27ae..bc958e43e80c 100644 --- a/fs/ksmbd/connection.c +++ b/fs/ksmbd/connection.c @@ -78,6 +78,8 @@ struct ksmbd_conn *ksmbd_conn_alloc(void) spin_lock_init(&conn->llist_lock); INIT_LIST_HEAD(&conn->lock_list);
+ init_rwsem(&conn->session_lock); + down_write(&conn_list_lock); list_add(&conn->conns_list, &conn_list); up_write(&conn_list_lock); diff --git a/fs/ksmbd/connection.h b/fs/ksmbd/connection.h index 5f44bb27bd82..cb6d67318d7c 100644 --- a/fs/ksmbd/connection.h +++ b/fs/ksmbd/connection.h @@ -48,6 +48,7 @@ struct ksmbd_conn { struct ksmbd_transport *transport; struct nls_table *local_nls; struct list_head conns_list; + struct rw_semaphore session_lock; /* smb session 1 per user */ struct xarray sessions; unsigned long last_active; diff --git a/fs/ksmbd/mgmt/user_session.c b/fs/ksmbd/mgmt/user_session.c index d3f8e9d93c3b..ee121d1168ce 100644 --- a/fs/ksmbd/mgmt/user_session.c +++ b/fs/ksmbd/mgmt/user_session.c @@ -181,7 +181,7 @@ static void ksmbd_expire_session(struct ksmbd_conn *conn) unsigned long id; struct ksmbd_session *sess;
- down_write(&sessions_table_lock); + down_write(&conn->session_lock); xa_for_each(&conn->sessions, id, sess) { if (sess->state != SMB2_SESSION_VALID || time_after(jiffies, @@ -192,7 +192,7 @@ static void ksmbd_expire_session(struct ksmbd_conn *conn) continue; } } - up_write(&sessions_table_lock); + up_write(&conn->session_lock); }
int ksmbd_session_register(struct ksmbd_conn *conn, @@ -234,7 +234,9 @@ void ksmbd_sessions_deregister(struct ksmbd_conn *conn) } } } + up_write(&sessions_table_lock);
+ down_write(&conn->session_lock); xa_for_each(&conn->sessions, id, sess) { unsigned long chann_id; struct channel *chann; @@ -251,7 +253,7 @@ void ksmbd_sessions_deregister(struct ksmbd_conn *conn) ksmbd_session_destroy(sess); } } - up_write(&sessions_table_lock); + up_write(&conn->session_lock); }
struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn, @@ -259,9 +261,11 @@ struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn, { struct ksmbd_session *sess;
+ down_read(&conn->session_lock); sess = xa_load(&conn->sessions, id); if (sess) sess->last_active = jiffies; + up_read(&conn->session_lock); return sess; }
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;
反馈: 您发送到kernel@openeuler.org的补丁/补丁集,已成功转换为PR! PR链接地址: https://gitee.com/openeuler/kernel/pulls/13005 邮件列表地址:https://mailweb.openeuler.org/hyperkitty/list/kernel@openeuler.org/message/Z...
FeedBack: The patch(es) which you have sent to kernel@openeuler.org mailing list has been converted to a pull request successfully! Pull request link: https://gitee.com/openeuler/kernel/pulls/13005 Mailing list address: https://mailweb.openeuler.org/hyperkitty/list/kernel@openeuler.org/message/Z...