| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * net/dccp/proto.c |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * An implementation of the DCCP protocol |
|---|
| 5 | 6 | * Arnaldo Carvalho de Melo <acme@conectiva.com.br> |
|---|
| 6 | | - * |
|---|
| 7 | | - * This program is free software; you can redistribute it and/or modify it |
|---|
| 8 | | - * under the terms of the GNU General Public License version 2 as |
|---|
| 9 | | - * published by the Free Software Foundation. |
|---|
| 10 | 7 | */ |
|---|
| 11 | 8 | |
|---|
| 12 | 9 | #include <linux/dccp.h> |
|---|
| .. | .. |
|---|
| 104 | 101 | if (inet_csk(sk)->icsk_bind_hash != NULL && |
|---|
| 105 | 102 | !(sk->sk_userlocks & SOCK_BINDPORT_LOCK)) |
|---|
| 106 | 103 | inet_put_port(sk); |
|---|
| 107 | | - /* fall through */ |
|---|
| 104 | + fallthrough; |
|---|
| 108 | 105 | default: |
|---|
| 109 | 106 | if (oldstate == DCCP_OPEN) |
|---|
| 110 | 107 | DCCP_DEC_STATS(DCCP_MIB_CURRESTAB); |
|---|
| .. | .. |
|---|
| 174 | 171 | |
|---|
| 175 | 172 | EXPORT_SYMBOL_GPL(dccp_packet_name); |
|---|
| 176 | 173 | |
|---|
| 177 | | -static void dccp_sk_destruct(struct sock *sk) |
|---|
| 174 | +void dccp_destruct_common(struct sock *sk) |
|---|
| 178 | 175 | { |
|---|
| 179 | 176 | struct dccp_sock *dp = dccp_sk(sk); |
|---|
| 180 | 177 | |
|---|
| 181 | 178 | ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk); |
|---|
| 182 | 179 | dp->dccps_hc_tx_ccid = NULL; |
|---|
| 180 | +} |
|---|
| 181 | +EXPORT_SYMBOL_GPL(dccp_destruct_common); |
|---|
| 182 | + |
|---|
| 183 | +static void dccp_sk_destruct(struct sock *sk) |
|---|
| 184 | +{ |
|---|
| 185 | + dccp_destruct_common(sk); |
|---|
| 183 | 186 | inet_sock_destruct(sk); |
|---|
| 184 | 187 | } |
|---|
| 185 | 188 | |
|---|
| .. | .. |
|---|
| 263 | 266 | struct inet_connection_sock *icsk = inet_csk(sk); |
|---|
| 264 | 267 | struct inet_sock *inet = inet_sk(sk); |
|---|
| 265 | 268 | struct dccp_sock *dp = dccp_sk(sk); |
|---|
| 266 | | - int err = 0; |
|---|
| 267 | 269 | const int old_state = sk->sk_state; |
|---|
| 268 | 270 | |
|---|
| 269 | 271 | if (old_state != DCCP_CLOSED) |
|---|
| .. | .. |
|---|
| 307 | 309 | WARN_ON(inet->inet_num && !icsk->icsk_bind_hash); |
|---|
| 308 | 310 | |
|---|
| 309 | 311 | sk->sk_error_report(sk); |
|---|
| 310 | | - return err; |
|---|
| 312 | + return 0; |
|---|
| 311 | 313 | } |
|---|
| 312 | 314 | |
|---|
| 313 | 315 | EXPORT_SYMBOL_GPL(dccp_disconnect); |
|---|
| .. | .. |
|---|
| 322 | 324 | __poll_t dccp_poll(struct file *file, struct socket *sock, |
|---|
| 323 | 325 | poll_table *wait) |
|---|
| 324 | 326 | { |
|---|
| 325 | | - __poll_t mask; |
|---|
| 326 | 327 | struct sock *sk = sock->sk; |
|---|
| 328 | + __poll_t mask; |
|---|
| 329 | + u8 shutdown; |
|---|
| 330 | + int state; |
|---|
| 327 | 331 | |
|---|
| 328 | 332 | sock_poll_wait(file, sock, wait); |
|---|
| 329 | | - if (sk->sk_state == DCCP_LISTEN) |
|---|
| 333 | + |
|---|
| 334 | + state = inet_sk_state_load(sk); |
|---|
| 335 | + if (state == DCCP_LISTEN) |
|---|
| 330 | 336 | return inet_csk_listen_poll(sk); |
|---|
| 331 | 337 | |
|---|
| 332 | 338 | /* Socket is not locked. We are protected from async events |
|---|
| .. | .. |
|---|
| 335 | 341 | */ |
|---|
| 336 | 342 | |
|---|
| 337 | 343 | mask = 0; |
|---|
| 338 | | - if (sk->sk_err) |
|---|
| 344 | + if (READ_ONCE(sk->sk_err)) |
|---|
| 339 | 345 | mask = EPOLLERR; |
|---|
| 346 | + shutdown = READ_ONCE(sk->sk_shutdown); |
|---|
| 340 | 347 | |
|---|
| 341 | | - if (sk->sk_shutdown == SHUTDOWN_MASK || sk->sk_state == DCCP_CLOSED) |
|---|
| 348 | + if (shutdown == SHUTDOWN_MASK || state == DCCP_CLOSED) |
|---|
| 342 | 349 | mask |= EPOLLHUP; |
|---|
| 343 | | - if (sk->sk_shutdown & RCV_SHUTDOWN) |
|---|
| 350 | + if (shutdown & RCV_SHUTDOWN) |
|---|
| 344 | 351 | mask |= EPOLLIN | EPOLLRDNORM | EPOLLRDHUP; |
|---|
| 345 | 352 | |
|---|
| 346 | 353 | /* Connected? */ |
|---|
| 347 | | - if ((1 << sk->sk_state) & ~(DCCPF_REQUESTING | DCCPF_RESPOND)) { |
|---|
| 354 | + if ((1 << state) & ~(DCCPF_REQUESTING | DCCPF_RESPOND)) { |
|---|
| 348 | 355 | if (atomic_read(&sk->sk_rmem_alloc) > 0) |
|---|
| 349 | 356 | mask |= EPOLLIN | EPOLLRDNORM; |
|---|
| 350 | 357 | |
|---|
| 351 | | - if (!(sk->sk_shutdown & SEND_SHUTDOWN)) { |
|---|
| 358 | + if (!(shutdown & SEND_SHUTDOWN)) { |
|---|
| 352 | 359 | if (sk_stream_is_writeable(sk)) { |
|---|
| 353 | 360 | mask |= EPOLLOUT | EPOLLWRNORM; |
|---|
| 354 | 361 | } else { /* send SIGIO later */ |
|---|
| .. | .. |
|---|
| 366 | 373 | } |
|---|
| 367 | 374 | return mask; |
|---|
| 368 | 375 | } |
|---|
| 369 | | - |
|---|
| 370 | 376 | EXPORT_SYMBOL_GPL(dccp_poll); |
|---|
| 371 | 377 | |
|---|
| 372 | 378 | int dccp_ioctl(struct sock *sk, int cmd, unsigned long arg) |
|---|
| .. | .. |
|---|
| 379 | 385 | goto out; |
|---|
| 380 | 386 | |
|---|
| 381 | 387 | switch (cmd) { |
|---|
| 388 | + case SIOCOUTQ: { |
|---|
| 389 | + int amount = sk_wmem_alloc_get(sk); |
|---|
| 390 | + /* Using sk_wmem_alloc here because sk_wmem_queued is not used by DCCP and |
|---|
| 391 | + * always 0, comparably to UDP. |
|---|
| 392 | + */ |
|---|
| 393 | + |
|---|
| 394 | + rc = put_user(amount, (int __user *)arg); |
|---|
| 395 | + } |
|---|
| 396 | + break; |
|---|
| 382 | 397 | case SIOCINQ: { |
|---|
| 383 | 398 | struct sk_buff *skb; |
|---|
| 384 | 399 | unsigned long amount = 0; |
|---|
| .. | .. |
|---|
| 406 | 421 | EXPORT_SYMBOL_GPL(dccp_ioctl); |
|---|
| 407 | 422 | |
|---|
| 408 | 423 | static int dccp_setsockopt_service(struct sock *sk, const __be32 service, |
|---|
| 409 | | - char __user *optval, unsigned int optlen) |
|---|
| 424 | + sockptr_t optval, unsigned int optlen) |
|---|
| 410 | 425 | { |
|---|
| 411 | 426 | struct dccp_sock *dp = dccp_sk(sk); |
|---|
| 412 | 427 | struct dccp_service_list *sl = NULL; |
|---|
| .. | .. |
|---|
| 421 | 436 | return -ENOMEM; |
|---|
| 422 | 437 | |
|---|
| 423 | 438 | sl->dccpsl_nr = optlen / sizeof(u32) - 1; |
|---|
| 424 | | - if (copy_from_user(sl->dccpsl_list, |
|---|
| 425 | | - optval + sizeof(service), |
|---|
| 426 | | - optlen - sizeof(service)) || |
|---|
| 439 | + if (copy_from_sockptr_offset(sl->dccpsl_list, optval, |
|---|
| 440 | + sizeof(service), optlen - sizeof(service)) || |
|---|
| 427 | 441 | dccp_list_has_service(sl, DCCP_SERVICE_INVALID_VALUE)) { |
|---|
| 428 | 442 | kfree(sl); |
|---|
| 429 | 443 | return -EFAULT; |
|---|
| .. | .. |
|---|
| 477 | 491 | } |
|---|
| 478 | 492 | |
|---|
| 479 | 493 | static int dccp_setsockopt_ccid(struct sock *sk, int type, |
|---|
| 480 | | - char __user *optval, unsigned int optlen) |
|---|
| 494 | + sockptr_t optval, unsigned int optlen) |
|---|
| 481 | 495 | { |
|---|
| 482 | 496 | u8 *val; |
|---|
| 483 | 497 | int rc = 0; |
|---|
| .. | .. |
|---|
| 485 | 499 | if (optlen < 1 || optlen > DCCP_FEAT_MAX_SP_VALS) |
|---|
| 486 | 500 | return -EINVAL; |
|---|
| 487 | 501 | |
|---|
| 488 | | - val = memdup_user(optval, optlen); |
|---|
| 502 | + val = memdup_sockptr(optval, optlen); |
|---|
| 489 | 503 | if (IS_ERR(val)) |
|---|
| 490 | 504 | return PTR_ERR(val); |
|---|
| 491 | 505 | |
|---|
| .. | .. |
|---|
| 502 | 516 | } |
|---|
| 503 | 517 | |
|---|
| 504 | 518 | static int do_dccp_setsockopt(struct sock *sk, int level, int optname, |
|---|
| 505 | | - char __user *optval, unsigned int optlen) |
|---|
| 519 | + sockptr_t optval, unsigned int optlen) |
|---|
| 506 | 520 | { |
|---|
| 507 | 521 | struct dccp_sock *dp = dccp_sk(sk); |
|---|
| 508 | 522 | int val, err = 0; |
|---|
| .. | .. |
|---|
| 524 | 538 | if (optlen < (int)sizeof(int)) |
|---|
| 525 | 539 | return -EINVAL; |
|---|
| 526 | 540 | |
|---|
| 527 | | - if (get_user(val, (int __user *)optval)) |
|---|
| 541 | + if (copy_from_sockptr(&val, optval, sizeof(int))) |
|---|
| 528 | 542 | return -EFAULT; |
|---|
| 529 | 543 | |
|---|
| 530 | 544 | if (optname == DCCP_SOCKOPT_SERVICE) |
|---|
| .. | .. |
|---|
| 567 | 581 | return err; |
|---|
| 568 | 582 | } |
|---|
| 569 | 583 | |
|---|
| 570 | | -int dccp_setsockopt(struct sock *sk, int level, int optname, |
|---|
| 571 | | - char __user *optval, unsigned int optlen) |
|---|
| 584 | +int dccp_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval, |
|---|
| 585 | + unsigned int optlen) |
|---|
| 572 | 586 | { |
|---|
| 573 | 587 | if (level != SOL_DCCP) |
|---|
| 574 | 588 | return inet_csk(sk)->icsk_af_ops->setsockopt(sk, level, |
|---|
| .. | .. |
|---|
| 578 | 592 | } |
|---|
| 579 | 593 | |
|---|
| 580 | 594 | EXPORT_SYMBOL_GPL(dccp_setsockopt); |
|---|
| 581 | | - |
|---|
| 582 | | -#ifdef CONFIG_COMPAT |
|---|
| 583 | | -int compat_dccp_setsockopt(struct sock *sk, int level, int optname, |
|---|
| 584 | | - char __user *optval, unsigned int optlen) |
|---|
| 585 | | -{ |
|---|
| 586 | | - if (level != SOL_DCCP) |
|---|
| 587 | | - return inet_csk_compat_setsockopt(sk, level, optname, |
|---|
| 588 | | - optval, optlen); |
|---|
| 589 | | - return do_dccp_setsockopt(sk, level, optname, optval, optlen); |
|---|
| 590 | | -} |
|---|
| 591 | | - |
|---|
| 592 | | -EXPORT_SYMBOL_GPL(compat_dccp_setsockopt); |
|---|
| 593 | | -#endif |
|---|
| 594 | 595 | |
|---|
| 595 | 596 | static int dccp_getsockopt_service(struct sock *sk, int len, |
|---|
| 596 | 597 | __be32 __user *optval, |
|---|
| .. | .. |
|---|
| 642 | 643 | return dccp_getsockopt_service(sk, len, |
|---|
| 643 | 644 | (__be32 __user *)optval, optlen); |
|---|
| 644 | 645 | case DCCP_SOCKOPT_GET_CUR_MPS: |
|---|
| 645 | | - val = dp->dccps_mss_cache; |
|---|
| 646 | + val = READ_ONCE(dp->dccps_mss_cache); |
|---|
| 646 | 647 | break; |
|---|
| 647 | 648 | case DCCP_SOCKOPT_AVAILABLE_CCIDS: |
|---|
| 648 | 649 | return ccid_getsockopt_builtin_ccids(sk, len, optval, optlen); |
|---|
| .. | .. |
|---|
| 700 | 701 | |
|---|
| 701 | 702 | EXPORT_SYMBOL_GPL(dccp_getsockopt); |
|---|
| 702 | 703 | |
|---|
| 703 | | -#ifdef CONFIG_COMPAT |
|---|
| 704 | | -int compat_dccp_getsockopt(struct sock *sk, int level, int optname, |
|---|
| 705 | | - char __user *optval, int __user *optlen) |
|---|
| 706 | | -{ |
|---|
| 707 | | - if (level != SOL_DCCP) |
|---|
| 708 | | - return inet_csk_compat_getsockopt(sk, level, optname, |
|---|
| 709 | | - optval, optlen); |
|---|
| 710 | | - return do_dccp_getsockopt(sk, level, optname, optval, optlen); |
|---|
| 711 | | -} |
|---|
| 712 | | - |
|---|
| 713 | | -EXPORT_SYMBOL_GPL(compat_dccp_getsockopt); |
|---|
| 714 | | -#endif |
|---|
| 715 | | - |
|---|
| 716 | 704 | static int dccp_msghdr_parse(struct msghdr *msg, struct sk_buff *skb) |
|---|
| 717 | 705 | { |
|---|
| 718 | 706 | struct cmsghdr *cmsg; |
|---|
| .. | .. |
|---|
| 764 | 752 | |
|---|
| 765 | 753 | trace_dccp_probe(sk, len); |
|---|
| 766 | 754 | |
|---|
| 767 | | - if (len > dp->dccps_mss_cache) |
|---|
| 755 | + if (len > READ_ONCE(dp->dccps_mss_cache)) |
|---|
| 768 | 756 | return -EMSGSIZE; |
|---|
| 769 | 757 | |
|---|
| 770 | 758 | lock_sock(sk); |
|---|
| 771 | | - |
|---|
| 772 | | - if (dccp_qpolicy_full(sk)) { |
|---|
| 773 | | - rc = -EAGAIN; |
|---|
| 774 | | - goto out_release; |
|---|
| 775 | | - } |
|---|
| 776 | 759 | |
|---|
| 777 | 760 | timeo = sock_sndtimeo(sk, noblock); |
|---|
| 778 | 761 | |
|---|
| .. | .. |
|---|
| 792 | 775 | if (skb == NULL) |
|---|
| 793 | 776 | goto out_release; |
|---|
| 794 | 777 | |
|---|
| 778 | + if (dccp_qpolicy_full(sk)) { |
|---|
| 779 | + rc = -EAGAIN; |
|---|
| 780 | + goto out_discard; |
|---|
| 781 | + } |
|---|
| 782 | + |
|---|
| 795 | 783 | if (sk->sk_state == DCCP_CLOSED) { |
|---|
| 796 | 784 | rc = -ENOTCONN; |
|---|
| 785 | + goto out_discard; |
|---|
| 786 | + } |
|---|
| 787 | + |
|---|
| 788 | + /* We need to check dccps_mss_cache after socket is locked. */ |
|---|
| 789 | + if (len > dp->dccps_mss_cache) { |
|---|
| 790 | + rc = -EMSGSIZE; |
|---|
| 797 | 791 | goto out_discard; |
|---|
| 798 | 792 | } |
|---|
| 799 | 793 | |
|---|
| .. | .. |
|---|
| 856 | 850 | case DCCP_PKT_CLOSEREQ: |
|---|
| 857 | 851 | if (!(flags & MSG_PEEK)) |
|---|
| 858 | 852 | dccp_finish_passive_close(sk); |
|---|
| 859 | | - /* fall through */ |
|---|
| 853 | + fallthrough; |
|---|
| 860 | 854 | case DCCP_PKT_RESET: |
|---|
| 861 | 855 | dccp_pr_debug("found fin (%s) ok!\n", |
|---|
| 862 | 856 | dccp_packet_name(dh->dccph_type)); |
|---|
| .. | .. |
|---|
| 948 | 942 | if (!((1 << old_state) & (DCCPF_CLOSED | DCCPF_LISTEN))) |
|---|
| 949 | 943 | goto out; |
|---|
| 950 | 944 | |
|---|
| 945 | + WRITE_ONCE(sk->sk_max_ack_backlog, backlog); |
|---|
| 951 | 946 | /* Really, if the socket is already in listen state |
|---|
| 952 | 947 | * we can only allow the backlog to be adjusted. |
|---|
| 953 | 948 | */ |
|---|
| .. | .. |
|---|
| 960 | 955 | if (err) |
|---|
| 961 | 956 | goto out; |
|---|
| 962 | 957 | } |
|---|
| 963 | | - sk->sk_max_ack_backlog = backlog; |
|---|
| 964 | 958 | err = 0; |
|---|
| 965 | 959 | |
|---|
| 966 | 960 | out: |
|---|
| .. | .. |
|---|
| 982 | 976 | case DCCP_PARTOPEN: |
|---|
| 983 | 977 | dccp_pr_debug("Stop PARTOPEN timer (%p)\n", sk); |
|---|
| 984 | 978 | inet_csk_clear_xmit_timer(sk, ICSK_TIME_DACK); |
|---|
| 985 | | - /* fall through */ |
|---|
| 979 | + fallthrough; |
|---|
| 986 | 980 | case DCCP_OPEN: |
|---|
| 987 | 981 | dccp_send_close(sk, 1); |
|---|
| 988 | 982 | |
|---|
| .. | .. |
|---|
| 991 | 985 | next_state = DCCP_ACTIVE_CLOSEREQ; |
|---|
| 992 | 986 | else |
|---|
| 993 | 987 | next_state = DCCP_CLOSING; |
|---|
| 994 | | - /* fall through */ |
|---|
| 988 | + fallthrough; |
|---|
| 995 | 989 | default: |
|---|
| 996 | 990 | dccp_set_state(sk, next_state); |
|---|
| 997 | 991 | } |
|---|
| .. | .. |
|---|
| 1131 | 1125 | static int __init dccp_init(void) |
|---|
| 1132 | 1126 | { |
|---|
| 1133 | 1127 | unsigned long goal; |
|---|
| 1128 | + unsigned long nr_pages = totalram_pages(); |
|---|
| 1134 | 1129 | int ehash_order, bhash_order, i; |
|---|
| 1135 | 1130 | int rc; |
|---|
| 1136 | 1131 | |
|---|
| 1137 | 1132 | BUILD_BUG_ON(sizeof(struct dccp_skb_cb) > |
|---|
| 1138 | | - FIELD_SIZEOF(struct sk_buff, cb)); |
|---|
| 1133 | + sizeof_field(struct sk_buff, cb)); |
|---|
| 1139 | 1134 | rc = percpu_counter_init(&dccp_orphan_count, 0, GFP_KERNEL); |
|---|
| 1140 | 1135 | if (rc) |
|---|
| 1141 | 1136 | goto out_fail; |
|---|
| 1142 | | - rc = -ENOBUFS; |
|---|
| 1143 | 1137 | inet_hashinfo_init(&dccp_hashinfo); |
|---|
| 1138 | + rc = inet_hashinfo2_init_mod(&dccp_hashinfo); |
|---|
| 1139 | + if (rc) |
|---|
| 1140 | + goto out_free_percpu; |
|---|
| 1141 | + rc = -ENOBUFS; |
|---|
| 1144 | 1142 | dccp_hashinfo.bind_bucket_cachep = |
|---|
| 1145 | 1143 | kmem_cache_create("dccp_bind_bucket", |
|---|
| 1146 | 1144 | sizeof(struct inet_bind_bucket), 0, |
|---|
| 1147 | 1145 | SLAB_HWCACHE_ALIGN, NULL); |
|---|
| 1148 | 1146 | if (!dccp_hashinfo.bind_bucket_cachep) |
|---|
| 1149 | | - goto out_free_percpu; |
|---|
| 1147 | + goto out_free_hashinfo2; |
|---|
| 1150 | 1148 | |
|---|
| 1151 | 1149 | /* |
|---|
| 1152 | 1150 | * Size and allocate the main established and bind bucket |
|---|
| .. | .. |
|---|
| 1154 | 1152 | * |
|---|
| 1155 | 1153 | * The methodology is similar to that of the buffer cache. |
|---|
| 1156 | 1154 | */ |
|---|
| 1157 | | - if (totalram_pages >= (128 * 1024)) |
|---|
| 1158 | | - goal = totalram_pages >> (21 - PAGE_SHIFT); |
|---|
| 1155 | + if (nr_pages >= (128 * 1024)) |
|---|
| 1156 | + goal = nr_pages >> (21 - PAGE_SHIFT); |
|---|
| 1159 | 1157 | else |
|---|
| 1160 | | - goal = totalram_pages >> (23 - PAGE_SHIFT); |
|---|
| 1158 | + goal = nr_pages >> (23 - PAGE_SHIFT); |
|---|
| 1161 | 1159 | |
|---|
| 1162 | 1160 | if (thash_entries) |
|---|
| 1163 | 1161 | goal = (thash_entries * |
|---|
| .. | .. |
|---|
| 1242 | 1240 | free_pages((unsigned long)dccp_hashinfo.ehash, ehash_order); |
|---|
| 1243 | 1241 | out_free_bind_bucket_cachep: |
|---|
| 1244 | 1242 | kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep); |
|---|
| 1243 | +out_free_hashinfo2: |
|---|
| 1244 | + inet_hashinfo2_free_mod(&dccp_hashinfo); |
|---|
| 1245 | 1245 | out_free_percpu: |
|---|
| 1246 | 1246 | percpu_counter_destroy(&dccp_orphan_count); |
|---|
| 1247 | 1247 | out_fail: |
|---|
| .. | .. |
|---|
| 1265 | 1265 | kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep); |
|---|
| 1266 | 1266 | dccp_ackvec_exit(); |
|---|
| 1267 | 1267 | dccp_sysctl_exit(); |
|---|
| 1268 | + inet_hashinfo2_free_mod(&dccp_hashinfo); |
|---|
| 1268 | 1269 | percpu_counter_destroy(&dccp_orphan_count); |
|---|
| 1269 | 1270 | } |
|---|
| 1270 | 1271 | |
|---|