aboutsummaryrefslogtreecommitdiffstats
path: root/net/socket.c
diff options
context:
space:
mode:
authorBen Hutchings <bhutchings@solarflare.com>2011-03-17 03:34:32 -0400
committerDavid S. Miller <davem@davemloft.net>2011-03-18 18:13:11 -0400
commit3a7da39d165e0c363c294feec119db1427032afd (patch)
tree901e54990fdd90d6f90d27533c36e61858216e34 /net/socket.c
parent5e5069b41d5b82bcadc1dbf73f48476b428c102f (diff)
ethtool: Compat handling for struct ethtool_rxnfc
This structure was accidentally defined such that its layout can differ between 32-bit and 64-bit processes. Add compat structure definitions and an ioctl wrapper function. Signed-off-by: Ben Hutchings <bhutchings@solarflare.com> Acked-by: Alexander Duyck <alexander.h.duyck@intel.com> Cc: stable@kernel.org [2.6.30+] Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/socket.c')
-rw-r--r--net/socket.c114
1 files changed, 107 insertions, 7 deletions
diff --git a/net/socket.c b/net/socket.c
index 937d0fcf74bc..5212447c86e7 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
2589static int ethtool_ioctl(struct net *net, struct compat_ifreq __user *ifr32) 2589static 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
2610static int compat_siocwandev(struct net *net, struct compat_ifreq __user *uifr32) 2710static int compat_siocwandev(struct net *net, struct compat_ifreq __user *uifr32)