Commit 676d80a3 authored by Namjae Jeon's avatar Namjae Jeon Committed by Wen Zhiwei
Browse files

ksmbd: fix race condition between destroy_previous_session() and smb2 operations()

stable inclusion
from stable-v6.6.48
commit 118fd997612d1efc94a86c307c6c6d659ebe29fc
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/IAWEBV

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=118fd997612d1efc94a86c307c6c6d659ebe29fc



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

[ Upstream commit 76e98a158b207771a6c9a0de0a60522a446a3447 ]

If there is ->PreviousSessionId field in the session setup request,
The session of the previous connection should be destroyed.
During this, if the smb2 operation requests in the previous session are
being processed, a racy issue could happen with ksmbd_destroy_file_table().
This patch sets conn->status to KSMBD_SESS_NEED_RECONNECT to block
incoming  operations and waits until on-going operations are complete
(i.e. idle) before desctorying the previous session.

Fixes: c8efcc786146 ("ksmbd: add support for durable handles v1/v2")
Cc: stable@vger.kernel.org # v6.6+
Reported-by: zdi-disclosures@trendmicro.com # ZDI-CAN-25040
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>
Signed-off-by: default avatarWen Zhiwei <wenzhiwei@kylinos.cn>
parent 393e7585
Loading
Loading
Loading
Loading
+33 −1
Original line number Diff line number Diff line
@@ -165,11 +165,43 @@ void ksmbd_all_conn_set_status(u64 sess_id, u32 status)
	up_read(&conn_list_lock);
}

void ksmbd_conn_wait_idle(struct ksmbd_conn *conn, u64 sess_id)
void ksmbd_conn_wait_idle(struct ksmbd_conn *conn)
{
	wait_event(conn->req_running_q, atomic_read(&conn->req_running) < 2);
}

int ksmbd_conn_wait_idle_sess_id(struct ksmbd_conn *curr_conn, u64 sess_id)
{
	struct ksmbd_conn *conn;
	int rc, retry_count = 0, max_timeout = 120;
	int rcount = 1;

retry_idle:
	if (retry_count >= max_timeout)
		return -EIO;

	down_read(&conn_list_lock);
	list_for_each_entry(conn, &conn_list, conns_list) {
		if (conn->binding || xa_load(&conn->sessions, sess_id)) {
			if (conn == curr_conn)
				rcount = 2;
			if (atomic_read(&conn->req_running) >= rcount) {
				rc = wait_event_timeout(conn->req_running_q,
					atomic_read(&conn->req_running) < rcount,
					HZ);
				if (!rc) {
					up_read(&conn_list_lock);
					retry_count++;
					goto retry_idle;
				}
			}
		}
	}
	up_read(&conn_list_lock);

	return 0;
}

int ksmbd_conn_write(struct ksmbd_work *work)
{
	struct ksmbd_conn *conn = work->conn;
+2 −1
Original line number Diff line number Diff line
@@ -145,7 +145,8 @@ extern struct list_head conn_list;
extern struct rw_semaphore conn_list_lock;

bool ksmbd_conn_alive(struct ksmbd_conn *conn);
void ksmbd_conn_wait_idle(struct ksmbd_conn *conn, u64 sess_id);
void ksmbd_conn_wait_idle(struct ksmbd_conn *conn);
int ksmbd_conn_wait_idle_sess_id(struct ksmbd_conn *curr_conn, u64 sess_id);
struct ksmbd_conn *ksmbd_conn_alloc(void);
void ksmbd_conn_free(struct ksmbd_conn *conn);
bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c);
+8 −0
Original line number Diff line number Diff line
@@ -310,6 +310,7 @@ void destroy_previous_session(struct ksmbd_conn *conn,
{
	struct ksmbd_session *prev_sess;
	struct ksmbd_user *prev_user;
	int err;

	down_write(&sessions_table_lock);
	down_write(&conn->session_lock);
@@ -324,8 +325,15 @@ void destroy_previous_session(struct ksmbd_conn *conn,
	    memcmp(user->passkey, prev_user->passkey, user->passkey_sz))
		goto out;

	ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_RECONNECT);
	err = ksmbd_conn_wait_idle_sess_id(conn, id);
	if (err) {
		ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_NEGOTIATE);
		goto out;
	}
	ksmbd_destroy_file_table(&prev_sess->file_table);
	prev_sess->state = SMB2_SESSION_EXPIRED;
	ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_NEGOTIATE);
out:
	up_write(&conn->session_lock);
	up_write(&sessions_table_lock);
+1 −1
Original line number Diff line number Diff line
@@ -2208,7 +2208,7 @@ int smb2_session_logoff(struct ksmbd_work *work)
	ksmbd_conn_unlock(conn);

	ksmbd_close_session_fds(work);
	ksmbd_conn_wait_idle(conn, sess_id);
	ksmbd_conn_wait_idle(conn);

	/*
	 * Re-lookup session to validate if session is deleted