Re: FAILED: patch "[PATCH] ksmbd: fix race condition between destroy_previous_session()" failed to apply to 6.10-stable tree

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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


[Index of Archives]     [Linux Kernel]     [Kernel Development Newbies]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite Hiking]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux