History

Merge tag '6.6-rc4-ksmbd-server-fixes' of git://git.samba.org/ksmbd

Pull smb server fixes from Steve French: "Six SMB3 server fixes for various races found by RO0T Lab of Huawei: - Fix oops when racing between oplock break ack and freeing file - Simultaneous request fixes for parallel logoffs, and for parallel lock requests - Fixes for tree disconnect race, session expire race, and close/open race" * tag '6.6-rc4-ksmbd-server-fixes' of git://git.samba.org/ksmbd: ksmbd: fix race condition between tree conn lookup and disconnect ksmbd: fix race condition from parallel smb2 lock requests ksmbd: fix race condition from parallel smb2 logoff requests ksmbd: fix uaf in smb20_oplock_break_ack ksmbd: fix race condition with fp ksmbd: fix race condition between session lookup and expire

Linus Torvalds committed 3 years ago

Commit 37faf072 parents f707e40 33b235a10 files · +151 −45Browse files

Unified · Split

10 files
  1. fs/smb/server/connection.c +2 −0
  2. fs/smb/server/connection.h +1 −0
  3. fs/smb/server/mgmt/tree_connect.c +39 −3
  4. fs/smb/server/mgmt/tree_connect.h +9 −2
  5. fs/smb/server/mgmt/user_session.c +8 −3
  6. fs/smb/server/mgmt/user_session.h +1 −0
  7. fs/smb/server/server.c +2 −0
  8. fs/smb/server/smb2pdu.c +60 −34
  9. fs/smb/server/vfs_cache.c +20 −3
  10. fs/smb/server/vfs_cache.h +9 −0
