]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
ksmbd: fix use-after-free in ksmbd_session_rpc_open
authorNamjae Jeon <linkinjeon@kernel.org>
Mon, 2 Feb 2026 03:15:50 +0000 (11:15 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 6 Feb 2026 15:44:24 +0000 (16:44 +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 f78820afd354243e09daaa5795c539159261028c..2603f7dcea58d583764452ae82d2b4f6f3d51fa4 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)
@@ -404,6 +411,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 f4da293c4dbb2cc63191740af58507bb9c9825e8..011362e975b0058c8deec3bd2a2317145be2a613 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)