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

ksmbd: fix user-after-free from session log off

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=tags/v6.6.58&id=5511999e9615e4318e9142d23b29bd1597befc08



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

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: 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/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: default avatarLong Li <leo.lilong@huawei.com>
parent d80f46b5
Loading
Loading
Loading
Loading
+20 −4
Original line number Diff line number Diff line
@@ -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)
+3 −0
Original line number Diff line number Diff line
@@ -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__ */
+2 −0
Original line number Diff line number Diff line
@@ -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) {
+7 −1
Original line number Diff line number Diff line
@@ -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;