devinet_ioctl(): take copyin/copyout to caller
authorAl Viro <viro@zeniv.linux.org.uk>
Sat, 1 Jul 2017 11:53:12 +0000 (07:53 -0400)
committerAl Viro <viro@zeniv.linux.org.uk>
Thu, 25 Jan 2018 00:13:45 +0000 (19:13 -0500)
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
include/linux/inetdevice.h
net/ipv4/af_inet.c
net/ipv4/devinet.c
net/ipv4/ipconfig.c

index 1ac5bf95bfdd7739388724612157367d4c6102b7..e16fe7d44a7124d32e83a40f1c1579b06b8ff356 100644 (file)
@@ -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);
index 54cccdd8b1e3f2bfad0b1b81a2478044196703e1..1c2bfee2e24938e44671e1595d68c0f4e0c438d0 100644 (file)
@@ -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)
index 1771549d24385f3372852b6732a081c4328ceb0e..e056c0067f2cff22a89dd8f68cd4f0913529ad55 100644 (file)
@@ -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 @@ done:
        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)
index e9e488e7290037013ab152e5d5bd16a3d1be64b5..6895fff609b1005ea90b8f117591bc8d95adbc39 100644 (file)
@@ -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;