]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
ksmbd: fix use-after-free in ksmbd_session_rpc_open
authorNamjae Jeon <linkinjeon@kernel.org>
Tue, 27 Jan 2026 08:31:49 +0000 (16:31 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 30 Jan 2026 09:27:43 +0000 (10:27 +0100)
[ Upstream commit a1f46c99d9ea411f9bf30025b912d881d36fc709 ]

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>
[ KSMBD_DEFAULT_GFP is introduced by commit 0066f623bce8 ("ksmbd: use __GFP_RETRY_MAYFAIL")
 after linux-6.13. Here we still use GFP_KERNEL. ]
Signed-off-by: Li hongliang <1468888505@139.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/smb/server/mgmt/user_session.c
fs/smb/server/mgmt/user_session.h

index 0ca788009c936c5401f9feed3b63f92c7fa08b99..450a9f8ca7c7b5d2d2fb4953272aec85c51f9ff9 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, GFP_KERNEL);
+       old = xa_store(&sess->rpc_handle_list, id, entry, GFP_KERNEL);
        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)
@@ -438,6 +445,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)