On Mon, Aug 26, 2024 at 8:38 PM <gregkh@xxxxxxxxxxxxxxxxxxx> wrote: > > > The patch below does not apply to the 6.10-stable tree. > If someone wants it applied there, or to any other stable or longterm > tree, then please email the backport, including the original git commit > id to <stable@xxxxxxxxxxxxxxx>. Could you please apply the attached backport patch for linux 6.10 stable kernel ? Thanks! > > To reproduce the conflict and resubmit, you may use the following commands: > > git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.10.y > git checkout FETCH_HEAD > git cherry-pick -x 76e98a158b207771a6c9a0de0a60522a446a3447 > # <resolve conflicts, build, test, etc.> > git commit -s > git send-email --to '<stable@xxxxxxxxxxxxxxx>' --in-reply-to '2024082625-savior-clinic-1a91@gregkh' --subject-prefix 'PATCH 6.10.y' HEAD^.. > > Possible dependencies: > > 76e98a158b20 ("ksmbd: fix race condition between destroy_previous_session() and smb2 operations()") > d484d621d40f ("ksmbd: add durable scavenger timer") > > thanks, > > greg k-h > > ------------------ original commit in Linus's tree ------------------ > > From 76e98a158b207771a6c9a0de0a60522a446a3447 Mon Sep 17 00:00:00 2001 > From: Namjae Jeon <linkinjeon@xxxxxxxxxx> > Date: Sat, 17 Aug 2024 14:03:49 +0900 > Subject: [PATCH] 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: c8efcc786146 ("ksmbd: add support for durable handles v1/v2") > Cc: stable@xxxxxxxxxxxxxxx # v6.6+ > Reported-by: zdi-disclosures@xxxxxxxxxxxxxx # ZDI-CAN-25040 > Signed-off-by: Namjae Jeon <linkinjeon@xxxxxxxxxx> > Signed-off-by: Steve French <stfrench@xxxxxxxxxxxxx> > > diff --git a/fs/smb/server/connection.c b/fs/smb/server/connection.c > index 09e1e7771592..7889df8112b4 100644 > --- a/fs/smb/server/connection.c > +++ b/fs/smb/server/connection.c > @@ -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; > diff --git a/fs/smb/server/connection.h b/fs/smb/server/connection.h > index 5c2845e47cf2..5b947175c048 100644 > --- a/fs/smb/server/connection.h > +++ b/fs/smb/server/connection.h > @@ -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); > diff --git a/fs/smb/server/mgmt/user_session.c b/fs/smb/server/mgmt/user_session.c > index 162a12685d2c..99416ce9f501 100644 > --- a/fs/smb/server/mgmt/user_session.c > +++ b/fs/smb/server/mgmt/user_session.c > @@ -311,6 +311,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); > @@ -325,8 +326,16 @@ 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); > ksmbd_launch_ksmbd_durable_scavenger(); > out: > up_write(&conn->session_lock); > diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c > index 3f4c56a10a86..cb7f487c96af 100644 > --- a/fs/smb/server/smb2pdu.c > +++ b/fs/smb/server/smb2pdu.c > @@ -2213,7 +2213,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 >
From 9e0a3d39a36f80fd39b5ec2f943b9514bba1e9bd Mon Sep 17 00:00:00 2001 From: Namjae Jeon <linkinjeon@kernel.org> Date: Tue, 27 Aug 2024 09:27:56 +0900 Subject: [PATCH linux-6.10.y ] ksmbd: fix race condition between destroy_previous_session() and smb2 operations() [ 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: Namjae Jeon <linkinjeon@kernel.org> Signed-off-by: Steve French <stfrench@microsoft.com> --- fs/smb/server/connection.c | 34 ++++++++++++++++++++++++++++++- fs/smb/server/connection.h | 3 ++- fs/smb/server/mgmt/user_session.c | 8 ++++++++ fs/smb/server/smb2pdu.c | 2 +- 4 files changed, 44 insertions(+), 3 deletions(-) diff --git a/fs/smb/server/connection.c b/fs/smb/server/connection.c index 09e1e7771592..7889df8112b4 100644 --- a/fs/smb/server/connection.c +++ b/fs/smb/server/connection.c @@ -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; diff --git a/fs/smb/server/connection.h b/fs/smb/server/connection.h index 0e04cf8b1d89..b93e5437793e 100644 --- a/fs/smb/server/connection.h +++ b/fs/smb/server/connection.h @@ -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); diff --git a/fs/smb/server/mgmt/user_session.c b/fs/smb/server/mgmt/user_session.c index aec0a7a12405..dac5f984f175 100644 --- a/fs/smb/server/mgmt/user_session.c +++ b/fs/smb/server/mgmt/user_session.c @@ -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); diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 592a2cdfd067..4537ea8fd3e5 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -2210,7 +2210,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 -- 2.34.1