mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	devinet_ioctl(): take copyin/copyout to caller
Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
		
							parent
							
								
									36fd633ec9
								
							
						
					
					
						commit
						03aef17bb7
					
				
					 4 changed files with 35 additions and 46 deletions
				
			
		| 
						 | 
				
			
			@ -173,7 +173,7 @@ static inline struct net_device *ip_dev_find(struct net *net, __be32 addr)
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b);
 | 
			
		||||
int devinet_ioctl(struct net *net, unsigned int cmd, void __user *);
 | 
			
		||||
int devinet_ioctl(struct net *net, unsigned int cmd, struct ifreq *);
 | 
			
		||||
void devinet_init(void);
 | 
			
		||||
struct in_device *inetdev_by_index(struct net *, int);
 | 
			
		||||
__be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -872,6 +872,8 @@ int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
 | 
			
		|||
	struct sock *sk = sock->sk;
 | 
			
		||||
	int err = 0;
 | 
			
		||||
	struct net *net = sock_net(sk);
 | 
			
		||||
	void __user *p = (void __user *)arg;
 | 
			
		||||
	struct ifreq ifr;
 | 
			
		||||
 | 
			
		||||
	switch (cmd) {
 | 
			
		||||
	case SIOCGSTAMP:
 | 
			
		||||
| 
						 | 
				
			
			@ -891,17 +893,26 @@ int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
 | 
			
		|||
		err = arp_ioctl(net, cmd, (void __user *)arg);
 | 
			
		||||
		break;
 | 
			
		||||
	case SIOCGIFADDR:
 | 
			
		||||
	case SIOCSIFADDR:
 | 
			
		||||
	case SIOCGIFBRDADDR:
 | 
			
		||||
	case SIOCSIFBRDADDR:
 | 
			
		||||
	case SIOCGIFNETMASK:
 | 
			
		||||
	case SIOCSIFNETMASK:
 | 
			
		||||
	case SIOCGIFDSTADDR:
 | 
			
		||||
	case SIOCGIFPFLAGS:
 | 
			
		||||
		if (copy_from_user(&ifr, p, sizeof(struct ifreq)))
 | 
			
		||||
			return -EFAULT;
 | 
			
		||||
		err = devinet_ioctl(net, cmd, &ifr);
 | 
			
		||||
		if (!err && copy_to_user(p, &ifr, sizeof(struct ifreq)))
 | 
			
		||||
			err = -EFAULT;
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case SIOCSIFADDR:
 | 
			
		||||
	case SIOCSIFBRDADDR:
 | 
			
		||||
	case SIOCSIFNETMASK:
 | 
			
		||||
	case SIOCSIFDSTADDR:
 | 
			
		||||
	case SIOCSIFPFLAGS:
 | 
			
		||||
	case SIOCGIFPFLAGS:
 | 
			
		||||
	case SIOCSIFFLAGS:
 | 
			
		||||
		err = devinet_ioctl(net, cmd, (void __user *)arg);
 | 
			
		||||
		if (copy_from_user(&ifr, p, sizeof(struct ifreq)))
 | 
			
		||||
			return -EFAULT;
 | 
			
		||||
		err = devinet_ioctl(net, cmd, &ifr);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		if (sk->sk_prot->ioctl)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -946,11 +946,10 @@ static int inet_abc_len(__be32 addr)
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
 | 
			
		||||
int devinet_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr)
 | 
			
		||||
{
 | 
			
		||||
	struct ifreq ifr;
 | 
			
		||||
	struct sockaddr_in sin_orig;
 | 
			
		||||
	struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr;
 | 
			
		||||
	struct sockaddr_in *sin = (struct sockaddr_in *)&ifr->ifr_addr;
 | 
			
		||||
	struct in_device *in_dev;
 | 
			
		||||
	struct in_ifaddr **ifap = NULL;
 | 
			
		||||
	struct in_ifaddr *ifa = NULL;
 | 
			
		||||
| 
						 | 
				
			
			@ -959,22 +958,16 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
 | 
			
		|||
	int ret = -EFAULT;
 | 
			
		||||
	int tryaddrmatch = 0;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 *	Fetch the caller's info block into kernel space
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
	if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
 | 
			
		||||
		goto out;
 | 
			
		||||
	ifr.ifr_name[IFNAMSIZ - 1] = 0;
 | 
			
		||||
	ifr->ifr_name[IFNAMSIZ - 1] = 0;
 | 
			
		||||
 | 
			
		||||
	/* save original address for comparison */
 | 
			
		||||
	memcpy(&sin_orig, sin, sizeof(*sin));
 | 
			
		||||
 | 
			
		||||
	colon = strchr(ifr.ifr_name, ':');
 | 
			
		||||
	colon = strchr(ifr->ifr_name, ':');
 | 
			
		||||
	if (colon)
 | 
			
		||||
		*colon = 0;
 | 
			
		||||
 | 
			
		||||
	dev_load(net, ifr.ifr_name);
 | 
			
		||||
	dev_load(net, ifr->ifr_name);
 | 
			
		||||
 | 
			
		||||
	switch (cmd) {
 | 
			
		||||
	case SIOCGIFADDR:	/* Get interface address */
 | 
			
		||||
| 
						 | 
				
			
			@ -1014,7 +1007,7 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
 | 
			
		|||
	rtnl_lock();
 | 
			
		||||
 | 
			
		||||
	ret = -ENODEV;
 | 
			
		||||
	dev = __dev_get_by_name(net, ifr.ifr_name);
 | 
			
		||||
	dev = __dev_get_by_name(net, ifr->ifr_name);
 | 
			
		||||
	if (!dev)
 | 
			
		||||
		goto done;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1031,7 +1024,7 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
 | 
			
		|||
			   This is checked above. */
 | 
			
		||||
			for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
 | 
			
		||||
			     ifap = &ifa->ifa_next) {
 | 
			
		||||
				if (!strcmp(ifr.ifr_name, ifa->ifa_label) &&
 | 
			
		||||
				if (!strcmp(ifr->ifr_name, ifa->ifa_label) &&
 | 
			
		||||
				    sin_orig.sin_addr.s_addr ==
 | 
			
		||||
							ifa->ifa_local) {
 | 
			
		||||
					break; /* found */
 | 
			
		||||
| 
						 | 
				
			
			@ -1044,7 +1037,7 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
 | 
			
		|||
		if (!ifa) {
 | 
			
		||||
			for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
 | 
			
		||||
			     ifap = &ifa->ifa_next)
 | 
			
		||||
				if (!strcmp(ifr.ifr_name, ifa->ifa_label))
 | 
			
		||||
				if (!strcmp(ifr->ifr_name, ifa->ifa_label))
 | 
			
		||||
					break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -1056,19 +1049,19 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
 | 
			
		|||
	switch (cmd) {
 | 
			
		||||
	case SIOCGIFADDR:	/* Get interface address */
 | 
			
		||||
		sin->sin_addr.s_addr = ifa->ifa_local;
 | 
			
		||||
		goto rarok;
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case SIOCGIFBRDADDR:	/* Get the broadcast address */
 | 
			
		||||
		sin->sin_addr.s_addr = ifa->ifa_broadcast;
 | 
			
		||||
		goto rarok;
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case SIOCGIFDSTADDR:	/* Get the destination address */
 | 
			
		||||
		sin->sin_addr.s_addr = ifa->ifa_address;
 | 
			
		||||
		goto rarok;
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
 | 
			
		||||
		sin->sin_addr.s_addr = ifa->ifa_mask;
 | 
			
		||||
		goto rarok;
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case SIOCSIFFLAGS:
 | 
			
		||||
		if (colon) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1076,11 +1069,11 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
 | 
			
		|||
			if (!ifa)
 | 
			
		||||
				break;
 | 
			
		||||
			ret = 0;
 | 
			
		||||
			if (!(ifr.ifr_flags & IFF_UP))
 | 
			
		||||
			if (!(ifr->ifr_flags & IFF_UP))
 | 
			
		||||
				inet_del_ifa(in_dev, ifap, 1);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		ret = dev_change_flags(dev, ifr.ifr_flags);
 | 
			
		||||
		ret = dev_change_flags(dev, ifr->ifr_flags);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case SIOCSIFADDR:	/* Set interface address (and family) */
 | 
			
		||||
| 
						 | 
				
			
			@ -1095,7 +1088,7 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
 | 
			
		|||
				break;
 | 
			
		||||
			INIT_HLIST_NODE(&ifa->hash);
 | 
			
		||||
			if (colon)
 | 
			
		||||
				memcpy(ifa->ifa_label, ifr.ifr_name, IFNAMSIZ);
 | 
			
		||||
				memcpy(ifa->ifa_label, ifr->ifr_name, IFNAMSIZ);
 | 
			
		||||
			else
 | 
			
		||||
				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
 | 
			
		||||
		} else {
 | 
			
		||||
| 
						 | 
				
			
			@ -1182,10 +1175,6 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
 | 
			
		|||
	rtnl_unlock();
 | 
			
		||||
out:
 | 
			
		||||
	return ret;
 | 
			
		||||
rarok:
 | 
			
		||||
	rtnl_unlock();
 | 
			
		||||
	ret = copy_to_user(arg, &ifr, sizeof(struct ifreq)) ? -EFAULT : 0;
 | 
			
		||||
	goto out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int inet_gifconf(struct net_device *dev, char __user *buf, int len, int size)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -329,17 +329,6 @@ set_sockaddr(struct sockaddr_in *sin, __be32 addr, __be16 port)
 | 
			
		|||
	sin->sin_port = port;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int __init ic_devinet_ioctl(unsigned int cmd, struct ifreq *arg)
 | 
			
		||||
{
 | 
			
		||||
	int res;
 | 
			
		||||
 | 
			
		||||
	mm_segment_t oldfs = get_fs();
 | 
			
		||||
	set_fs(get_ds());
 | 
			
		||||
	res = devinet_ioctl(&init_net, cmd, (struct ifreq __user *) arg);
 | 
			
		||||
	set_fs(oldfs);
 | 
			
		||||
	return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int __init ic_dev_ioctl(unsigned int cmd, struct ifreq *arg)
 | 
			
		||||
{
 | 
			
		||||
	int res;
 | 
			
		||||
| 
						 | 
				
			
			@ -375,19 +364,19 @@ static int __init ic_setup_if(void)
 | 
			
		|||
	memset(&ir, 0, sizeof(ir));
 | 
			
		||||
	strcpy(ir.ifr_ifrn.ifrn_name, ic_dev->dev->name);
 | 
			
		||||
	set_sockaddr(sin, ic_myaddr, 0);
 | 
			
		||||
	if ((err = ic_devinet_ioctl(SIOCSIFADDR, &ir)) < 0) {
 | 
			
		||||
	if ((err = devinet_ioctl(&init_net, SIOCSIFADDR, &ir)) < 0) {
 | 
			
		||||
		pr_err("IP-Config: Unable to set interface address (%d)\n",
 | 
			
		||||
		       err);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
	set_sockaddr(sin, ic_netmask, 0);
 | 
			
		||||
	if ((err = ic_devinet_ioctl(SIOCSIFNETMASK, &ir)) < 0) {
 | 
			
		||||
	if ((err = devinet_ioctl(&init_net, SIOCSIFNETMASK, &ir)) < 0) {
 | 
			
		||||
		pr_err("IP-Config: Unable to set interface netmask (%d)\n",
 | 
			
		||||
		       err);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
	set_sockaddr(sin, ic_myaddr | ~ic_netmask, 0);
 | 
			
		||||
	if ((err = ic_devinet_ioctl(SIOCSIFBRDADDR, &ir)) < 0) {
 | 
			
		||||
	if ((err = devinet_ioctl(&init_net, SIOCSIFBRDADDR, &ir)) < 0) {
 | 
			
		||||
		pr_err("IP-Config: Unable to set interface broadcast address (%d)\n",
 | 
			
		||||
		       err);
 | 
			
		||||
		return -1;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue