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

Commit 44c02a2c authored by Al Viro's avatar Al Viro
Browse files

dev_ioctl(): move copyin/copyout to callers



Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 6a88fbe7
Loading
Loading
Loading
Loading
+2 −1
Original line number Original line Diff line number Diff line
@@ -3315,7 +3315,8 @@ int netdev_rx_handler_register(struct net_device *dev,
void netdev_rx_handler_unregister(struct net_device *dev);
void netdev_rx_handler_unregister(struct net_device *dev);


bool dev_valid_name(const char *name);
bool dev_valid_name(const char *name);
int dev_ioctl(struct net *net, unsigned int cmd, void __user *);
int dev_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr,
		bool *need_copyout);
int dev_ifconf(struct net *net, struct ifconf *, int);
int dev_ifconf(struct net *net, struct ifconf *, int);
int dev_ethtool(struct net *net, struct ifreq *);
int dev_ethtool(struct net *net, struct ifreq *);
unsigned int dev_get_flags(const struct net_device *);
unsigned int dev_get_flags(const struct net_device *);
+24 −61
Original line number Original line Diff line number Diff line
@@ -18,26 +18,10 @@
 *	match.  --pb
 *	match.  --pb
 */
 */


static int dev_ifname(struct net *net, struct ifreq __user *arg)
static int dev_ifname(struct net *net, struct ifreq *ifr)
{
{
	struct ifreq ifr;
	ifr->ifr_name[IFNAMSIZ-1] = 0;
	int error;
	return netdev_get_name(net, ifr->ifr_name, ifr->ifr_ifindex);

	/*
	 *	Fetch the caller's info block.
	 */

	if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
		return -EFAULT;
	ifr.ifr_name[IFNAMSIZ-1] = 0;

	error = netdev_get_name(net, ifr.ifr_name, ifr.ifr_ifindex);
	if (error)
		return error;

	if (copy_to_user(arg, &ifr, sizeof(struct ifreq)))
		return -EFAULT;
	return 0;
}
}


static gifconf_func_t *gifconf_list[NPROTO];
static gifconf_func_t *gifconf_list[NPROTO];
@@ -402,24 +386,24 @@ EXPORT_SYMBOL(dev_load);
 *	positive or a negative errno code on error.
 *	positive or a negative errno code on error.
 */
 */


