diff options
Diffstat (limited to 'net/socket.c')
| -rw-r--r-- | net/socket.c | 116 |
1 files changed, 108 insertions, 8 deletions
diff --git a/net/socket.c b/net/socket.c index 937d0fcf74bc..310d16b1b3c9 100644 --- a/net/socket.c +++ b/net/socket.c | |||
| @@ -2588,23 +2588,123 @@ static int dev_ifconf(struct net *net, struct compat_ifconf __user *uifc32) | |||
| 2588 | 2588 | ||
| 2589 | 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) |
| 2590 | { | 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; | ||
| 2591 | struct ifreq __user *ifr; | 2595 | struct ifreq __user *ifr; |
| 2596 | u32 rule_cnt = 0, actual_rule_cnt; | ||
| 2597 | u32 ethcmd; | ||
| 2592 | u32 data; | 2598 | u32 data; |
| 2593 | void __user *datap; | 2599 | int ret; |
| 2600 | |||
| 2601 | if (get_user(data, &ifr32->ifr_ifru.ifru_data)) | ||
| 2602 | return -EFAULT; | ||
| 2594 | 2603 | ||
| 2595 | ifr = compat_alloc_user_space(sizeof(*ifr)); | 2604 | compat_rxnfc = compat_ptr(data); |
| 2596 | 2605 | ||
| 2597 | if (copy_in_user(&ifr->ifr_name, &ifr32->ifr_name, IFNAMSIZ)) | 2606 | if (get_user(ethcmd, &compat_rxnfc->cmd)) |
| 2598 | return -EFAULT; | 2607 | return -EFAULT; |
| 2599 | 2608 | ||
| 2600 | 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)) | ||
| 2601 | return -EFAULT; | 2639 | return -EFAULT; |
| 2602 | 2640 | ||
| 2603 | datap = compat_ptr(data); | 2641 | if (put_user(convert_in ? rxnfc : compat_ptr(data), |
| 2604 | if (put_user(datap, &ifr->ifr_ifru.ifru_data)) | 2642 | &ifr->ifr_ifru.ifru_data)) |
| 2605 | return -EFAULT; | 2643 | return -EFAULT; |
| 2606 | 2644 | ||
| 2607 | 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; | ||
| 2608 | } | 2708 | } |
| 2609 | 2709 | ||
| 2610 | 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) |
| @@ -2886,7 +2986,7 @@ out: | |||
| 2886 | 2986 | ||
| 2887 | /* Since old style bridge ioctl's endup using SIOCDEVPRIVATE | 2987 | /* Since old style bridge ioctl's endup using SIOCDEVPRIVATE |
| 2888 | * 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 |
| 2889 | * use compatiable ioctls | 2989 | * use compatible ioctls |
| 2890 | */ | 2990 | */ |
| 2891 | static int old_bridge_ioctl(compat_ulong_t __user *argp) | 2991 | static int old_bridge_ioctl(compat_ulong_t __user *argp) |
| 2892 | { | 2992 | { |
