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

Configure Feed

Select the types of activity you want to include in your feed.

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 <davem@davemloft.net>

+71 -38
+71 -38
net/ethtool/ioctl.c
··· 2078 2078 return ret; 2079 2079 } 2080 2080 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) 2082 2082 { 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); 2098 2083 if (n_stats < 0) 2099 2084 return n_stats; 2100 2085 if (n_stats > S32_MAX / sizeof(u64)) 2101 2086 return -ENOMEM; 2102 - WARN_ON_ONCE(!n_stats); 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 + { 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; 2103 2145 2104 2146 if (copy_from_user(&stats, useraddr, sizeof(stats))) 2105 2147 return -EFAULT; 2106 2148 2107 - stats.n_stats = n_stats; 2149 + if (phydev) 2150 + ret = ethtool_get_phy_stats_phydev(phydev, &stats, &data); 2108 2151 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); 2113 2154 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; 2155 + if (ret) 2156 + goto out; 2157 + 2158 + if (copy_to_user(useraddr, &stats, sizeof(stats))) { 2159 + ret = -EFAULT; 2160 + goto out; 2124 2161 } 2125 2162 2126 - ret = -EFAULT; 2127 - if (copy_to_user(useraddr, &stats, sizeof(stats))) 2128 - goto out; 2129 2163 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; 2133 2166 2134 2167 out: 2135 2168 vfree(data);