int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)
int dev_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr, bool *need_copyout)
{
{
	struct ifreq ifr;
	int ret;
	int ret;
	char *colon;
	char *colon;


	if (need_copyout)
		*need_copyout = true;
	if (cmd == SIOCGIFNAME)
	if (cmd == SIOCGIFNAME)
		return dev_ifname(net, (struct ifreq __user *)arg);
		return dev_ifname(net, ifr);

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


	ifr.ifr_name[IFNAMSIZ-1] = 0;
	ifr->ifr_name[IFNAMSIZ-1] = 0;


	colon = strchr(ifr.ifr_name, ':');
	colon = strchr(ifr->ifr_name, ':');
	if (colon)
	if (colon)
		*colon = 0;
		*colon = 0;


	dev_load(net, ifr->ifr_name);

	/*
	/*
	 *	See which interface the caller is talking about.
	 *	See which interface the caller is talking about.
	 */
	 */
@@ -439,31 +423,19 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)
	case SIOCGIFMAP:
	case SIOCGIFMAP:
	case SIOCGIFINDEX:
	case SIOCGIFINDEX:
	case SIOCGIFTXQLEN:
	case SIOCGIFTXQLEN:
		dev_load(net, ifr.ifr_name);
		rcu_read_lock();
		rcu_read_lock();
		ret = dev_ifsioc_locked(net, &ifr, cmd);
		ret = dev_ifsioc_locked(net, ifr, cmd);
		rcu_read_unlock();
		rcu_read_unlock();
		if (!ret) {
		if (colon)
		if (colon)
			*colon = ':';
			*colon = ':';
			if (copy_to_user(arg, &ifr,
					 sizeof(struct ifreq)))
				ret = -EFAULT;
		}
		return ret;
		return ret;


	case SIOCETHTOOL:
	case SIOCETHTOOL:
		dev_load(net, ifr.ifr_name);
		rtnl_lock();
		rtnl_lock();
		ret = dev_ethtool(net, &ifr);
		ret = dev_ethtool(net, ifr);
		rtnl_unlock();
		rtnl_unlock();
		if (!ret) {
		if (colon)
		if (colon)
			*colon = ':';
			*colon = ':';
			if (copy_to_user(arg, &ifr,
					 sizeof(struct ifreq)))
				ret = -EFAULT;
		}
		return ret;
		return ret;


	/*
	/*
@@ -477,17 +449,11 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)
	case SIOCSIFNAME:
	case SIOCSIFNAME:
		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
			return -EPERM;
			return -EPERM;
		dev_load(net, ifr.ifr_name);
		rtnl_lock();
		rtnl_lock();
		ret = dev_ifsioc(net, &ifr, cmd);
		ret = dev_ifsioc(net, ifr, cmd);
		rtnl_unlock();
		rtnl_unlock();
		if (!ret) {
		if (colon)
		if (colon)
			*colon = ':';
			*colon = ':';
			if (copy_to_user(arg, &ifr,
					 sizeof(struct ifreq)))
				ret = -EFAULT;
		}
		return ret;
		return ret;


	/*
	/*
@@ -528,10 +494,11 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)
		/* fall through */
		/* fall through */
	case SIOCBONDSLAVEINFOQUERY:
	case SIOCBONDSLAVEINFOQUERY:
	case SIOCBONDINFOQUERY:
	case SIOCBONDINFOQUERY:
		dev_load(net, ifr.ifr_name);
		rtnl_lock();
		rtnl_lock();
		ret = dev_ifsioc(net, &ifr, cmd);
		ret = dev_ifsioc(net, ifr, cmd);
		rtnl_unlock();
		rtnl_unlock();
		if (need_copyout)
			*need_copyout = false;
		return ret;
		return ret;


	case SIOCGIFMEM:
	case SIOCGIFMEM:
@@ -551,13 +518,9 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)
		    cmd == SIOCGHWTSTAMP ||
		    cmd == SIOCGHWTSTAMP ||
		    (cmd >= SIOCDEVPRIVATE &&
		    (cmd >= SIOCDEVPRIVATE &&
		     cmd <= SIOCDEVPRIVATE + 15)) {
		     cmd <= SIOCDEVPRIVATE + 15)) {
			dev_load(net, ifr.ifr_name);
			rtnl_lock();
			rtnl_lock();
			ret = dev_ifsioc(net, &ifr, cmd);
			ret = dev_ifsioc(net, ifr, cmd);
			rtnl_unlock();
			rtnl_unlock();
			if (!ret && copy_to_user(arg, &ifr,
						 sizeof(struct ifreq)))
				ret = -EFAULT;
			return ret;
			return ret;
		}
		}
		return -ENOTTY;
		return -ENOTTY;
+45 −46
Original line number Original line Diff line number Diff line
@@ -973,10 +973,17 @@ static long sock_do_ioctl(struct net *net, struct socket *sock,
		rtnl_unlock();
		rtnl_unlock();
		if (!err && copy_to_user(argp, &ifc, sizeof(struct ifconf)))
		if (!err && copy_to_user(argp, &ifc, sizeof(struct ifconf)))
			err = -EFAULT;
			err = -EFAULT;
		return err;
	} else {
		struct ifreq ifr;
		bool need_copyout;
		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, sizeof(struct ifreq)))
				return -EFAULT;
	}
	}

	return err;
	return dev_ioctl(net, cmd, argp);
}
}


