Skip to content

Commit 095cfcf

Browse files
kuba-moodavem330
authored andcommitted
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 <[email protected]> Reviewed-by: Leon Romanovsky <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent f49deaa commit 095cfcf

File tree

1 file changed

+69
-41
lines changed

1 file changed

+69
-41
lines changed

net/ethtool/ioctl.c

Lines changed: 69 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@
3232
#include <generated/utsrelease.h>
3333
#include "common.h"
3434

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+
3543
/*
3644
* Some useful ethtool_ops methods that're device independent.
3745
* If we find that all drivers want to do the same thing here,
@@ -697,22 +705,20 @@ static int ethtool_set_settings(struct net_device *dev, void __user *useraddr)
697705
return ret;
698706
}
699707

700-
static noinline_for_stack int ethtool_get_drvinfo(struct net_device *dev,
701-
void __user *useraddr)
708+
static int
709+
ethtool_get_drvinfo(struct net_device *dev, struct ethtool_devlink_compat *rsp)
702710
{
703-
struct ethtool_drvinfo info;
704711
const struct ethtool_ops *ops = dev->ethtool_ops;
705712

706-
memset(&info, 0, sizeof(info));
707-
info.cmd = ETHTOOL_GDRVINFO;
708-
strlcpy(info.version, UTS_RELEASE, sizeof(info.version));
713+
rsp->info.cmd = ETHTOOL_GDRVINFO;
714+
strlcpy(rsp->info.version, UTS_RELEASE, sizeof(rsp->info.version));
709715
if (ops->get_drvinfo) {
710-
ops->get_drvinfo(dev, &info);
716+
ops->get_drvinfo(dev, &rsp->info);
711717
} else if (dev->dev.parent && dev->dev.parent->driver) {
712-
strlcpy(info.bus_info, dev_name(dev->dev.parent),
713-
sizeof(info.bus_info));
714-
strlcpy(info.driver, dev->dev.parent->driver->name,
715-
sizeof(info.driver));
718+
strlcpy(rsp->info.bus_info, dev_name(dev->dev.parent),
719+
sizeof(rsp->info.bus_info));
720+
strlcpy(rsp->info.driver, dev->dev.parent->driver->name,
721+
sizeof(rsp->info.driver));
716722
} else {
717723
return -EOPNOTSUPP;
718724
}
@@ -726,30 +732,27 @@ static noinline_for_stack int ethtool_get_drvinfo(struct net_device *dev,
726732

727733
rc = ops->get_sset_count(dev, ETH_SS_TEST);
728734
if (rc >= 0)
729-
info.testinfo_len = rc;
735+
rsp->info.testinfo_len = rc;
730736
rc = ops->get_sset_count(dev, ETH_SS_STATS);
731737
if (rc >= 0)
732-
info.n_stats = rc;
738+
rsp->info.n_stats = rc;
733739
rc = ops->get_sset_count(dev, ETH_SS_PRIV_FLAGS);
734740
if (rc >= 0)
735-
info.n_priv_flags = rc;
741+
rsp->info.n_priv_flags = rc;
736742
}
737743
if (ops->get_regs_len) {
738744
int ret = ops->get_regs_len(dev);
739745

740746
if (ret > 0)
741-
info.regdump_len = ret;
747+
rsp->info.regdump_len = ret;
742748
}
743749

744750
if (ops->get_eeprom_len)
745-
info.eedump_len = ops->get_eeprom_len(dev);
746-
747-
if (!info.fw_version[0])
748-
devlink_compat_running_version(dev, info.fw_version,
749-
sizeof(info.fw_version));
751+
rsp->info.eedump_len = ops->get_eeprom_len(dev);
750752

751-
if (copy_to_user(useraddr, &info, sizeof(info)))
752-
return -EFAULT;
753+
if (!rsp->info.fw_version[0])
754+
devlink_compat_running_version(dev, rsp->info.fw_version,
755+
sizeof(rsp->info.fw_version));
753756
return 0;
754757
}
755758

@@ -2178,19 +2181,13 @@ static int ethtool_set_value(struct net_device *dev, char __user *useraddr,
21782181
return actor(dev, edata.data);
21792182
}
21802183

2181-
static noinline_for_stack int ethtool_flash_device(struct net_device *dev,
2182-
char __user *useraddr)
2184+
static int
2185+
ethtool_flash_device(struct net_device *dev, struct ethtool_devlink_compat *req)
21832186
{
2184-
struct ethtool_flash efl;
2185-
2186-
if (copy_from_user(&efl, useraddr, sizeof(efl)))
2187-
return -EFAULT;
2188-
efl.data[ETHTOOL_FLASH_MAX_FILENAME - 1] = 0;
2189-
21902187
if (!dev->ethtool_ops->flash_device)
2191-
return devlink_compat_flash_update(dev, efl.data);
2188+
return devlink_compat_flash_update(dev, req->efl.data);
21922189

2193-
return dev->ethtool_ops->flash_device(dev, &efl);
2190+
return dev->ethtool_ops->flash_device(dev, &req->efl);
21942191
}
21952192

21962193
static int ethtool_set_dump(struct net_device *dev,
@@ -2701,19 +2698,18 @@ static int ethtool_set_fecparam(struct net_device *dev, void __user *useraddr)
27012698
/* The main entry point in this file. Called from net/core/dev_ioctl.c */
27022699

