ksmbd: fix use-after-free in ksmbd_session_rpc_open

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>
This commit is contained in:
Namjae Jeon 2025-04-17 10:10:15 +09:00 committed by Steve French
parent af5226abb4
commit a1f46c99d9
2 changed files with 15 additions and 6 deletions

View File

@ -59,10 +59,12 @@ static void ksmbd_session_rpc_clear_list(struct ksmbd_session *sess)
struct ksmbd_session_rpc *entry; struct ksmbd_session_rpc *entry;
long index; long index;
down_write(&sess->rpc_lock);
xa_for_each(&sess->rpc_handle_list, index, entry) { xa_for_each(&sess->rpc_handle_list, index, entry) {
xa_erase(&sess->rpc_handle_list, index); xa_erase(&sess->rpc_handle_list, index);
__session_rpc_close(sess, entry); __session_rpc_close(sess, entry);
} }
up_write(&sess->rpc_lock);
xa_destroy(&sess->rpc_handle_list); 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_session_rpc *entry, *old;
struct ksmbd_rpc_command *resp; struct ksmbd_rpc_command *resp;
int method; int method, id;
method = __rpc_method(rpc_name); method = __rpc_method(rpc_name);
if (!method) if (!method)
@ -102,26 +104,29 @@ int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name)
if (!entry) if (!entry)
return -ENOMEM; return -ENOMEM;
down_read(&sess->rpc_lock);
entry->method = method; entry->method = method;
entry->id = ksmbd_ipc_id_alloc(); entry->id = id = ksmbd_ipc_id_alloc();
if (entry->id < 0) if (id < 0)
goto free_entry; 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)) if (xa_is_err(old))
goto free_id; goto free_id;
resp = ksmbd_rpc_open(sess, entry->id); resp = ksmbd_rpc_open(sess, id);
if (!resp) if (!resp)
goto erase_xa; goto erase_xa;
up_read(&sess->rpc_lock);
kvfree(resp); kvfree(resp);
return entry->id; return id;
erase_xa: erase_xa:
xa_erase(&sess->rpc_handle_list, entry->id); xa_erase(&sess->rpc_handle_list, entry->id);
free_id: free_id:
ksmbd_rpc_id_free(entry->id); ksmbd_rpc_id_free(entry->id);
free_entry: free_entry:
kfree(entry); kfree(entry);
up_read(&sess->rpc_lock);
return -EINVAL; return -EINVAL;
} }
@ -129,9 +134,11 @@ void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id)
{ {
struct ksmbd_session_rpc *entry; struct ksmbd_session_rpc *entry;
down_write(&sess->rpc_lock);
entry = xa_erase(&sess->rpc_handle_list, id); entry = xa_erase(&sess->rpc_handle_list, id);
if (entry) if (entry)
__session_rpc_close(sess, entry); __session_rpc_close(sess, entry);
up_write(&sess->rpc_lock);
} }
int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id) 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; sess->sequence_number = 1;
rwlock_init(&sess->tree_conns_lock); rwlock_init(&sess->tree_conns_lock);
atomic_set(&sess->refcnt, 2); atomic_set(&sess->refcnt, 2);
init_rwsem(&sess->rpc_lock);
ret = __init_smb2_session(sess); ret = __init_smb2_session(sess);
if (ret) if (ret)

View File

@ -63,6 +63,7 @@ struct ksmbd_session {
rwlock_t tree_conns_lock; rwlock_t tree_conns_lock;
atomic_t refcnt; atomic_t refcnt;
struct rw_semaphore rpc_lock;
}; };
static inline int test_session_flag(struct ksmbd_session *sess, int bit) static inline int test_session_flag(struct ksmbd_session *sess, int bit)