All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/2] ethtool: improve compat ioctl handling
@ 2020-09-18 12:05 Arnd Bergmann
  2020-09-18 12:05 ` [PATCH 2/2] dev_ioctl: split out SIOC?IFMAP ioctls Arnd Bergmann
                   ` (3 more replies)
  0 siblings, 4 replies; 10+ messages in thread
From: Arnd Bergmann @ 2020-09-18 12:05 UTC (permalink / raw)
  To: David S. Miller, Jakub Kicinski
  Cc: Christoph Hellwig, Arnd Bergmann, Michal Kubecek, Andrew Lunn,
	Florian Fainelli, Gustavo A. R. Silva, Jens Axboe, linux-kernel,
	netdev

The ethtool compat ioctl handling is hidden away in net/socket.c,
which introduces a couple of minor oddities:

- The implementation may end up diverging, as seen in the RXNFC
  extension in commit 84a1d9c48200 ("net: ethtool: extend RXNFC
  API to support RSS spreading of filter matches") that does not work
  in compat mode.

- Most architectures do not need the compat handling at all
  because u64 and compat_u64 have the same alignment.

- On x86, the conversion is done for both x32 and i386 user space,
  but it's actually wrong to do it for x32 and cannot work there.

- On 32-bit Arm, it never worked for compat oabi user space, since
  that needs to do the same conversion but does not.

- It would be nice to get rid of both compat_alloc_user_space()
  and copy_in_user() throughout the kernel.

None of these actually seems to be a serious problem that real
users are likely to encounter, but fixing all of them actually
leads to code that is both shorter and more readable.

Signed-off-by: Arnd Bergmann <arnd@arndb.de>
---
 include/linux/ethtool.h |   4 --
 net/ethtool/ioctl.c     | 129 +++++++++++++++++++++++++++++++++++-----
 net/socket.c            | 125 +-------------------------------------
 3 files changed, 114 insertions(+), 144 deletions(-)

diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
index 969a80211df6..283987123022 100644
--- a/include/linux/ethtool.h
+++ b/include/linux/ethtool.h
@@ -17,8 +17,6 @@
 #include <linux/compat.h>
 #include <uapi/linux/ethtool.h>
 
-#ifdef CONFIG_COMPAT
-
 struct compat_ethtool_rx_flow_spec {
 	u32		flow_type;
 	union ethtool_flow_union h_u;
@@ -38,8 +36,6 @@ struct compat_ethtool_rxnfc {
 	u32				rule_locs[];
 };
 
-#endif /* CONFIG_COMPAT */
-
 #include <linux/rculist.h>
 
 /**
diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c
index 441794e0034f..38ae16f095a5 100644
--- a/net/ethtool/ioctl.c
+++ b/net/ethtool/ioctl.c
@@ -7,6 +7,7 @@
  * the information ethtool needs.
  */
 
+#include <linux/compat.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/capability.h>
@@ -807,6 +808,113 @@ static noinline_for_stack int ethtool_get_sset_info(struct net_device *dev,
 	return ret;
 }
 
+static inline bool ethtool_translate_compat(void)
+{
+#ifdef CONFIG_X86_64
+	/* On x86, translation is needed for i386 but not x32 */
+	return in_ia32_syscall();
+#elif defined(CONFIG_ARM)
+	/* On 32-bit Arm, translation is needed for OABI only */
+	return in_oabi_syscall();
+#else
+	BUILD_BUG_ON(sizeof(struct compat_ethtool_rxnfc) !=
+		     sizeof(struct ethtool_rxnfc));
+#endif
+
+	return 0;
+}
+
+static int ethtool_rxnfc_copy_from_user(struct ethtool_rxnfc *rxnfc,
+			const struct compat_ethtool_rxnfc __user *useraddr,
+			size_t size)
+{
+	/* We expect there to be holes between fs.m_ext and
+	 * fs.ring_cookie and at the end of fs, but nowhere else.
+	 */
+	BUILD_BUG_ON(offsetof(struct compat_ethtool_rxnfc, fs.m_ext) +
+		     sizeof(useraddr->fs.m_ext) !=
+		     offsetof(struct ethtool_rxnfc, fs.m_ext) +
+		     sizeof(rxnfc->fs.m_ext));
+	BUILD_BUG_ON(offsetof(struct compat_ethtool_rxnfc, fs.location) -
+		     offsetof(struct compat_ethtool_rxnfc, fs.ring_cookie) !=
+		     offsetof(struct ethtool_rxnfc, fs.location) -
+		     offsetof(struct ethtool_rxnfc, fs.ring_cookie));
+
+	if (ethtool_translate_compat()) {
+		struct compat_ethtool_rxnfc crxnfc = {};
+
+		if (copy_from_user(&crxnfc, useraddr,
+				   min(size, sizeof(crxnfc))))
+			return -EFAULT;
+
+		*rxnfc = (struct ethtool_rxnfc) {
+			.cmd		= crxnfc.cmd,
+			.flow_type	= crxnfc.flow_type,
+			.data		= crxnfc.data,
+			.fs		= {
+				.flow_type	= crxnfc.fs.flow_type,
+				.h_u		= crxnfc.fs.h_u,
+				.h_ext		= crxnfc.fs.h_ext,
+				.m_u		= crxnfc.fs.m_u,
+				.m_ext		= crxnfc.fs.m_ext,
+				.ring_cookie	= crxnfc.fs.ring_cookie,
+				.location	= crxnfc.fs.location,
+			},
+			.rule_cnt	= crxnfc.rule_cnt,
+		};
+	} else {
+		if (copy_from_user(&rxnfc, useraddr, size))
+			return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int ethtool_rxnfc_copy_to_user(void __user *useraddr,
+			const struct ethtool_rxnfc *rxnfc,
+			size_t size, const u32 *rule_buf)
+{
+	int ret;
+
+	if (ethtool_translate_compat()) {
+		struct compat_ethtool_rxnfc crxnfc;
+
+		memset(&crxnfc, 0, sizeof(crxnfc));
+		crxnfc = (struct compat_ethtool_rxnfc) {
+			.cmd		= rxnfc->cmd,
+			.flow_type	= rxnfc->flow_type,
+			.data		= rxnfc->data,
+			.fs		= {
+				.flow_type	= rxnfc->fs.flow_type,
+				.h_u		= rxnfc->fs.h_u,
+				.h_ext		= rxnfc->fs.h_ext,
+				.m_u		= rxnfc->fs.m_u,
+				.m_ext		= rxnfc->fs.m_ext,
+				.ring_cookie	= rxnfc->fs.ring_cookie,
+				.location	= rxnfc->fs.location,
+			},
+			.rule_cnt	= rxnfc->rule_cnt,
+		};
+
+		ret = copy_to_user(useraddr, &crxnfc, min(size, sizeof(crxnfc)));
+		useraddr += offsetof(struct compat_ethtool_rxnfc, rule_locs);
+	} else {
+		ret = copy_to_user(useraddr, &rxnfc, size);
+		useraddr += offsetof(struct ethtool_rxnfc, rule_locs);
+	}
+
+	if (ret)
+		return -EFAULT;
+
+	if (rule_buf) {
+		if (copy_to_user(useraddr, rule_buf,
+				 rxnfc->rule_cnt * sizeof(u32)))
+			return -EFAULT;
+	}
+
+	return 0;
+}
+
 static noinline_for_stack int ethtool_set_rxnfc(struct net_device *dev,
 						u32 cmd, void __user *useraddr)
 {
@@ -825,7 +933,7 @@ static noinline_for_stack int ethtool_set_rxnfc(struct net_device *dev,
 		info_size = (offsetof(struct ethtool_rxnfc, data) +
 			     sizeof(info.data));
 
-	if (copy_from_user(&info, useraddr, info_size))
+	if (ethtool_rxnfc_copy_from_user(&info, useraddr, info_size))
 		return -EFAULT;
 
 	rc = dev->ethtool_ops->set_rxnfc(dev, &info);
@@ -833,7 +941,7 @@ static noinline_for_stack int ethtool_set_rxnfc(struct net_device *dev,
 		return rc;
 
 	if (cmd == ETHTOOL_SRXCLSRLINS &&
-	    copy_to_user(useraddr, &info, info_size))
+	    ethtool_rxnfc_copy_to_user(useraddr, &info, info_size, NULL))
 		return -EFAULT;
 
 	return 0;
@@ -859,7 +967,7 @@ static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev,
 		info_size = (offsetof(struct ethtool_rxnfc, data) +
 			     sizeof(info.data));
 
-	if (copy_from_user(&info, useraddr, info_size))
+	if (ethtool_rxnfc_copy_from_user(&info, useraddr, info_size))
 		return -EFAULT;
 
 	/* If FLOW_RSS was requested then user-space must be using the
@@ -867,7 +975,7 @@ static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev,
 	 */
 	if (cmd == ETHTOOL_GRXFH && info.flow_type & FLOW_RSS) {
 		info_size = sizeof(info);
-		if (copy_from_user(&info, useraddr, info_size))
+		if (ethtool_rxnfc_copy_from_user(&info, useraddr, info_size))
 			return -EFAULT;
 		/* Since malicious users may modify the original data,
 		 * we need to check whether FLOW_RSS is still requested.
@@ -893,18 +1001,7 @@ static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev,
 	if (ret < 0)
 		goto err_out;
 
-	ret = -EFAULT;
-	if (copy_to_user(useraddr, &info, info_size))
-		goto err_out;
-
-	if (rule_buf) {
-		useraddr += offsetof(struct ethtool_rxnfc, rule_locs);
-		if (copy_to_user(useraddr, rule_buf,
-				 info.rule_cnt * sizeof(u32)))
-			goto err_out;
-	}
-	ret = 0;
-
+	ret = ethtool_rxnfc_copy_to_user(useraddr, &info, info_size, rule_buf);
 err_out:
 	kfree(rule_buf);
 
diff --git a/net/socket.c b/net/socket.c
index dbbe8ea7d395..3fe30ba2a09a 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -3121,128 +3121,6 @@ static int compat_dev_ifconf(struct net *net, struct compat_ifconf __user *uifc3
 	return 0;
 }
 
-static int ethtool_ioctl(struct net *net, struct compat_ifreq __user *ifr32)
-{
-	struct compat_ethtool_rxnfc __user *compat_rxnfc;
-	bool convert_in = false, convert_out = false;
-	size_t buf_size = 0;
-	struct ethtool_rxnfc __user *rxnfc = NULL;
-	struct ifreq ifr;
-	u32 rule_cnt = 0, actual_rule_cnt;
-	u32 ethcmd;
-	u32 data;
-	int ret;
-
-	if (get_user(data, &ifr32->ifr_ifru.ifru_data))
-		return -EFAULT;
-
-	compat_rxnfc = compat_ptr(data);
-
-	if (get_user(ethcmd, &compat_rxnfc->cmd))
-		return -EFAULT;
-
-	/* Most ethtool structures are defined without padding.
-	 * Unfortunately struct ethtool_rxnfc is an exception.
-	 */
-	switch (ethcmd) {
-	default:
-		break;
-	case ETHTOOL_GRXCLSRLALL:
-		/* Buffer size is variable */
-		if (get_user(rule_cnt, &compat_rxnfc->rule_cnt))
-			return -EFAULT;
-		if (rule_cnt > KMALLOC_MAX_SIZE / sizeof(u32))
-			return -ENOMEM;
-		buf_size += rule_cnt * sizeof(u32);
-		fallthrough;
-	case ETHTOOL_GRXRINGS:
-	case ETHTOOL_GRXCLSRLCNT:
-	case ETHTOOL_GRXCLSRULE:
-	case ETHTOOL_SRXCLSRLINS:
-		convert_out = true;
-		fallthrough;
-	case ETHTOOL_SRXCLSRLDEL:
-		buf_size += sizeof(struct ethtool_rxnfc);
-		convert_in = true;
-		rxnfc = compat_alloc_user_space(buf_size);
-		break;
-	}
-
-	if (copy_from_user(&ifr.ifr_name, &ifr32->ifr_name, IFNAMSIZ))
-		return -EFAULT;
-
-	ifr.ifr_data = convert_in ? rxnfc : (void __user *)compat_rxnfc;
-
-	if (convert_in) {
-		/* We expect there to be holes between fs.m_ext and
-		 * fs.ring_cookie and at the end of fs, but nowhere else.
-		 */
-		BUILD_BUG_ON(offsetof(struct compat_ethtool_rxnfc, fs.m_ext) +
-			     sizeof(compat_rxnfc->fs.m_ext) !=
-			     offsetof(struct ethtool_rxnfc, fs.m_ext) +
-			     sizeof(rxnfc->fs.m_ext));
-		BUILD_BUG_ON(
-			offsetof(struct compat_ethtool_rxnfc, fs.location) -
-			offsetof(struct compat_ethtool_rxnfc, fs.ring_cookie) !=
-			offsetof(struct ethtool_rxnfc, fs.location) -
-			offsetof(struct ethtool_rxnfc, fs.ring_cookie));
-
-		if (copy_in_user(rxnfc, compat_rxnfc,
-				 (void __user *)(&rxnfc->fs.m_ext + 1) -
-				 (void __user *)rxnfc) ||
-		    copy_in_user(&rxnfc->fs.ring_cookie,
-				 &compat_rxnfc->fs.ring_cookie,
-				 (void __user *)(&rxnfc->fs.location + 1) -
-				 (void __user *)&rxnfc->fs.ring_cookie))
-			return -EFAULT;
-		if (ethcmd == ETHTOOL_GRXCLSRLALL) {
-			if (put_user(rule_cnt, &rxnfc->rule_cnt))
-				return -EFAULT;
-		} else if (copy_in_user(&rxnfc->rule_cnt,
-					&compat_rxnfc->rule_cnt,
-					sizeof(rxnfc->rule_cnt)))
-			return -EFAULT;
-	}
-
-	ret = dev_ioctl(net, SIOCETHTOOL, &ifr, NULL);
-	if (ret)
-		return ret;
-
-	if (convert_out) {
-		if (copy_in_user(compat_rxnfc, rxnfc,
-				 (const void __user *)(&rxnfc->fs.m_ext + 1) -
-				 (const void __user *)rxnfc) ||
-		    copy_in_user(&compat_rxnfc->fs.ring_cookie,
-				 &rxnfc->fs.ring_cookie,
-				 (const void __user *)(&rxnfc->fs.location + 1) -
-				 (const void __user *)&rxnfc->fs.ring_cookie) ||
-		    copy_in_user(&compat_rxnfc->rule_cnt, &rxnfc->rule_cnt,
-				 sizeof(rxnfc->rule_cnt)))
-			return -EFAULT;
-
-		if (ethcmd == ETHTOOL_GRXCLSRLALL) {
-			/* As an optimisation, we only copy the actual
-			 * number of rules that the underlying
-			 * function returned.  Since Mallory might
-			 * change the rule count in user memory, we
-			 * check that it is less than the rule count
-			 * originally given (as the user buffer size),
-			 * which has been range-checked.
-			 */
-			if (get_user(actual_rule_cnt, &rxnfc->rule_cnt))
-				return -EFAULT;
-			if (actual_rule_cnt < rule_cnt)
-				rule_cnt = actual_rule_cnt;
-			if (copy_in_user(&compat_rxnfc->rule_locs[0],
-					 &rxnfc->rule_locs[0],
-					 rule_cnt * sizeof(u32)))
-				return -EFAULT;
-		}
-	}
-
-	return 0;
-}
-
 static int compat_siocwandev(struct net *net, struct compat_ifreq __user *uifr32)
 {
 	compat_uptr_t uptr32;
@@ -3397,8 +3275,6 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock,
 		return old_bridge_ioctl(argp);
 	case SIOCGIFCONF:
 		return compat_dev_ifconf(net, argp);
-	case SIOCETHTOOL:
-		return ethtool_ioctl(net, argp);
 	case SIOCWANDEV:
 		return compat_siocwandev(net, argp);
 	case SIOCGIFMAP:
@@ -3411,6 +3287,7 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock,
 		return sock->ops->gettstamp(sock, argp, cmd == SIOCGSTAMP_OLD,
 					    !COMPAT_USE_64BIT_TIME);
 
+	case SIOCETHTOOL:
 	case SIOCBONDSLAVEINFOQUERY:
 	case SIOCBONDINFOQUERY:
 	case SIOCSHWTSTAMP:
-- 
2.27.0


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

* [PATCH 2/2] dev_ioctl: split out SIOC?IFMAP ioctls
  2020-09-18 12:05 [PATCH 1/2] ethtool: improve compat ioctl handling Arnd Bergmann
@ 2020-09-18 12:05 ` Arnd Bergmann
  2020-09-19  5:48   ` Christoph Hellwig
  2020-09-23  7:53   ` kernel test robot
  2020-09-19  5:43 ` [PATCH 1/2] ethtool: improve compat ioctl handling Christoph Hellwig
                   ` (2 subsequent siblings)
  3 siblings, 2 replies; 10+ messages in thread
From: Arnd Bergmann @ 2020-09-18 12:05 UTC (permalink / raw)
  To: David S. Miller, Jakub Kicinski
  Cc: Christoph Hellwig, Arnd Bergmann, Jiri Pirko, Taehee Yoo,
	Eric Dumazet, Alexei Starovoitov, Andrew Lunn, Jens Axboe,
	netdev, linux-kernel

The ifreq ioctls need a special compat handler at the moment because of
the size difference between the struct on native and compat architectures,
but this difference exists only for one pair of ioctls, SIOCGIFMAP
and SIOCSIFMAP.

Splitting these two out of dev_ioctl() into their own higher level
implementation means the ifreq structure can be redefined in the kernel
to be identical for all applications, avoiding the need for copying the
structure around multiple times, and removing one of the call sites for
compat_alloc_user_space() and copy_in_user().

This should also make it easier for drivers to implement ioctls correct
that take an ifreq __user pointer and need to be careful about the size
difference. This has been a problem in the past, but currently the kernel
does not appear to have any drivers suffering from it.

Note that the user space definition is unchanged, so struct ifreq now
has a different size between user space and kernel, but this is not a
problem when the user space definition is larger, and the only time the
extra members are accessed is in the ifmap ioctls.

Signed-off-by: Arnd Bergmann <arnd@arndb.de>
---
 include/linux/netdevice.h |   1 +
 include/uapi/linux/if.h   |   6 +++
 net/core/dev_ioctl.c      | 107 +++++++++++++++++++++++++++++++-------
 net/socket.c              |  93 +++------------------------------
 4 files changed, 101 insertions(+), 106 deletions(-)

diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index b0e303f6603f..31b170a9ac9c 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -3787,6 +3787,7 @@ void netdev_rx_handler_unregister(struct net_device *dev);
 bool dev_valid_name(const char *name);
 int dev_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr,
 		bool *need_copyout);
+int dev_ifmap(struct net *net, struct ifreq __user *ifr, unsigned int cmd);
 int dev_ifconf(struct net *net, struct ifconf *, int);
 int dev_ethtool(struct net *net, struct ifreq *);
 unsigned int dev_get_flags(const struct net_device *);
diff --git a/include/uapi/linux/if.h b/include/uapi/linux/if.h
index 797ba2c1562a..a332d6ae4dc6 100644
--- a/include/uapi/linux/if.h
+++ b/include/uapi/linux/if.h
@@ -247,7 +247,13 @@ struct ifreq {
 		short	ifru_flags;
 		int	ifru_ivalue;
 		int	ifru_mtu;
+#ifndef __KERNEL__
+		/*
+		 * ifru_map is rarely used but causes the incompatibility
+		 * between native and compat mode.
+		 */
 		struct  ifmap ifru_map;
+#endif
 		char	ifru_slave[IFNAMSIZ];	/* Just fits the size */
 		char	ifru_newname[IFNAMSIZ];
 		void __user *	ifru_data;
diff --git a/net/core/dev_ioctl.c b/net/core/dev_ioctl.c
index b2cf9b7bb7b8..7c8aeff98214 100644
--- a/net/core/dev_ioctl.c
+++ b/net/core/dev_ioctl.c
@@ -98,6 +98,94 @@ int dev_ifconf(struct net *net, struct ifconf *ifc, int size)
 	return 0;
 }
 
+int dev_ifmap(struct net *net, struct ifreq __user *ifr, unsigned int cmd)
+{
+	struct net_device *dev;
+	char ifname[IFNAMSIZ];
+	char *colon;
+	struct compat_ifmap cifmap;
+	struct ifmap ifmap;
+	int ret;
+
+	if (copy_from_user(ifname, ifr->ifr_name, sizeof(ifname)))
+		return -EFAULT;
+	ifname[IFNAMSIZ-1] = 0;
+	colon = strchr(ifname, ':');
+	if (colon)
+		*colon = 0;
+	dev_load(net, ifname);
+
+	switch (cmd) {
+	case SIOCGIFMAP:
+		rcu_read_lock();
+		dev = dev_get_by_name_rcu(net, ifname);
+		if (!dev) {
+			rcu_read_unlock();
+			return -ENODEV;
+		}
+
+		if (in_compat_syscall()) {
+			cifmap.mem_start = dev->mem_start;
+			cifmap.mem_end   = dev->mem_end;
+			cifmap.base_addr = dev->base_addr;
+			cifmap.irq       = dev->irq;
+			cifmap.dma       = dev->dma;
+			cifmap.port      = dev->if_port;
+			rcu_read_unlock();
+
+			ret = copy_to_user(&ifr->ifr_data,
+					   &cifmap, sizeof(cifmap));
+		} else {
+			ifmap.mem_start  = dev->mem_start;
+			ifmap.mem_end    = dev->mem_end;
+			ifmap.base_addr  = dev->base_addr;
+			ifmap.irq        = dev->irq;
+			ifmap.dma        = dev->dma;
+			ifmap.port       = dev->if_port;
+			rcu_read_unlock();
+
+			ret = copy_to_user(&ifr->ifr_data,
+					   &ifmap, sizeof(ifmap));
+		}
+		ret = ret ? -EFAULT : 0;
+		break;
+
+	case SIOCSIFMAP:
+		if (!capable(CAP_NET_ADMIN) ||
+		    !ns_capable(net->user_ns, CAP_NET_ADMIN))
+			return -EPERM;
+
+		if (in_compat_syscall()) {
+			if (copy_from_user(&cifmap, &ifr->ifr_data,
+					   sizeof(cifmap)))
+				return -EFAULT;
+
+			ifmap.mem_start  = cifmap.mem_start;
+			ifmap.mem_end    = cifmap.mem_end;
+			ifmap.base_addr  = cifmap.base_addr;
+			ifmap.irq        = cifmap.irq;
+			ifmap.dma        = cifmap.dma;
+			ifmap.port       = cifmap.port;
+		} else {
+			if (copy_from_user(&ifmap, &ifr->ifr_data,
+					   sizeof(ifmap)))
+				return -EFAULT;
+		}
+
+		rtnl_lock();
+		dev = __dev_get_by_name(net, ifname);
+		if (!dev || !netif_device_present(dev))
+			ret = -ENODEV;
+		else if (!dev->netdev_ops->ndo_set_config)
+			ret = -EOPNOTSUPP;
+		else
+			ret = dev->netdev_ops->ndo_set_config(dev, &ifmap);
+		rtnl_unlock();
+		break;
+	}
+	return ret;
+}
+
 /*
  *	Perform the SIOCxIFxxx calls, inside rcu_read_lock()
  */
@@ -138,15 +226,6 @@ static int dev_ifsioc_locked(struct net *net, struct ifreq *ifr, unsigned int cm
 		err = -EINVAL;
 		break;
 
-	case SIOCGIFMAP:
-		ifr->ifr_map.mem_start = dev->mem_start;
-		ifr->ifr_map.mem_end   = dev->mem_end;
-		ifr->ifr_map.base_addr = dev->base_addr;
-		ifr->ifr_map.irq       = dev->irq;
-		ifr->ifr_map.dma       = dev->dma;
-		ifr->ifr_map.port      = dev->if_port;
-		return 0;
-
 	case SIOCGIFINDEX:
 		ifr->ifr_ifindex = dev->ifindex;
 		return 0;
@@ -285,14 +364,6 @@ static int dev_ifsioc(struct net *net, struct ifreq *ifr, unsigned int cmd)
 		call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
 		return 0;
 
-	case SIOCSIFMAP:
-		if (ops->ndo_set_config) {
-			if (!netif_device_present(dev))
-				return -ENODEV;
-			return ops->ndo_set_config(dev, &ifr->ifr_map);
-		}
-		return -EOPNOTSUPP;
-
 	case SIOCADDMULTI:
 		if (!ops->ndo_set_rx_mode ||
 		    ifr->ifr_hwaddr.sa_family != AF_UNSPEC)
@@ -429,7 +500,6 @@ int dev_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr, bool *need_c
 	case SIOCGIFMTU:
 	case SIOCGIFHWADDR:
 	case SIOCGIFSLAVE:
-	case SIOCGIFMAP:
 	case SIOCGIFINDEX:
 	case SIOCGIFTXQLEN:
 		dev_load(net, ifr->ifr_name);
@@ -474,7 +544,6 @@ int dev_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr, bool *need_c
 	 *	- require strict serialization.
 	 *	- do not return a value
 	 */
-	case SIOCSIFMAP:
 	case SIOCSIFTXQLEN:
 		if (!capable(CAP_NET_ADMIN))
 			return -EPERM;
diff --git a/net/socket.c b/net/socket.c
index 3fe30ba2a09a..f57a24f10109 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -1194,6 +1194,10 @@ static long sock_ioctl(struct file *file, unsigned cmd, unsigned long arg)
 						   cmd == SIOCGSTAMP_NEW,
 						   false);
 			break;
+		case SIOCGIFMAP:
+		case SIOCSIFMAP:
+			err = dev_ifmap(net, argp, cmd);
+			break;
 		default:
 			err = sock_do_ioctl(net, sock, cmd, arg);
 			break;
@@ -3162,88 +3166,6 @@ static int compat_ifr_data_ioctl(struct net *net, unsigned int cmd,
 	return dev_ioctl(net, cmd, &ifreq, NULL);
 }
 
-static int compat_ifreq_ioctl(struct net *net, struct socket *sock,
-			      unsigned int cmd,
-			      struct compat_ifreq __user *uifr32)
-{
-	struct ifreq __user *uifr;
-	int err;
-
-	/* Handle the fact that while struct ifreq has the same *layout* on
-	 * 32/64 for everything but ifreq::ifru_ifmap and ifreq::ifru_data,
-	 * which are handled elsewhere, it still has different *size* due to
-	 * ifreq::ifru_ifmap (which is 16 bytes on 32 bit, 24 bytes on 64-bit,
-	 * resulting in struct ifreq being 32 and 40 bytes respectively).
-	 * As a result, if the struct happens to be at the end of a page and
-	 * the next page isn't readable/writable, we get a fault. To prevent
-	 * that, copy back and forth to the full size.
-	 */
-
-	uifr = compat_alloc_user_space(sizeof(*uifr));
-	if (copy_in_user(uifr, uifr32, sizeof(*uifr32)))
-		return -EFAULT;
-
-	err = sock_do_ioctl(net, sock, cmd, (unsigned long)uifr);
-
-	if (!err) {
-		switch (cmd) {
-		case SIOCGIFFLAGS:
-		case SIOCGIFMETRIC:
-		case SIOCGIFMTU:
-		case SIOCGIFMEM:
-		case SIOCGIFHWADDR:
-		case SIOCGIFINDEX:
-		case SIOCGIFADDR:
-		case SIOCGIFBRDADDR:
-		case SIOCGIFDSTADDR:
-		case SIOCGIFNETMASK:
-		case SIOCGIFPFLAGS:
-		case SIOCGIFTXQLEN:
-		case SIOCGMIIPHY:
-		case SIOCGMIIREG:
-		case SIOCGIFNAME:
-			if (copy_in_user(uifr32, uifr, sizeof(*uifr32)))
-				err = -EFAULT;
-			break;
-		}
-	}
-	return err;
-}
-
-static int compat_sioc_ifmap(struct net *net, unsigned int cmd,
-			struct compat_ifreq __user *uifr32)
-{
-	struct ifreq ifr;
-	struct compat_ifmap __user *uifmap32;
-	int err;
-
-	uifmap32 = &uifr32->ifr_ifru.ifru_map;
-	err = copy_from_user(&ifr, uifr32, sizeof(ifr.ifr_name));
-	err |= get_user(ifr.ifr_map.mem_start, &uifmap32->mem_start);
-	err |= get_user(ifr.ifr_map.mem_end, &uifmap32->mem_end);
-	err |= get_user(ifr.ifr_map.base_addr, &uifmap32->base_addr);
-	err |= get_user(ifr.ifr_map.irq, &uifmap32->irq);
-	err |= get_user(ifr.ifr_map.dma, &uifmap32->dma);
-	err |= get_user(ifr.ifr_map.port, &uifmap32->port);
-	if (err)
-		return -EFAULT;
-
-	err = dev_ioctl(net, cmd, &ifr, NULL);
-
-	if (cmd == SIOCGIFMAP && !err) {
-		err = copy_to_user(uifr32, &ifr, sizeof(ifr.ifr_name));
-		err |= put_user(ifr.ifr_map.mem_start, &uifmap32->mem_start);
-		err |= put_user(ifr.ifr_map.mem_end, &uifmap32->mem_end);
-		err |= put_user(ifr.ifr_map.base_addr, &uifmap32->base_addr);
-		err |= put_user(ifr.ifr_map.irq, &uifmap32->irq);
-		err |= put_user(ifr.ifr_map.dma, &uifmap32->dma);
-		err |= put_user(ifr.ifr_map.port, &uifmap32->port);
-		if (err)
-			err = -EFAULT;
-	}
-	return err;
-}
-
 /* Since old style bridge ioctl's endup using SIOCDEVPRIVATE
  * for some operations; this forces use of the newer bridge-utils that
  * use compatible ioctls
@@ -3277,9 +3199,6 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock,
 		return compat_dev_ifconf(net, argp);
 	case SIOCWANDEV:
 		return compat_siocwandev(net, argp);
-	case SIOCGIFMAP:
-	case SIOCSIFMAP:
-		return compat_sioc_ifmap(net, cmd, argp);
 	case SIOCGSTAMP_OLD:
 	case SIOCGSTAMPNS_OLD:
 		if (!sock->ops->gettstamp)
@@ -3294,6 +3213,8 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock,
 	case SIOCGHWTSTAMP:
 		return compat_ifr_data_ioctl(net, cmd, argp);
 
+	case SIOCGIFMAP:
+	case SIOCSIFMAP:
 	case FIOSETOWN:
 	case SIOCSPGRP:
 	case FIOGETOWN:
@@ -3347,8 +3268,6 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock,
 	case SIOCBONDRELEASE:
 	case SIOCBONDSETHWADDR:
 	case SIOCBONDCHANGEACTIVE:
-		return compat_ifreq_ioctl(net, sock, cmd, argp);
-
 	case SIOCSARP:
 	case SIOCGARP:
 	case SIOCDARP:
-- 
2.27.0


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

* Re: [PATCH 1/2] ethtool: improve compat ioctl handling
  2020-09-18 12:05 [PATCH 1/2] ethtool: improve compat ioctl handling Arnd Bergmann
  2020-09-18 12:05 ` [PATCH 2/2] dev_ioctl: split out SIOC?IFMAP ioctls Arnd Bergmann
@ 2020-09-19  5:43 ` Christoph Hellwig
  2020-09-19 23:40 ` David Miller
  2020-09-23 12:40 ` kernel test robot
  3 siblings, 0 replies; 10+ messages in thread
From: Christoph Hellwig @ 2020-09-19  5:43 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: David S. Miller, Jakub Kicinski, Christoph Hellwig,
	Michal Kubecek, Andrew Lunn, Florian Fainelli,
	Gustavo A. R. Silva, Jens Axboe, linux-kernel, netdev

> +	if (ethtool_translate_compat()) {
> +		struct compat_ethtool_rxnfc crxnfc = {};
> +
> +		if (copy_from_user(&crxnfc, useraddr,
> +				   min(size, sizeof(crxnfc))))
> +			return -EFAULT;
> +
> +		*rxnfc = (struct ethtool_rxnfc) {
> +			.cmd		= crxnfc.cmd,
> +			.flow_type	= crxnfc.flow_type,
> +			.data		= crxnfc.data,
> +			.fs		= {
> +				.flow_type	= crxnfc.fs.flow_type,
> +				.h_u		= crxnfc.fs.h_u,
> +				.h_ext		= crxnfc.fs.h_ext,
> +				.m_u		= crxnfc.fs.m_u,
> +				.m_ext		= crxnfc.fs.m_ext,
> +				.ring_cookie	= crxnfc.fs.ring_cookie,
> +				.location	= crxnfc.fs.location,
> +			},
> +			.rule_cnt	= crxnfc.rule_cnt,
> +		};

I'd split the compat version into a self-contained noinline helper.
Same for ethtool_rxnfc_copy_to_user.

Otherwise this looks good:

Reviewed-by: Christoph Hellwig <hch@lst.de>

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

* Re: [PATCH 2/2] dev_ioctl: split out SIOC?IFMAP ioctls
  2020-09-18 12:05 ` [PATCH 2/2] dev_ioctl: split out SIOC?IFMAP ioctls Arnd Bergmann
@ 2020-09-19  5:48   ` Christoph Hellwig
  2020-09-25 12:28     ` Arnd Bergmann
  2020-09-23  7:53   ` kernel test robot
  1 sibling, 1 reply; 10+ messages in thread
From: Christoph Hellwig @ 2020-09-19  5:48 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: David S. Miller, Jakub Kicinski, Christoph Hellwig, Jiri Pirko,
	Taehee Yoo, Eric Dumazet, Alexei Starovoitov, Andrew Lunn,
	Jens Axboe, netdev, linux-kernel

> diff --git a/include/uapi/linux/if.h b/include/uapi/linux/if.h
> index 797ba2c1562a..a332d6ae4dc6 100644
> --- a/include/uapi/linux/if.h
> +++ b/include/uapi/linux/if.h
> @@ -247,7 +247,13 @@ struct ifreq {
>  		short	ifru_flags;
>  		int	ifru_ivalue;
>  		int	ifru_mtu;
> +#ifndef __KERNEL__
> +		/*
> +		 * ifru_map is rarely used but causes the incompatibility
> +		 * between native and compat mode.
> +		 */
>  		struct  ifmap ifru_map;
> +#endif

Do we need a way to verify that this never changes the struct size?

> +int dev_ifmap(struct net *net, struct ifreq __user *ifr, unsigned int cmd)
> +{
> +	struct net_device *dev;
> +	char ifname[IFNAMSIZ];
> +	char *colon;
> +	struct compat_ifmap cifmap;
> +	struct ifmap ifmap;
> +	int ret;
> +
> +	if (copy_from_user(ifname, ifr->ifr_name, sizeof(ifname)))
> +		return -EFAULT;
> +	ifname[IFNAMSIZ-1] = 0;
> +	colon = strchr(ifname, ':');
> +	if (colon)
> +		*colon = 0;
> +	dev_load(net, ifname);
> +
> +	switch (cmd) {
> +	case SIOCGIFMAP:
> +		rcu_read_lock();
> +		dev = dev_get_by_name_rcu(net, ifname);
> +		if (!dev) {
> +			rcu_read_unlock();
> +			return -ENODEV;
> +		}
> +
> +		if (in_compat_syscall()) {
> +			cifmap.mem_start = dev->mem_start;
> +			cifmap.mem_end   = dev->mem_end;
> +			cifmap.base_addr = dev->base_addr;
> +			cifmap.irq       = dev->irq;
> +			cifmap.dma       = dev->dma;
> +			cifmap.port      = dev->if_port;
> +			rcu_read_unlock();
> +
> +			ret = copy_to_user(&ifr->ifr_data,
> +					   &cifmap, sizeof(cifmap));
> +		} else {
> +			ifmap.mem_start  = dev->mem_start;
> +			ifmap.mem_end    = dev->mem_end;
> +			ifmap.base_addr  = dev->base_addr;
> +			ifmap.irq        = dev->irq;
> +			ifmap.dma        = dev->dma;
> +			ifmap.port       = dev->if_port;
> +			rcu_read_unlock();
> +
> +			ret = copy_to_user(&ifr->ifr_data,
> +					   &ifmap, sizeof(ifmap));
> +		}
> +		ret = ret ? -EFAULT : 0;
> +		break;
> +
> +	case SIOCSIFMAP:
> +		if (!capable(CAP_NET_ADMIN) ||
> +		    !ns_capable(net->user_ns, CAP_NET_ADMIN))
> +			return -EPERM;
> +
> +		if (in_compat_syscall()) {
> +			if (copy_from_user(&cifmap, &ifr->ifr_data,
> +					   sizeof(cifmap)))
> +				return -EFAULT;
> +
> +			ifmap.mem_start  = cifmap.mem_start;
> +			ifmap.mem_end    = cifmap.mem_end;
> +			ifmap.base_addr  = cifmap.base_addr;
> +			ifmap.irq        = cifmap.irq;
> +			ifmap.dma        = cifmap.dma;
> +			ifmap.port       = cifmap.port;
> +		} else {
> +			if (copy_from_user(&ifmap, &ifr->ifr_data,
> +					   sizeof(ifmap)))
> +				return -EFAULT;
> +		}
> +
> +		rtnl_lock();
> +		dev = __dev_get_by_name(net, ifname);
> +		if (!dev || !netif_device_present(dev))
> +			ret = -ENODEV;
> +		else if (!dev->netdev_ops->ndo_set_config)
> +			ret = -EOPNOTSUPP;
> +		else
> +			ret = dev->netdev_ops->ndo_set_config(dev, &ifmap);
> +		rtnl_unlock();
> +		break;
> +	}
> +	return ret;

I'd rather split this into a separate hepers for each ioctl command
instead of having anothr multiplexer here, maybe with another helper
for the common code.

I also find the rcu unlock inside the branches rather strange, but
I can't think of a good alternative.

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

* Re: [PATCH 1/2] ethtool: improve compat ioctl handling
  2020-09-18 12:05 [PATCH 1/2] ethtool: improve compat ioctl handling Arnd Bergmann
  2020-09-18 12:05 ` [PATCH 2/2] dev_ioctl: split out SIOC?IFMAP ioctls Arnd Bergmann
  2020-09-19  5:43 ` [PATCH 1/2] ethtool: improve compat ioctl handling Christoph Hellwig
@ 2020-09-19 23:40 ` David Miller
  2020-09-23 12:40 ` kernel test robot
  3 siblings, 0 replies; 10+ messages in thread
From: David Miller @ 2020-09-19 23:40 UTC (permalink / raw)
  To: arnd
  Cc: kuba, hch, mkubecek, andrew, f.fainelli, gustavo, axboe,
	linux-kernel, netdev

From: Arnd Bergmann <arnd@arndb.de>
Date: Fri, 18 Sep 2020 14:05:18 +0200

> --- a/net/ethtool/ioctl.c
> +++ b/net/ethtool/ioctl.c
 ...
> +static inline bool ethtool_translate_compat(void)
> +{

Please don't use the inline keyword in foo.c files.

Thank you.

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

* Re: [PATCH 2/2] dev_ioctl: split out SIOC?IFMAP ioctls
  2020-09-18 12:05 ` [PATCH 2/2] dev_ioctl: split out SIOC?IFMAP ioctls Arnd Bergmann
  2020-09-19  5:48   ` Christoph Hellwig
@ 2020-09-23  7:53   ` kernel test robot
  1 sibling, 0 replies; 10+ messages in thread
From: kernel test robot @ 2020-09-23  7:53 UTC (permalink / raw)
  To: kbuild-all

[-- Attachment #1: Type: text/plain, Size: 4995 bytes --]

Hi Arnd,

I love your patch! Yet something to improve:

[auto build test ERROR on net-next/master]
[also build test ERROR on net/master linus/master v5.9-rc6 next-20200922]
[cannot apply to sparc-next/master]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Arnd-Bergmann/ethtool-improve-compat-ioctl-handling/20200918-200719
base:   https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git 1d39cd8cf75f79d082ee97f5fd2a6286bcc692c1
config: x86_64-randconfig-a005-20200923 (attached as .config)
compiler: clang version 12.0.0 (https://github.com/llvm/llvm-project d6ac649ccda289ecc2d2c0cb51892d57e8ec328c)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # install x86_64 cross compiling tool for clang build
        # apt-get install binutils-x86-64-linux-gnu
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=x86_64 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

>> net/core/dev_ioctl.c:106:22: error: variable has incomplete type 'struct compat_ifmap'
           struct compat_ifmap cifmap;
                               ^
   net/core/dev_ioctl.c:106:9: note: forward declaration of 'struct compat_ifmap'
           struct compat_ifmap cifmap;
                  ^
   1 error generated.

# https://github.com/0day-ci/linux/commit/f05d11ddcbc5653d5521601f0635e3b78aa83c26
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Arnd-Bergmann/ethtool-improve-compat-ioctl-handling/20200918-200719
git checkout f05d11ddcbc5653d5521601f0635e3b78aa83c26
vim +106 net/core/dev_ioctl.c

   100	
   101	int dev_ifmap(struct net *net, struct ifreq __user *ifr, unsigned int cmd)
   102	{
   103		struct net_device *dev;
   104		char ifname[IFNAMSIZ];
   105		char *colon;
 > 106		struct compat_ifmap cifmap;
   107		struct ifmap ifmap;
   108		int ret;
   109	
   110		if (copy_from_user(ifname, ifr->ifr_name, sizeof(ifname)))
   111			return -EFAULT;
   112		ifname[IFNAMSIZ-1] = 0;
   113		colon = strchr(ifname, ':');
   114		if (colon)
   115			*colon = 0;
   116		dev_load(net, ifname);
   117	
   118		switch (cmd) {
   119		case SIOCGIFMAP:
   120			rcu_read_lock();
   121			dev = dev_get_by_name_rcu(net, ifname);
   122			if (!dev) {
   123				rcu_read_unlock();
   124				return -ENODEV;
   125			}
   126	
   127			if (in_compat_syscall()) {
   128				cifmap.mem_start = dev->mem_start;
   129				cifmap.mem_end   = dev->mem_end;
   130				cifmap.base_addr = dev->base_addr;
   131				cifmap.irq       = dev->irq;
   132				cifmap.dma       = dev->dma;
   133				cifmap.port      = dev->if_port;
   134				rcu_read_unlock();
   135	
   136				ret = copy_to_user(&ifr->ifr_data,
   137						   &cifmap, sizeof(cifmap));
   138			} else {
   139				ifmap.mem_start  = dev->mem_start;
   140				ifmap.mem_end    = dev->mem_end;
   141				ifmap.base_addr  = dev->base_addr;
   142				ifmap.irq        = dev->irq;
   143				ifmap.dma        = dev->dma;
   144				ifmap.port       = dev->if_port;
   145				rcu_read_unlock();
   146	
   147				ret = copy_to_user(&ifr->ifr_data,
   148						   &ifmap, sizeof(ifmap));
   149			}
   150			ret = ret ? -EFAULT : 0;
   151			break;
   152	
   153		case SIOCSIFMAP:
   154			if (!capable(CAP_NET_ADMIN) ||
   155			    !ns_capable(net->user_ns, CAP_NET_ADMIN))
   156				return -EPERM;
   157	
   158			if (in_compat_syscall()) {
   159				if (copy_from_user(&cifmap, &ifr->ifr_data,
   160						   sizeof(cifmap)))
   161					return -EFAULT;
   162	
   163				ifmap.mem_start  = cifmap.mem_start;
   164				ifmap.mem_end    = cifmap.mem_end;
   165				ifmap.base_addr  = cifmap.base_addr;
   166				ifmap.irq        = cifmap.irq;
   167				ifmap.dma        = cifmap.dma;
   168				ifmap.port       = cifmap.port;
   169			} else {
   170				if (copy_from_user(&ifmap, &ifr->ifr_data,
   171						   sizeof(ifmap)))
   172					return -EFAULT;
   173			}
   174	
   175			rtnl_lock();
   176			dev = __dev_get_by_name(net, ifname);
   177			if (!dev || !netif_device_present(dev))
   178				ret = -ENODEV;
   179			else if (!dev->netdev_ops->ndo_set_config)
   180				ret = -EOPNOTSUPP;
   181			else
   182				ret = dev->netdev_ops->ndo_set_config(dev, &ifmap);
   183			rtnl_unlock();
   184			break;
   185		}
   186		return ret;
   187	}
   188	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org

[-- Attachment #2: config.gz --]
[-- Type: application/gzip, Size: 36671 bytes --]

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

* Re: [PATCH 1/2] ethtool: improve compat ioctl handling
  2020-09-18 12:05 [PATCH 1/2] ethtool: improve compat ioctl handling Arnd Bergmann
                   ` (2 preceding siblings ...)
  2020-09-19 23:40 ` David Miller
@ 2020-09-23 12:40 ` kernel test robot
  3 siblings, 0 replies; 10+ messages in thread
From: kernel test robot @ 2020-09-23 12:40 UTC (permalink / raw)
  To: kbuild-all

[-- Attachment #1: Type: text/plain, Size: 2900 bytes --]

Hi Arnd,

I love your patch! Yet something to improve:

[auto build test ERROR on net-next/master]
[also build test ERROR on net/master linus/master v5.9-rc6 next-20200922]
[cannot apply to sparc-next/master]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Arnd-Bergmann/ethtool-improve-compat-ioctl-handling/20200918-200719
base:   https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git 1d39cd8cf75f79d082ee97f5fd2a6286bcc692c1
config: arm-randconfig-r035-20200923 (attached as .config)
compiler: arm-linux-gnueabi-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=arm 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   In file included from net/ethtool/ioctl.c:15:
   include/linux/ethtool.h:26:2: error: unknown type name 'compat_u64'
      26 |  compat_u64 ring_cookie;
         |  ^~~~~~~~~~
   include/linux/ethtool.h:33:2: error: unknown type name 'compat_u64'
      33 |  compat_u64   data;
         |  ^~~~~~~~~~
   net/ethtool/ioctl.c: In function 'ethtool_translate_compat':
>> net/ethtool/ioctl.c:818:9: error: implicit declaration of function 'in_oabi_syscall'; did you mean 'in_compat_syscall'? [-Werror=implicit-function-declaration]
     818 |  return in_oabi_syscall();
         |         ^~~~~~~~~~~~~~~
         |         in_compat_syscall
   cc1: some warnings being treated as errors

# https://github.com/0day-ci/linux/commit/1381f2a35bcba8f11a9c00ea3a16143312e04ad4
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Arnd-Bergmann/ethtool-improve-compat-ioctl-handling/20200918-200719
git checkout 1381f2a35bcba8f11a9c00ea3a16143312e04ad4
vim +818 net/ethtool/ioctl.c

   810	
   811	static inline bool ethtool_translate_compat(void)
   812	{
   813	#ifdef CONFIG_X86_64
   814		/* On x86, translation is needed for i386 but not x32 */
   815		return in_ia32_syscall();
   816	#elif defined(CONFIG_ARM)
   817		/* On 32-bit Arm, translation is needed for OABI only */
 > 818		return in_oabi_syscall();
   819	#else
   820		BUILD_BUG_ON(sizeof(struct compat_ethtool_rxnfc) !=
   821			     sizeof(struct ethtool_rxnfc));
   822	#endif
   823	
   824		return 0;
   825	}
   826	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org

[-- Attachment #2: config.gz --]
[-- Type: application/gzip, Size: 26390 bytes --]

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

* Re: [PATCH 2/2] dev_ioctl: split out SIOC?IFMAP ioctls
  2020-09-19  5:48   ` Christoph Hellwig
@ 2020-09-25 12:28     ` Arnd Bergmann
  2020-09-29 17:52       ` Christoph Hellwig
  0 siblings, 1 reply; 10+ messages in thread
From: Arnd Bergmann @ 2020-09-25 12:28 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: David S. Miller, Jakub Kicinski, Jiri Pirko, Taehee Yoo,
	Eric Dumazet, Alexei Starovoitov, Andrew Lunn, Jens Axboe,
	Networking, linux-kernel

On Sat, Sep 19, 2020 at 7:48 AM Christoph Hellwig <hch@infradead.org> wrote:
>
> > diff --git a/include/uapi/linux/if.h b/include/uapi/linux/if.h
> > index 797ba2c1562a..a332d6ae4dc6 100644
> > --- a/include/uapi/linux/if.h
> > +++ b/include/uapi/linux/if.h
> > @@ -247,7 +247,13 @@ struct ifreq {
> >               short   ifru_flags;
> >               int     ifru_ivalue;
> >               int     ifru_mtu;
> > +#ifndef __KERNEL__
> > +             /*
> > +              * ifru_map is rarely used but causes the incompatibility
> > +              * between native and compat mode.
> > +              */
> >               struct  ifmap ifru_map;
> > +#endif
>
> Do we need a way to verify that this never changes the struct size?

Not sure which way you would want to check. The point of the patch
is that it does change the struct size inside of the kernel but not
in user space.

Do you mean we should check that the (larger) user space size
remains what it is for future changes, or that the (smaller)
kernel size remains the same on all kernels, or maybe both?

> > +int dev_ifmap(struct net *net, struct ifreq __user *ifr, unsigned int cmd)
> > +{
> > +     struct net_device *dev;
> > +     char ifname[IFNAMSIZ];
> > +     char *colon;
> > +     struct compat_ifmap cifmap;
> > +     struct ifmap ifmap;
> > +     int ret;
> > +
> > +     if (copy_from_user(ifname, ifr->ifr_name, sizeof(ifname)))
> > +             return -EFAULT;
> > +     ifname[IFNAMSIZ-1] = 0;
> > +     colon = strchr(ifname, ':');
> > +     if (colon)
> > +             *colon = 0;
> > +     dev_load(net, ifname);
> > +
> > +     switch (cmd) {
> > +     case SIOCGIFMAP:
> > +             rcu_read_lock();
...
> > +             break;
> > +
> > +     case SIOCSIFMAP:
> > +             if (!capable(CAP_NET_ADMIN) ||
...
> > +             break;
> > +     }
> > +     return ret;
>
> I'd rather split this into a separate hepers for each ioctl command
> instead of having anothr multiplexer here, maybe with another helper
> for the common code.

Yes, good idea.

> I also find the rcu unlock inside the branches rather strange, but
> I can't think of a good alternative.

I could assign to the local 'struct ifmap' first under the lock, and
then only copy from there to 'struct compat_ifmap' without the lock
in the compat path. It's probably not better, but I'll give it a try.

The kernel test robot found a build regression with CONFIG_COMPAT
is disabled, I'm fixing that by moving the struct definition of the
global #ifdef in linux/compat.h, which seems nicer than adding
another #ifdef in dev_ifmap.

     Arnd

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

* Re: [PATCH 2/2] dev_ioctl: split out SIOC?IFMAP ioctls
  2020-09-25 12:28     ` Arnd Bergmann
@ 2020-09-29 17:52       ` Christoph Hellwig
  2020-10-01 15:00         ` Arnd Bergmann
  0 siblings, 1 reply; 10+ messages in thread
From: Christoph Hellwig @ 2020-09-29 17:52 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Christoph Hellwig, David S. Miller, Jakub Kicinski, Jiri Pirko,
	Taehee Yoo, Eric Dumazet, Alexei Starovoitov, Andrew Lunn,
	Jens Axboe, Networking, linux-kernel

On Fri, Sep 25, 2020 at 02:28:29PM +0200, Arnd Bergmann wrote:
> > > +++ b/include/uapi/linux/if.h
> > > @@ -247,7 +247,13 @@ struct ifreq {
> > >               short   ifru_flags;
> > >               int     ifru_ivalue;
> > >               int     ifru_mtu;
> > > +#ifndef __KERNEL__
> > > +             /*
> > > +              * ifru_map is rarely used but causes the incompatibility
> > > +              * between native and compat mode.
> > > +              */
> > >               struct  ifmap ifru_map;
> > > +#endif
> >
> > Do we need a way to verify that this never changes the struct size?
> 
> Not sure which way you would want to check. The point of the patch
> is that it does change the struct size inside of the kernel but not
> in user space.
>
> Do you mean we should check that the (larger) user space size
> remains what it is for future changes, or that the (smaller)
> kernel size remains the same on all kernels, or maybe both?

I had something like:

	BUILD_BUG_ON(sizeof(struct ifmap) >
		     sizeof(struct ifreq) - IFNAMSIZ);

plus a suitable comment in mind.

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

* Re: [PATCH 2/2] dev_ioctl: split out SIOC?IFMAP ioctls
  2020-09-29 17:52       ` Christoph Hellwig
@ 2020-10-01 15:00         ` Arnd Bergmann
  0 siblings, 0 replies; 10+ messages in thread
From: Arnd Bergmann @ 2020-10-01 15:00 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: David S. Miller, Jakub Kicinski, Jiri Pirko, Taehee Yoo,
	Eric Dumazet, Alexei Starovoitov, Andrew Lunn, Jens Axboe,
	Networking, linux-kernel

On Tue, Sep 29, 2020 at 7:53 PM Christoph Hellwig <hch@infradead.org> wrote:
> On Fri, Sep 25, 2020 at 02:28:29PM +0200, Arnd Bergmann wrote:

> > Do you mean we should check that the (larger) user space size
> > remains what it is for future changes, or that the (smaller)
> > kernel size remains the same on all kernels, or maybe both?
>
> I had something like:
>
>         BUILD_BUG_ON(sizeof(struct ifmap) >
>                      sizeof(struct ifreq) - IFNAMSIZ);
>
> plus a suitable comment in mind.

But that condition is true on all 64-bit architectures, which is the
fundamental issue I'm working around. I can try to capture that
better in the comment though.

My expectation here is that passing the smaller 'ifreq' structure
to ndo_do_ioctl() is safe as long as all drivers use only the
remaining members of ifr_ifru that all fit into the first 16 bytes.
Do you see a problem with that assumption?

      Arnd

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

end of thread, other threads:[~2020-10-01 15:00 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-09-18 12:05 [PATCH 1/2] ethtool: improve compat ioctl handling Arnd Bergmann
2020-09-18 12:05 ` [PATCH 2/2] dev_ioctl: split out SIOC?IFMAP ioctls Arnd Bergmann
2020-09-19  5:48   ` Christoph Hellwig
2020-09-25 12:28     ` Arnd Bergmann
2020-09-29 17:52       ` Christoph Hellwig
2020-10-01 15:00         ` Arnd Bergmann
2020-09-23  7:53   ` kernel test robot
2020-09-19  5:43 ` [PATCH 1/2] ethtool: improve compat ioctl handling Christoph Hellwig
2020-09-19 23:40 ` David Miller
2020-09-23 12:40 ` kernel test robot

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.