Commit d80f46b5 authored by Namjae Jeon's avatar Namjae Jeon Committed by Long Li
Browse files

ksmbd: fix race condition between session lookup and expire

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=tags/v5.15.145&id=c77fd3e25a51ac92b0f1b347a96eff6a0b4f066f



--------------------------------

[ Upstream commit 53ff5cf8 ]

 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: default avatarluosili <rootlab@huawei.com>
Signed-off-by: default avatarNamjae Jeon <linkinjeon@kernel.org>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
Signed-off-by: default avatarGreg 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: default avatarLong Li <leo.lilong@huawei.com>
parent 351ec151
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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);
+1 −0
Original line number Diff line number Diff line
@@ -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;
+7 −3
Original line number Diff line number Diff line
@@ -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;
}