Commit 7c57706b authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'ndo_ioctl-rework'

Arnd Bergmann says:

====================
ndo_ioctl rework

This series is a follow-up to the series for removing
compat_alloc_user_space() and copy_in_user() that has now
been merged.

I wanted to be sure I address all the ways that 'struct ifreq' is used
in device drivers through .ndo_do_ioctl, originally to prove that
my approach of changing the struct definition was correct, but then
I discarded that approach and went on anyway.

Roughly, the contents here are:

 - split out all the users of SIOCDEVPRIVATE ioctls into a
   separate ndo_siocdevprivate callback, to better see what
   gets used where

 - fix compat handling for those drivers that pass data
   directly inside of 'ifreq' rather than using an indirect
   ifr_data pointer

 - remove unreachable code in ndo_ioctl handlers that relies
   on command codes we never pass into that, in particular
   for wireless drivers

 - split out the ethernet specific ioctls into yet another
   ndo_eth_ioctl callback, as these are by far the most
   common use of ndo_do_ioctl today. I considered splitting
   them further into MII and timestamp controls, but
   went with the simpler change for now.

 - split out bonding and wandev ioctls into separate helpers

 - rework the bridge handling with a separate callback

At this point, only a few oddball things remain in ndo_do_ioctl:
appletalk and ieee802154 pass down SIOCSIFADDR/SIOCGIFADDR and
some wireless drivers have completely dead code.

I have thoroughly compile tested this on randconfig builds,
but not done any notable runtime testing, so please review.
All of it is also available as part of a larger branch at

https://git.kernel.org/pub/scm/linux/kernel/git/arnd/playground.git \
	compat-alloc-user-space-12

Changes since v2:
- rebase to net-next
- fix qeth regression
- Cc driver maintainers for each patch and in cover letter

Changes since v1:

- rebase to linux-5.14-rc2
- add conversion for ndo_siowandev, bridge and bonding drivers
- leave broken wifi drivers untouched for now

Link: https://lore.kernel.org/netdev/20201106221743.3271965-14-arnd@kernel.org/
====================
parents 2fba2eae 3d9d00bd
Loading
Loading
Loading
Loading
+29 −0
Original line number Diff line number Diff line
@@ -222,6 +222,35 @@ ndo_do_ioctl:
	Synchronization: rtnl_lock() semaphore.
	Context: process

        This is only called by network subsystems internally,
        not by user space calling ioctl as it was in before
        linux-5.14.

ndo_siocbond:
        Synchronization: rtnl_lock() semaphore.
        Context: process

        Used by the bonding driver for the SIOCBOND family of
        ioctl commands.

ndo_siocwandev:
	Synchronization: rtnl_lock() semaphore.
	Context: process

	Used by the drivers/net/wan framework to handle
	the SIOCWANDEV ioctl with the if_settings structure.

ndo_siocdevprivate:
	Synchronization: rtnl_lock() semaphore.
	Context: process

	This is used to implement SIOCDEVPRIVATE ioctl helpers.
	These should not be added to new drivers, so don't use.

ndo_eth_ioctl:
	Synchronization: rtnl_lock() semaphore.
	Context: process

