Skip to content

Commit e71460d

Browse files
committed
Merge branch 'ethtool_gert_phy_stats-fixes'
Daniil Tatianin says: ==================== net/ethtool/ioctl: split ethtool_get_phy_stats into multiple helpers This series fixes a potential NULL dereference in ethtool_get_phy_stats while also attempting to refactor/split said function into multiple helpers so that it's easier to reason about what's going on. I've taken Andrew Lunn's suggestions on the previous version of this patch and added a bit of my own. Changes since v1: - Remove an extra newline in the first patch - Move WARN_ON_ONCE into the if check as it already returns the result of the comparison - Actually split ethtool_get_phy_stats instead of attempting to refactor it ==================== Signed-off-by: David S. Miller <[email protected]>
2 parents 8ac718c + 201ed31 commit e71460d

File tree

1 file changed

+70
-37
lines changed

1 file changed

+70
-37
lines changed

net/ethtool/ioctl.c

Lines changed: 70 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2078,58 +2078,91 @@ static int ethtool_get_stats(struct net_device *dev, void __user *useraddr)
20782078
return ret;
20792079
}
20802080

2081-
static int ethtool_get_phy_stats(struct net_device *dev, void __user *useraddr)
2081+
static int ethtool_vzalloc_stats_array(int n_stats, u64 **data)
20822082
{
2083+
if (n_stats < 0)
2084+
return n_stats;
2085+
if (n_stats > S32_MAX / sizeof(u64))
2086+
return -ENOMEM;
2087+
if (WARN_ON_ONCE(!n_stats))
2088+
return -EOPNOTSUPP;
2089+
2090+
*data = vzalloc(array_size(n_stats, sizeof(u64)));
2091+
if (!*data)
2092+
return -ENOMEM;
2093+
2094+
return 0;
2095+
}
2096+
2097+
static int ethtool_get_phy_stats_phydev(struct phy_device *phydev,
2098+
struct ethtool_stats *stats,
2099+
u64 **data)
2100+
{
20832101
const struct ethtool_phy_ops *phy_ops = ethtool_phy_ops;
2102+
int n_stats, ret;
2103+
2104+
if (!phy_ops || !phy_ops->get_sset_count || !phy_ops->get_stats)
2105+
return -EOPNOTSUPP;
2106+
2107+
n_stats = phy_ops->get_sset_count(phydev);
2108+
2109+
ret = ethtool_vzalloc_stats_array(n_stats, data);
2110+
if (ret)
2111+
return ret;
2112+
2113+
stats->n_stats = n_stats;
2114+
return phy_ops->get_stats(phydev, stats, *data);
2115+
}
2116+
2117+
static int ethtool_get_phy_stats_ethtool(struct net_device *dev,
2118+
struct ethtool_stats *stats,
2119+
u64 **data)
2120+
{
20842121
const struct ethtool_ops *ops = dev->ethtool_ops;
2085-
struct phy_device *phydev = dev->phydev;
2086-
struct ethtool_stats stats;
2087-
u64 *data;
2088-
int ret, n_stats;
2122+
int n_stats, ret;
20892123

2090-
if (!phydev && (!ops->get_ethtool_phy_stats || !ops->get_sset_count))
2124+
if (!ops || !ops->get_sset_count || ops->get_ethtool_phy_stats)
20912125
return -EOPNOTSUPP;
20922126

2093-
if (phydev && !ops->get_ethtool_phy_stats &&
2094-
phy_ops && phy_ops->get_sset_count)
2095-
n_stats = phy_ops->get_sset_count(phydev);
2096-
else
2097-
n_stats = ops->get_sset_count(dev, ETH_SS_PHY_STATS);
2098-
if (n_stats < 0)
2099-
return n_stats;
2100-
if (n_stats > S32_MAX / sizeof(u64))
2101-
return -ENOMEM;
2102-
WARN_ON_ONCE(!n_stats);
2127+
n_stats = ops->get_sset_count(dev, ETH_SS_PHY_STATS);
2128+
2129+
ret = ethtool_vzalloc_stats_array(n_stats, data);
2130+
if (ret)
2131+
return ret;
2132+
2133+
stats->n_stats = n_stats;
2134+
ops->get_ethtool_phy_stats(dev, stats, *data);
2135+
2136+
return 0;
2137+
}
2138+
2139+
static int ethtool_get_phy_stats(struct net_device *dev, void __user *useraddr)
2140+
{
2141+
struct phy_device *phydev = dev->phydev;
2142+
struct ethtool_stats stats;
2143+
u64 *data = NULL;
2144+
int ret = -EOPNOTSUPP;
21032145

21042146
if (copy_from_user(&stats, useraddr, sizeof(stats)))
21052147
return -EFAULT;
21062148

2107-
stats.n_stats = n_stats;
2149+
if (phydev)
2150+
ret = ethtool_get_phy_stats_phydev(phydev, &stats, &data);
21082151

2109-
if (n_stats) {
2110-
data = vzalloc(array_size(n_stats, sizeof(u64)));
2111-
if (!data)
2112-
return -ENOMEM;
2152+
if (ret == -EOPNOTSUPP)
2153+
ret = ethtool_get_phy_stats_ethtool(dev, &stats, &data);
21132154

2114-
if (phydev && !ops->get_ethtool_phy_stats &&
2115-
phy_ops && phy_ops->get_stats) {
2116-
ret = phy_ops->get_stats(phydev, &stats, data);
2117-
if (ret < 0)
2118-
goto out;
2119-
} else {
2120-
ops->get_ethtool_phy_stats(dev, &stats, data);
2121-
}
2122-
} else {
2123-
data = NULL;
2124-
}
2155+
if (ret)
2156+
goto out;
21252157

2126-
ret = -EFAULT;
2127-
if (copy_to_user(useraddr, &stats, sizeof(stats)))
2158+
if (copy_to_user(useraddr, &stats, sizeof(stats))) {
2159+
ret = -EFAULT;
21282160
goto out;
2161+
}
2162+
21292163
useraddr += sizeof(stats);
2130-
if (n_stats && copy_to_user(useraddr, data, array_size(n_stats, sizeof(u64))))
2131-
goto out;
2132-
ret = 0;
2164+
if (copy_to_user(useraddr, data, array_size(stats.n_stats, sizeof(u64))))
2165+
ret = -EFAULT;
21332166

21342167
out:
21352168
vfree(data);

0 commit comments

Comments
 (0)