Commit 29c49648 authored by Arnd Bergmann's avatar Arnd Bergmann Committed by David S. Miller
Browse files

net: socket: rework compat_ifreq_ioctl()



compat_ifreq_ioctl() is one of the last users of copy_in_user() and
compat_alloc_user_space(), as it attempts to convert the 'struct ifreq'
arguments from 32-bit to 64-bit format as used by dev_ioctl() and a
couple of socket family specific interpretations.

The current implementation works correctly when calling dev_ioctl(),
inet_ioctl(), ieee802154_sock_ioctl(), atalk_ioctl(), qrtr_ioctl()
and packet_ioctl(). The ioctl handlers for x25, netrom, rose and x25 do
not interpret the arguments and only block the corresponding commands,
so they do not care.

For af_inet6 and af_decnet however, the compat conversion is slightly
incorrect, as it will copy more data than the native handler accesses,
both of them use a structure that is shorter than ifreq.

Replace the copy_in_user() conversion with a pair of accessor functions
to read and write the ifreq data in place with the correct length where
needed, while leaving the other ones to copy the (already compatible)
structures directly.

Signed-off-by: default avatarArnd Bergmann <arnd@arndb.de>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 876f0bf9
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -4006,6 +4006,8 @@ int netdev_rx_handler_register(struct net_device *dev,
void netdev_rx_handler_unregister(struct net_device *dev);

bool dev_valid_name(const char *name);
int get_user_ifreq(struct ifreq *ifr, void __user **ifrdata, void __user *arg);
int put_user_ifreq(struct ifreq *ifr, void __user *arg);
int dev_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr,
		bool *need_copyout);
int dev_ifconf(struct net *net, struct ifconf __user *ifc);
+2 −2
Original line number Diff line number Diff line
@@ -666,7 +666,7 @@ static int atif_ioctl(int cmd, void __user *arg)
	struct rtentry rtdef;
	int add_route;

	if (copy_from_user(&atreq, arg, sizeof(atreq)))
	if (get_user_ifreq(&atreq, NULL, arg))
		return -EFAULT;

	dev = __dev_get_by_name(&init_net, atreq.ifr_name);
@@ -865,7 +865,7 @@ static int atif_ioctl(int cmd, void __user *arg)
		return 0;
	}

	return copy_to_user(arg, &atreq, sizeof(atreq)) ? -EFAULT : 0;
	return put_user_ifreq(&atreq, arg);
}

static int atrtr_ioctl_addrt(struct rtentry *rt)
+2 −2
Original line number Diff line number Diff line
@@ -129,7 +129,7 @@ static int ieee802154_dev_ioctl(struct sock *sk, struct ifreq __user *arg,
	int ret = -ENOIOCTLCMD;
	struct net_device *dev;

	if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
	if (get_user_ifreq(&ifr, NULL, arg))
		return -EFAULT;

	ifr.ifr_name[IFNAMSIZ-1] = 0;
@@ -143,7 +143,7 @@ static int ieee802154_dev_ioctl(struct sock *sk, struct ifreq __user *arg,
	if (dev->type == ARPHRD_IEEE802154 && dev->netdev_ops->ndo_do_ioctl)
		ret = dev->netdev_ops->ndo_do_ioctl(dev, &ifr, cmd);

	if (!ret && copy_to_user(arg, &ifr, sizeof(struct ifreq)))
	if (!ret && put_user_ifreq(&ifr, arg))
		ret = -EFAULT;
	dev_put(dev);

+3 −3
Original line number Diff line number Diff line
@@ -953,10 +953,10 @@ int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
	case SIOCGIFNETMASK:
	case SIOCGIFDSTADDR:
	case SIOCGIFPFLAGS:
		if (copy_from_user(&ifr, p, sizeof(struct ifreq)))
		if (get_user_ifreq(&ifr, NULL, p))
			return -EFAULT;
		err = devinet_ioctl(net, cmd, &ifr);
		if (!err && copy_to_user(p, &ifr, sizeof(struct ifreq)))
		if (!err && put_user_ifreq(&ifr, p))
			err = -EFAULT;
		break;

@@ -966,7 +966,7 @@ int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
	case SIOCSIFDSTADDR:
	case SIOCSIFPFLAGS:
	case SIOCSIFFLAGS:
		if (copy_from_user(&ifr, p, sizeof(struct ifreq)))
		if (get_user_ifreq(&ifr, NULL, p))
			return -EFAULT;
		err = devinet_ioctl(net, cmd, &ifr);
		break;
+2 −2
Original line number Diff line number Diff line
@@ -1153,14 +1153,14 @@ static int qrtr_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
		rc = put_user(len, (int __user *)argp);
		break;
	case SIOCGIFADDR:
		if (copy_from_user(&ifr, argp, sizeof(ifr))) {
		if (get_user_ifreq(&ifr, NULL, argp)) {
			rc = -EFAULT;
			break;
		}

		sq = (struct sockaddr_qrtr *)&ifr.ifr_addr;
		*sq = ipc->us;
		if (copy_to_user(argp, &ifr, sizeof(ifr))) {
		if (put_user_ifreq(&ifr, argp)) {
			rc = -EFAULT;
			break;
		}
Loading