Skip to content

Commit 201ed31

Browse files
d-tatianindavem330
authored andcommitted
net/ethtool/ioctl: split ethtool_get_phy_stats into multiple helpers
So that it's easier to follow and make sense of the branching and various conditions. Stats retrieval has been split into two separate functions ethtool_get_phy_stats_phydev & ethtool_get_phy_stats_ethtool. The former attempts to retrieve the stats using phydev & phy_ops, while the latter uses ethtool_ops. Actual n_stats validation & array allocation has been moved into a new ethtool_vzalloc_stats_array helper. This also fixes a potential NULL dereference of ops->get_ethtool_phy_stats where it was getting called in an else branch unconditionally without making sure it was actually present. Found by Linux Verification Center (linuxtesting.org) with the SVACE static analysis tool. Signed-off-by: Daniil Tatianin <d-tatianin@yandex-team.ru> Reviewed-by: Andrew Lunn <andrew@lunn.ch> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent fd47785 commit 201ed31

File tree

1 file changed

+69
-33
lines changed

1 file changed

+69
-33
lines changed

net/ethtool/ioctl.c

Lines changed: 69 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2078,55 +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-
const struct ethtool_phy_ops *phy_ops = ethtool_phy_ops;
2084-
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;
2089-
2090-
if (!phydev && (!ops->get_ethtool_phy_stats || !ops->get_sset_count))
2091-
return -EOPNOTSUPP;
2092-
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);
20982083
if (n_stats < 0)
20992084
return n_stats;
21002085
if (n_stats > S32_MAX / sizeof(u64))
21012086
return -ENOMEM;
21022087
if (WARN_ON_ONCE(!n_stats))
21032088
return -EOPNOTSUPP;
21042089

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+
{
2101+
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+
{
2121+
const struct ethtool_ops *ops = dev->ethtool_ops;
2122+
int n_stats, ret;
2123+
2124+
if (!ops || !ops->get_sset_count || ops->get_ethtool_phy_stats)
2125+
return -EOPNOTSUPP;
2126+
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;
2145+
21052146
if (copy_from_user(&stats, useraddr, sizeof(stats)))
21062147
return -EFAULT;
21072148

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

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-
}
2155+
if (ret)
2156+
goto out;
21222157

2123-
ret = -EFAULT;
2124-
if (copy_to_user(useraddr, &stats, sizeof(stats)))
2158+
if (copy_to_user(useraddr, &stats, sizeof(stats))) {
2159+
ret = -EFAULT;
21252160
goto out;
2161+
}
2162+
21262163
useraddr += sizeof(stats);
2127-
if (copy_to_user(useraddr, data, array_size(n_stats, sizeof(u64))))
2128-
goto out;
2129-
ret = 0;
2164+
if (copy_to_user(useraddr, data, array_size(stats.n_stats, sizeof(u64))))
2165+
ret = -EFAULT;
21302166

21312167
out:
21322168
vfree(data);

0 commit comments

Comments
 (0)