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)
committerSteve French <stfrench@microsoft.com>
Thu, 5 Oct 2023 01:21:48 +0000 (20:21 -0500)
 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>
fs/smb/server/connection.c
fs/smb/server/connection.h
fs/smb/server/mgmt/user_session.c

index db7fa70..4b38c3a 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 ab2583f..3c00524 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 8a5dcab..b8be14a 100644 (file)
@@ -174,7 +174,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,
@@ -185,7 +185,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,
@@ -227,7 +227,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;
@@ -244,7 +246,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,
@@ -252,9 +254,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;
 }