All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH net v2 0/3] net/ethtool/ioctl: split ethtool_get_phy_stats into multiple helpers
@ 2022-12-26 11:48 Daniil Tatianin
  2022-12-26 11:48 ` [PATCH net v2 1/3] net/ethtool/ioctl: return -EOPNOTSUPP if we have no phy stats Daniil Tatianin
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Daniil Tatianin @ 2022-12-26 11:48 UTC (permalink / raw)
  To: David S. Miller
  Cc: Daniil Tatianin, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Andrew Lunn, Sean Anderson, Jiri Pirko, Wolfram Sang,
	Maxim Korotkov, Gal Pressman, Vincent Mailhol, Tom Rix,
	Marco Bonelli, netdev, linux-kernel

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

Daniil Tatianin (3):
  net/ethtool/ioctl: return -EOPNOTSUPP if we have no phy stats
  net/ethtool/ioctl: remove if n_stats checks from ethtool_get_phy_stats
  net/ethtool/ioctl: split ethtool_get_phy_stats into multiple helpers

 net/ethtool/ioctl.c | 107 +++++++++++++++++++++++++++++---------------
 1 file changed, 70 insertions(+), 37 deletions(-)

-- 
2.25.1


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH net v2 1/3] net/ethtool/ioctl: return -EOPNOTSUPP if we have no phy stats
  2022-12-26 11:48 [PATCH net v2 0/3] net/ethtool/ioctl: split ethtool_get_phy_stats into multiple helpers Daniil Tatianin
@ 2022-12-26 11:48 ` Daniil Tatianin
  2022-12-26 11:48 ` [PATCH net v2 2/3] net/ethtool/ioctl: remove if n_stats checks from ethtool_get_phy_stats Daniil Tatianin
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Daniil Tatianin @ 2022-12-26 11:48 UTC (permalink / raw)
  To: David S. Miller
  Cc: Daniil Tatianin, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Andrew Lunn, Sean Anderson, Jiri Pirko, Wolfram Sang,
	Maxim Korotkov, Gal Pressman, Vincent Mailhol, Tom Rix,
	Marco Bonelli, netdev, linux-kernel

It's not very useful to copy back an empty ethtool_stats struct and
return 0 if we didn't actually have any stats. This also allows for
further simplification of this function in the future commits.

Signed-off-by: Daniil Tatianin <d-tatianin@yandex-team.ru>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
---
 net/ethtool/ioctl.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c
index 57e7238a4136..e8a294b38b7b 100644
--- a/net/ethtool/ioctl.c
+++ b/net/ethtool/ioctl.c
@@ -2093,7 +2093,8 @@ static int ethtool_get_phy_stats(struct net_device *dev, void __user *useraddr)
 		return n_stats;
 	if (n_stats > S32_MAX / sizeof(u64))
 		return -ENOMEM;
-	WARN_ON_ONCE(!n_stats);
+	if (WARN_ON_ONCE(!n_stats))
+		return -EOPNOTSUPP;
 
 	if (copy_from_user(&stats, useraddr, sizeof(stats)))
 		return -EFAULT;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH net v2 2/3] net/ethtool/ioctl: remove if n_stats checks from ethtool_get_phy_stats
  2022-12-26 11:48 [PATCH net v2 0/3] net/ethtool/ioctl: split ethtool_get_phy_stats into multiple helpers Daniil Tatianin
  2022-12-26 11:48 ` [PATCH net v2 1/3] net/ethtool/ioctl: return -EOPNOTSUPP if we have no phy stats Daniil Tatianin
