diff options
Diffstat (limited to 'net/socket.c')
-rw-r--r-- | net/socket.c | 147 |
1 files changed, 127 insertions, 20 deletions
diff --git a/net/socket.c b/net/socket.c index ac2219f90d5d..310d16b1b3c9 100644 --- a/net/socket.c +++ b/net/socket.c | |||
@@ -240,17 +240,19 @@ static struct kmem_cache *sock_inode_cachep __read_mostly; | |||
240 | static struct inode *sock_alloc_inode(struct super_block *sb) | 240 | static struct inode *sock_alloc_inode(struct super_block *sb) |
241 | { | 241 | { |
242 | struct socket_alloc *ei; | 242 | struct socket_alloc *ei; |
243 | struct socket_wq *wq; | ||
243 | 244 | ||
244 | ei = kmem_cache_alloc(sock_inode_cachep, GFP_KERNEL); | 245 | ei = kmem_cache_alloc(sock_inode_cachep, GFP_KERNEL); |
245 | if (!ei) | 246 | if (!ei) |
246 | return NULL; | 247 | return NULL; |
247 | ei->socket.wq = kmalloc(sizeof(struct socket_wq), GFP_KERNEL); | 248 | wq = kmalloc(sizeof(*wq), GFP_KERNEL); |
248 | if (!ei->socket.wq) { | 249 | if (!wq) { |
249 | kmem_cache_free(sock_inode_cachep, ei); | 250 | kmem_cache_free(sock_inode_cachep, ei); |
250 | return NULL; | 251 | return NULL; |
251 | } | 252 | } |
252 | init_waitqueue_head(&ei->socket.wq->wait); | 253 | init_waitqueue_head(&wq->wait); |
253 | ei->socket.wq->fasync_list = NULL; | 254 | wq->fasync_list = NULL; |
255 | RCU_INIT_POINTER(ei->socket.wq, wq); | ||
254 | 256 | ||
255 | ei->socket.state = SS_UNCONNECTED; | 257 | ei->socket.state = SS_UNCONNECTED; |
256 | ei->socket.flags = 0; | 258 | ei->socket.flags = 0; |
@@ -273,9 +275,11 @@ static void wq_free_rcu(struct rcu_head *head) | |||
273 | static void sock_destroy_inode(struct inode *inode) | 275 | static void sock_destroy_inode(struct inode *inode) |
274 | { | 276 | { |
275 | struct socket_alloc *ei; | 277 | struct socket_alloc *ei; |
278 | struct socket_wq *wq; | ||
276 | 279 | ||
277 | ei = container_of(inode, struct socket_alloc, vfs_inode); | 280 | ei = container_of(inode, struct socket_alloc, vfs_inode); |
278 | call_rcu(&ei->socket.wq->rcu, wq_free_rcu); | 281 | wq = rcu_dereference_protected(ei->socket.wq, 1); |
282 | call_rcu(&wq->rcu, wq_free_rcu); | ||
279 | kmem_cache_free(sock_inode_cachep, ei); | 283 | kmem_cache_free(sock_inode_cachep, ei); |
280 | } | 284 | } |
281 | 285 | ||
@@ -524,7 +528,7 @@ void sock_release(struct socket *sock) | |||
524 | module_put(owner); | 528 | module_put(owner); |
525 | } | 529 | } |
526 | 530 | ||
527 | if (sock->wq->fasync_list) | 531 | if (rcu_dereference_protected(sock->wq, 1)->fasync_list) |
528 | printk(KERN_ERR "sock_release: fasync list not empty!\n"); | 532 | printk(KERN_ERR "sock_release: fasync list not empty!\n"); |
529 | 533 | ||
530 | percpu_sub(sockets_in_use, 1); | 534 | percpu_sub(sockets_in_use, 1); |
@@ -1108,15 +1112,16 @@ static int sock_fasync(int fd, struct file *filp, int on) | |||
1108 | { | 1112 | { |
1109 | struct socket *sock = filp->private_data; | 1113 | struct socket *sock = filp->private_data; |
1110 | struct sock *sk = sock->sk; | 1114 | struct sock *sk = sock->sk; |
1115 | struct socket_wq *wq; | ||
1111 | 1116 | ||
1112 | if (sk == NULL) | 1117 | if (sk == NULL) |
1113 | return -EINVAL; | 1118 | return -EINVAL; |
1114 | 1119 | ||
1115 | lock_sock(sk); | 1120 | lock_sock(sk); |
1121 | wq = rcu_dereference_protected(sock->wq, sock_owned_by_user(sk)); | ||
1122 | fasync_helper(fd, filp, on, &wq->fasync_list); | ||
1116 | 1123 | ||
1117 | fasync_helper(fd, filp, on, &sock->wq->fasync_list); | 1124 | if (!wq->fasync_list) |
1118 | |||
1119 | if (!sock->wq->fasync_list) | ||
1120 | sock_reset_flag(sk, SOCK_FASYNC); | 1125 | sock_reset_flag(sk, SOCK_FASYNC); |
1121 | else | 1126 | else |
1122 | sock_set_flag(sk, SOCK_FASYNC); | 1127 | sock_set_flag(sk, SOCK_FASYNC); |
@@ -2583,23 +2588,123 @@ static int dev_ifconf(struct net *net, struct compat_ifconf __user *uifc32) | |||
2583 | 2588 | ||
2584 | static int ethtool_ioctl(struct net *net, struct compat_ifreq __user *ifr32) | 2589 | static int ethtool_ioctl(struct net *net, struct compat_ifreq __user *ifr32) |
2585 | { | 2590 | { |
2591 | struct compat_ethtool_rxnfc __user *compat_rxnfc; | ||
2592 | bool convert_in = false, convert_out = false; | ||
2593 | size_t buf_size = ALIGN(sizeof(struct ifreq), 8); | ||
2594 | struct ethtool_rxnfc __user *rxnfc; | ||
2586 | struct ifreq __user *ifr; | 2595 | struct ifreq __user *ifr; |
2596 | u32 rule_cnt = 0, actual_rule_cnt; | ||
2597 | u32 ethcmd; | ||
2587 | u32 data; | 2598 | u32 data; |
2588 | void __user *datap; | 2599 | int ret; |
2589 | 2600 | ||
2590 | ifr = compat_alloc_user_space(sizeof(*ifr)); | 2601 | if (get_user(data, &ifr32->ifr_ifru.ifru_data)) |
2602 | return -EFAULT; | ||
2591 | 2603 | ||
2592 | if (copy_in_user(&ifr->ifr_name, &ifr32->ifr_name, IFNAMSIZ)) | 2604 | compat_rxnfc = compat_ptr(data); |
2605 | |||
2606 | if (get_user(ethcmd, &compat_rxnfc->cmd)) | ||
2593 | return -EFAULT; | 2607 | return -EFAULT; |
2594 | 2608 | ||
2595 | if (get_user(data, &ifr32->ifr_ifru.ifru_data)) | 2609 | /* Most ethtool structures are defined without padding. |
2610 | * Unfortunately struct ethtool_rxnfc is an exception. | ||
2611 | */ | ||
2612 | switch (ethcmd) { | ||
2613 | default: | ||
2614 | break; | ||
2615 | case ETHTOOL_GRXCLSRLALL: | ||
2616 | /* Buffer size is variable */ | ||
2617 | if (get_user(rule_cnt, &compat_rxnfc->rule_cnt)) | ||
2618 | return -EFAULT; | ||
2619 | if (rule_cnt > KMALLOC_MAX_SIZE / sizeof(u32)) | ||
2620 | return -ENOMEM; | ||
2621 | buf_size += rule_cnt * sizeof(u32); | ||
2622 | /* fall through */ | ||
2623 | case ETHTOOL_GRXRINGS: | ||
2624 | case ETHTOOL_GRXCLSRLCNT: | ||
2625 | case ETHTOOL_GRXCLSRULE: | ||
2626 | convert_out = true; | ||
2627 | /* fall through */ | ||
2628 | case ETHTOOL_SRXCLSRLDEL: | ||
2629 | case ETHTOOL_SRXCLSRLINS: | ||
2630 | buf_size += sizeof(struct ethtool_rxnfc); | ||
2631 | convert_in = true; | ||
2632 | break; | ||
2633 | } | ||
2634 | |||
2635 | ifr = compat_alloc_user_space(buf_size); | ||
2636 | rxnfc = (void *)ifr + ALIGN(sizeof(struct ifreq), 8); | ||
2637 | |||
2638 | if (copy_in_user(&ifr->ifr_name, &ifr32->ifr_name, IFNAMSIZ)) | ||
2596 | return -EFAULT; | 2639 | return -EFAULT; |
2597 | 2640 | ||
2598 | datap = compat_ptr(data); | 2641 | if (put_user(convert_in ? rxnfc : compat_ptr(data), |
2599 | if (put_user(datap, &ifr->ifr_ifru.ifru_data)) | 2642 | &ifr->ifr_ifru.ifru_data)) |
2600 | return -EFAULT; | 2643 | return -EFAULT; |
2601 | 2644 | ||
2602 | return dev_ioctl(net, SIOCETHTOOL, ifr); | 2645 | if (convert_in) { |
2646 | /* We expect there to be holes between fs.m_u and | ||
2647 | * fs.ring_cookie and at the end of fs, but nowhere else. | ||
2648 | */ | ||
2649 | BUILD_BUG_ON(offsetof(struct compat_ethtool_rxnfc, fs.m_u) + | ||
2650 | sizeof(compat_rxnfc->fs.m_u) != | ||
2651 | offsetof(struct ethtool_rxnfc, fs.m_u) + | ||
2652 | sizeof(rxnfc->fs.m_u)); | ||
2653 | BUILD_BUG_ON( | ||
2654 | offsetof(struct compat_ethtool_rxnfc, fs.location) - | ||
2655 | offsetof(struct compat_ethtool_rxnfc, fs.ring_cookie) != | ||
2656 | offsetof(struct ethtool_rxnfc, fs.location) - | ||
2657 | offsetof(struct ethtool_rxnfc, fs.ring_cookie)); | ||
2658 | |||
2659 | if (copy_in_user(rxnfc, compat_rxnfc, | ||
2660 | (void *)(&rxnfc->fs.m_u + 1) - | ||
2661 | (void *)rxnfc) || | ||
2662 | copy_in_user(&rxnfc->fs.ring_cookie, | ||
2663 | &compat_rxnfc->fs.ring_cookie, | ||
2664 | (void *)(&rxnfc->fs.location + 1) - | ||
2665 | (void *)&rxnfc->fs.ring_cookie) || | ||
2666 | copy_in_user(&rxnfc->rule_cnt, &compat_rxnfc->rule_cnt, | ||
2667 | sizeof(rxnfc->rule_cnt))) | ||
2668 | return -EFAULT; | ||
2669 | } | ||
2670 | |||
2671 | ret = dev_ioctl(net, SIOCETHTOOL, ifr); | ||
2672 | if (ret) | ||
2673 | return ret; | ||
2674 | |||
2675 | if (convert_out) { | ||
2676 | if (copy_in_user(compat_rxnfc, rxnfc, | ||
2677 | (const void *)(&rxnfc->fs.m_u + 1) - | ||
2678 | (const void *)rxnfc) || | ||
2679 | copy_in_user(&compat_rxnfc->fs.ring_cookie, | ||
2680 | &rxnfc->fs.ring_cookie, | ||
2681 | (const void *)(&rxnfc->fs.location + 1) - | ||
2682 | (const void *)&rxnfc->fs.ring_cookie) || | ||
2683 | copy_in_user(&compat_rxnfc->rule_cnt, &rxnfc->rule_cnt, | ||
2684 | sizeof(rxnfc->rule_cnt))) | ||
2685 | return -EFAULT; | ||
2686 | |||
2687 | if (ethcmd == ETHTOOL_GRXCLSRLALL) { | ||
2688 | /* As an optimisation, we only copy the actual | ||
2689 | * number of rules that the underlying | ||
2690 | * function returned. Since Mallory might | ||
2691 | * change the rule count in user memory, we | ||
2692 | * check that it is less than the rule count | ||
2693 | * originally given (as the user buffer size), | ||
2694 | * which has been range-checked. | ||
2695 | */ | ||
2696 | if (get_user(actual_rule_cnt, &rxnfc->rule_cnt)) | ||
2697 | return -EFAULT; | ||
2698 | if (actual_rule_cnt < rule_cnt) | ||
2699 | rule_cnt = actual_rule_cnt; | ||
2700 | if (copy_in_user(&compat_rxnfc->rule_locs[0], | ||
2701 | &rxnfc->rule_locs[0], | ||
2702 | rule_cnt * sizeof(u32))) | ||
2703 | return -EFAULT; | ||
2704 | } | ||
2705 | } | ||
2706 | |||
2707 | return 0; | ||
2603 | } | 2708 | } |
2604 | 2709 | ||
2605 | static int compat_siocwandev(struct net *net, struct compat_ifreq __user *uifr32) | 2710 | static int compat_siocwandev(struct net *net, struct compat_ifreq __user *uifr32) |
@@ -2643,7 +2748,8 @@ static int bond_ioctl(struct net *net, unsigned int cmd, | |||
2643 | 2748 | ||
2644 | old_fs = get_fs(); | 2749 | old_fs = get_fs(); |
2645 | set_fs(KERNEL_DS); | 2750 | set_fs(KERNEL_DS); |
2646 | err = dev_ioctl(net, cmd, &kifr); | 2751 | err = dev_ioctl(net, cmd, |
2752 | (struct ifreq __user __force *) &kifr); | ||
2647 | set_fs(old_fs); | 2753 | set_fs(old_fs); |
2648 | 2754 | ||
2649 | return err; | 2755 | return err; |
@@ -2752,7 +2858,7 @@ static int compat_sioc_ifmap(struct net *net, unsigned int cmd, | |||
2752 | 2858 | ||
2753 | old_fs = get_fs(); | 2859 | old_fs = get_fs(); |
2754 | set_fs(KERNEL_DS); | 2860 | set_fs(KERNEL_DS); |
2755 | err = dev_ioctl(net, cmd, (void __user *)&ifr); | 2861 | err = dev_ioctl(net, cmd, (void __user __force *)&ifr); |
2756 | set_fs(old_fs); | 2862 | set_fs(old_fs); |
2757 | 2863 | ||
2758 | if (cmd == SIOCGIFMAP && !err) { | 2864 | if (cmd == SIOCGIFMAP && !err) { |
@@ -2857,7 +2963,8 @@ static int routing_ioctl(struct net *net, struct socket *sock, | |||
2857 | ret |= __get_user(rtdev, &(ur4->rt_dev)); | 2963 | ret |= __get_user(rtdev, &(ur4->rt_dev)); |
2858 | if (rtdev) { | 2964 | if (rtdev) { |
2859 | ret |= copy_from_user(devname, compat_ptr(rtdev), 15); | 2965 | ret |= copy_from_user(devname, compat_ptr(rtdev), 15); |
2860 | r4.rt_dev = devname; devname[15] = 0; | 2966 | r4.rt_dev = (char __user __force *)devname; |
2967 | devname[15] = 0; | ||
2861 | } else | 2968 | } else |
2862 | r4.rt_dev = NULL; | 2969 | r4.rt_dev = NULL; |
2863 | 2970 | ||
@@ -2879,7 +2986,7 @@ out: | |||
2879 | 2986 | ||
2880 | /* Since old style bridge ioctl's endup using SIOCDEVPRIVATE | 2987 | /* Since old style bridge ioctl's endup using SIOCDEVPRIVATE |
2881 | * for some operations; this forces use of the newer bridge-utils that | 2988 | * for some operations; this forces use of the newer bridge-utils that |
2882 | * use compatiable ioctls | 2989 | * use compatible ioctls |
2883 | */ | 2990 | */ |
2884 | static int old_bridge_ioctl(compat_ulong_t __user *argp) | 2991 | static int old_bridge_ioctl(compat_ulong_t __user *argp) |
2885 | { | 2992 | { |