Skip to content

Commit 7aa8804

Browse files
namjaejeonSteve French
authored andcommitted
ksmbd: fix user-after-free from session log off
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. Cc: [email protected] # v5.15+ Reported-by: [email protected] # ZDI-CAN-25282 Signed-off-by: Namjae Jeon <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent 8cf0b93 commit 7aa8804

File tree

4 files changed

+34
-6
lines changed

4 files changed

+34
-6
lines changed

fs/smb/server/mgmt/user_session.c

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -177,9 +177,10 @@ static void ksmbd_expire_session(struct ksmbd_conn *conn)
177177

178178
down_write(&conn->session_lock);
179179
xa_for_each(&conn->sessions, id, sess) {
180-
if (sess->state != SMB2_SESSION_VALID ||
181-
time_after(jiffies,
182-
sess->last_active + SMB2_SESSION_TIMEOUT)) {
180+
if (atomic_read(&sess->refcnt) == 0 &&
181+
(sess->state != SMB2_SESSION_VALID ||
182+
time_after(jiffies,
183+
sess->last_active + SMB2_SESSION_TIMEOUT))) {
183184
xa_erase(&conn->sessions, sess->id);
184185
hash_del(&sess->hlist);
185186
ksmbd_session_destroy(sess);
@@ -269,8 +270,6 @@ struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id)
269270

270271
down_read(&sessions_table_lock);
271272
sess = __session_lookup(id);
272-
if (sess)
273-
sess->last_active = jiffies;
274273
up_read(&sessions_table_lock);
275274

276275
return sess;
@@ -289,6 +288,22 @@ struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn,
289288
return sess;
290289
}
291290

291+
void ksmbd_user_session_get(struct ksmbd_session *sess)
292+
{
293+
atomic_inc(&sess->refcnt);
294+
}
295+
296+
void ksmbd_user_session_put(struct ksmbd_session *sess)
297+
{
298+
if (!sess)
299+
return;
300+
301+
if (atomic_read(&sess->refcnt) <= 0)
302+
WARN_ON(1);
303+
else
304+
atomic_dec(&sess->refcnt);
305+
}
306+
292307
struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn,
293308
u64 sess_id)
294309
{
@@ -393,6 +408,7 @@ static struct ksmbd_session *__session_create(int protocol)
393408
xa_init(&sess->rpc_handle_list);
394409
sess->sequence_number = 1;
395410
rwlock_init(&sess->tree_conns_lock);
411+
atomic_set(&sess->refcnt, 1);
396412

397413
ret = __init_smb2_session(sess);
398414
if (ret)

fs/smb/server/mgmt/user_session.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ struct ksmbd_session {
6161
struct ksmbd_file_table file_table;
6262
unsigned long last_active;
6363
rwlock_t tree_conns_lock;
64+
65+
atomic_t refcnt;
6466
};
6567

6668
static inline int test_session_flag(struct ksmbd_session *sess, int bit)
@@ -104,4 +106,6 @@ void ksmbd_release_tree_conn_id(struct ksmbd_session *sess, int id);
104106
int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name);
105107
void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id);
106108
int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id);
109+
void ksmbd_user_session_get(struct ksmbd_session *sess);
110+
void ksmbd_user_session_put(struct ksmbd_session *sess);
107111
#endif /* __USER_SESSION_MANAGEMENT_H__ */

fs/smb/server/server.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,8 @@ static void __handle_ksmbd_work(struct ksmbd_work *work,
238238
} while (is_chained == true);
239239

240240
send:
241+
if (work->sess)
242+
ksmbd_user_session_put(work->sess);
241243
if (work->tcon)
242244
ksmbd_tree_connect_put(work->tcon);
243245
smb3_preauth_hash_rsp(work);

fs/smb/server/smb2pdu.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -605,8 +605,10 @@ int smb2_check_user_session(struct ksmbd_work *work)
605605

606606
/* Check for validity of user session */
607607
work->sess = ksmbd_session_lookup_all(conn, sess_id);
608-
if (work->sess)
608+
if (work->sess) {
609+
ksmbd_user_session_get(work->sess);
609610
return 1;
611+
}
610612
ksmbd_debug(SMB, "Invalid user session, Uid %llu\n", sess_id);
611613
return -ENOENT;
612614
}
@@ -1740,6 +1742,7 @@ int smb2_sess_setup(struct ksmbd_work *work)
17401742
}
17411743

17421744
conn->binding = true;
1745+
ksmbd_user_session_get(sess);
17431746
} else if ((conn->dialect < SMB30_PROT_ID ||
17441747
server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) &&
17451748
(req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) {
@@ -1766,6 +1769,7 @@ int smb2_sess_setup(struct ksmbd_work *work)
17661769
}
17671770

17681771
conn->binding = false;
1772+
ksmbd_user_session_get(sess);
17691773
}
17701774
work->sess = sess;
17711775

@@ -2228,7 +2232,9 @@ int smb2_session_logoff(struct ksmbd_work *work)
22282232
}
22292233

22302234
ksmbd_destroy_file_table(&sess->file_table);
2235+
down_write(&conn->session_lock);
22312236
sess->state = SMB2_SESSION_EXPIRED;
2237+
up_write(&conn->session_lock);
22322238

22332239
ksmbd_free_user(sess->user);
22342240
sess->user = NULL;

0 commit comments

Comments
 (0)