/*
/*
@@ -1000,8 +1007,15 @@ static long sock_ioctl(struct file *file, unsigned cmd, unsigned long arg)
	sock = file->private_data;
	sock = file->private_data;
	sk = sock->sk;
	sk = sock->sk;
	net = sock_net(sk);
	net = sock_net(sk);
	if (cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15)) {
	if (unlikely(cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15))) {
		err = dev_ioctl(net, cmd, argp);
		struct ifreq ifr;
		bool need_copyout;
		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, sizeof(struct ifreq)))
				return -EFAULT;
	} else
	} else
#ifdef CONFIG_WEXT_CORE
#ifdef CONFIG_WEXT_CORE
	if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) {
	if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) {
@@ -2695,9 +2709,9 @@ static int ethtool_ioctl(struct net *net, struct compat_ifreq __user *ifr32)
{
{
	struct compat_ethtool_rxnfc __user *compat_rxnfc;
	struct compat_ethtool_rxnfc __user *compat_rxnfc;
	bool convert_in = false, convert_out = false;
	bool convert_in = false, convert_out = false;
	size_t buf_size = ALIGN(sizeof(struct ifreq), 8);
	size_t buf_size = 0;
	struct ethtool_rxnfc __user *rxnfc;
	struct ethtool_rxnfc __user *rxnfc = NULL;
	struct ifreq __user *ifr;
	struct ifreq ifr;
	u32 rule_cnt = 0, actual_rule_cnt;
	u32 rule_cnt = 0, actual_rule_cnt;
	u32 ethcmd;
	u32 ethcmd;
	u32 data;
	u32 data;
@@ -2734,18 +2748,14 @@ static int ethtool_ioctl(struct net *net, struct compat_ifreq __user *ifr32)
	case ETHTOOL_SRXCLSRLDEL:
	case ETHTOOL_SRXCLSRLDEL:
		buf_size += sizeof(struct ethtool_rxnfc);
		buf_size += sizeof(struct ethtool_rxnfc);
		convert_in = true;
		convert_in = true;
		rxnfc = compat_alloc_user_space(buf_size);
		break;
		break;
	}
	}


	ifr = compat_alloc_user_space(buf_size);
	if (copy_from_user(&ifr.ifr_name, &ifr32->ifr_name, IFNAMSIZ))
	rxnfc = (void __user *)ifr + ALIGN(sizeof(struct ifreq), 8);

	if (copy_in_user(&ifr->ifr_name, &ifr32->ifr_name, IFNAMSIZ))
		return -EFAULT;
		return -EFAULT;


	if (put_user(convert_in ? rxnfc : compat_ptr(data),
	ifr.ifr_data = convert_in ? rxnfc : (void __user *)compat_rxnfc;
		     &ifr->ifr_ifru.ifru_data))
		return -EFAULT;


	if (convert_in) {
	if (convert_in) {
		/* We expect there to be holes between fs.m_ext and
		/* We expect there to be holes between fs.m_ext and
@@ -2773,7 +2783,7 @@ static int ethtool_ioctl(struct net *net, struct compat_ifreq __user *ifr32)
			return -EFAULT;
			return -EFAULT;
	}
	}


	ret = dev_ioctl(net, SIOCETHTOOL, ifr);
	ret = dev_ioctl(net, SIOCETHTOOL, &ifr, NULL);
	if (ret)
	if (ret)
		return ret;
		return ret;


@@ -2814,50 +2824,43 @@ static int ethtool_ioctl(struct net *net, struct compat_ifreq __user *ifr32)


static int compat_siocwandev(struct net *net, struct compat_ifreq __user *uifr32)
static int compat_siocwandev(struct net *net, struct compat_ifreq __user *uifr32)
{
{
	void __user *uptr;
	compat_uptr_t uptr32;
	compat_uptr_t uptr32;
	struct ifreq __user *uifr;
	struct ifreq ifr;
	void __user *saved;
	int err;


	uifr = compat_alloc_user_space(sizeof(*uifr));
	if (copy_from_user(&ifr, uifr32, sizeof(struct compat_ifreq)))
	if (copy_in_user(uifr, uifr32, sizeof(struct compat_ifreq)))
		return -EFAULT;
		return -EFAULT;


	if (get_user(uptr32, &uifr32->ifr_settings.ifs_ifsu))
	if (get_user(uptr32, &uifr32->ifr_settings.ifs_ifsu))
		return -EFAULT;
		return -EFAULT;


	uptr = compat_ptr(uptr32);
	saved = ifr.ifr_settings.ifs_ifsu.raw_hdlc;

	ifr.ifr_settings.ifs_ifsu.raw_hdlc = compat_ptr(uptr32);
	if (put_user(uptr, &uifr->ifr_settings.ifs_ifsu.raw_hdlc))
		return -EFAULT;


	return dev_ioctl(net, SIOCWANDEV, uifr);
	err = dev_ioctl(net, SIOCWANDEV, &ifr, NULL);
	if (!err) {
		ifr.ifr_settings.ifs_ifsu.raw_hdlc = saved;
		if (copy_to_user(uifr32, &ifr, sizeof(struct compat_ifreq)))
			err = -EFAULT;
	}
	return err;
}
}


/* Handle ioctls that use ifreq::ifr_data and just need struct ifreq converted */
/* Handle ioctls that use ifreq::ifr_data and just need struct ifreq converted */
static int compat_ifr_data_ioctl(struct net *net, unsigned int cmd,
static int compat_ifr_data_ioctl(struct net *net, unsigned int cmd,
				 struct compat_ifreq __user *u_ifreq32)
				 struct compat_ifreq __user *u_ifreq32)
{
{
	struct ifreq __user *u_ifreq64;
	struct ifreq ifreq;
	char tmp_buf[IFNAMSIZ];
	void __user *data64;
	u32 data32;
	u32 data32;


	if (copy_from_user(&tmp_buf[0], &(u_ifreq32->ifr_ifrn.ifrn_name[0]),
	if (copy_from_user(ifreq.ifr_name, u_ifreq32->ifr_name, IFNAMSIZ))
			   IFNAMSIZ))
		return -EFAULT;
		return -EFAULT;
	if (get_user(data32, &u_ifreq32->ifr_ifru.ifru_data))
	if (get_user(data32, &u_ifreq32->ifr_data))
		return -EFAULT;
		return -EFAULT;
	data64 = compat_ptr(data32);
	ifreq.ifr_data = compat_ptr(data32);


	u_ifreq64 = compat_alloc_user_space(sizeof(*u_ifreq64));
	return dev_ioctl(net, cmd, &ifreq, NULL);

	if (copy_to_user(&u_ifreq64->ifr_ifrn.ifrn_name[0], &tmp_buf[0],
			 IFNAMSIZ))
		return -EFAULT;
	if (put_user(data64, &u_ifreq64->ifr_ifru.ifru_data))
		return -EFAULT;

	return dev_ioctl(net, cmd, u_ifreq64);
}
}


