Skip to content

Commit f5c779b

Browse files
namjaejeonsmfrench
authored andcommitted
ksmbd: fix racy issue from session setup and logoff
This racy issue is triggered by sending concurrent session setup and logoff requests. This patch does not set connection status as KSMBD_SESS_GOOD if state is KSMBD_SESS_NEED_RECONNECT in session setup. And relookup session to validate if session is deleted in logoff. Cc: stable@vger.kernel.org Reported-by: zdi-disclosures@trendmicro.com # ZDI-CAN-20481, ZDI-CAN-20590, ZDI-CAN-20596 Signed-off-by: Namjae Jeon <linkinjeon@kernel.org> Signed-off-by: Steve French <stfrench@microsoft.com>
1 parent 3ac00a2 commit f5c779b

6 files changed

Lines changed: 77 additions & 49 deletions

File tree

fs/ksmbd/connection.c

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ struct ksmbd_conn *ksmbd_conn_alloc(void)
5656
return NULL;
5757

5858
conn->need_neg = true;
59-
conn->status = KSMBD_SESS_NEW;
59+
ksmbd_conn_set_new(conn);
6060
conn->local_nls = load_nls("utf8");
6161
if (!conn->local_nls)
6262
conn->local_nls = load_nls_default();
@@ -147,12 +147,12 @@ int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work)
147147
return ret;
148148
}
149149

150-
static void ksmbd_conn_lock(struct ksmbd_conn *conn)
150+
void ksmbd_conn_lock(struct ksmbd_conn *conn)
151151
{
152152
mutex_lock(&conn->srv_mutex);
153153
}
154154

155-
static void ksmbd_conn_unlock(struct ksmbd_conn *conn)
155+
void ksmbd_conn_unlock(struct ksmbd_conn *conn)
156156
{
157157
mutex_unlock(&conn->srv_mutex);
158158
}
@@ -243,7 +243,7 @@ bool ksmbd_conn_alive(struct ksmbd_conn *conn)
243243
if (!ksmbd_server_running())
244244
return false;
245245

246-
if (conn->status == KSMBD_SESS_EXITING)
246+
if (ksmbd_conn_exiting(conn))
247247
return false;
248248

249249
if (kthread_should_stop())
@@ -303,7 +303,7 @@ int ksmbd_conn_handler_loop(void *p)
303303
pdu_size = get_rfc1002_len(hdr_buf);
304304
ksmbd_debug(CONN, "RFC1002 header %u bytes\n", pdu_size);
305305

306-
if (conn->status == KSMBD_SESS_GOOD)
306+
if (ksmbd_conn_good(conn))
307307
max_allowed_pdu_size =
308308
SMB3_MAX_MSGSIZE + conn->vals->max_write_size;
309309
else
@@ -312,7 +312,7 @@ int ksmbd_conn_handler_loop(void *p)
312312
if (pdu_size > max_allowed_pdu_size) {
313313
pr_err_ratelimited("PDU length(%u) exceeded maximum allowed pdu size(%u) on connection(%d)\n",
314314
pdu_size, max_allowed_pdu_size,
315-
conn->status);
315+
READ_ONCE(conn->status));
316316
break;
317317
}
318318

