Commit 76e98a15 authored by Namjae Jeon's avatar Namjae Jeon Committed by Steve French

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

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: c8efcc78 ("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>
parent dfd046d0
...@@ -165,11 +165,43 @@ void ksmbd_all_conn_set_status(u64 sess_id, u32 status) ...@@ -165,11 +165,43 @@ void ksmbd_all_conn_set_status(u64 sess_id, u32 status)
up_read(&conn_list_lock); 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); 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) int ksmbd_conn_write(struct ksmbd_work *work)
{ {
struct ksmbd_conn *conn = work->conn; struct ksmbd_conn *conn = work->conn;
......
...@@ -145,7 +145,8 @@ extern struct list_head conn_list; ...@@ -145,7 +145,8 @@ extern struct list_head conn_list;
extern struct rw_semaphore conn_list_lock; extern struct rw_semaphore conn_list_lock;
bool ksmbd_conn_alive(struct ksmbd_conn *conn); 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); struct ksmbd_conn *ksmbd_conn_alloc(void);
void ksmbd_conn_free(struct ksmbd_conn *conn); void ksmbd_conn_free(struct ksmbd_conn *conn);
bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c); bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c);
......
...@@ -311,6 +311,7 @@ void destroy_previous_session(struct ksmbd_conn *conn, ...@@ -311,6 +311,7 @@ void destroy_previous_session(struct ksmbd_conn *conn,
{ {
struct ksmbd_session *prev_sess; struct ksmbd_session *prev_sess;
struct ksmbd_user *prev_user; struct ksmbd_user *prev_user;
int err;
down_write(&sessions_table_lock); down_write(&sessions_table_lock);
down_write(&conn->session_lock); down_write(&conn->session_lock);
...@@ -325,8 +326,16 @@ void destroy_previous_session(struct ksmbd_conn *conn, ...@@ -325,8 +326,16 @@ void destroy_previous_session(struct ksmbd_conn *conn,
memcmp(user->passkey, prev_user->passkey, user->passkey_sz)) memcmp(user->passkey, prev_user->passkey, user->passkey_sz))
goto out; 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); ksmbd_destroy_file_table(&prev_sess->file_table);
prev_sess->state = SMB2_SESSION_EXPIRED; prev_sess->state = SMB2_SESSION_EXPIRED;
ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_NEGOTIATE);
ksmbd_launch_ksmbd_durable_scavenger(); ksmbd_launch_ksmbd_durable_scavenger();
out: out:
up_write(&conn->session_lock); up_write(&conn->session_lock);
......
...@@ -2213,7 +2213,7 @@ int smb2_session_logoff(struct ksmbd_work *work) ...@@ -2213,7 +2213,7 @@ int smb2_session_logoff(struct ksmbd_work *work)
ksmbd_conn_unlock(conn); ksmbd_conn_unlock(conn);
ksmbd_close_session_fds(work); 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 * Re-lookup session to validate if session is deleted
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment