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

Configure Feed

Select the types of activity you want to include in your feed.

Merge tag 'v6.18rc1-part1-ksmbd-server-fixes' of git://git.samba.org/ksmbd

Pull smb server fixes from Steve French:

- Fix potential UAFs and corruptions in rpc open and close

- Fix copy_file_range when ranges overlap

- Improve session, share, connection lookup performance

- Fix potential hash collisions in share and session lists

- Debugging improvement - making per-connection threads easier to
identify

- Improve socket creation

- Fix return code mapping for posix query fs info

- Add support for limiting the maximum number of connections per IP
address, extending the existing connection limiting mechanism to
enforce per-IP connection limits alongside the global connection
limit

* tag 'v6.18rc1-part1-ksmbd-server-fixes' of git://git.samba.org/ksmbd:
ksmbd: increase session and share hash table bits
ksmbd: replace connection list with hash table
ksmbd: add an error print when maximum IP connections limit is reached
ksmbd: add max ip connections parameter
ksmbd: fix error code overwriting in smb2_get_info_filesystem()
ksmbd: copy overlapped range within the same file
ksmbd: use sock_create_kern interface to create kernel socket
ksmbd: make ksmbd thread names distinct by client IP
ksmbd: Fix race condition in RPC handle list access

+119 -75
+11 -12
fs/smb/server/connection.c
··· 19 19 20 20 static struct ksmbd_conn_ops default_conn_ops; 21 21 22 - LIST_HEAD(conn_list); 22 + DEFINE_HASHTABLE(conn_list, CONN_HASH_BITS); 23 23 DECLARE_RWSEM(conn_list_lock); 24 24 25 25 /** ··· 33 33 void ksmbd_conn_free(struct ksmbd_conn *conn) 34 34 { 35 35 down_write(&conn_list_lock); 36 - list_del(&conn->conns_list); 36 + hash_del(&conn->hlist); 37 37 up_write(&conn_list_lock); 38 38 39 39 xa_destroy(&conn->sessions); ··· 77 77 78 78 init_waitqueue_head(&conn->req_running_q); 79 79 init_waitqueue_head(&conn->r_count_q); 80 - INIT_LIST_HEAD(&conn->conns_list); 81 80 INIT_LIST_HEAD(&conn->requests); 82 81 INIT_LIST_HEAD(&conn->async_requests); 83 82 spin_lock_init(&conn->request_lock); ··· 89 90 90 91 init_rwsem(&conn->session_lock); 91 92 92 - down_write(&conn_list_lock); 93 - list_add(&conn->conns_list, &conn_list); 94 - up_write(&conn_list_lock); 95 93 return conn; 96 94 } 97 95 98 96 bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c) 99 97 { 100 98 struct ksmbd_conn *t; 99 + int bkt; 101 100 bool ret = false; 102 101 103 102 down_read(&conn_list_lock); 104 - list_for_each_entry(t, &conn_list, conns_list) { 103 + hash_for_each(conn_list, bkt, t, hlist) { 105 104 if (memcmp(t->ClientGUID, c->ClientGUID, SMB2_CLIENT_GUID_SIZE)) 106 105 continue; 107 106 ··· 160 163 void ksmbd_all_conn_set_status(u64 sess_id, u32 status) 161 164 { 162 165 struct ksmbd_conn *conn; 166 + int bkt; 163 167 164 168 down_read(&conn_list_lock); 165 - list_for_each_entry(conn, &conn_list, conns_list) { 169 + hash_for_each(conn_list, bkt, conn, hlist) { 166 170 if (conn->binding || xa_load(&conn->sessions, sess_id)) 167 171 WRITE_ONCE(conn->status, status); 168 172 } ··· 179 181 { 180 182 struct ksmbd_conn *conn; 181 183 int rc, retry_count = 0, max_timeout = 120; 182 - int rcount = 1; 184 + int rcount = 1, bkt; 183 185 184 186 retry_idle: 185 187 if (retry_count >= max_timeout) 186 188 return -EIO; 187 189 188 190 down_read(&conn_list_lock); 189 - list_for_each_entry(conn, &conn_list, conns_list) { 191 + hash_for_each(conn_list, bkt, conn, hlist) { 190 192 if (conn->binding || xa_load(&conn->sessions, sess_id)) { 191 193 if (conn == curr_conn) 192 194 rcount = 2; ··· 478 480 { 479 481 struct ksmbd_conn *conn; 480 482 struct ksmbd_transport *t; 483 + int bkt; 481 484 482 485 again: 483 486 down_read(&conn_list_lock); 484 - list_for_each_entry(conn, &conn_list, conns_list) { 487 + hash_for_each(conn_list, bkt, conn, hlist) { 485 488 t = conn->transport; 486 489 ksmbd_conn_set_exiting(conn); 487 490 if (t->ops->shutdown) { ··· 493 494 } 494 495 up_read(&conn_list_lock); 495 496 496 - if (!list_empty(&conn_list)) { 497 + if (!hash_empty(conn_list)) { 497 498 msleep(100); 498 499 goto again; 499 500 }
+4 -2
fs/smb/server/connection.h
··· 54 54 u8 inet6_addr[16]; 55 55 #endif 56 56 }; 57 + unsigned int inet_hash; 57 58 char *request_buf; 58 59 struct ksmbd_transport *transport; 59 60 struct nls_table *local_nls; 60 61 struct unicode_map *um; 61 - struct list_head conns_list; 62 + struct hlist_node hlist; 62 63 struct rw_semaphore session_lock; 63 64 /* smb session 1 per user */ 64 65 struct xarray sessions; ··· 154 153 #define KSMBD_TCP_SEND_TIMEOUT (5 * HZ) 155 154 #define KSMBD_TCP_PEER_SOCKADDR(c) ((struct sockaddr *)&((c)->peer_addr)) 156 155 157 - extern struct list_head conn_list; 156 + #define CONN_HASH_BITS 12 157 + extern DECLARE_HASHTABLE(conn_list, CONN_HASH_BITS); 158 158 extern struct rw_semaphore conn_list_lock; 159 159 160 160 bool ksmbd_conn_alive(struct ksmbd_conn *conn);
+3 -2
fs/smb/server/ksmbd_netlink.h
··· 112 112 __u32 smbd_max_io_size; /* smbd read write size */ 113 113 __u32 max_connections; /* Number of maximum simultaneous connections */ 114 114 __s8 bind_interfaces_only; 115 - __s8 reserved[503]; /* Reserved room */ 115 + __u32 max_ip_connections; /* Number of maximum connection per ip address */ 116 + __s8 reserved[499]; /* Reserved room */ 116 117 __u32 ifc_list_sz; /* interfaces list size */ 117 118 __s8 ____payload[]; 118 - }; 119 + } __packed; 119 120 120 121 #define KSMBD_STARTUP_CONFIG_INTERFACES(s) ((s)->____payload) 121 122
+1 -1
fs/smb/server/mgmt/share_config.c
··· 19 19 #include "../transport_ipc.h" 20 20 #include "../misc.h" 21 21 22 - #define SHARE_HASH_BITS 3 22 + #define SHARE_HASH_BITS 12 23 23 static DEFINE_HASHTABLE(shares_table, SHARE_HASH_BITS); 24 24 static DECLARE_RWSEM(shares_table_lock); 25 25
+18 -10
fs/smb/server/mgmt/user_session.c
··· 18 18 19 19 static DEFINE_IDA(session_ida); 20 20 21 - #define SESSION_HASH_BITS 3 21 + #define SESSION_HASH_BITS 12 22 22 static DEFINE_HASHTABLE(sessions_table, SESSION_HASH_BITS); 23 23 static DECLARE_RWSEM(sessions_table_lock); 24 24 ··· 104 104 if (!entry) 105 105 return -ENOMEM; 106 106 107 - down_read(&sess->rpc_lock); 108 107 entry->method = method; 109 108 entry->id = id = ksmbd_ipc_id_alloc(); 110 109 if (id < 0) 111 110 goto free_entry; 111 + 112 + down_write(&sess->rpc_lock); 112 113 old = xa_store(&sess->rpc_handle_list, id, entry, KSMBD_DEFAULT_GFP); 113 - if (xa_is_err(old)) 114 + if (xa_is_err(old)) { 115 + up_write(&sess->rpc_lock); 114 116 goto free_id; 117 + } 115 118 116 119 resp = ksmbd_rpc_open(sess, id); 117 - if (!resp) 118 - goto erase_xa; 120 + if (!resp) { 121 + xa_erase(&sess->rpc_handle_list, entry->id); 122 + up_write(&sess->rpc_lock); 123 + goto free_id; 124 + } 119 125 120 - up_read(&sess->rpc_lock); 126 + up_write(&sess->rpc_lock); 121 127 kvfree(resp); 122 128 return id; 123 - erase_xa: 124 - xa_erase(&sess->rpc_handle_list, entry->id); 125 129 free_id: 126 130 ksmbd_rpc_id_free(entry->id); 127 131 free_entry: 128 132 kfree(entry); 129 - up_read(&sess->rpc_lock); 130 133 return -EINVAL; 131 134 } 132 135 ··· 147 144 int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id) 148 145 { 149 146 struct ksmbd_session_rpc *entry; 147 + int method; 150 148 149 + down_read(&sess->rpc_lock); 151 150 entry = xa_load(&sess->rpc_handle_list, id); 152 - return entry ? entry->method : 0; 151 + method = entry ? entry->method : 0; 152 + up_read(&sess->rpc_lock); 153 + 154 + return method; 153 155 } 154 156 155 157 void ksmbd_session_destroy(struct ksmbd_session *sess)
+1
fs/smb/server/server.h
··· 43 43 unsigned int auth_mechs; 44 44 unsigned int max_connections; 45 45 unsigned int max_inflight_req; 46 + unsigned int max_ip_connections; 46 47 47 48 char *conf[SERVER_CONF_WORK_GROUP + 1]; 48 49 struct task_struct *dh_task;
+4 -3
fs/smb/server/smb2pdu.c
··· 5629 5629 5630 5630 if (!work->tcon->posix_extensions) { 5631 5631 pr_err("client doesn't negotiate with SMB3.1.1 POSIX Extensions\n"); 5632 - rc = -EOPNOTSUPP; 5632 + path_put(&path); 5633 + return -EOPNOTSUPP; 5633 5634 } else { 5634 5635 info = (struct filesystem_posix_info *)(rsp->Buffer); 5635 5636 info->OptimalTransferSize = cpu_to_le32(stfs.f_bsize); ··· 7362 7361 int nolock = 0; 7363 7362 LIST_HEAD(lock_list); 7364 7363 LIST_HEAD(rollback_list); 7365 - int prior_lock = 0; 7364 + int prior_lock = 0, bkt; 7366 7365 7367 7366 WORK_BUFFERS(work, req, rsp); 7368 7367 ··· 7472 7471 nolock = 1; 7473 7472 /* check locks in connection list */ 7474 7473 down_read(&conn_list_lock); 7475 - list_for_each_entry(conn, &conn_list, conns_list) { 7474 + hash_for_each(conn_list, bkt, conn, hlist) { 7476 7475 spin_lock(&conn->llist_lock); 7477 7476 list_for_each_entry_safe(cmp_lock, tmp2, &conn->lock_list, clist) { 7478 7477 if (file_inode(cmp_lock->fl->c.flc_file) !=
+3
fs/smb/server/transport_ipc.c
··· 335 335 if (req->max_connections) 336 336 server_conf.max_connections = req->max_connections; 337 337 338 + if (req->max_ip_connections) 339 + server_conf.max_ip_connections = req->max_ip_connections; 340 + 338 341 ret = ksmbd_set_netbios_name(req->netbios_name); 339 342 ret |= ksmbd_set_server_string(req->server_string); 340 343 ret |= ksmbd_set_work_group(req->work_group);
+5
fs/smb/server/transport_rdma.c
··· 425 425 conn = ksmbd_conn_alloc(); 426 426 if (!conn) 427 427 goto err; 428 + 429 + down_write(&conn_list_lock); 430 + hash_add(conn_list, &conn->hlist, 0); 431 + up_write(&conn_list_lock); 432 + 428 433 conn->transport = KSMBD_TRANS(t); 429 434 KSMBD_TRANS(t)->conn = conn; 430 435 KSMBD_TRANS(t)->ops = &ksmbd_smb_direct_transport_ops;
+55 -43
fs/smb/server/transport_tcp.c
··· 86 86 } 87 87 88 88 #if IS_ENABLED(CONFIG_IPV6) 89 - if (client_sk->sk->sk_family == AF_INET6) 89 + if (client_sk->sk->sk_family == AF_INET6) { 90 90 memcpy(&conn->inet6_addr, &client_sk->sk->sk_v6_daddr, 16); 91 - else 91 + conn->inet_hash = ipv6_addr_hash(&client_sk->sk->sk_v6_daddr); 92 + } else { 92 93 conn->inet_addr = inet_sk(client_sk->sk)->inet_daddr; 94 + conn->inet_hash = ipv4_addr_hash(inet_sk(client_sk->sk)->inet_daddr); 95 + } 93 96 #else 94 97 conn->inet_addr = inet_sk(client_sk->sk)->inet_daddr; 98 + conn->inet_hash = ipv4_addr_hash(inet_sk(client_sk->sk)->inet_daddr); 95 99 #endif 100 + down_write(&conn_list_lock); 101 + hash_add(conn_list, &conn->hlist, conn->inet_hash); 102 + up_write(&conn_list_lock); 103 + 96 104 conn->transport = KSMBD_TRANS(t); 97 105 KSMBD_TRANS(t)->conn = conn; 98 106 KSMBD_TRANS(t)->ops = &ksmbd_tcp_transport_ops; ··· 178 170 return new_iov; 179 171 } 180 172 181 - static unsigned short ksmbd_tcp_get_port(const struct sockaddr *sa) 182 - { 183 - switch (sa->sa_family) { 184 - case AF_INET: 185 - return ntohs(((struct sockaddr_in *)sa)->sin_port); 186 - case AF_INET6: 187 - return ntohs(((struct sockaddr_in6 *)sa)->sin6_port); 188 - } 189 - return 0; 190 - } 191 - 192 173 /** 193 174 * ksmbd_tcp_new_connection() - create a new tcp session on mount 194 175 * @client_sk: socket associated with new connection ··· 189 192 */ 190 193 static int ksmbd_tcp_new_connection(struct socket *client_sk) 191 194 { 192 - struct sockaddr *csin; 193 195 int rc = 0; 194 196 struct tcp_transport *t; 195 197 struct task_struct *handler; ··· 199 203 return -ENOMEM; 200 204 } 201 205 202 - csin = KSMBD_TCP_PEER_SOCKADDR(KSMBD_TRANS(t)->conn); 203 - if (kernel_getpeername(client_sk, csin) < 0) { 204 - pr_err("client ip resolution failed\n"); 205 - rc = -EINVAL; 206 - goto out_error; 207 - } 208 - 206 + #if IS_ENABLED(CONFIG_IPV6) 207 + if (client_sk->sk->sk_family == AF_INET6) 208 + handler = kthread_run(ksmbd_conn_handler_loop, 209 + KSMBD_TRANS(t)->conn, "ksmbd:%pI6c", 210 + &KSMBD_TRANS(t)->conn->inet6_addr); 211 + else 212 + handler = kthread_run(ksmbd_conn_handler_loop, 213 + KSMBD_TRANS(t)->conn, "ksmbd:%pI4", 214 + &KSMBD_TRANS(t)->conn->inet_addr); 215 + #else 209 216 handler = kthread_run(ksmbd_conn_handler_loop, 210 - KSMBD_TRANS(t)->conn, 211 - "ksmbd:%u", 212 - ksmbd_tcp_get_port(csin)); 217 + KSMBD_TRANS(t)->conn, "ksmbd:%pI4", 218 + &KSMBD_TRANS(t)->conn->inet_addr); 219 + #endif 213 220 if (IS_ERR(handler)) { 214 221 pr_err("cannot start conn thread\n"); 215 222 rc = PTR_ERR(handler); 216 223 free_transport(t); 217 224 } 218 - return rc; 219 - 220 - out_error: 221 - free_transport(t); 222 225 return rc; 223 226 } 224 227 ··· 232 237 struct socket *client_sk = NULL; 233 238 struct interface *iface = (struct interface *)p; 234 239 struct ksmbd_conn *conn; 235 - int ret; 240 + int ret, inet_hash; 241 + unsigned int max_ip_conns; 236 242 237 243 while (!kthread_should_stop()) { 238 244 mutex_lock(&iface->sock_release_lock); ··· 251 255 continue; 252 256 } 253 257 258 + if (!server_conf.max_ip_connections) 259 + goto skip_max_ip_conns_limit; 260 + 254 261 /* 255 262 * Limits repeated connections from clients with the same IP. 256 263 */ 264 + #if IS_ENABLED(CONFIG_IPV6) 265 + if (client_sk->sk->sk_family == AF_INET6) 266 + inet_hash = ipv6_addr_hash(&client_sk->sk->sk_v6_daddr); 267 + else 268 + inet_hash = ipv4_addr_hash(inet_sk(client_sk->sk)->inet_daddr); 269 + #else 270 + inet_hash = ipv4_addr_hash(inet_sk(client_sk->sk)->inet_daddr); 271 + #endif 272 + 273 + max_ip_conns = 0; 257 274 down_read(&conn_list_lock); 258 - list_for_each_entry(conn, &conn_list, conns_list) 275 + hash_for_each_possible(conn_list, conn, hlist, inet_hash) { 259 276 #if IS_ENABLED(CONFIG_IPV6) 260 277 if (client_sk->sk->sk_family == AF_INET6) { 261 278 if (memcmp(&client_sk->sk->sk_v6_daddr, 262 - &conn->inet6_addr, 16) == 0) { 263 - ret = -EAGAIN; 264 - break; 265 - } 279 + &conn->inet6_addr, 16) == 0) 280 + max_ip_conns++; 266 281 } else if (inet_sk(client_sk->sk)->inet_daddr == 267 - conn->inet_addr) { 268 - ret = -EAGAIN; 269 - break; 270 - } 282 + conn->inet_addr) 283 + max_ip_conns++; 271 284 #else 272 285 if (inet_sk(client_sk->sk)->inet_daddr == 273 - conn->inet_addr) { 286 + conn->inet_addr) 287 + max_ip_conns++; 288 + #endif 289 + if (server_conf.max_ip_connections <= max_ip_conns) { 290 + pr_info_ratelimited("Maximum IP connections exceeded (%u/%u)\n", 291 + max_ip_conns, server_conf.max_ip_connections); 274 292 ret = -EAGAIN; 275 293 break; 276 294 } 277 - #endif 295 + } 278 296 up_read(&conn_list_lock); 279 297 if (ret == -EAGAIN) 280 298 continue; 281 299 300 + skip_max_ip_conns_limit: 282 301 if (server_conf.max_connections && 283 302 atomic_inc_return(&active_num_conn) >= server_conf.max_connections) { 284 303 pr_info_ratelimited("Limit the maximum number of connections(%u)\n", ··· 479 468 struct socket *ksmbd_socket; 480 469 bool ipv4 = false; 481 470 482 - ret = sock_create(PF_INET6, SOCK_STREAM, IPPROTO_TCP, &ksmbd_socket); 471 + ret = sock_create_kern(current->nsproxy->net_ns, PF_INET6, SOCK_STREAM, 472 + IPPROTO_TCP, &ksmbd_socket); 483 473 if (ret) { 484 474 if (ret != -EAFNOSUPPORT) 485 475 pr_err("Can't create socket for ipv6, fallback to ipv4: %d\n", ret); 486 - ret = sock_create(PF_INET, SOCK_STREAM, IPPROTO_TCP, 487 - &ksmbd_socket); 476 + ret = sock_create_kern(current->nsproxy->net_ns, PF_INET, 477 + SOCK_STREAM, IPPROTO_TCP, &ksmbd_socket); 488 478 if (ret) { 489 479 pr_err("Can't create socket for ipv4: %d\n", ret); 490 480 goto out_clear;
+14 -2
fs/smb/server/vfs.c
··· 20 20 #include <linux/sched/xacct.h> 21 21 #include <linux/crc32c.h> 22 22 #include <linux/namei.h> 23 + #include <linux/splice.h> 23 24 24 25 #include "glob.h" 25 26 #include "oplock.h" ··· 1830 1829 if (src_off + len > src_file_size) 1831 1830 return -E2BIG; 1832 1831 1833 - ret = vfs_copy_file_range(src_fp->filp, src_off, 1834 - dst_fp->filp, dst_off, len, 0); 1832 + /* 1833 + * vfs_copy_file_range does not allow overlapped copying 1834 + * within the same file. 1835 + */ 1836 + if (file_inode(src_fp->filp) == file_inode(dst_fp->filp) && 1837 + dst_off + len > src_off && 1838 + dst_off < src_off + len) 1839 + ret = do_splice_direct(src_fp->filp, &src_off, 1840 + dst_fp->filp, &dst_off, 1841 + min_t(size_t, len, MAX_RW_COUNT), 0); 1842 + else 1843 + ret = vfs_copy_file_range(src_fp->filp, src_off, 1844 + dst_fp->filp, dst_off, len, 0); 1835 1845 if (ret == -EOPNOTSUPP || ret == -EXDEV) 1836 1846 ret = vfs_copy_file_range(src_fp->filp, src_off, 1837 1847 dst_fp->filp, dst_off, len,