Skip to content

Commit c1e5502

Browse files
namjaejeongregkh
authored andcommitted
ksmbd: fix race condition between destroy_previous_session() and smb2 operations()
[ Upstream commit 76e98a1 ] 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: stable@vger.kernel.org # v6.6+ Reported-by: zdi-disclosures@trendmicro.com # ZDI-CAN-25040 Signed-off-by: Namjae Jeon <linkinjeon@kernel.org> Signed-off-by: Steve French <stfrench@microsoft.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 91bd3b2 commit c1e5502

4 files changed

Lines changed: 44 additions & 3 deletions

File tree

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: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,7 @@ void destroy_previous_session(struct ksmbd_conn *conn,
310310
{
311311
struct ksmbd_session *prev_sess;
312312
struct ksmbd_user *prev_user;
313+
int err;
313314

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

328+
ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_RECONNECT);
329+
err = ksmbd_conn_wait_idle_sess_id(conn, id);
330+
if (err) {
331+
ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_NEGOTIATE);
332+
goto out;
333+
}
327334
ksmbd_destroy_file_table(&prev_sess->file_table);
328335
prev_sess->state = SMB2_SESSION_EXPIRED;
336+
ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_NEGOTIATE);
329337
out:
330338
up_write(&conn->session_lock);
331339
up_write(&sessions_table_lock);

fs/smb/server/smb2pdu.c

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

22122212
ksmbd_close_session_fds(work);
2213-
ksmbd_conn_wait_idle(conn, sess_id);
2213+
ksmbd_conn_wait_idle(conn);
22142214

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

0 commit comments

Comments
 (0)