Skip to content

Commit 5511999

Browse files
namjaejeongregkh
authored andcommitted
ksmbd: fix user-after-free from session log off
commit 7aa8804 upstream. 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]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 7fc7c47 commit 5511999

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
@@ -176,9 +176,10 @@ static void ksmbd_expire_session(struct ksmbd_conn *conn)
176176

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

269270
down_read(&sessions_table_lock);
270271
sess = __session_lookup(id);
271-
if (sess)
272-
sess->last_active = jiffies;
273272
up_read(&sessions_table_lock);
274273

275274
return sess;
@@ -288,6 +287,22 @@ struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn,
288287
return sess;
289288
}
290289

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

394410
ret = __init_smb2_session(sess);
395411
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
}
@@ -1743,6 +1745,7 @@ int smb2_sess_setup(struct ksmbd_work *work)
17431745
}
17441746

17451747
conn->binding = true;
1748+
ksmbd_user_session_get(sess);
17461749
} else if ((conn->dialect < SMB30_PROT_ID ||
17471750
server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) &&
17481751
(req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) {
@@ -1769,6 +1772,7 @@ int smb2_sess_setup(struct ksmbd_work *work)
17691772
}
17701773

17711774
conn->binding = false;
1775+
ksmbd_user_session_get(sess);
17721776
}
17731777
work->sess = sess;
17741778

@@ -2229,7 +2233,9 @@ int smb2_session_logoff(struct ksmbd_work *work)
22292233
}
22302234

22312235
ksmbd_destroy_file_table(&sess->file_table);
2236+
down_write(&conn->session_lock);
22322237
sess->state = SMB2_SESSION_EXPIRED;
2238+
up_write(&conn->session_lock);
22332239

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

0 commit comments

Comments
 (0)