@@ -416,7 +416,7 @@ static void stop_sessions(void)
416416
if (task)
417417
ksmbd_debug(CONN, "Stop session handler %s/%d\n",
418418
task->comm, task_pid_nr(task));
419-
conn->status = KSMBD_SESS_EXITING;
419+
ksmbd_conn_set_exiting(conn);
420420
if (t->ops->shutdown) {
421421
read_unlock(&conn_list_lock);
422422
t->ops->shutdown(t);

fs/ksmbd/connection.h

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -162,50 +162,57 @@ void ksmbd_conn_init_server_callbacks(struct ksmbd_conn_ops *ops);
162162
int ksmbd_conn_handler_loop(void *p);
163163
int ksmbd_conn_transport_init(void);
164164
void ksmbd_conn_transport_destroy(void);
165+
void ksmbd_conn_lock(struct ksmbd_conn *conn);
166+
void ksmbd_conn_unlock(struct ksmbd_conn *conn);
165167

166168
/*
167169
* WARNING
168170
*
169171
* This is a hack. We will move status to a proper place once we land
170172
* a multi-sessions support.
171173
*/
172-
static inline bool ksmbd_conn_good(struct ksmbd_work *work)
174+
static inline bool ksmbd_conn_good(struct ksmbd_conn *conn)
173175
{
174-
return work->conn->status == KSMBD_SESS_GOOD;
176+
return READ_ONCE(conn->status) == KSMBD_SESS_GOOD;
175177
}
176178

177-
static inline bool ksmbd_conn_need_negotiate(struct ksmbd_work *work)
179+
static inline bool ksmbd_conn_need_negotiate(struct ksmbd_conn *conn)
178180
{
179-
return work->conn->status == KSMBD_SESS_NEED_NEGOTIATE;
181+
return READ_ONCE(conn->status) == KSMBD_SESS_NEED_NEGOTIATE;
180182
}
181183

182-
static inline bool ksmbd_conn_need_reconnect(struct ksmbd_work *work)
184+
static inline bool ksmbd_conn_need_reconnect(struct ksmbd_conn *conn)
183185
{
184-
return work->conn->status == KSMBD_SESS_NEED_RECONNECT;
186+
return READ_ONCE(conn->status) == KSMBD_SESS_NEED_RECONNECT;
185187
}
186188

187-
static inline bool ksmbd_conn_exiting(struct ksmbd_work *work)
189+
static inline bool ksmbd_conn_exiting(struct ksmbd_conn *conn)
188190
{
189-
return work->conn->status == KSMBD_SESS_EXITING;
191+
return READ_ONCE(conn->status) == KSMBD_SESS_EXITING;
190192
}
191193

192-
static inline void ksmbd_conn_set_good(struct ksmbd_work *work)
194+
static inline void ksmbd_conn_set_new(struct ksmbd_conn *conn)
193195
{
194-
work->conn->status = KSMBD_SESS_GOOD;
196+
WRITE_ONCE(conn->status, KSMBD_SESS_NEW);
195197
}
196198

197-
static inline void ksmbd_conn_set_need_negotiate(struct ksmbd_work *work)
199+
static inline void ksmbd_conn_set_good(struct ksmbd_conn *conn)
198200
{
199-
work->conn->status = KSMBD_SESS_NEED_NEGOTIATE;
201+
WRITE_ONCE(conn->status, KSMBD_SESS_GOOD);
200202
}
201203

202-
static inline void ksmbd_conn_set_need_reconnect(struct ksmbd_work *work)
204+
static inline void ksmbd_conn_set_need_negotiate(struct ksmbd_conn *conn)
203205
{
204-
work->conn->status = KSMBD_SESS_NEED_RECONNECT;
206+
WRITE_ONCE(conn->status, KSMBD_SESS_NEED_NEGOTIATE);
205207
}
206208

207-
static inline void ksmbd_conn_set_exiting(struct ksmbd_work *work)
209+
static inline void ksmbd_conn_set_need_reconnect(struct ksmbd_conn *conn)
208210
{
209-
work->conn->status = KSMBD_SESS_EXITING;
211+
WRITE_ONCE(conn->status, KSMBD_SESS_NEED_RECONNECT);
212+
}
213+
214+
static inline void ksmbd_conn_set_exiting(struct ksmbd_conn *conn)
215+
{
216+
WRITE_ONCE(conn->status, KSMBD_SESS_EXITING);
210217
}
211218
#endif /* __CONNECTION_H__ */

fs/ksmbd/mgmt/user_session.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,7 @@ static struct ksmbd_session *__session_create(int protocol)
315315
if (ksmbd_init_file_table(&sess->file_table))
316316
goto error;
317317

318+
sess->state = SMB2_SESSION_IN_PROGRESS;
318319
set_session_flag(sess, protocol);
319320
xa_init(&sess->tree_conns);
320321
xa_init(&sess->ksmbd_chann_list);

fs/ksmbd/server.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,8 @@ static inline int check_conn_state(struct ksmbd_work *work)
9393
{
9494
struct smb_hdr *rsp_hdr;
9595

96-
if (ksmbd_conn_exiting(work) || ksmbd_conn_need_reconnect(work)) {
96+
if (ksmbd_conn_exiting(work->conn) ||
97+
ksmbd_conn_need_reconnect(work->conn)) {
9798
rsp_hdr = work->response_buf;
9899
rsp_hdr->Status.CifsError = STATUS_CONNECTION_DISCONNECTED;
99100
return 1;

fs/ksmbd/smb2pdu.c

Lines changed: 43 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ int init_smb2_neg_rsp(struct ksmbd_work *work)
248248

249249
rsp = smb2_get_msg(work->response_buf);
250250

251-
WARN_ON(ksmbd_conn_good(work));
251+
WARN_ON(ksmbd_conn_good(conn));
252252

253253
rsp->StructureSize = cpu_to_le16(65);
254254
ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect);
@@ -277,7 +277,7 @@ int init_smb2_neg_rsp(struct ksmbd_work *work)
277277
rsp->SecurityMode |= SMB2_NEGOTIATE_SIGNING_REQUIRED_LE;
278278
conn->use_spnego = true;
279279

280-
ksmbd_conn_set_need_negotiate(work);
280+
ksmbd_conn_set_need_negotiate(conn);
281281
return 0;
282282
}
283283

@@ -561,7 +561,7 @@ int smb2_check_user_session(struct ksmbd_work *work)
561561
cmd == SMB2_SESSION_SETUP_HE)
562562
return 0;
563563

564-
if (!ksmbd_conn_good(work))
564+
if (!ksmbd_conn_good(conn))
565565
return -EINVAL;
566566

567567
sess_id = le64_to_cpu(req_hdr->SessionId);
@@ -594,7 +594,7 @@ static void destroy_previous_session(struct ksmbd_conn *conn,
594594

595595
prev_sess->state = SMB2_SESSION_EXPIRED;
596596
xa_for_each(&prev_sess->ksmbd_chann_list, index, chann)
597-
chann->conn->status = KSMBD_SESS_EXITING;
597+
ksmbd_conn_set_exiting(chann->conn);
598598
}
599599

600600
/**
@@ -1051,7 +1051,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work)
10511051

10521052
ksmbd_debug(SMB, "Received negotiate request\n");
10531053
conn->need_neg = false;
1054-
if (ksmbd_conn_good(work)) {
1054+
if (ksmbd_conn_good(conn)) {
10551055
pr_err("conn->tcp_status is already in CifsGood State\n");
10561056
work->send_no_response = 1;
10571057
return rc;
@@ -1205,7 +1205,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work)
12051205
}
12061206

12071207
conn->srv_sec_mode = le16_to_cpu(rsp->SecurityMode);
1208-
ksmbd_conn_set_need_negotiate(work);
1208+
ksmbd_conn_set_need_negotiate(conn);
12091209

12101210
err_out:
12111211
if (rc < 0)
@@ -1628,6 +1628,7 @@ int smb2_sess_setup(struct ksmbd_work *work)
16281628
rsp->SecurityBufferLength = 0;
16291629
inc_rfc1001_len(work->response_buf, 9);
16301630

1631+
ksmbd_conn_lock(conn);
16311632
if (!req->hdr.SessionId) {
16321633
sess = ksmbd_smb2_session_create();
16331634
if (!sess) {
@@ -1675,6 +1676,12 @@ int smb2_sess_setup(struct ksmbd_work *work)
16751676
goto out_err;
16761677
}
16771678

1679+
if (ksmbd_conn_need_reconnect(conn)) {
1680+
rc = -EFAULT;
1681+
sess = NULL;
1682+
goto out_err;
1683+
}
1684+
16781685
if (ksmbd_session_lookup(conn, sess_id)) {
16791686
rc = -EACCES;
16801687
goto out_err;
@@ -1694,12 +1701,20 @@ int smb2_sess_setup(struct ksmbd_work *work)
16941701
rc = -ENOENT;
16951702
goto out_err;
16961703
}
1704+
1705+
if (sess->state == SMB2_SESSION_EXPIRED) {
1706+
rc = -EFAULT;
1707+
goto out_err;
1708+
}
1709+
1710+
if (ksmbd_conn_need_reconnect(conn)) {
1711+
rc = -EFAULT;
1712+
sess = NULL;
1713+
goto out_err;
1714+
}
16971715
}
16981716
work->sess = sess;
16991717

1700-
if (sess->state == SMB2_SESSION_EXPIRED)
1701-
sess->state = SMB2_SESSION_IN_PROGRESS;
1702-
17031718
negblob_off = le16_to_cpu(req->SecurityBufferOffset);
17041719
negblob_len = le16_to_cpu(req->SecurityBufferLength);
17051720
if (negblob_off < offsetof(struct smb2_sess_setup_req, Buffer) ||
@@ -1729,8 +1744,10 @@ int smb2_sess_setup(struct ksmbd_work *work)
17291744
goto out_err;
17301745
}
17311746

1732-
ksmbd_conn_set_good(work);
1733-
sess->state = SMB2_SESSION_VALID;
1747+
if (!ksmbd_conn_need_reconnect(conn)) {
1748+
ksmbd_conn_set_good(conn);
1749+
sess->state = SMB2_SESSION_VALID;
1750+
}
17341751
kfree(sess->Preauth_HashValue);
17351752
sess->Preauth_HashValue = NULL;
17361753
} else if (conn->preferred_auth_mech == KSMBD_AUTH_NTLMSSP) {
@@ -1752,8 +1769,10 @@ int smb2_sess_setup(struct ksmbd_work *work)
17521769
if (rc)
17531770
goto out_err;
17541771

1755-
ksmbd_conn_set_good(work);
1756-
sess->state = SMB2_SESSION_VALID;
1772+
if (!ksmbd_conn_need_reconnect(conn)) {
1773+
ksmbd_conn_set_good(conn);
1774+
sess->state = SMB2_SESSION_VALID;
1775+
}
17571776
if (conn->binding) {
17581777
struct preauth_session *preauth_sess;
17591778

@@ -1819,14 +1838,13 @@ int smb2_sess_setup(struct ksmbd_work *work)
18191838
if (sess->user && sess->user->flags & KSMBD_USER_FLAG_DELAY_SESSION)
18201839
try_delay = true;
18211840

1822-
xa_erase(&conn->sessions, sess->id);
1823-
ksmbd_session_destroy(sess);
1824-
work->sess = NULL;
1841+
sess->state = SMB2_SESSION_EXPIRED;
18251842
if (try_delay)
18261843
ssleep(5);
18271844
}
18281845
}
18291846

1847+
ksmbd_conn_unlock(conn);
18301848
return rc;
18311849
}
18321850

@@ -2050,21 +2068,24 @@ int smb2_session_logoff(struct ksmbd_work *work)
20502068
{
20512069
struct ksmbd_conn *conn = work->conn;
20522070
struct smb2_logoff_rsp *rsp = smb2_get_msg(work->response_buf);
2053-
struct ksmbd_session *sess = work->sess;
2071+
struct ksmbd_session *sess;
2072+
struct smb2_logoff_req *req = smb2_get_msg(work->request_buf);
20542073

20552074
rsp->StructureSize = cpu_to_le16(4);
20562075
inc_rfc1001_len(work->response_buf, 4);
20572076

20582077
ksmbd_debug(SMB, "request\n");
20592078

2060-
/* setting CifsExiting here may race with start_tcp_sess */
2061-
ksmbd_conn_set_need_reconnect(work);
2079+
ksmbd_conn_set_need_reconnect(conn);
20622080
ksmbd_close_session_fds(work);
20632081
ksmbd_conn_wait_idle(conn);
20642082

2083+
/*
2084+
* Re-lookup session to validate if session is deleted
2085+
* while waiting request complete
2086+
*/
2087+
sess = ksmbd_session_lookup(conn, le64_to_cpu(req->hdr.SessionId));
20652088
if (ksmbd_tree_conn_session_logoff(sess)) {
2066-
struct smb2_logoff_req *req = smb2_get_msg(work->request_buf);
2067-
20682089
ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId);
20692090
rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED;
20702091
smb2_set_err_rsp(work);
@@ -2076,9 +2097,7 @@ int smb2_session_logoff(struct ksmbd_work *work)
20762097

20772098
ksmbd_free_user(sess->user);
20782099
sess->user = NULL;
2079-
2080-
/* let start_tcp_sess free connection info now */
2081-
ksmbd_conn_set_need_negotiate(work);
2100+
ksmbd_conn_set_need_negotiate(conn);
20822101
return 0;
20832102
}
20842103

fs/ksmbd/transport_tcp.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ static int ksmbd_tcp_readv(struct tcp_transport *t, struct kvec *iov_orig,
333333
if (length == -EINTR) {
334334
total_read = -ESHUTDOWN;
335335
break;
336-
} else if (conn->status == KSMBD_SESS_NEED_RECONNECT) {
336+
} else if (ksmbd_conn_need_reconnect(conn)) {
337337
total_read = -EAGAIN;
338338
break;
339339
} else if (length == -ERESTARTSYS || length == -EAGAIN) {

0 commit comments

Comments
 (0)