@ 2022-12-26 11:48 ` Daniil Tatianin
  2022-12-26 11:48 ` [PATCH net v2 3/3] net/ethtool/ioctl: split ethtool_get_phy_stats into multiple helpers Daniil Tatianin
  2022-12-28 12:00 ` [PATCH net v2 0/3] " patchwork-bot+netdevbpf
  3 siblings, 0 replies; 5+ messages in thread
From: Daniil Tatianin @ 2022-12-26 11:48 UTC (permalink / raw)
  To: David S. Miller
  Cc: Daniil Tatianin, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Andrew Lunn, Sean Anderson, Jiri Pirko, Wolfram Sang,
	Maxim Korotkov, Gal Pressman, Vincent Mailhol, Tom Rix,
	Marco Bonelli, netdev, linux-kernel

Now that we always early return if we don't have any stats we can remove
these checks as they're no longer necessary.

Signed-off-by: Daniil Tatianin <d-tatianin@yandex-team.ru>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
---
 net/ethtool/ioctl.c | 24 ++++++++++--------------
 1 file changed, 10 insertions(+), 14 deletions(-)

diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c
index e8a294b38b7b..3379af21c29f 100644
--- a/net/ethtool/ioctl.c
+++ b/net/ethtool/ioctl.c
@@ -2101,28 +2101,24 @@ static int ethtool_get_phy_stats(struct net_device *dev, void __user *useraddr)
 
 	stats.n_stats = n_stats;
 
-	if (n_stats) {
-		data = vzalloc(array_size(n_stats, sizeof(u64)));
-		if (!data)
-			return -ENOMEM;
+	data = vzalloc(array_size(n_stats, sizeof(u64)));
+	if (!data)
+		return -ENOMEM;
 
-		if (phydev && !ops->get_ethtool_phy_stats &&
-		    phy_ops && phy_ops->get_stats) {
-			ret = phy_ops->get_stats(phydev, &stats, data);
-			if (ret < 0)
-				goto out;
-		} else {
-			ops->get_ethtool_phy_stats(dev, &stats, data);
-		}
+	if (phydev && !ops->get_ethtool_phy_stats &&
+		phy_ops && phy_ops->get_stats) {
+		ret = phy_ops->get_stats(phydev, &stats, data);
+		if (ret < 0)
+			goto out;
 	} else {
-		data = NULL;
+		ops->get_ethtool_phy_stats(dev, &stats, data);
 	}
 
 	ret = -EFAULT;
 	if (copy_to_user(useraddr, &stats, sizeof(stats)))
 		goto out;
 	useraddr += sizeof(stats);
-	if (n_stats && copy_to_user(useraddr, data, array_size(n_stats, sizeof(u64))))
+	if (copy_to_user(useraddr, data, array_size(n_stats, sizeof(u64))))
 		goto out;
 	ret = 0;
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH net v2 3/3] net/ethtool/ioctl: split ethtool_get_phy_stats into multiple helpers
  2022-12-26 11:48 [PATCH net v2 0/3] net/ethtool/ioctl: split ethtool_get_phy_stats into multiple helpers Daniil Tatianin
  2022-12-26 11:48 ` [PATCH net v2 1/3] net/ethtool/ioctl: return -EOPNOTSUPP if we have no phy stats Daniil Tatianin
  2022-12-26 11:48 ` [PATCH net v2 2/3] net/ethtool/ioctl: remove if n_stats checks from ethtool_get_phy_stats Daniil Tatianin
@ 2022-12-26 11:48 ` Daniil Tatianin
  2022-12-28 12:00 ` [PATCH net v2 0/3] " patchwork-bot+netdevbpf
  3 siblings, 0 replies; 5+ messages in thread
From: Daniil Tatianin @ 2022-12-26 11:48 UTC (permalink / raw)
  To: David S. Miller
  Cc: Daniil Tatianin, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Andrew Lunn, Sean Anderson, Jiri Pirko, Wolfram Sang,
	Maxim Korotkov, Gal Pressman, Vincent Mailhol, Tom Rix,
	Marco Bonelli, netdev, linux-kernel

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>
---
 net/ethtool/ioctl.c | 102 ++++++++++++++++++++++++++++++--------------
 1 file changed, 69 insertions(+), 33 deletions(-)

diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c
index 3379af21c29f..36792633ce5f 100644
--- a/net/ethtool/ioctl.c
+++ b/net/ethtool/ioctl.c
@@ -2072,23 +2072,8 @@ static int ethtool_get_stats(struct net_device *dev, void __user *useraddr)
 	return ret;
 }
 
-static int ethtool_get_phy_stats(struct net_device *dev, void __user *useraddr)
+static int ethtool_vzalloc_stats_array(int n_stats, u64 **data)
 {
-	const struct ethtool_phy_ops *phy_ops = ethtool_phy_ops;
-	const struct ethtool_ops *ops = dev->ethtool_ops;
-	struct phy_device *phydev = dev->phydev;
-	struct ethtool_stats stats;
-	u64 *data;
-	int ret, n_stats;
-
-	if (!phydev && (!ops->get_ethtool_phy_stats || !ops->get_sset_count))
-		return -EOPNOTSUPP;
-
-	if (phydev && !ops->get_ethtool_phy_stats &&
-	    phy_ops && phy_ops->get_sset_count)
-		n_stats = phy_ops->get_sset_count(phydev);
-	else
-		n_stats = ops->get_sset_count(dev, ETH_SS_PHY_STATS);
 	if (n_stats < 0)
 		return n_stats;
 	if (n_stats > S32_MAX / sizeof(u64))
@@ -2096,31 +2081,82 @@ static int ethtool_get_phy_stats(struct net_device *dev, void __user *useraddr)
 	if (WARN_ON_ONCE(!n_stats))
 		return -EOPNOTSUPP;
 
+	*data = vzalloc(array_size(n_stats, sizeof(u64)));
+	if (!*data)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static int ethtool_get_phy_stats_phydev(struct phy_device *phydev,
+					 struct ethtool_stats *stats,
+					 u64 **data)
+ {
+	const struct ethtool_phy_ops *phy_ops = ethtool_phy_ops;
+	int n_stats, ret;
+
+	if (!phy_ops || !phy_ops->get_sset_count || !phy_ops->get_stats)
+		return -EOPNOTSUPP;
+
+	n_stats = phy_ops->get_sset_count(phydev);
+
+	ret = ethtool_vzalloc_stats_array(n_stats, data);
+	if (ret)
+		return ret;
+
+	stats->n_stats = n_stats;
+	return phy_ops->get_stats(phydev, stats, *data);
+}
+
+static int ethtool_get_phy_stats_ethtool(struct net_device *dev,
+					  struct ethtool_stats *stats,
+					  u64 **data)
+{
+	const struct ethtool_ops *ops = dev->ethtool_ops;
+	int n_stats, ret;
+
+	if (!ops || !ops->get_sset_count || ops->get_ethtool_phy_stats)
+		return -EOPNOTSUPP;
+
+	n_stats = ops->get_sset_count(dev, ETH_SS_PHY_STATS);
+
+	ret = ethtool_vzalloc_stats_array(n_stats, data);
+	if (ret)
+		return ret;
+
+	stats->n_stats = n_stats;
+	ops->get_ethtool_phy_stats(dev, stats, *data);
+
+	return 0;
+}
+
+static int ethtool_get_phy_stats(struct net_device *dev, void __user *useraddr)
+{
+	struct phy_device *phydev = dev->phydev;
+	struct ethtool_stats stats;
+	u64 *data = NULL;
+	int ret = -EOPNOTSUPP;
+
 	if (copy_from_user(&stats, useraddr, sizeof(stats)))
 		return -EFAULT;
 
-	stats.n_stats = n_stats;
+	if (phydev)
+		ret = ethtool_get_phy_stats_phydev(phydev, &stats, &data);
 
-	data = vzalloc(array_size(n_stats, sizeof(u64)));
-	if (!data)
-		return -ENOMEM;
+	if (ret == -EOPNOTSUPP)
+		ret = ethtool_get_phy_stats_ethtool(dev, &stats, &data);
 
-	if (phydev && !ops->get_ethtool_phy_stats &&
-		phy_ops && phy_ops->get_stats) {
-		ret = phy_ops->get_stats(phydev, &stats, data);
-		if (ret < 0)
-			goto out;
-	} else {
-		ops->get_ethtool_phy_stats(dev, &stats, data);
-	}
+	if (ret)
+		goto out;
 
-	ret = -EFAULT;
-	if (copy_to_user(useraddr, &stats, sizeof(stats)))
+	if (copy_to_user(useraddr, &stats, sizeof(stats))) {
+		ret = -EFAULT;
 		goto out;
+	}
+
 	useraddr += sizeof(stats);
-	if (copy_to_user(useraddr, data, array_size(n_stats, sizeof(u64))))
-		goto out;
-	ret = 0;
+	if (copy_to_user(useraddr, data, array_size(stats.n_stats, sizeof(u64))))
+		ret = -EFAULT;
 
  out:
 	vfree(data);
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH net v2 0/3] net/ethtool/ioctl: split ethtool_get_phy_stats into multiple helpers
  2022-12-26 11:48 [PATCH net v2 0/3] net/ethtool/ioctl: split ethtool_get_phy_stats into multiple helpers Daniil Tatianin
                   ` (2 preceding siblings ...)
  2022-12-26 11:48 ` [PATCH net v2 3/3] net/ethtool/ioctl: split ethtool_get_phy_stats into multiple helpers Daniil Tatianin
@ 2022-12-28 12:00 ` patchwork-bot+netdevbpf
  3 siblings, 0 replies; 5+ messages in thread