27032700
static int
2704-
__dev_ethtool(struct net *net, struct ifreq *ifr, void __user *useraddr)
2701+
__dev_ethtool(struct net *net, struct ifreq *ifr, void __user *useraddr,
2702+
u32 ethcmd, struct ethtool_devlink_compat *devlink_state)
27052703
{
2706-
struct net_device *dev = __dev_get_by_name(net, ifr->ifr_name);
2707-
u32 ethcmd, sub_cmd;
2704+
struct net_device *dev;
2705+
u32 sub_cmd;
27082706
int rc;
27092707
netdev_features_t old_features;
27102708

2709+
dev = __dev_get_by_name(net, ifr->ifr_name);
27112710
if (!dev)
27122711
return -ENODEV;
27132712

2714-
if (copy_from_user(&ethcmd, useraddr, sizeof(ethcmd)))
2715-
return -EFAULT;
2716-
27172713
if (ethcmd == ETHTOOL_PERQUEUE) {
27182714
if (copy_from_user(&sub_cmd, useraddr + sizeof(ethcmd), sizeof(sub_cmd)))
27192715
return -EFAULT;
@@ -2787,7 +2783,7 @@ __dev_ethtool(struct net *net, struct ifreq *ifr, void __user *useraddr)
27872783
rc = ethtool_set_settings(dev, useraddr);
27882784
break;
27892785
case ETHTOOL_GDRVINFO:
2790-
rc = ethtool_get_drvinfo(dev, useraddr);
2786+
rc = ethtool_get_drvinfo(dev, devlink_state);
27912787
break;
27922788
case ETHTOOL_GREGS:
27932789
rc = ethtool_get_regs(dev, useraddr);
@@ -2889,7 +2885,7 @@ __dev_ethtool(struct net *net, struct ifreq *ifr, void __user *useraddr)
28892885
rc = ethtool_set_rxnfc(dev, ethcmd, useraddr);
28902886
break;
28912887
case ETHTOOL_FLASHDEV:
2892-
rc = ethtool_flash_device(dev, useraddr);
2888+
rc = ethtool_flash_device(dev, devlink_state);
28932889
break;
28942890
case ETHTOOL_RESET:
28952891
rc = ethtool_reset(dev, useraddr);
@@ -3003,12 +2999,44 @@ __dev_ethtool(struct net *net, struct ifreq *ifr, void __user *useraddr)
30032999

30043000
int dev_ethtool(struct net *net, struct ifreq *ifr, void __user *useraddr)
30053001
{
3002+
struct ethtool_devlink_compat *state;
3003+
u32 ethcmd;
30063004
int rc;
30073005

3006+
if (copy_from_user(&ethcmd, useraddr, sizeof(ethcmd)))
3007+
return -EFAULT;
3008+
3009+
state = kzalloc(sizeof(*state), GFP_KERNEL);
3010+
if (!state)
3011+
return -ENOMEM;
3012+
3013+
switch (ethcmd) {
3014+
case ETHTOOL_FLASHDEV:
3015+
if (copy_from_user(&state->efl, useraddr, sizeof(state->efl))) {
3016+
rc = -EFAULT;
3017+
goto exit_free;
3018+
}
3019+
state->efl.data[ETHTOOL_FLASH_MAX_FILENAME - 1] = 0;
3020+
break;
3021+
}
3022+
30083023
rtnl_lock();
3009-
rc = __dev_ethtool(net, ifr, useraddr);
3024+
rc = __dev_ethtool(net, ifr, useraddr, ethcmd, state);
30103025
rtnl_unlock();
3026+
if (rc)
3027+
goto exit_free;
3028+
3029+
switch (ethcmd) {
3030+
case ETHTOOL_GDRVINFO:
3031+
if (copy_to_user(useraddr, &state->info, sizeof(state->info))) {
3032+
rc = -EFAULT;
3033+
goto exit_free;
3034+
}
3035+
break;
3036+
}
30113037

3038+
exit_free:
3039+
kfree(state);
30123040
return rc;
30133041
}
30143042

0 commit comments

Comments
 (0)