summaryrefslogtreecommitdiff
path: root/fs/ksmbd/mgmt/user_session.c
diff options
context:
space:
mode:
authorNamjae Jeon <linkinjeon@kernel.org>2022-07-25 07:36:52 +0300
committerSteve French <stfrench@microsoft.com>2022-08-01 07:14:32 +0300
commitaf7c39d971e43cd0af488729bca362427ad99488 (patch)
tree3c174aa7d504f663a6741a5dffee77666f86df65 /fs/ksmbd/mgmt/user_session.c
parenta14c573870a664386adc10526a6c2648ea56dae1 (diff)
downloadlinux-af7c39d971e43cd0af488729bca362427ad99488.tar.xz
ksmbd: fix racy issue while destroying session on multichannel
After multi-channel connection with windows, Several channels of session are connected. Among them, if there is a problem in one channel, Windows connects again after disconnecting the channel. In this process, the session is released and a kernel oop can occurs while processing requests to other channels. When the channel is disconnected, if other channels still exist in the session after deleting the channel from the channel list in the session, the session should not be released. Finally, the session will be released after all channels are disconnected. Signed-off-by: Namjae Jeon <linkinjeon@kernel.org> Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Steve French <stfrench@microsoft.com>
Diffstat (limited to 'fs/ksmbd/mgmt/user_session.c')
-rw-r--r--fs/ksmbd/mgmt/user_session.c67
1 files changed, 45 insertions, 22 deletions
diff --git a/fs/ksmbd/mgmt/user_session.c b/fs/ksmbd/mgmt/user_session.c
index b9acb6770b03..3fa2139a0b30 100644
--- a/fs/ksmbd/mgmt/user_session.c
+++ b/fs/ksmbd/mgmt/user_session.c
@@ -151,9 +151,6 @@ void ksmbd_session_destroy(struct ksmbd_session *sess)
if (!sess)
return;
- if (!atomic_dec_and_test(&sess->refcnt))
- return;
-
down_write(&sessions_table_lock);
hash_del(&sess->hlist);
up_write(&sessions_table_lock);
@@ -184,16 +181,58 @@ static struct ksmbd_session *__session_lookup(unsigned long long id)
int ksmbd_session_register(struct ksmbd_conn *conn,
struct ksmbd_session *sess)
{
- sess->conn = conn;
+ sess->dialect = conn->dialect;
+ memcpy(sess->ClientGUID, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE);
return xa_err(xa_store(&conn->sessions, sess->id, sess, GFP_KERNEL));
}
+static int ksmbd_chann_del(struct ksmbd_conn *conn, struct ksmbd_session *sess)
+{
+ struct channel *chann, *tmp;
+
+ write_lock(&sess->chann_lock);
+ list_for_each_entry_safe(chann, tmp, &sess->ksmbd_chann_list,
+ chann_list) {
+ if (chann->conn == conn) {
+ list_del(&chann->chann_list);
+ kfree(chann);
+ write_unlock(&sess->chann_lock);
+ return 0;
+ }
+ }
+ write_unlock(&sess->chann_lock);
+
+ return -ENOENT;
+}
+
void ksmbd_sessions_deregister(struct ksmbd_conn *conn)
{
struct ksmbd_session *sess;
- unsigned long id;
- xa_for_each(&conn->sessions, id, sess) {
+ if (conn->binding) {
+ int bkt;
+
+ down_write(&sessions_table_lock);
+ hash_for_each(sessions_table, bkt, sess, hlist) {
+ if (!ksmbd_chann_del(conn, sess)) {
+ up_write(&sessions_table_lock);
+ goto sess_destroy;
+ }
+ }
+ up_write(&sessions_table_lock);
+ } else {
+ unsigned long id;
+
+ xa_for_each(&conn->sessions, id, sess) {
+ if (!ksmbd_chann_del(conn, sess))
+ goto sess_destroy;
+ }
+ }
+
+ return;
+
+sess_destroy:
+ if (list_empty(&sess->ksmbd_chann_list)) {
xa_erase(&conn->sessions, sess->id);
ksmbd_session_destroy(sess);
}
@@ -205,27 +244,12 @@ struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn,
return xa_load(&conn->sessions, id);
}
-int get_session(struct ksmbd_session *sess)
-{
- return atomic_inc_not_zero(&sess->refcnt);
-}
-
-void put_session(struct ksmbd_session *sess)
-{
- if (atomic_dec_and_test(&sess->refcnt))
- pr_err("get/%s seems to be mismatched.", __func__);
-}
-
struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id)
{
struct ksmbd_session *sess;
down_read(&sessions_table_lock);
sess = __session_lookup(id);
- if (sess) {
- if (!get_session(sess))
- sess = NULL;
- }
up_read(&sessions_table_lock);
return sess;
@@ -306,7 +330,6 @@ static struct ksmbd_session *__session_create(int protocol)
INIT_LIST_HEAD(&sess->ksmbd_chann_list);
INIT_LIST_HEAD(&sess->rpc_handle_list);
sess->sequence_number = 1;
- atomic_set(&sess->refcnt, 1);
rwlock_init(&sess->chann_lock);
switch (protocol) {