From: patchwork-bot+netdevbpf @ 2022-12-28 12:00 UTC (permalink / raw)
  To: Daniil Tatianin
  Cc: davem, edumazet, kuba, pabeni, andrew, sean.anderson, jiri,
	wsa+renesas, korotkov.maxim.s, gal, mailhol.vincent, trix, marco,
	netdev, linux-kernel

Hello:

This series was applied to netdev/net.git (master)
by David S. Miller <davem@davemloft.net>:

On Mon, 26 Dec 2022 14:48:22 +0300 you wrote:
> 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.
> 
> [...]

Here is the summary with links:
  - [net,v2,1/3] net/ethtool/ioctl: return -EOPNOTSUPP if we have no phy stats
    https://git.kernel.org/netdev/net/c/9deb1e9fb88b
  - [net,v2,2/3] net/ethtool/ioctl: remove if n_stats checks from ethtool_get_phy_stats
    https://git.kernel.org/netdev/net/c/fd4778581d61
  - [net,v2,3/3] net/ethtool/ioctl: split ethtool_get_phy_stats into multiple helpers
    https://git.kernel.org/netdev/net/c/201ed315f967

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2022-12-28 12:00 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2022-12-26 11:48 [PATCH net v2 0/3] net/ethtool/ioctl: split ethtool_get_phy_stats into multiple helpers Daniil Tatianin
2022-12-26 11:48 ` [PATCH net v2 1/3] net/ethtool/ioctl: return -EOPNOTSUPP if we have no phy stats Daniil Tatianin
2022-12-26 11:48 ` [PATCH net v2 2/3] net/ethtool/ioctl: remove if n_stats checks from ethtool_get_phy_stats Daniil Tatianin
2022-12-26 11:48 ` [PATCH net v2 3/3] net/ethtool/ioctl: split ethtool_get_phy_stats into multiple helpers Daniil Tatianin
2022-12-28 12:00 ` [PATCH net v2 0/3] " patchwork-bot+netdevbpf

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.