Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 794827f3 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'net-various-compat-ioctl-fixes'



Johannes Berg says:

====================
various compat ioctl fixes

Back a long time ago, I already fixed a few of these by passing
the size of the struct ifreq to do_sock_ioctl(). However, Robert
found more cases, and now it won't be as simple because we'd have
to pass that down all the way to e.g. bond_do_ioctl() which isn't
really feasible.

Therefore, restore the old code.

While looking at why SIOCGIFNAME was broken, I realized that Al
had removed that case - which had been handled in an explicit
separate function - as well, and looking through his work at the
time I saw that bond ioctls were also affected by the erroneous
removal.

I've restored SIOCGIFNAME and bond ioctls by going through the
(now renamed) dev_ifsioc() instead of reintroducing their own
helper functions, which I hope is correct but have only tested
with SIOCGIFNAME.
====================

Acked-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 62967898 98406133
Loading
Loading
Loading
Loading
+63 −19
Original line number Diff line number Diff line
@@ -941,8 +941,7 @@ void dlci_ioctl_set(int (*hook) (unsigned int, void __user *))
EXPORT_SYMBOL(dlci_ioctl_set);

static long sock_do_ioctl(struct net *net, struct socket *sock,
			  unsigned int cmd, unsigned long arg,
			  unsigned int ifreq_size)
			  unsigned int cmd, unsigned long arg)
{
	int err;
	void __user *argp = (void __user *)arg;
@@ -968,11 +967,11 @@ static long sock_do_ioctl(struct net *net, struct socket *sock,
	} else {
		struct ifreq ifr;
		bool need_copyout;
		if (copy_from_user(&ifr, argp, ifreq_size))
		if (copy_from_user(&ifr, argp, sizeof(struct ifreq)))
			return -EFAULT;
		err = dev_ioctl(net, cmd, &ifr, &need_copyout);
		if (!err && need_copyout)
			if (copy_to_user(argp, &ifr, ifreq_size))
			if (copy_to_user(argp, &ifr, sizeof(struct ifreq)))
				return -EFAULT;
	}
	return err;
@@ -1071,8 +1070,7 @@ static long sock_ioctl(struct file *file, unsigned cmd, unsigned long arg)
			err = open_related_ns(&net->ns, get_net_ns);
			break;
		default:
			err = sock_do_ioctl(net, sock, cmd, arg,
					    sizeof(struct ifreq));
			err = sock_do_ioctl(net, sock, cmd, arg);
			break;
		}
	return err;
@@ -2780,8 +2778,7 @@ static int do_siocgstamp(struct net *net, struct socket *sock,
	int err;

	set_fs(KERNEL_DS);
	err = sock_do_ioctl(net, sock, cmd, (unsigned long)&ktv,
			    sizeof(struct compat_ifreq));
	err = sock_do_ioctl(net, sock, cmd, (unsigned long)&ktv);
	set_fs(old_fs);
	if (!err)
		err = compat_put_timeval(&ktv, up);
@@ -2797,8 +2794,7 @@ static int do_siocgstampns(struct net *net, struct socket *sock,
	int err;

	set_fs(KERNEL_DS);
	err = sock_do_ioctl(net, sock, cmd, (unsigned long)&kts,
			    sizeof(struct compat_ifreq));
	err = sock_do_ioctl(net, sock, cmd, (unsigned long)&kts);
	set_fs(old_fs);
	if (!err)
		err = compat_put_timespec(&kts, up);
@@ -2994,6 +2990,54 @@ 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)
{
@@ -3109,8 +3153,7 @@ static int routing_ioctl(struct net *net, struct socket *sock,
	}

	set_fs(KERNEL_DS);
	ret = sock_do_ioctl(net, sock, cmd, (unsigned long) r,
			    sizeof(struct compat_ifreq));
	ret = sock_do_ioctl(net, sock, cmd, (unsigned long) r);
	set_fs(old_fs);

out:
@@ -3210,21 +3253,22 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock,
	case SIOCSIFTXQLEN:
	case SIOCBRADDIF:
	case SIOCBRDELIF:
	case SIOCGIFNAME:
	case SIOCSIFNAME:
	case SIOCGMIIPHY:
	case SIOCGMIIREG:
	case SIOCSMIIREG:
	case SIOCSARP:
	case SIOCGARP:
	case SIOCDARP:
	case SIOCATMARK:
	case SIOCBONDENSLAVE:
	case SIOCBONDRELEASE:
	case SIOCBONDSETHWADDR:
	case SIOCBONDCHANGEACTIVE:
	case SIOCGIFNAME:
		return sock_do_ioctl(net, sock, cmd, arg,
				     sizeof(struct compat_ifreq));
		return compat_ifreq_ioctl(net, sock, cmd, argp);

	case SIOCSARP:
	case SIOCGARP:
	case SIOCDARP:
	case SIOCATMARK:
		return sock_do_ioctl(net, sock, cmd, arg);
	}

	return -ENOIOCTLCMD;