Skip to content

Commit 76e98a1

Browse files
namjaejeonsmfrench
authored andcommitted
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: c8efcc7 ("ksmbd: add support for durable handles v1/v2") Cc: [email protected] # v6.6+ Reported-by: [email protected] # ZDI-CAN-25040 Signed-off-by: Namjae Jeon <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent dfd046d commit 76e98a1

File tree

4 files changed

+45
-3
lines changed

4 files changed

+45
-3
lines changed

fs/smb/server/connection.c

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,11 +165,43 @@ void ksmbd_all_conn_set_status(u64 sess_id, u32 status)
165165
up_read(&conn_list_lock);
166166
}
167167

168-
void ksmbd_conn_wait_idle(struct ksmbd_conn *conn, u64 sess_id)
168+
void ksmbd_conn_wait_idle(struct ksmbd_conn *conn)
169169
{
170170
wait_event(conn->req_running_q, atomic_read(&conn->req_running) < 2);
171171
}
172172

173+
int ksmbd_conn_wait_idle_sess_id(struct ksmbd_conn *curr_conn, u64 sess_id)
174+
{
175+
struct ksmbd_conn *conn;
176+
int rc, retry_count = 0, max_timeout = 120;
177+
int rcount = 1;
178+
179+
retry_idle:
180+
if (retry_count >= max_timeout)
181+
return -EIO;
182+
183+
down_read(&conn_list_lock);
184+
list_for_each_entry(conn, &conn_list, conns_list) {
185+
if (conn->binding || xa_load(&conn->sessions, sess_id)) {
186+
if (conn == curr_conn)
187+
rcount = 2;
188+
if (atomic_read(&conn->req_running) >= rcount) {
189+
rc = wait_event_timeout(conn->req_running_q,
190+
atomic_read(&conn->req_running) < rcount,
191+
HZ);
192+
if (!rc) {
193+
up_read(&conn_list_lock);
194+
retry_count++;
195+
goto retry_idle;
196+
}
197+
}
198+
}
199+
}
200+
up_read(&conn_list_lock);
201+
202+
return 0;
203+
}
204+
173205
int ksmbd_conn_write(struct ksmbd_work *work)
174206
{
175207
struct ksmbd_conn *conn = work->conn;

fs/smb/server/connection.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,8 @@ extern struct list_head conn_list;
145145
extern struct rw_semaphore conn_list_lock;
146146

147147
bool ksmbd_conn_alive(struct ksmbd_conn *conn);
148-
void ksmbd_conn_wait_idle(struct ksmbd_conn *conn, u64 sess_id);
148+
void ksmbd_conn_wait_idle(struct ksmbd_conn *conn);
149+
int ksmbd_conn_wait_idle_sess_id(struct ksmbd_conn *curr_conn, u64 sess_id);
149150
struct ksmbd_conn *ksmbd_conn_alloc(void);
150151
void ksmbd_conn_free(struct ksmbd_conn *conn);
151152
bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c);

fs/smb/server/mgmt/user_session.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,7 @@ void destroy_previous_session(struct ksmbd_conn *conn,
311311
{
312312
struct ksmbd_session *prev_sess;
313313
struct ksmbd_user *prev_user;
314+
int err;
314315

315316
down_write(&sessions_table_lock);
316317
down_write(&conn->session_lock);
@@ -325,8 +326,16 @@ void destroy_previous_session(struct ksmbd_conn *conn,
325326
memcmp(user->passkey, prev_user->passkey, user->passkey_sz))
326327
goto out;
327328

329+
ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_RECONNECT);
330+
err = ksmbd_conn_wait_idle_sess_id(conn, id);
331+
if (err) {
332+
ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_NEGOTIATE);
333+
goto out;
334+
}
335+
328336
ksmbd_destroy_file_table(&prev_sess->file_table);
329337
prev_sess->state = SMB2_SESSION_EXPIRED;
338+
ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_NEGOTIATE);
330339
ksmbd_launch_ksmbd_durable_scavenger();
331340
out:
332341
up_write(&conn->session_lock);

fs/smb/server/smb2pdu.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2213,7 +2213,7 @@ int smb2_session_logoff(struct ksmbd_work *work)
22132213
ksmbd_conn_unlock(conn);
22142214

22152215
ksmbd_close_session_fds(work);
2216-
ksmbd_conn_wait_idle(conn, sess_id);
2216+
ksmbd_conn_wait_idle(conn);
22172217

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

0 commit comments

Comments
 (0)