Skip to content

Commit

Permalink
ksmbd: fix session use-after-free in multichannel connection
Browse files Browse the repository at this point in the history
There is a race condition between session setup and
ksmbd_sessions_deregister. The session can be freed before the connection
is added to channel list of session.
This patch check reference count of session before freeing it.

Cc: stable@vger.kernel.org
Reported-by: Sean Heelan <seanheelan@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
  • Loading branch information
Namjae Jeon authored and Steve French committed Apr 1, 2025
1 parent f64a72b commit fa4cdb8
Show file tree
Hide file tree
Showing 3 changed files with 14 additions and 11 deletions.
4 changes: 2 additions & 2 deletions fs/smb/server/auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -1016,9 +1016,9 @@ static int ksmbd_get_encryption_key(struct ksmbd_work *work, __u64 ses_id,

ses_enc_key = enc ? sess->smb3encryptionkey :
sess->smb3decryptionkey;
if (enc)
ksmbd_user_session_get(sess);
memcpy(key, ses_enc_key, SMB3_ENC_DEC_KEY_SIZE);
if (!enc)
ksmbd_user_session_put(sess);

return 0;
}
Expand Down
14 changes: 8 additions & 6 deletions fs/smb/server/mgmt/user_session.c
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ static void ksmbd_expire_session(struct ksmbd_conn *conn)
down_write(&sessions_table_lock);
down_write(&conn->session_lock);
xa_for_each(&conn->sessions, id, sess) {
if (atomic_read(&sess->refcnt) == 0 &&
if (atomic_read(&sess->refcnt) <= 1 &&
(sess->state != SMB2_SESSION_VALID ||
time_after(jiffies,
sess->last_active + SMB2_SESSION_TIMEOUT))) {
Expand Down Expand Up @@ -233,7 +233,8 @@ void ksmbd_sessions_deregister(struct ksmbd_conn *conn)
down_write(&conn->session_lock);
xa_erase(&conn->sessions, sess->id);
up_write(&conn->session_lock);
ksmbd_session_destroy(sess);
if (atomic_dec_and_test(&sess->refcnt))
ksmbd_session_destroy(sess);
}
}
}
Expand All @@ -252,7 +253,8 @@ void ksmbd_sessions_deregister(struct ksmbd_conn *conn)
if (xa_empty(&sess->ksmbd_chann_list)) {
xa_erase(&conn->sessions, sess->id);
hash_del(&sess->hlist);
ksmbd_session_destroy(sess);
if (atomic_dec_and_test(&sess->refcnt))
ksmbd_session_destroy(sess);
}
}
up_write(&conn->session_lock);
Expand Down Expand Up @@ -328,8 +330,8 @@ void ksmbd_user_session_put(struct ksmbd_session *sess)

if (atomic_read(&sess->refcnt) <= 0)
WARN_ON(1);
else
atomic_dec(&sess->refcnt);
else if (atomic_dec_and_test(&sess->refcnt))
ksmbd_session_destroy(sess);
}

struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn,
Expand Down Expand Up @@ -436,7 +438,7 @@ static struct ksmbd_session *__session_create(int protocol)
xa_init(&sess->rpc_handle_list);
sess->sequence_number = 1;
rwlock_init(&sess->tree_conns_lock);
atomic_set(&sess->refcnt, 1);
atomic_set(&sess->refcnt, 2);

ret = __init_smb2_session(sess);
if (ret)
Expand Down
7 changes: 4 additions & 3 deletions fs/smb/server/smb2pdu.c
Original file line number Diff line number Diff line change
Expand Up @@ -2235,13 +2235,14 @@ int smb2_session_logoff(struct ksmbd_work *work)
return -ENOENT;
}

ksmbd_destroy_file_table(&sess->file_table);
down_write(&conn->session_lock);
sess->state = SMB2_SESSION_EXPIRED;
up_write(&conn->session_lock);

ksmbd_free_user(sess->user);
sess->user = NULL;
if (sess->user) {
ksmbd_free_user(sess->user);
sess->user = NULL;
}
ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_NEGOTIATE);

rsp->StructureSize = cpu_to_le16(4);
Expand Down

0 comments on commit fa4cdb8

Please sign in to comment.