static int compat_sioc_ifmap(struct net *net, unsigned int cmd,
static int compat_sioc_ifmap(struct net *net, unsigned int cmd,
@@ -2865,7 +2868,6 @@ static int compat_sioc_ifmap(struct net *net, unsigned int cmd,
{
{
	struct ifreq ifr;
	struct ifreq ifr;
	struct compat_ifmap __user *uifmap32;
	struct compat_ifmap __user *uifmap32;
	mm_segment_t old_fs;
	int err;
	int err;


	uifmap32 = &uifr32->ifr_ifru.ifru_map;
	uifmap32 = &uifr32->ifr_ifru.ifru_map;
@@ -2879,10 +2881,7 @@ static int compat_sioc_ifmap(struct net *net, unsigned int cmd,
	if (err)
	if (err)
		return -EFAULT;
		return -EFAULT;


	old_fs = get_fs();
	err = dev_ioctl(net, cmd, &ifr, NULL);
	set_fs(KERNEL_DS);
	err = dev_ioctl(net, cmd, (void  __user __force *)&ifr);
	set_fs(old_fs);


	if (cmd == SIOCGIFMAP && !err) {
	if (cmd == SIOCGIFMAP && !err) {
		err = copy_to_user(uifr32, &ifr, sizeof(ifr.ifr_name));
		err = copy_to_user(uifr32, &ifr, sizeof(ifr.ifr_name));