Skip to content

Commit

Permalink
ksmbd: fix user-after-free from session log off
Browse files Browse the repository at this point in the history
There is racy issue between smb2 session log off and smb2 session setup.
It will cause user-after-free from session log off.
This add session_lock when setting SMB2_SESSION_EXPIRED and referece
count to session struct not to free session while it is being used.

Reported-by: [email protected] # ZDI-CAN-25282
Signed-off-by: Namjae Jeon <[email protected]>
  • Loading branch information
namjaejeon committed Oct 6, 2024
1 parent 8cd0f00 commit 85da158
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 6 deletions.
26 changes: 21 additions & 5 deletions mgmt/user_session.c
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,10 @@ static void ksmbd_expire_session(struct ksmbd_conn *conn)

down_write(&conn->session_lock);
xa_for_each(&conn->sessions, id, sess) {
if (sess->state != SMB2_SESSION_VALID ||
time_after(jiffies,
sess->last_active + SMB2_SESSION_TIMEOUT)) {
if (atomic_read(&sess->refcnt) == 0 &&
(sess->state != SMB2_SESSION_VALID ||
time_after(jiffies,
sess->last_active + SMB2_SESSION_TIMEOUT))) {
xa_erase(&conn->sessions, sess->id);
#ifdef CONFIG_SMB_INSECURE_SERVER
if (hash_hashed(&sess->hlist))
Expand Down Expand Up @@ -284,8 +285,6 @@ struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id)

down_read(&sessions_table_lock);
sess = __session_lookup(id);
if (sess)
sess->last_active = jiffies;
up_read(&sessions_table_lock);

return sess;
Expand All @@ -304,6 +303,22 @@ struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn,
return sess;
}

void ksmbd_user_session_get(struct ksmbd_session *sess)
{
atomic_inc(&sess->refcnt);
}

void ksmbd_user_session_put(struct ksmbd_session *sess)
{
if (!sess)
return;

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

struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn,
u64 sess_id)
{
Expand Down Expand Up @@ -417,6 +432,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);

switch (protocol) {
#ifdef CONFIG_SMB_INSECURE_SERVER
Expand Down
4 changes: 4 additions & 0 deletions mgmt/user_session.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ struct ksmbd_session {
struct ksmbd_file_table file_table;
unsigned long last_active;
rwlock_t tree_conns_lock;

atomic_t refcnt;
};

static inline int test_session_flag(struct ksmbd_session *sess, int bit)
Expand Down Expand Up @@ -97,6 +99,8 @@ void ksmbd_sessions_deregister(struct ksmbd_conn *conn);
struct ksmbd_session *__session_lookup(unsigned long long id);
struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn,
unsigned long long id);
void ksmbd_user_session_get(struct ksmbd_session *sess);
void ksmbd_user_session_put(struct ksmbd_session *sess);
void destroy_previous_session(struct ksmbd_conn *conn,
struct ksmbd_user *user, u64 id);
struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn,
Expand Down
2 changes: 2 additions & 0 deletions server.c
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,8 @@ static void __handle_ksmbd_work(struct ksmbd_work *work,
} while (is_chained == true);

send:
if (work->sess)
ksmbd_user_session_put(work->sess);
if (work->tcon)
ksmbd_tree_connect_put(work->tcon);
smb3_preauth_hash_rsp(work);
Expand Down
8 changes: 7 additions & 1 deletion smb2pdu.c
Original file line number Diff line number Diff line change
Expand Up @@ -611,8 +611,10 @@ int smb2_check_user_session(struct ksmbd_work *work)

/* Check for validity of user session */
work->sess = ksmbd_session_lookup_all(conn, sess_id);
if (work->sess)
if (work->sess) {
ksmbd_user_session_get(work->sess);
return 1;
}
ksmbd_debug(SMB, "Invalid user session, Uid %llu\n", sess_id);
return -ENOENT;
}
Expand Down Expand Up @@ -1758,6 +1760,7 @@ int smb2_sess_setup(struct ksmbd_work *work)
}

conn->binding = true;
ksmbd_user_session_get(sess);
} else if ((conn->dialect < SMB30_PROT_ID ||
server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) &&
(req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) {
Expand All @@ -1784,6 +1787,7 @@ int smb2_sess_setup(struct ksmbd_work *work)
}

conn->binding = false;
ksmbd_user_session_get(sess);
}
work->sess = sess;

Expand Down Expand Up @@ -2245,7 +2249,9 @@ int smb2_session_logoff(struct ksmbd_work *work)
}

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;
Expand Down

0 comments on commit 85da158

Please sign in to comment.