fs/smb/server/connection.c+2−0
@@ -84,6 +84,8 @@ struct ksmbd_conn *ksmbd_conn_alloc(void) · 83 unmodified lines
8484 spin_lock_init(&conn->llist_lock);
8585 INIT_LIST_HEAD(&conn->lock_list);
8686
87+ init_rwsem(&conn->session_lock);
88+
8789 down_write(&conn_list_lock);
8890 list_add(&conn->conns_list, &conn_list);
8991 up_write(&conn_list_lock);
fs/smb/server/connection.h+1−0
@@ -50,6 +50,7 @@ struct ksmbd_conn { · 49 unmodified lines
5050 struct nls_table *local_nls;
5151 struct unicode_map *um;
5252 struct list_head conns_list;
53+ struct rw_semaphore session_lock;
5354 /* smb session 1 per user */
5455 struct xarray sessions;
5556 unsigned long last_active;
fs/smb/server/mgmt/tree_connect.c+39−3
@@ -73,7 +73,10 @@ ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess, · 72 unmodified lines
7373
7474 tree_conn->user = sess->user;
7575 tree_conn->share_conf = sc;
76+ tree_conn->t_state = TREE_NEW;
7677 status.tree_conn = tree_conn;
78+ atomic_set(&tree_conn->refcount, 1);
79+ init_waitqueue_head(&tree_conn->refcount_q);
7780
7881 ret = xa_err(xa_store(&sess->tree_conns, tree_conn->id, tree_conn,
7982 GFP_KERNEL));
@@ -93,14 +96,33 @@ out_error: · 13 unmodified lines
9396 return status;
9497 }
9598
99+void ksmbd_tree_connect_put(struct ksmbd_tree_connect *tcon)
100+{
101+ /*
102+ * Checking waitqueue to releasing tree connect on
103+ * tree disconnect. waitqueue_active is safe because it
104+ * uses atomic operation for condition.
105+ */
106+ if (!atomic_dec_return(&tcon->refcount) &&
107+ waitqueue_active(&tcon->refcount_q))
108+ wake_up(&tcon->refcount_q);
109+}
110+
96111 int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess,
97112 struct ksmbd_tree_connect *tree_conn)
98113 {
99114 int ret;
100115
116+ write_lock(&sess->tree_conns_lock);
117+ xa_erase(&sess->tree_conns, tree_conn->id);
118+ write_unlock(&sess->tree_conns_lock);
119+
120+ if (!atomic_dec_and_test(&tree_conn->refcount))
121+ wait_event(tree_conn->refcount_q,
122+ atomic_read(&tree_conn->refcount) == 0);
123+
101124 ret = ksmbd_ipc_tree_disconnect_request(sess->id, tree_conn->id);
102125 ksmbd_release_tree_conn_id(sess, tree_conn->id);
103− xa_erase(&sess->tree_conns, tree_conn->id);
104126 ksmbd_share_config_put(tree_conn->share_conf);
105127 kfree(tree_conn);
106128 return ret;
@@ -111,11 +133,15 @@ struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess, · 4 unmodified lines
111133 {
112134 struct ksmbd_tree_connect *tcon;
113135
136+ read_lock(&sess->tree_conns_lock);
114137 tcon = xa_load(&sess->tree_conns, id);
115138 if (tcon) {
116− if (test_bit(TREE_CONN_EXPIRE, &tcon->status))
139+ if (tcon->t_state != TREE_CONNECTED)
140+ tcon = NULL;
141+ else if (!atomic_inc_not_zero(&tcon->refcount))
117142 tcon = NULL;
118143 }
144+ read_unlock(&sess->tree_conns_lock);
119145
120146 return tcon;
121147 }
@@ -129,8 +155,18 @@ int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess) · 7 unmodified lines
129155 if (!sess)
130156 return -EINVAL;
131157
132− xa_for_each(&sess->tree_conns, id, tc)
158+ xa_for_each(&sess->tree_conns, id, tc) {
159+ write_lock(&sess->tree_conns_lock);
160+ if (tc->t_state == TREE_DISCONNECTED) {
161+ write_unlock(&sess->tree_conns_lock);
162+ ret = -ENOENT;
163+ continue;
164+ }
165+ tc->t_state = TREE_DISCONNECTED;
166+ write_unlock(&sess->tree_conns_lock);
167+
133168 ret |= ksmbd_tree_conn_disconnect(sess, tc);
169+ }
134170 xa_destroy(&sess->tree_conns);
135171 return ret;
136172 }
fs/smb/server/mgmt/tree_connect.h+9−2
@@ -14,7 +14,11 @@ struct ksmbd_share_config; · 13 unmodified lines
1414 struct ksmbd_user;
1515 struct ksmbd_conn;
1616
17−#define TREE_CONN_EXPIRE 1
17+enum {
18+ TREE_NEW = 0,
19+ TREE_CONNECTED,
20+ TREE_DISCONNECTED
21+};
1822
1923 struct ksmbd_tree_connect {
2024 int id;
@@ -27,7 +31,9 @@ struct ksmbd_tree_connect { · 6 unmodified lines
2731
2832 int maximal_access;
2933 bool posix_extensions;
30− unsigned long status;
34+ atomic_t refcount;
35+ wait_queue_head_t refcount_q;
36+ unsigned int t_state;
3137 };
3238
3339 struct ksmbd_tree_conn_status {
@@ -46,6 +52,7 @@ struct ksmbd_session; · 12 unmodified lines
4652 struct ksmbd_tree_conn_status
4753 ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess,
4854 const char *share_name);
55+void ksmbd_tree_connect_put(struct ksmbd_tree_connect *tcon);
4956
5057 int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess,
5158 struct ksmbd_tree_connect *tree_conn);
fs/smb/server/mgmt/user_session.c+8−3
@@ -174,7 +174,7 @@ static void ksmbd_expire_session(struct ksmbd_conn *conn) · 173 unmodified lines
174174 unsigned long id;
175175 struct ksmbd_session *sess;
176176
177− down_write(&sessions_table_lock);
177+ down_write(&conn->session_lock);
178178 xa_for_each(&conn->sessions, id, sess) {
179179 if (sess->state != SMB2_SESSION_VALID ||
180180 time_after(jiffies,
@@ -185,7 +185,7 @@ static void ksmbd_expire_session(struct ksmbd_conn *conn) · 4 unmodified lines
185185 continue;
186186 }
187187 }
188− up_write(&sessions_table_lock);
188+ up_write(&conn->session_lock);
189189 }
190190
191191 int ksmbd_session_register(struct ksmbd_conn *conn,
@@ -227,7 +227,9 @@ void ksmbd_sessions_deregister(struct ksmbd_conn *conn) · 35 unmodified lines
227227 }
228228 }
229229 }
230+ up_write(&sessions_table_lock);
230231
232+ down_write(&conn->session_lock);
231233 xa_for_each(&conn->sessions, id, sess) {
232234 unsigned long chann_id;
233235 struct channel *chann;
@@ -244,7 +246,7 @@ void ksmbd_sessions_deregister(struct ksmbd_conn *conn) · 10 unmodified lines
244246 ksmbd_session_destroy(sess);
245247 }
246248 }
247− up_write(&sessions_table_lock);
249+ up_write(&conn->session_lock);
248250 }
249251
250252 struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn,
@@ -252,9 +254,11 @@ struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn, · 1 unmodified line
252254 {
253255 struct ksmbd_session *sess;
254256
257+ down_read(&conn->session_lock);
255258 sess = xa_load(&conn->sessions, id);
256259 if (sess)
257260 sess->last_active = jiffies;
261+ up_read(&conn->session_lock);
258262 return sess;
259263 }
260264
@@ -351,6 +355,7 @@ static struct ksmbd_session *__session_create(int protocol) · 90 unmodified lines
351355 xa_init(&sess->ksmbd_chann_list);
352356 xa_init(&sess->rpc_handle_list);
353357 sess->sequence_number = 1;
358+ rwlock_init(&sess->tree_conns_lock);
354359
355360 ret = __init_smb2_session(sess);
356361 if (ret)
fs/smb/server/mgmt/user_session.h+1−0
@@ -60,6 +60,7 @@ struct ksmbd_session { · 59 unmodified lines
6060
6161 struct ksmbd_file_table file_table;
6262 unsigned long last_active;
63+ rwlock_t tree_conns_lock;
6364 };
6465
6566 static inline int test_session_flag(struct ksmbd_session *sess, int bit)
fs/smb/server/server.c+2−0
@@ -241,6 +241,8 @@ static void __handle_ksmbd_work(struct ksmbd_work *work, · 240 unmodified lines
241241 } while (is_chained == true);
242242
243243 send:
244+ if (work->tcon)
245+ ksmbd_tree_connect_put(work->tcon);
244246 smb3_preauth_hash_rsp(work);
245247 if (work->sess && work->sess->enc && work->encrypted &&
246248 conn->ops->encrypt_resp) {
fs/smb/server/smb2pdu.c+60−34
@@ -1993,6 +1993,9 @@ int smb2_tree_connect(struct ksmbd_work *work) · 1992 unmodified lines
19931993 if (conn->posix_ext_supported)
19941994 status.tree_conn->posix_extensions = true;
19951995
1996+ write_lock(&sess->tree_conns_lock);
1997+ status.tree_conn->t_state = TREE_CONNECTED;
1998+ write_unlock(&sess->tree_conns_lock);
19961999 rsp->StructureSize = cpu_to_le16(16);
19972000 out_err1:
19982001 rsp->Capabilities = 0;
@@ -2122,27 +2125,50 @@ int smb2_tree_disconnect(struct ksmbd_work *work) · 123 unmodified lines
21222125
21232126 ksmbd_debug(SMB, "request\n");
21242127
2128+ if (!tcon) {
2129+ ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId);
2130+
2131+ rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED;
2132+ err = -ENOENT;
2133+ goto err_out;
2134+ }
2135+
2136+ ksmbd_close_tree_conn_fds(work);
2137+
2138+ write_lock(&sess->tree_conns_lock);
2139+ if (tcon->t_state == TREE_DISCONNECTED) {
2140+ write_unlock(&sess->tree_conns_lock);
2141+ rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED;
2142+ err = -ENOENT;
2143+ goto err_out;
2144+ }
2145+
2146+ WARN_ON_ONCE(atomic_dec_and_test(&tcon->refcount));
2147+ tcon->t_state = TREE_DISCONNECTED;
2148+ write_unlock(&sess->tree_conns_lock);
2149+
2150+ err = ksmbd_tree_conn_disconnect(sess, tcon);
2151+ if (err) {
2152+ rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED;
2153+ goto err_out;
2154+ }
2155+
2156+ work->tcon = NULL;
2157+
21252158 rsp->StructureSize = cpu_to_le16(4);
21262159 err = ksmbd_iov_pin_rsp(work, rsp,
21272160 sizeof(struct smb2_tree_disconnect_rsp));
21282161 if (err) {
21292162 rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES;
2130− smb2_set_err_rsp(work);
2131− return err;
2163+ goto err_out;
21322164 }
21332165
2134− if (!tcon || test_and_set_bit(TREE_CONN_EXPIRE, &tcon->status)) {
2135− ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId);
2166+ return 0;
21362167
2137− rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED;
2138− smb2_set_err_rsp(work);
2139− return -ENOENT;
2140− }
2168+err_out:
2169+ smb2_set_err_rsp(work);
2170+ return err;
21412171
2142− ksmbd_close_tree_conn_fds(work);
2143− ksmbd_tree_conn_disconnect(sess, tcon);
2144− work->tcon = NULL;
2145− return 0;
21462172 }
21472173
21482174 /**
@@ -2164,17 +2190,17 @@ int smb2_session_logoff(struct ksmbd_work *work) · 15 unmodified lines
21642190
21652191 ksmbd_debug(SMB, "request\n");
21662192
2167− sess_id = le64_to_cpu(req->hdr.SessionId);
2168−
2169− rsp->StructureSize = cpu_to_le16(4);
2170− err = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_logoff_rsp));
2171− if (err) {
2172− rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES;
2193+ ksmbd_conn_lock(conn);
2194+ if (!ksmbd_conn_good(conn)) {
2195+ ksmbd_conn_unlock(conn);
2196+ rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED;
21732197 smb2_set_err_rsp(work);
2174− return err;
2198+ return -ENOENT;
21752199 }
2176−
2200+ sess_id = le64_to_cpu(req->hdr.SessionId);
21772201 ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_RECONNECT);
2202+ ksmbd_conn_unlock(conn);
2203+
21782204 ksmbd_close_session_fds(work);
21792205 ksmbd_conn_wait_idle(conn, sess_id);
21802206
@@ -2196,6 +2222,14 @@ int smb2_session_logoff(struct ksmbd_work *work) · 15 unmodified lines
21962222 ksmbd_free_user(sess->user);
21972223 sess->user = NULL;
21982224 ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_NEGOTIATE);
2225+
2226+ rsp->StructureSize = cpu_to_le16(4);
2227+ err = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_logoff_rsp));
2228+ if (err) {
2229+ rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES;
2230+ smb2_set_err_rsp(work);
2231+ return err;
2232+ }
21992233 return 0;
22002234 }
22012235
@@ -3370,8 +3404,10 @@ err_out: · 1168 unmodified lines
33703404 }
33713405 ksmbd_revert_fsids(work);
33723406 err_out1:
3373− if (!rc)
3407+ if (!rc) {
3408+ ksmbd_update_fstate(&work->sess->file_table, fp, FP_INITED);
33743409 rc = ksmbd_iov_pin_rsp(work, (void *)rsp, iov_len);
3410+ }
33753411 if (rc) {
33763412 if (rc == -EINVAL)
33773413 rsp->hdr.Status = STATUS_INVALID_PARAMETER;
@@ -7028,10 +7064,6 @@ skip: · 3650 unmodified lines
70287064
70297065 ksmbd_debug(SMB,
70307066 "would have to wait for getting lock\n");
7031− spin_lock(&work->conn->llist_lock);
7032− list_add_tail(&smb_lock->clist,
7033− &work->conn->lock_list);
7034− spin_unlock(&work->conn->llist_lock);
70357067 list_add(&smb_lock->llist, &rollback_list);
70367068
70377069 argv = kmalloc(sizeof(void *), GFP_KERNEL);
@@ -7062,9 +7094,6 @@ skip: · 24 unmodified lines
70627094
70637095 if (work->state != KSMBD_WORK_ACTIVE) {
70647096 list_del(&smb_lock->llist);
7065− spin_lock(&work->conn->llist_lock);
7066− list_del(&smb_lock->clist);
7067− spin_unlock(&work->conn->llist_lock);
70687097 locks_free_lock(flock);
70697098
70707099 if (work->state == KSMBD_WORK_CANCELLED) {
@@ -7084,19 +7113,16 @@ skip: · 13 unmodified lines
70847113 }
70857114
70867115 list_del(&smb_lock->llist);
7087− spin_lock(&work->conn->llist_lock);
7088− list_del(&smb_lock->clist);
7089− spin_unlock(&work->conn->llist_lock);
70907116 release_async_work(work);
70917117 goto retry;
70927118 } else if (!rc) {
7119+ list_add(&smb_lock->llist, &rollback_list);
70937120 spin_lock(&work->conn->llist_lock);
70947121 list_add_tail(&smb_lock->clist,
70957122 &work->conn->lock_list);
70967123 list_add_tail(&smb_lock->flist,
70977124 &fp->lock_list);
70987125 spin_unlock(&work->conn->llist_lock);
7099− list_add(&smb_lock->llist, &rollback_list);
71007126 ksmbd_debug(SMB, "successful in taking lock\n");
71017127 } else {
71027128 goto out;
@@ -8036,10 +8062,10 @@ static void smb20_oplock_break_ack(struct ksmbd_work *work) · 933 unmodified lines
80368062 goto err_out;
80378063 }
80388064
8039− opinfo_put(opinfo);
8040− ksmbd_fd_put(work, fp);
80418065 opinfo->op_state = OPLOCK_STATE_NONE;
80428066 wake_up_interruptible_all(&opinfo->oplock_q);
8067+ opinfo_put(opinfo);
8068+ ksmbd_fd_put(work, fp);
80438069
80448070 rsp->StructureSize = cpu_to_le16(24);
80458071 rsp->OplockLevel = rsp_oplevel;
fs/smb/server/vfs_cache.c+20−3
@@ -333,6 +333,9 @@ static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp) · 332 unmodified lines
333333
334334 static struct ksmbd_file *ksmbd_fp_get(struct ksmbd_file *fp)
335335 {
336+ if (fp->f_state != FP_INITED)
337+ return NULL;
338+
336339 if (!atomic_inc_not_zero(&fp->refcount))
337340 return NULL;
338341 return fp;
@@ -382,15 +385,20 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id) · 43 unmodified lines
382385 return 0;
383386
384387 ft = &work->sess->file_table;
385− read_lock(&ft->lock);
388+ write_lock(&ft->lock);
386389 fp = idr_find(ft->idr, id);
387390 if (fp) {
388391 set_close_state_blocked_works(fp);
389392
390− if (!atomic_dec_and_test(&fp->refcount))
393+ if (fp->f_state != FP_INITED)
391394 fp = NULL;
395+ else {
396+ fp->f_state = FP_CLOSED;
397+ if (!atomic_dec_and_test(&fp->refcount))
398+ fp = NULL;
399+ }
392400 }
393− read_unlock(&ft->lock);
401+ write_unlock(&ft->lock);
394402
395403 if (!fp)
396404 return -EINVAL;
@@ -570,6 +578,7 @@ struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp) · 173 unmodified lines
570578 fp->tcon = work->tcon;
571579 fp->volatile_id = KSMBD_NO_FID;
572580 fp->persistent_id = KSMBD_NO_FID;
581+ fp->f_state = FP_NEW;
573582 fp->f_ci = ksmbd_inode_get(fp);
574583
575584 if (!fp->f_ci) {
@@ -591,6 +600,14 @@ err_out: · 15 unmodified lines
591600 return ERR_PTR(ret);
592601 }
593602
603+void ksmbd_update_fstate(struct ksmbd_file_table *ft, struct ksmbd_file *fp,
604+ unsigned int state)
605+{
606+ write_lock(&ft->lock);
607+ fp->f_state = state;
608+ write_unlock(&ft->lock);
609+}
610+
594611 static int
595612 __close_file_table_ids(struct ksmbd_file_table *ft,
596613 struct ksmbd_tree_connect *tcon,
fs/smb/server/vfs_cache.h+9−0
@@ -60,6 +60,12 @@ struct ksmbd_inode { · 59 unmodified lines
6060 __le32 m_fattr;
6161 };
6262
63+enum {
64+ FP_NEW = 0,
65+ FP_INITED,
66+ FP_CLOSED
67+};
68+
6369 struct ksmbd_file {
6470 struct file *filp;
6571 u64 persistent_id;
@@ -98,6 +104,7 @@ struct ksmbd_file { · 32 unmodified lines
98104 /* if ls is happening on directory, below is valid*/
99105 struct ksmbd_readdir_data readdir_data;
100106 int dot_dotdot[2];
107+ unsigned int f_state;
101108 };
102109
103110 static inline void set_ctx_actor(struct dir_context *ctx,
@@ -142,6 +149,8 @@ int ksmbd_close_inode_fds(struct ksmbd_work *work, struct inode *inode); · 38 unmodified lines
142149 int ksmbd_init_global_file_table(void);
143150 void ksmbd_free_global_file_table(void);
144151 void ksmbd_set_fd_limit(unsigned long limit);
152+void ksmbd_update_fstate(struct ksmbd_file_table *ft, struct ksmbd_file *fp,
153+ unsigned int state);
145154
146155 /*
147156 * INODE hash

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t