]> git.itanic.dy.fi Git - linux-stable/commitdiff
ksmbd: fix kernel oops from idr_remove()
authorNamjae Jeon <linkinjeon@kernel.org>
Fri, 22 Jul 2022 01:17:06 +0000 (10:17 +0900)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 17 May 2023 09:50:28 +0000 (11:50 +0200)
[ Upstream commit 17ea92a9f6d0b9a97aaec5ab748e4591d70a562c ]

There is a report that kernel oops happen from idr_remove().

kernel: BUG: kernel NULL pointer dereference, address: 0000000000000010
kernel: RIP: 0010:idr_remove+0x1/0x20
kernel:  __ksmbd_close_fd+0xb2/0x2d0 [ksmbd]
kernel:  ksmbd_vfs_read+0x91/0x190 [ksmbd]
kernel:  ksmbd_fd_put+0x29/0x40 [ksmbd]
kernel:  smb2_read+0x210/0x390 [ksmbd]
kernel:  __process_request+0xa4/0x180 [ksmbd]
kernel:  __handle_ksmbd_work+0xf0/0x290 [ksmbd]
kernel:  handle_ksmbd_work+0x2d/0x50 [ksmbd]
kernel:  process_one_work+0x21d/0x3f0
kernel:  worker_thread+0x50/0x3d0
kernel:  rescuer_thread+0x390/0x390
kernel:  kthread+0xee/0x120
kthread_complete_and_exit+0x20/0x20
kernel:  ret_from_fork+0x22/0x30

While accessing files, If connection is disconnected, windows send
session setup request included previous session destroy. But while still
processing requests on previous session, this request destroy file
table, which mean file table idr will be freed and set to NULL.
So kernel oops happen from ft->idr in __ksmbd_close_fd().
This patch don't directly destroy session in destroy_previous_session().
It just set to KSMBD_SESS_EXITING so that connection will be
terminated after finishing the rest of requests.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
Stable-dep-of: 7b4323373d84 ("ksmbd: fix deadlock in ksmbd_find_crypto_ctx()")
Signed-off-by: Sasha Levin <sashal@kernel.org>
fs/ksmbd/mgmt/user_session.c
fs/ksmbd/smb2pdu.c

index 0c7b5335c12aff1e21fdd297a100658104589ec2..9c4a9c8c027953c2eb36faf73affdb1e4baafc1d 100644 (file)
@@ -241,6 +241,8 @@ struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn,
        sess = ksmbd_session_lookup(conn, id);
        if (!sess && conn->binding)
                sess = ksmbd_session_lookup_slowpath(id);
+       if (sess && sess->state != SMB2_SESSION_VALID)
+               sess = NULL;
        return sess;
 }
 
index e17f7a5dd9974001c2c9797bc563c03726c3a320..8bfe2a6c05f92f397a98eade073c7bf470f8e761 100644 (file)
@@ -600,6 +600,7 @@ static void destroy_previous_session(struct ksmbd_conn *conn,
 {
        struct ksmbd_session *prev_sess = ksmbd_session_lookup_slowpath(id);
        struct ksmbd_user *prev_user;
+       struct channel *chann;
 
        if (!prev_sess)
                return;
@@ -615,8 +616,11 @@ static void destroy_previous_session(struct ksmbd_conn *conn,
        }
 
        put_session(prev_sess);
-       xa_erase(&conn->sessions, prev_sess->id);
-       ksmbd_session_destroy(prev_sess);
+       prev_sess->state = SMB2_SESSION_EXPIRED;
+       write_lock(&prev_sess->chann_lock);
+       list_for_each_entry(chann, &prev_sess->ksmbd_chann_list, chann_list)
+               chann->conn->status = KSMBD_SESS_EXITING;
+       write_unlock(&prev_sess->chann_lock);
 }
 
 /**