]> git.baikalelectronics.ru Git - kernel.git/commitdiff
ksmbd: fix race condition between session lookup and expire
authorNamjae Jeon <linkinjeon@kernel.org>
Wed, 4 Oct 2023 09:25:01 +0000 (18:25 +0900)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 10 Oct 2023 20:00:45 +0000 (22:00 +0200)
commit 53ff5cf89142b978b1a5ca8dc4d4425e6a09745f upstream.

 Thread A                        +  Thread B
 ksmbd_session_lookup            |  smb2_sess_setup
   sess = xa_load                |
                                 |
                                 |    xa_erase(&conn->sessions, sess->id);
                                 |
                                 |    ksmbd_session_destroy(sess) --> kfree(sess)
                                 |
   // UAF!                       |
   sess->last_active = jiffies   |
                                 +

This patch add rwsem to fix race condition between ksmbd_session_lookup
and ksmbd_expire_session.

Reported-by: luosili <rootlab@huawei.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/mgmt/user_session.c

index e1d2be19cddfa96b712cb19b3ecf3350dcbae384..ff97cad8d5b45aaeb333ed1d468ccc0def22cfc7 100644 (file)
@@ -84,6 +84,8 @@ struct ksmbd_conn *ksmbd_conn_alloc(void)
        spin_lock_init(&conn->llist_lock);
        INIT_LIST_HEAD(&conn->lock_list);
 
+       init_rwsem(&conn->session_lock);
+
        down_write(&conn_list_lock);
        list_add(&conn->conns_list, &conn_list);
        up_write(&conn_list_lock);
index ad8dfaa48ffb381cd7cc515dc8a9fa926d76b389..335fdd714d595c7302c10ccf94f583ffc43550cb 100644 (file)
@@ -50,6 +50,7 @@ struct ksmbd_conn {
        struct nls_table                *local_nls;
        struct unicode_map              *um;
        struct list_head                conns_list;
+       struct rw_semaphore             session_lock;
        /* smb session 1 per user */
        struct xarray                   sessions;
        unsigned long                   last_active;
index ea4b56d570fbb13397265d72050ce9a84e570446..cf6621e21ba36ba10d00f418976e1a91e830f381 100644 (file)
@@ -183,7 +183,7 @@ static void ksmbd_expire_session(struct ksmbd_conn *conn)
        unsigned long id;
        struct ksmbd_session *sess;
 
-       down_write(&sessions_table_lock);
+       down_write(&conn->session_lock);
        xa_for_each(&conn->sessions, id, sess) {
                if (sess->state != SMB2_SESSION_VALID ||
                    time_after(jiffies,
@@ -194,7 +194,7 @@ static void ksmbd_expire_session(struct ksmbd_conn *conn)
                        continue;
                }
        }
-       up_write(&sessions_table_lock);
+       up_write(&conn->session_lock);
 }
 
 int ksmbd_session_register(struct ksmbd_conn *conn,
@@ -236,7 +236,9 @@ void ksmbd_sessions_deregister(struct ksmbd_conn *conn)
                        }
                }
        }
+       up_write(&sessions_table_lock);
 
+       down_write(&conn->session_lock);
        xa_for_each(&conn->sessions, id, sess) {
                unsigned long chann_id;
                struct channel *chann;
@@ -253,7 +255,7 @@ void ksmbd_sessions_deregister(struct ksmbd_conn *conn)
                        ksmbd_session_destroy(sess);
                }
        }
-       up_write(&sessions_table_lock);
+       up_write(&conn->session_lock);
 }
 
 struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn,
@@ -261,9 +263,11 @@ struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn,
 {
        struct ksmbd_session *sess;
 
+       down_read(&conn->session_lock);
        sess = xa_load(&conn->sessions, id);
        if (sess)
                sess->last_active = jiffies;
+       up_read(&conn->session_lock);
        return sess;
 }