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

ethtool: handle info/flash data copying outside rtnl_lock

We need to increase the lifetime of the data for .get_info
and .flash_update beyond their handlers inside rtnl_lock.

Allocate a union on the heap and use it instead.

Note that we now copy the ethcmd before we lookup dev,
hopefully there is no crazy user space depending on error
codes.

Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Reviewed-by: Leon Romanovsky <leonro@nvidia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Jakub Kicinski and committed by
David S. Miller
095cfcfe f49deaa6

+71 -43
+71 -43
net/ethtool/ioctl.c
··· 32 32 #include <generated/utsrelease.h> 33 33 #include "common.h" 34 34 35 + /* State held across locks and calls for commands which have devlink fallback */ 36 + struct ethtool_devlink_compat { 37 + union { 38 + struct ethtool_flash efl; 39 + struct ethtool_drvinfo info; 40 + }; 41 + }; 42 + 35 43 /* 36 44 * Some useful ethtool_ops methods that're device independent. 37 45 * If we find that all drivers want to do the same thing here, ··· 705 697 return ret; 706 698 } 707 699 708 - static noinline_for_stack int ethtool_get_drvinfo(struct net_device *dev, 709 - void __user *useraddr) 700 + static int 701 + ethtool_get_drvinfo(struct net_device *dev, struct ethtool_devlink_compat *rsp) 710 702 { 711 - struct ethtool_drvinfo info; 712 703 const struct ethtool_ops *ops = dev->ethtool_ops; 713 704 714 - memset(&info, 0, sizeof(info)); 715 - info.cmd = ETHTOOL_GDRVINFO; 716 - strlcpy(info.version, UTS_RELEASE, sizeof(info.version)); 705 + rsp->info.cmd = ETHTOOL_GDRVINFO; 706 + strlcpy(rsp->info.version, UTS_RELEASE, sizeof(rsp->info.version)); 717 707 if (ops->get_drvinfo) { 718 - ops->get_drvinfo(dev, &info); 708 + ops->get_drvinfo(dev, &rsp->info); 719 709 } else if (dev->dev.parent && dev->dev.parent->driver) { 720 - strlcpy(info.bus_info, dev_name(dev->dev.parent), 721 - sizeof(info.bus_info)); 722 - strlcpy(info.driver, dev->dev.parent->driver->name, 723 - sizeof(info.driver)); 710 + strlcpy(rsp->info.bus_info, dev_name(dev->dev.parent), 711 + sizeof(rsp->info.bus_info)); 712 + strlcpy(rsp->info.driver, dev->dev.parent->driver->name, 713 + sizeof(rsp->info.driver)); 724 714 } else { 725 715 return -EOPNOTSUPP; 726 716 } ··· 732 726 733 727 rc = ops->get_sset_count(dev, ETH_SS_TEST); 734 728 if (rc >= 0) 735 - info.testinfo_len = rc; 729 + rsp->info.testinfo_len = rc; 736 730 rc = ops->get_sset_count(dev, ETH_SS_STATS); 737 731 if (rc >= 0) 738 - info.n_stats = rc; 732 + rsp->info.n_stats = rc; 739 733 rc = ops->get_sset_count(dev, ETH_SS_PRIV_FLAGS); 740 734 if (rc >= 0) 741 - info.n_priv_flags = rc; 735 + rsp->info.n_priv_flags = rc; 742 736 } 743 737 if (ops->get_regs_len) { 744 738 int ret = ops->get_regs_len(dev); 745 739 746 740 if (ret > 0) 747 - info.regdump_len = ret; 741 + rsp->info.regdump_len = ret; 748 742 } 749 743 750 744 if (ops->get_eeprom_len) 751 - info.eedump_len = ops->get_eeprom_len(dev); 745 + rsp->info.eedump_len = ops->get_eeprom_len(dev); 752 746 753 - if (!info.fw_version[0]) 754 - devlink_compat_running_version(dev, info.fw_version, 755 - sizeof(info.fw_version)); 756 - 757 - if (copy_to_user(useraddr, &info, sizeof(info))) 758 - return -EFAULT; 747 + if (!rsp->info.fw_version[0]) 748 + devlink_compat_running_version(dev, rsp->info.fw_version, 749 + sizeof(rsp->info.fw_version)); 759 750 return 0; 760 751 } 761 752 ··· 2181 2178 return actor(dev, edata.data); 2182 2179 } 2183 2180 2184 - static noinline_for_stack int ethtool_flash_device(struct net_device *dev, 2185 - char __user *useraddr) 2181 + static int 2182 + ethtool_flash_device(struct net_device *dev, struct ethtool_devlink_compat *req) 2186 2183 { 2187 - struct ethtool_flash efl; 2188 - 2189 - if (copy_from_user(&efl, useraddr, sizeof(efl))) 2190 - return -EFAULT; 2191 - efl.data[ETHTOOL_FLASH_MAX_FILENAME - 1] = 0; 2192 - 2193 2184 if (!dev->ethtool_ops->flash_device) 2194 - return devlink_compat_flash_update(dev, efl.data); 2185 + return devlink_compat_flash_update(dev, req->efl.data); 2195 2186 2196 - return dev->ethtool_ops->flash_device(dev, &efl); 2187 + return dev->ethtool_ops->flash_device(dev, &req->efl); 2197 2188 } 2198 2189 2199 2190 static int ethtool_set_dump(struct net_device *dev, ··· 2698 2701 /* The main entry point in this file. Called from net/core/dev_ioctl.c */ 2699 2702 2700 2703 static int 2701 - __dev_ethtool(struct net *net, struct ifreq *ifr, void __user *useraddr) 2704 + __dev_ethtool(struct net *net, struct ifreq *ifr, void __user *useraddr, 2705 + u32 ethcmd, struct ethtool_devlink_compat *devlink_state) 2702 2706 { 2703 - struct net_device *dev = __dev_get_by_name(net, ifr->ifr_name); 2704 - u32 ethcmd, sub_cmd; 2707 + struct net_device *dev; 2708 + u32 sub_cmd; 2705 2709 int rc; 2706 2710 netdev_features_t old_features; 2707 2711 2712 + dev = __dev_get_by_name(net, ifr->ifr_name); 2708 2713 if (!dev) 2709 2714 return -ENODEV; 2710 - 2711 - if (copy_from_user(&ethcmd, useraddr, sizeof(ethcmd))) 2712 - return -EFAULT; 2713 2715 2714 2716 if (ethcmd == ETHTOOL_PERQUEUE) { 2715 2717 if (copy_from_user(&sub_cmd, useraddr + sizeof(ethcmd), sizeof(sub_cmd))) ··· 2783 2787 rc = ethtool_set_settings(dev, useraddr); 2784 2788 break; 2785 2789 case ETHTOOL_GDRVINFO: 2786 - rc = ethtool_get_drvinfo(dev, useraddr); 2790 + rc = ethtool_get_drvinfo(dev, devlink_state); 2787 2791 break; 2788 2792 case ETHTOOL_GREGS: 2789 2793 rc = ethtool_get_regs(dev, useraddr); ··· 2885 2889 rc = ethtool_set_rxnfc(dev, ethcmd, useraddr); 2886 2890 break; 2887 2891 case ETHTOOL_FLASHDEV: 2888 - rc = ethtool_flash_device(dev, useraddr); 2892 + rc = ethtool_flash_device(dev, devlink_state); 2889 2893 break; 2890 2894 case ETHTOOL_RESET: 2891 2895 rc = ethtool_reset(dev, useraddr); ··· 2999 3003 3000 3004 int dev_ethtool(struct net *net, struct ifreq *ifr, void __user *useraddr) 3001 3005 { 3006 + struct ethtool_devlink_compat *state; 3007 + u32 ethcmd; 3002 3008 int rc; 3003 3009 3004 - rtnl_lock(); 3005 - rc = __dev_ethtool(net, ifr, useraddr); 3006 - rtnl_unlock(); 3010 + if (copy_from_user(&ethcmd, useraddr, sizeof(ethcmd))) 3011 + return -EFAULT; 3007 3012 3013 + state = kzalloc(sizeof(*state), GFP_KERNEL); 3014 + if (!state) 3015 + return -ENOMEM; 3016 + 3017 + switch (ethcmd) { 3018 + case ETHTOOL_FLASHDEV: 3019 + if (copy_from_user(&state->efl, useraddr, sizeof(state->efl))) { 3020 + rc = -EFAULT; 3021 + goto exit_free; 3022 + } 3023 + state->efl.data[ETHTOOL_FLASH_MAX_FILENAME - 1] = 0; 3024 + break; 3025 + } 3026 + 3027 + rtnl_lock(); 3028 + rc = __dev_ethtool(net, ifr, useraddr, ethcmd, state); 3029 + rtnl_unlock(); 3030 + if (rc) 3031 + goto exit_free; 3032 + 3033 + switch (ethcmd) { 3034 + case ETHTOOL_GDRVINFO: 3035 + if (copy_to_user(useraddr, &state->info, sizeof(state->info))) { 3036 + rc = -EFAULT; 3037 + goto exit_free; 3038 + } 3039 + break; 3040 + } 3041 + 3042 + exit_free: 3043 + kfree(state); 3008 3044 return rc; 3009 3045 } 3010 3046