Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ksmbd: fix use-after-free in ksmbd_tree_connect_put under concurrency

Under high concurrency, A tree-connection object (tcon) is freed on
a disconnect path while another path still holds a reference and later
executes *_put()/write on it.

Reported-by: Qianchang Zhao <pioooooooooip@gmail.com>
Reported-by: Zhitong Liu <liuzhitong1993@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>

authored by

Namjae Jeon and committed by
Steve French
b39a1833 3316a8fc

+4 -18
+4 -14
fs/smb/server/mgmt/tree_connect.c
··· 78 78 tree_conn->t_state = TREE_NEW; 79 79 status.tree_conn = tree_conn; 80 80 atomic_set(&tree_conn->refcount, 1); 81 - init_waitqueue_head(&tree_conn->refcount_q); 82 81 83 82 ret = xa_err(xa_store(&sess->tree_conns, tree_conn->id, tree_conn, 84 83 KSMBD_DEFAULT_GFP)); ··· 99 100 100 101 void ksmbd_tree_connect_put(struct ksmbd_tree_connect *tcon) 101 102 { 102 - /* 103 - * Checking waitqueue to releasing tree connect on 104 - * tree disconnect. waitqueue_active is safe because it 105 - * uses atomic operation for condition. 106 - */ 107 - if (!atomic_dec_return(&tcon->refcount) && 108 - waitqueue_active(&tcon->refcount_q)) 109 - wake_up(&tcon->refcount_q); 103 + if (atomic_dec_and_test(&tcon->refcount)) 104 + kfree(tcon); 110 105 } 111 106 112 107 int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, ··· 112 119 xa_erase(&sess->tree_conns, tree_conn->id); 113 120 write_unlock(&sess->tree_conns_lock); 114 121 115 - if (!atomic_dec_and_test(&tree_conn->refcount)) 116 - wait_event(tree_conn->refcount_q, 117 - atomic_read(&tree_conn->refcount) == 0); 118 - 119 122 ret = ksmbd_ipc_tree_disconnect_request(sess->id, tree_conn->id); 120 123 ksmbd_release_tree_conn_id(sess, tree_conn->id); 121 124 ksmbd_share_config_put(tree_conn->share_conf); 122 - kfree(tree_conn); 125 + if (atomic_dec_and_test(&tree_conn->refcount)) 126 + kfree(tree_conn); 123 127 return ret; 124 128 } 125 129
-1
fs/smb/server/mgmt/tree_connect.h
··· 33 33 int maximal_access; 34 34 bool posix_extensions; 35 35 atomic_t refcount; 36 - wait_queue_head_t refcount_q; 37 36 unsigned int t_state; 38 37 }; 39 38
-3
fs/smb/server/smb2pdu.c
··· 2190 2190 goto err_out; 2191 2191 } 2192 2192 2193 - WARN_ON_ONCE(atomic_dec_and_test(&tcon->refcount)); 2194 2193 tcon->t_state = TREE_DISCONNECTED; 2195 2194 write_unlock(&sess->tree_conns_lock); 2196 2195 ··· 2198 2199 rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; 2199 2200 goto err_out; 2200 2201 } 2201 - 2202 - work->tcon = NULL; 2203 2202 2204 2203 rsp->StructureSize = cpu_to_le16(4); 2205 2204 err = ksmbd_iov_pin_rsp(work, rsp,