ksmbd: fix session use-after-free in multichannel connection
commit fa4cdb8cbca7d6cb6aa13e4d8d83d1103f6345db upstream. 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> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
f0eb3f5751
commit
596407adb9
@ -1010,9 +1010,9 @@ static int ksmbd_get_encryption_key(struct ksmbd_work *work, __u64 ses_id,
|
|||||||
|
|
||||||
ses_enc_key = enc ? sess->smb3encryptionkey :
|
ses_enc_key = enc ? sess->smb3encryptionkey :
|
||||||
sess->smb3decryptionkey;
|
sess->smb3decryptionkey;
|
||||||
if (enc)
|
|
||||||
ksmbd_user_session_get(sess);
|
|
||||||
memcpy(key, ses_enc_key, SMB3_ENC_DEC_KEY_SIZE);
|
memcpy(key, ses_enc_key, SMB3_ENC_DEC_KEY_SIZE);
|
||||||
|
if (!enc)
|
||||||
|
ksmbd_user_session_put(sess);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -180,7 +180,7 @@ static void ksmbd_expire_session(struct ksmbd_conn *conn)
|
|||||||
down_write(&sessions_table_lock);
|
down_write(&sessions_table_lock);
|
||||||
down_write(&conn->session_lock);
|
down_write(&conn->session_lock);
|
||||||
xa_for_each(&conn->sessions, id, sess) {
|
xa_for_each(&conn->sessions, id, sess) {
|
||||||
if (atomic_read(&sess->refcnt) == 0 &&
|
if (atomic_read(&sess->refcnt) <= 1 &&
|
||||||
(sess->state != SMB2_SESSION_VALID ||
|
(sess->state != SMB2_SESSION_VALID ||
|
||||||
time_after(jiffies,
|
time_after(jiffies,
|
||||||
sess->last_active + SMB2_SESSION_TIMEOUT))) {
|
sess->last_active + SMB2_SESSION_TIMEOUT))) {
|
||||||
@ -232,6 +232,7 @@ void ksmbd_sessions_deregister(struct ksmbd_conn *conn)
|
|||||||
down_write(&conn->session_lock);
|
down_write(&conn->session_lock);
|
||||||
xa_erase(&conn->sessions, sess->id);
|
xa_erase(&conn->sessions, sess->id);
|
||||||
up_write(&conn->session_lock);
|
up_write(&conn->session_lock);
|
||||||
|
if (atomic_dec_and_test(&sess->refcnt))
|
||||||
ksmbd_session_destroy(sess);
|
ksmbd_session_destroy(sess);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -251,6 +252,7 @@ void ksmbd_sessions_deregister(struct ksmbd_conn *conn)
|
|||||||
if (xa_empty(&sess->ksmbd_chann_list)) {
|
if (xa_empty(&sess->ksmbd_chann_list)) {
|
||||||
xa_erase(&conn->sessions, sess->id);
|
xa_erase(&conn->sessions, sess->id);
|
||||||
hash_del(&sess->hlist);
|
hash_del(&sess->hlist);
|
||||||
|
if (atomic_dec_and_test(&sess->refcnt))
|
||||||
ksmbd_session_destroy(sess);
|
ksmbd_session_destroy(sess);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -327,8 +329,8 @@ void ksmbd_user_session_put(struct ksmbd_session *sess)
|
|||||||
|
|
||||||
if (atomic_read(&sess->refcnt) <= 0)
|
if (atomic_read(&sess->refcnt) <= 0)
|
||||||
WARN_ON(1);
|
WARN_ON(1);
|
||||||
else
|
else if (atomic_dec_and_test(&sess->refcnt))
|
||||||
atomic_dec(&sess->refcnt);
|
ksmbd_session_destroy(sess);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn,
|
struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn,
|
||||||
@ -399,7 +401,7 @@ static struct ksmbd_session *__session_create(int protocol)
|
|||||||
xa_init(&sess->rpc_handle_list);
|
xa_init(&sess->rpc_handle_list);
|
||||||
sess->sequence_number = 1;
|
sess->sequence_number = 1;
|
||||||
rwlock_init(&sess->tree_conns_lock);
|
rwlock_init(&sess->tree_conns_lock);
|
||||||
atomic_set(&sess->refcnt, 1);
|
atomic_set(&sess->refcnt, 2);
|
||||||
|
|
||||||
ret = __init_smb2_session(sess);
|
ret = __init_smb2_session(sess);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
@ -2242,13 +2242,14 @@ int smb2_session_logoff(struct ksmbd_work *work)
|
|||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
ksmbd_destroy_file_table(&sess->file_table);
|
|
||||||
down_write(&conn->session_lock);
|
down_write(&conn->session_lock);
|
||||||
sess->state = SMB2_SESSION_EXPIRED;
|
sess->state = SMB2_SESSION_EXPIRED;
|
||||||
up_write(&conn->session_lock);
|
up_write(&conn->session_lock);
|
||||||
|
|
||||||
|
if (sess->user) {
|
||||||
ksmbd_free_user(sess->user);
|
ksmbd_free_user(sess->user);
|
||||||
sess->user = NULL;
|
sess->user = NULL;
|
||||||
|
}
|
||||||
ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_NEGOTIATE);
|
ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_NEGOTIATE);
|
||||||
|
|
||||||
rsp->StructureSize = cpu_to_le16(4);
|
rsp->StructureSize = cpu_to_le16(4);
|
||||||
|
Loading…
Reference in New Issue
Block a user