ksmbd: fix use-after-free in ksmbd_session_rpc_open
authorNamjae Jeon <linkinjeon@kernel.org>
Thu, 17 Apr 2025 01:10:15 +0000 (10:10 +0900)
committerSteve French <stfrench@microsoft.com>
Wed, 23 Apr 2025 01:45:43 +0000 (20:45 -0500)
A UAF issue can occur due to a race condition between
ksmbd_session_rpc_open() and __session_rpc_close().
Add rpc_lock to the session to protect it.

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>
fs/smb/server/mgmt/user_session.c
fs/smb/server/mgmt/user_session.h

index 3f45f28f6f0f8e07179f61d595fb7e3fc56f7b28..9dec4c2940bc0467def4c865504b52ca502a19f7 100644 (file)
@@ -59,10 +59,12 @@ static void ksmbd_session_rpc_clear_list(struct ksmbd_session *sess)
        struct ksmbd_session_rpc *entry;
        long index;
 
+       down_write(&sess->rpc_lock);
        xa_for_each(&sess->rpc_handle_list, index, entry) {
                xa_erase(&sess->rpc_handle_list, index);
                __session_rpc_close(sess, entry);
        }
+       up_write(&sess->rpc_lock);
 
        xa_destroy(&sess->rpc_handle_list);
 }
@@ -92,7 +94,7 @@ int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name)
 {
        struct ksmbd_session_rpc *entry, *old;
        struct ksmbd_rpc_command *resp;
-       int method;
+       int method, id;
 
        method = __rpc_method(rpc_name);
        if (!method)
@@ -102,26 +104,29 @@ int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name)
        if (!entry)
                return -ENOMEM;
 
+       down_read(&sess->rpc_lock);
        entry->method = method;
-       entry->id = ksmbd_ipc_id_alloc();
-       if (entry->id < 0)
+       entry->id = id = ksmbd_ipc_id_alloc();
+       if (id < 0)
                goto free_entry;
-       old = xa_store(&sess->rpc_handle_list, entry->id, entry, KSMBD_DEFAULT_GFP);
+       old = xa_store(&sess->rpc_handle_list, id, entry, KSMBD_DEFAULT_GFP);
        if (xa_is_err(old))
                goto free_id;
 
-       resp = ksmbd_rpc_open(sess, entry->id);
+       resp = ksmbd_rpc_open(sess, id);
        if (!resp)
                goto erase_xa;
 
+       up_read(&sess->rpc_lock);
        kvfree(resp);
-       return entry->id;
+       return id;
 erase_xa:
        xa_erase(&sess->rpc_handle_list, entry->id);
 free_id:
        ksmbd_rpc_id_free(entry->id);
 free_entry:
        kfree(entry);
+       up_read(&sess->rpc_lock);
        return -EINVAL;
 }
 
@@ -129,9 +134,11 @@ void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id)
 {
        struct ksmbd_session_rpc *entry;
 
+       down_write(&sess->rpc_lock);
        entry = xa_erase(&sess->rpc_handle_list, id);
        if (entry)
                __session_rpc_close(sess, entry);
+       up_write(&sess->rpc_lock);
 }
 
 int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id)
@@ -439,6 +446,7 @@ static struct ksmbd_session *__session_create(int protocol)
        sess->sequence_number = 1;
        rwlock_init(&sess->tree_conns_lock);
        atomic_set(&sess->refcnt, 2);
+       init_rwsem(&sess->rpc_lock);
 
        ret = __init_smb2_session(sess);
        if (ret)
index f21348381d5984fd02b77c4eefbfd02ef02a5f25..c5749d6ec7151caf826282f93f33701138eb2d77 100644 (file)
@@ -63,6 +63,7 @@ struct ksmbd_session {
        rwlock_t                        tree_conns_lock;
 
        atomic_t                        refcnt;
+       struct rw_semaphore             rpc_lock;
 };
 
 static inline int test_session_flag(struct ksmbd_session *sess, int bit)