ksmbd: scope session state changes to bound connections

ksmbd_all_conn_set_status() treats every connection whose transient
binding flag is set as belonging to the target SessionId.  A logoff or
session replacement can consequently move an unrelated connection to
NEED_RECONNECT or NEED_SETUP.

Pass the target session itself and select connections using either the
connection-local session xarray or the session's permanent channel list.
Use the same association test while waiting for requests to drain.

Serialize session-wide status changes under request_lock and do not
overwrite EXITING or RELEASING. Protect the shutdown transition with the
same lock so a concurrent session update cannot revive a closing
connection.

Fixes: f5a544e3ba ("ksmbd: add support for SMB3 multichannel")
Fixes: abcc506a9a ("ksmbd: fix racy issue from smb2 close and logoff with multichannel")
Fixes: c444139cb7 ("ksmbd: rewrite stop_sessions() with restartable iteration")
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
This commit is contained in:
Namjae Jeon 2026-08-13 09:01:00 +09:00
parent 2cbd4a8bf4
commit c50e628122
4 changed files with 41 additions and 16 deletions

View File

@ -404,15 +404,37 @@ void ksmbd_conn_unlock(struct ksmbd_conn *conn)
mutex_unlock(&conn->srv_mutex);
}
void ksmbd_all_conn_set_status(u64 sess_id, u32 status)
static bool ksmbd_session_is_bound_to_conn(struct ksmbd_session *sess,
struct ksmbd_conn *conn)
{
bool found;
rcu_read_lock();
found = xa_load(&conn->sessions, sess->id) == sess;
rcu_read_unlock();
if (found)
return true;
down_read(&sess->chann_lock);
found = xa_load(&sess->ksmbd_chann_list, (long)conn);
up_read(&sess->chann_lock);
return found;
}
void ksmbd_all_conn_set_status(struct ksmbd_session *sess, u32 status)
{
struct ksmbd_conn *conn;
int bkt;
down_read(&conn_list_lock);
hash_for_each(conn_list, bkt, conn, hlist) {
if (conn->binding || xa_load(&conn->sessions, sess_id))
WRITE_ONCE(conn->status, status);
if (ksmbd_session_is_bound_to_conn(sess, conn)) {
spin_lock(&conn->request_lock);
if (!ksmbd_conn_exiting(conn) &&
!ksmbd_conn_releasing(conn))
WRITE_ONCE(conn->status, status);
spin_unlock(&conn->request_lock);
}
}
up_read(&conn_list_lock);
}
@ -422,7 +444,8 @@ 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)
int ksmbd_conn_wait_idle_sess(struct ksmbd_conn *curr_conn,
struct ksmbd_session *sess)
{
struct ksmbd_conn *conn;
int rc, retry_count = 0, max_timeout = 120;
@ -434,7 +457,7 @@ int ksmbd_conn_wait_idle_sess_id(struct ksmbd_conn *curr_conn, u64 sess_id)
down_read(&conn_list_lock);
hash_for_each(conn_list, bkt, conn, hlist) {
if (conn->binding || xa_load(&conn->sessions, sess_id)) {
if (ksmbd_session_is_bound_to_conn(sess, conn)) {
rcount = (conn == curr_conn) ? 2 : 1;
if (atomic_read(&conn->req_running) >= rcount) {
rc = wait_event_timeout(conn->req_running_q,
@ -780,8 +803,10 @@ static void stop_sessions(void)
* handler exited its receive loop for an unrelated
* reason).
*/
if (READ_ONCE(conn->status) != KSMBD_SESS_RELEASING)
spin_lock(&conn->request_lock);
if (!ksmbd_conn_releasing(conn))
ksmbd_conn_set_exiting(conn);
spin_unlock(&conn->request_lock);
target = conn;
break;
}

View File

@ -23,6 +23,7 @@
#include "ksmbd_work.h"
struct smbdirect_buffer_descriptor_v1;
struct ksmbd_session;
#define KSMBD_SOCKET_BACKLOG 16
@ -196,7 +197,8 @@ extern struct rw_semaphore conn_list_lock;
bool ksmbd_conn_alive(struct ksmbd_conn *conn);
void ksmbd_conn_wait_idle(struct ksmbd_conn *conn);
int ksmbd_conn_wait_idle_sess_id(struct ksmbd_conn *curr_conn, u64 sess_id);
int ksmbd_conn_wait_idle_sess(struct ksmbd_conn *curr_conn,
struct ksmbd_session *sess);
struct ksmbd_conn *ksmbd_conn_alloc(void);
void ksmbd_conn_free(struct ksmbd_conn *conn);
struct ksmbd_conn *ksmbd_conn_get(struct ksmbd_conn *conn);
@ -310,5 +312,5 @@ static inline void ksmbd_conn_set_releasing(struct ksmbd_conn *conn)
WRITE_ONCE(conn->status, KSMBD_SESS_RELEASING);
}
void ksmbd_all_conn_set_status(u64 sess_id, u32 status);
void ksmbd_all_conn_set_status(struct ksmbd_session *sess, u32 status);
#endif /* __CONNECTION_H__ */

View File

@ -647,17 +647,17 @@ 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);
ksmbd_all_conn_set_status(prev_sess, KSMBD_SESS_NEED_RECONNECT);
err = ksmbd_conn_wait_idle_sess(conn, prev_sess);
if (err) {
ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_SETUP);
ksmbd_all_conn_set_status(prev_sess, KSMBD_SESS_NEED_SETUP);
goto out;
}
ksmbd_destroy_file_table(prev_sess);
prev_sess->kerberos_expiry = 0;
prev_sess->state = SMB2_SESSION_EXPIRED;
ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_SETUP);
ksmbd_all_conn_set_status(prev_sess, KSMBD_SESS_NEED_SETUP);
ksmbd_launch_ksmbd_durable_scavenger();
out:
up_write(&conn->session_lock);

View File

@ -2899,7 +2899,6 @@ int smb2_session_logoff(struct ksmbd_work *work)
struct ksmbd_session *sess = work->sess;
struct smb2_logoff_req *req;
struct smb2_logoff_rsp *rsp;
u64 sess_id;
int err;
WORK_BUFFERS(work, req, rsp);
@ -2913,8 +2912,7 @@ int smb2_session_logoff(struct ksmbd_work *work)
smb2_set_err_rsp(work);
return -ENOENT;
}
sess_id = le64_to_cpu(req->hdr.SessionId);
ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_RECONNECT);
ksmbd_all_conn_set_status(sess, KSMBD_SESS_NEED_RECONNECT);
ksmbd_conn_unlock(conn);
ksmbd_close_session_fds(work);
@ -2932,7 +2930,7 @@ int smb2_session_logoff(struct ksmbd_work *work)
sess->state = SMB2_SESSION_EXPIRED;
up_write(&conn->session_lock);
ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_SETUP);
ksmbd_all_conn_set_status(sess, KSMBD_SESS_NEED_SETUP);
rsp->StructureSize = cpu_to_le16(4);
err = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_logoff_rsp));