]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
ksmbd: prevent connection release during oplock break notification
authorNamjae Jeon <linkinjeon@kernel.org>
Thu, 6 Mar 2025 05:14:58 +0000 (14:14 +0900)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 22 Mar 2025 19:54:24 +0000 (12:54 -0700)
commit 3aa660c059240e0c795217182cf7df32909dd917 upstream.

ksmbd_work could be freed when after connection release.
Increment r_count of ksmbd_conn to indicate that requests
are not finished yet and to not release the connection.

Cc: stable@vger.kernel.org
Reported-by: Norbert Szetei <norbert@doyensec.com>
Tested-by: Norbert Szetei <norbert@doyensec.com>
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>
fs/smb/server/connection.c
fs/smb/server/connection.h
fs/smb/server/oplock.c
fs/smb/server/server.c

index bf45822db5d589742d81c2d2c5d913593c2a1c02..ab11246ccd8a098d9c3a61a9c8c77a61fe97b0f1 100644 (file)
@@ -432,6 +432,26 @@ void ksmbd_conn_init_server_callbacks(struct ksmbd_conn_ops *ops)
        default_conn_ops.terminate_fn = ops->terminate_fn;
 }
 
+void ksmbd_conn_r_count_inc(struct ksmbd_conn *conn)
+{
+       atomic_inc(&conn->r_count);
+}
+
+void ksmbd_conn_r_count_dec(struct ksmbd_conn *conn)
+{
+       /*
+        * Checking waitqueue to dropping pending requests on
+        * disconnection. waitqueue_active is safe because it
+        * uses atomic operation for condition.
+        */
+       atomic_inc(&conn->refcnt);
+       if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q))
+               wake_up(&conn->r_count_q);
+
+       if (atomic_dec_and_test(&conn->refcnt))
+               kfree(conn);
+}
+
 int ksmbd_conn_transport_init(void)
 {
        int ret;
index b379ae4fdcdffa98bfe854b77c64423ad943c3b0..91c2318639e76646ee1706bf2fb93c51bdeefb29 100644 (file)
@@ -168,6 +168,8 @@ int ksmbd_conn_transport_init(void);
 void ksmbd_conn_transport_destroy(void);
 void ksmbd_conn_lock(struct ksmbd_conn *conn);
 void ksmbd_conn_unlock(struct ksmbd_conn *conn);
+void ksmbd_conn_r_count_inc(struct ksmbd_conn *conn);
+void ksmbd_conn_r_count_dec(struct ksmbd_conn *conn);
 
 /*
  * WARNING
index 3e84fc90d7df6a5b01fa38c38495acd7680f962c..592fe665973a87cbb4d844d611d9102caf3ddd25 100644 (file)
@@ -634,6 +634,7 @@ static void __smb2_oplock_break_noti(struct work_struct *wk)
 {
        struct smb2_oplock_break *rsp = NULL;
        struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work);
+       struct ksmbd_conn *conn = work->conn;
        struct oplock_break_info *br_info = work->request_buf;
        struct smb2_hdr *rsp_hdr;
        struct ksmbd_file *fp;
@@ -689,6 +690,7 @@ static void __smb2_oplock_break_noti(struct work_struct *wk)
 
 out:
        ksmbd_free_work_struct(work);
+       ksmbd_conn_r_count_dec(conn);
 }
 
 /**
@@ -723,6 +725,7 @@ static int smb2_oplock_break_noti(struct oplock_info *opinfo)
        work->sess = opinfo->sess;
 
        if (opinfo->op_state == OPLOCK_ACK_WAIT) {
+               ksmbd_conn_r_count_inc(conn);
                INIT_WORK(&work->work, __smb2_oplock_break_noti);
                ksmbd_queue_work(work);
 
@@ -744,6 +747,7 @@ static void __smb2_lease_break_noti(struct work_struct *wk)
 {
        struct smb2_lease_break *rsp = NULL;
        struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work);
+       struct ksmbd_conn *conn = work->conn;
        struct lease_break_info *br_info = work->request_buf;
        struct smb2_hdr *rsp_hdr;
 
@@ -790,6 +794,7 @@ static void __smb2_lease_break_noti(struct work_struct *wk)
 
 out:
        ksmbd_free_work_struct(work);
+       ksmbd_conn_r_count_dec(conn);
 }
 
 /**
@@ -829,6 +834,7 @@ static int smb2_lease_break_noti(struct oplock_info *opinfo)
        work->sess = opinfo->sess;
 
        if (opinfo->op_state == OPLOCK_ACK_WAIT) {
+               ksmbd_conn_r_count_inc(conn);
                INIT_WORK(&work->work, __smb2_lease_break_noti);
                ksmbd_queue_work(work);
                wait_for_break_ack(opinfo);
index d146b0e7c3a9ddfc45836667bd483550d4c0e7a0..d523b860236ab3ef4df86f31114e5fef68bf4692 100644 (file)
@@ -270,17 +270,7 @@ static void handle_ksmbd_work(struct work_struct *wk)
 
        ksmbd_conn_try_dequeue_request(work);
        ksmbd_free_work_struct(work);
-       /*
-        * Checking waitqueue to dropping pending requests on
-        * disconnection. waitqueue_active is safe because it
-        * uses atomic operation for condition.
-        */
-       atomic_inc(&conn->refcnt);
-       if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q))
-               wake_up(&conn->r_count_q);
-
-       if (atomic_dec_and_test(&conn->refcnt))
-               kfree(conn);
+       ksmbd_conn_r_count_dec(conn);
 }
 
 /**
@@ -310,7 +300,7 @@ static int queue_ksmbd_work(struct ksmbd_conn *conn)
        conn->request_buf = NULL;
 
        ksmbd_conn_enqueue_request(work);
-       atomic_inc(&conn->r_count);
+       ksmbd_conn_r_count_inc(conn);
        /* update activity on connection */
        conn->last_active = jiffies;
        INIT_WORK(&work->work, handle_ksmbd_work);