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; }