ndo_get_stats:
	Synchronization: rtnl_lock() semaphore, dev_base_lock rwlock, or RCU.
	Context: atomic (can't sleep under rwlock or RCU)
+3 −3
Original line number Diff line number Diff line
@@ -625,7 +625,7 @@ interfaces of a DSA switch to share the same PHC.
By design, PTP timestamping with a DSA switch does not need any special
handling in the driver for the host port it is attached to.  However, when the
host port also supports PTP timestamping, DSA will take care of intercepting
the ``.ndo_do_ioctl`` calls towards the host port, and block attempts to enable
the ``.ndo_eth_ioctl`` calls towards the host port, and block attempts to enable
hardware timestamping on it. This is because the SO_TIMESTAMPING API does not
allow the delivery of multiple hardware timestamps for the same packet, so
anybody else except for the DSA switch port must be prevented from doing so.
@@ -688,7 +688,7 @@ ethtool ioctl operations for them need to be mediated by their respective MAC
driver.  Therefore, as opposed to DSA switches, modifications need to be done
to each individual MAC driver for PHY timestamping support. This entails:

- Checking, in ``.ndo_do_ioctl``, whether ``phy_has_hwtstamp(netdev->phydev)``
- Checking, in ``.ndo_eth_ioctl``, whether ``phy_has_hwtstamp(netdev->phydev)``
  is true or not. If it is, then the MAC driver should not process this request
  but instead pass it on to the PHY using ``phy_mii_ioctl()``.

@@ -747,7 +747,7 @@ For example, a typical driver design for TX timestamping might be to split the
transmission part into 2 portions:

1. "TX": checks whether PTP timestamping has been previously enabled through
   the ``.ndo_do_ioctl`` ("``priv->hwtstamp_tx_enabled == true``") and the
   the ``.ndo_eth_ioctl`` ("``priv->hwtstamp_tx_enabled == true``") and the
   current skb requires a TX timestamp ("``skb_shinfo(skb)->tx_flags &
   SKBTX_HW_TSTAMP``"). If this is true, it sets the
   "``skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS``" flag. Note: as
+9 −14
Original line number Diff line number Diff line
@@ -4050,16 +4050,15 @@ static int hdlcdev_close(struct net_device *dev)
 * called by network layer to process IOCTL call to network device
 *
 * dev  pointer to network device structure
 * ifr  pointer to network interface request structure
 * cmd  IOCTL command code
 * ifs  pointer to network interface settings structure
 *
 * returns 0 if success, otherwise error code
 */
static int hdlcdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
static int hdlcdev_wan_ioctl(struct net_device *dev, struct if_settings *ifs)
{
	const size_t size = sizeof(sync_serial_settings);
	sync_serial_settings new_line;
	sync_serial_settings __user *line = ifr->ifr_settings.ifs_ifsu.sync;
	sync_serial_settings __user *line = ifs->ifs_ifsu.sync;
	MGSLPC_INFO *info = dev_to_port(dev);
	unsigned int flags;

@@ -4070,17 +4069,14 @@ static int hdlcdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
	if (info->port.count)
		return -EBUSY;

	if (cmd != SIOCWANDEV)
		return hdlc_ioctl(dev, ifr, cmd);

	memset(&new_line, 0, size);

	switch(ifr->ifr_settings.type) {
	switch (ifs->type) {
	case IF_GET_IFACE: /* return current sync_serial_settings */

		ifr->ifr_settings.type = IF_IFACE_SYNC_SERIAL;
		if (ifr->ifr_settings.size < size) {
			ifr->ifr_settings.size = size; /* data size wanted */
		ifs->type = IF_IFACE_SYNC_SERIAL;
		if (ifs->size < size) {
			ifs->size = size; /* data size wanted */
			return -ENOBUFS;
		}

@@ -4148,9 +4144,8 @@ static int hdlcdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
			tty_kref_put(tty);
		}
		return 0;

	default:
		return hdlc_ioctl(dev, ifr, cmd);
		return hdlc_ioctl(dev, ifs);
	}
}

@@ -4225,7 +4220,7 @@ static const struct net_device_ops hdlcdev_ops = {
	.ndo_open       = hdlcdev_open,
	.ndo_stop       = hdlcdev_close,
	.ndo_start_xmit = hdlc_start_xmit,
	.ndo_do_ioctl   = hdlcdev_ioctl,
	.ndo_siocwandev = hdlcdev_wan_ioctl,
	.ndo_tx_timeout = hdlcdev_tx_timeout,
};

+4 −4
Original line number Diff line number Diff line
@@ -1745,10 +1745,10 @@ static int ipoib_ioctl(struct net_device *dev, struct ifreq *ifr,
{
	struct ipoib_dev_priv *priv = ipoib_priv(dev);

	if (!priv->rn_ops->ndo_do_ioctl)
	if (!priv->rn_ops->ndo_eth_ioctl)
		return -EOPNOTSUPP;

	return priv->rn_ops->ndo_do_ioctl(dev, ifr, cmd);
	return priv->rn_ops->ndo_eth_ioctl(dev, ifr, cmd);
}

static int ipoib_dev_init(struct net_device *dev)
@@ -2078,7 +2078,7 @@ static const struct net_device_ops ipoib_netdev_ops_pf = {
	.ndo_set_vf_guid	 = ipoib_set_vf_guid,
	.ndo_set_mac_address	 = ipoib_set_mac,
	.ndo_get_stats64	 = ipoib_get_stats,
	.ndo_do_ioctl		 = ipoib_ioctl,
	.ndo_eth_ioctl		 = ipoib_ioctl,
};

static const struct net_device_ops ipoib_netdev_ops_vf = {
@@ -2093,7 +2093,7 @@ static const struct net_device_ops ipoib_netdev_ops_vf = {
	.ndo_set_rx_mode	 = ipoib_set_mcast_list,
	.ndo_get_iflink		 = ipoib_get_iflink,
	.ndo_get_stats64	 = ipoib_get_stats,
	.ndo_do_ioctl		 = ipoib_ioctl,
	.ndo_eth_ioctl		 = ipoib_ioctl,
};

static const struct net_device_ops ipoib_netdev_default_pf = {
+10 −6
Original line number Diff line number Diff line
@@ -54,11 +54,12 @@ static netdev_tx_t ipddp_xmit(struct sk_buff *skb,
static int ipddp_create(struct ipddp_route *new_rt);
static int ipddp_delete(struct ipddp_route *rt);
static struct ipddp_route* __ipddp_find_route(struct ipddp_route *rt);
static int ipddp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
static int ipddp_siocdevprivate(struct net_device *dev, struct ifreq *ifr,
				void __user *data, int cmd);

static const struct net_device_ops ipddp_netdev_ops = {
	.ndo_start_xmit		= ipddp_xmit,
	.ndo_do_ioctl   	= ipddp_ioctl,
	.ndo_siocdevprivate	= ipddp_siocdevprivate,
	.ndo_set_mac_address 	= eth_mac_addr,
	.ndo_validate_addr	= eth_validate_addr,
};
@@ -268,15 +269,18 @@ static struct ipddp_route* __ipddp_find_route(struct ipddp_route *rt)
        return NULL;
}

static int ipddp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
static int ipddp_siocdevprivate(struct net_device *dev, struct ifreq *ifr,
				void __user *data, int cmd)
{
        struct ipddp_route __user *rt = ifr->ifr_data;
        struct ipddp_route rcp, rcp2, *rp;

	if (in_compat_syscall())
		return -EOPNOTSUPP;

        if(!capable(CAP_NET_ADMIN))
                return -EPERM;

	if(copy_from_user(&rcp, rt, sizeof(rcp)))
	if (copy_from_user(&rcp, data, sizeof(rcp)))
		return -EFAULT;

        switch(cmd)
@@ -296,7 +300,7 @@ static int ipddp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
			spin_unlock_bh(&ipddp_route_lock);

			if (rp) {
				if (copy_to_user(rt, &rcp2,
				if (copy_to_user(data, &rcp2,
						 sizeof(struct ipddp_route)))
					return -EFAULT;
				return 0;
Loading