| From e422c48bc373790c7a99f05459d85a5335552b26 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Fri, 22 Jul 2022 10:17:06 +0900 |
| Subject: ksmbd: fix kernel oops from idr_remove() |
| |
| From: Namjae Jeon <linkinjeon@kernel.org> |
| |
| [ 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 | 2 ++ |
| fs/ksmbd/smb2pdu.c | 8 ++++++-- |
| 2 files changed, 8 insertions(+), 2 deletions(-) |
| |
| diff --git a/fs/ksmbd/mgmt/user_session.c b/fs/ksmbd/mgmt/user_session.c |
| index 0c7b5335c12af..9c4a9c8c02795 100644 |
| --- a/fs/ksmbd/mgmt/user_session.c |
| +++ b/fs/ksmbd/mgmt/user_session.c |
| @@ -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; |
| } |
| |
| diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c |
| index e17f7a5dd9974..8bfe2a6c05f92 100644 |
| --- a/fs/ksmbd/smb2pdu.c |
| +++ b/fs/ksmbd/smb2pdu.c |
| @@ -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); |
| } |
| |
| /** |
| -- |
| 2.39.2 |
| |