aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2017-06-26 13:19:16 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2018-01-24 19:13:45 -0500
commit36fd633ec98acd2028585c22128fcaa3da6d5770 (patch)
tree1fa77e8bf1662dea936b52107a0d39485110edb1
parentbe1b6e8b5470e8311bfa1a3dfd7bd59e85a99759 (diff)
net: separate SIOCGIFCONF handling from dev_ioctl()
Only two of dev_ioctl() callers may pass SIOCGIFCONF to it. Separating that codepath from the rest of dev_ioctl() allows both to simplify dev_ioctl() itself (all other cases work with struct ifreq *) *and* seriously simplify the compat side of that beast: all it takes is passing to inet_gifconf() an extra argument - the size of individual records (sizeof(struct ifreq) or sizeof(struct compat_ifreq)). With dev_ifconf() called directly from sock_do_ioctl()/compat_dev_ifconf() that's easy to arrange. As the result, compat side of SIOCGIFCONF doesn't need any allocations, copy_in_user() back and forth, etc. Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r--include/linux/netdevice.h4
-rw-r--r--net/core/dev_ioctl.c29
-rw-r--r--net/ipv4/devinet.c16
-rw-r--r--net/socket.c79
4 files changed, 42 insertions, 86 deletions
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 581495f4e487..df5565d0369c 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -2761,7 +2761,8 @@ static inline bool dev_validate_header(const struct net_device *dev,
2761 return false; 2761 return false;
2762} 2762}
2763 2763
2764typedef int gifconf_func_t(struct net_device * dev, char __user * bufptr, int len); 2764typedef int gifconf_func_t(struct net_device * dev, char __user * bufptr,
2765 int len, int size);
2765int register_gifconf(unsigned int family, gifconf_func_t *gifconf); 2766int register_gifconf(unsigned int family, gifconf_func_t *gifconf);
2766static inline int unregister_gifconf(unsigned int family) 2767static inline int unregister_gifconf(unsigned int family)
2767{ 2768{
@@ -3315,6 +3316,7 @@ void netdev_rx_handler_unregister(struct net_device *dev);
3315 3316
3316bool dev_valid_name(const char *name); 3317bool dev_valid_name(const char *name);
3317int dev_ioctl(struct net *net, unsigned int cmd, void __user *); 3318int dev_ioctl(struct net *net, unsigned int cmd, void __user *);
3319int dev_ifconf(struct net *net, struct ifconf *, int);
3318int dev_ethtool(struct net *net, struct ifreq *); 3320int dev_ethtool(struct net *net, struct ifreq *);
3319unsigned int dev_get_flags(const struct net_device *); 3321unsigned int dev_get_flags(const struct net_device *);
3320int __dev_change_flags(struct net_device *, unsigned int flags); 3322int __dev_change_flags(struct net_device *, unsigned int flags);
diff --git a/net/core/dev_ioctl.c b/net/core/dev_ioctl.c
index 7e690d0ccd05..5cdec23dd28e 100644
--- a/net/core/dev_ioctl.c
+++ b/net/core/dev_ioctl.c
@@ -66,9 +66,8 @@ EXPORT_SYMBOL(register_gifconf);
66 * Thus we will need a 'compatibility mode'. 66 * Thus we will need a 'compatibility mode'.
67 */ 67 */
68 68
69static int dev_ifconf(struct net *net, char __user *arg) 69int dev_ifconf(struct net *net, struct ifconf *ifc, int size)
70{ 70{
71 struct ifconf ifc;
72 struct net_device *dev; 71 struct net_device *dev;
73 char __user *pos; 72 char __user *pos;
74 int len; 73 int len;
@@ -79,11 +78,8 @@ static int dev_ifconf(struct net *net, char __user *arg)
79 * Fetch the caller's info block. 78 * Fetch the caller's info block.
80 */ 79 */
81 80
82 if (copy_from_user(&ifc, arg, sizeof(struct ifconf))) 81 pos = ifc->ifc_buf;
83 return -EFAULT; 82 len = ifc->ifc_len;
84
85 pos = ifc.ifc_buf;
86 len = ifc.ifc_len;
87 83
88 /* 84 /*
89 * Loop over the interfaces, and write an info block for each. 85 * Loop over the interfaces, and write an info block for each.
@@ -95,10 +91,10 @@ static int dev_ifconf(struct net *net, char __user *arg)
95 if (gifconf_list[i]) { 91 if (gifconf_list[i]) {
96 int done; 92 int done;
97 if (!pos) 93 if (!pos)
98 done = gifconf_list[i](dev, NULL, 0); 94 done = gifconf_list[i](dev, NULL, 0, size);
99 else 95 else
100 done = gifconf_list[i](dev, pos + total, 96 done = gifconf_list[i](dev, pos + total,
101 len - total); 97 len - total, size);
102 if (done < 0) 98 if (done < 0)
103 return -EFAULT; 99 return -EFAULT;
104 total += done; 100 total += done;
@@ -109,12 +105,12 @@ static int dev_ifconf(struct net *net, char __user *arg)
109 /* 105 /*
110 * All done. Write the updated control block back to the caller. 106 * All done. Write the updated control block back to the caller.
111 */ 107 */
112 ifc.ifc_len = total; 108 ifc->ifc_len = total;
113 109
114 /* 110 /*
115 * Both BSD and Solaris return 0 here, so we do too. 111 * Both BSD and Solaris return 0 here, so we do too.
116 */ 112 */
117 return copy_to_user(arg, &ifc, sizeof(struct ifconf)) ? -EFAULT : 0; 113 return 0;
118} 114}
119 115
120/* 116/*
@@ -412,17 +408,6 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)
412 int ret; 408 int ret;
413 char *colon; 409 char *colon;
414 410
415 /* One special case: SIOCGIFCONF takes ifconf argument
416 and requires shared lock, because it sleeps writing
417 to user space.
418 */
419
420 if (cmd == SIOCGIFCONF) {
421 rtnl_lock();
422 ret = dev_ifconf(net, (char __user *) arg);
423 rtnl_unlock();
424 return ret;
425 }
426 if (cmd == SIOCGIFNAME) 411 if (cmd == SIOCGIFNAME)
427 return dev_ifname(net, (struct ifreq __user *)arg); 412 return dev_ifname(net, (struct ifreq __user *)arg);
428 413
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
index 7a93359fbc72..1771549d2438 100644
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -1188,22 +1188,25 @@ rarok:
1188 goto out; 1188 goto out;
1189} 1189}
1190 1190
1191static int inet_gifconf(struct net_device *dev, char __user *buf, int len) 1191static int inet_gifconf(struct net_device *dev, char __user *buf, int len, int size)
1192{ 1192{
1193 struct in_device *in_dev = __in_dev_get_rtnl(dev); 1193 struct in_device *in_dev = __in_dev_get_rtnl(dev);
1194 struct in_ifaddr *ifa; 1194 struct in_ifaddr *ifa;
1195 struct ifreq ifr; 1195 struct ifreq ifr;
1196 int done = 0; 1196 int done = 0;
1197 1197
1198 if (WARN_ON(size > sizeof(struct ifreq)))
1199 goto out;
1200
1198 if (!in_dev) 1201 if (!in_dev)
1199 goto out; 1202 goto out;
1200 1203
1201 for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { 1204 for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
1202 if (!buf) { 1205 if (!buf) {
1203 done += sizeof(ifr); 1206 done += size;
1204 continue; 1207 continue;
1205 } 1208 }
1206 if (len < (int) sizeof(ifr)) 1209 if (len < size)
1207 break; 1210 break;
1208 memset(&ifr, 0, sizeof(struct ifreq)); 1211 memset(&ifr, 0, sizeof(struct ifreq));
1209 strcpy(ifr.ifr_name, ifa->ifa_label); 1212 strcpy(ifr.ifr_name, ifa->ifa_label);
@@ -1212,13 +1215,12 @@ static int inet_gifconf(struct net_device *dev, char __user *buf, int len)
1212 (*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr = 1215 (*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr =
1213 ifa->ifa_local; 1216 ifa->ifa_local;
1214 1217
1215 if (copy_to_user(buf, &ifr, sizeof(struct ifreq))) { 1218 if (copy_to_user(buf + done, &ifr, size)) {
1216 done = -EFAULT; 1219 done = -EFAULT;
1217 break; 1220 break;
1218 } 1221 }
1219 buf += sizeof(struct ifreq); 1222 len -= size;
1220 len -= sizeof(struct ifreq); 1223 done += size;
1221 done += sizeof(struct ifreq);
1222 } 1224 }
1223out: 1225out:
1224 return done; 1226 return done;
diff --git a/net/socket.c b/net/socket.c
index 1536515b6437..96e5b23a2a2e 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -961,10 +961,22 @@ static long sock_do_ioctl(struct net *net, struct socket *sock,
961 * If this ioctl is unknown try to hand it down 961 * If this ioctl is unknown try to hand it down
962 * to the NIC driver. 962 * to the NIC driver.
963 */ 963 */
964 if (err == -ENOIOCTLCMD) 964 if (err != -ENOIOCTLCMD)
965 err = dev_ioctl(net, cmd, argp); 965 return err;
966 966
967 return err; 967 if (cmd == SIOCGIFCONF) {
968 struct ifconf ifc;
969 if (copy_from_user(&ifc, argp, sizeof(struct ifconf)))
970 return -EFAULT;
971 rtnl_lock();
972 err = dev_ifconf(net, &ifc, sizeof(struct ifreq));
973 rtnl_unlock();
974 if (!err && copy_to_user(argp, &ifc, sizeof(struct ifconf)))
975 err = -EFAULT;
976 return err;
977 }
978
979 return dev_ioctl(net, cmd, argp);
968} 980}
969 981
970/* 982/*
@@ -2673,70 +2685,25 @@ static int dev_ifname32(struct net *net, struct compat_ifreq __user *uifr32)
2673 return 0; 2685 return 0;
2674} 2686}
2675 2687
2676static int dev_ifconf(struct net *net, struct compat_ifconf __user *uifc32) 2688static int compat_dev_ifconf(struct net *net, struct compat_ifconf __user *uifc32)
2677{ 2689{
2678 struct compat_ifconf ifc32; 2690 struct compat_ifconf ifc32;
2679 struct ifconf ifc; 2691 struct ifconf ifc;
2680 struct ifconf __user *uifc;
2681 struct compat_ifreq __user *ifr32;
2682 struct ifreq __user *ifr;
2683 unsigned int i, j;
2684 int err; 2692 int err;
2685 2693
2686 if (copy_from_user(&ifc32, uifc32, sizeof(struct compat_ifconf))) 2694 if (copy_from_user(&ifc32, uifc32, sizeof(struct compat_ifconf)))
2687 return -EFAULT; 2695 return -EFAULT;
2688 2696
2689 memset(&ifc, 0, sizeof(ifc)); 2697 ifc.ifc_len = ifc32.ifc_len;
2690 if (ifc32.ifcbuf == 0) { 2698 ifc.ifc_req = compat_ptr(ifc32.ifcbuf);
2691 ifc32.ifc_len = 0;
2692 ifc.ifc_len = 0;
2693 ifc.ifc_req = NULL;
2694 uifc = compat_alloc_user_space(sizeof(struct ifconf));
2695 } else {
2696 size_t len = ((ifc32.ifc_len / sizeof(struct compat_ifreq)) + 1) *
2697 sizeof(struct ifreq);
2698 uifc = compat_alloc_user_space(sizeof(struct ifconf) + len);
2699 ifc.ifc_len = len;
2700 ifr = ifc.ifc_req = (void __user *)(uifc + 1);
2701 ifr32 = compat_ptr(ifc32.ifcbuf);
2702 for (i = 0; i < ifc32.ifc_len; i += sizeof(struct compat_ifreq)) {
2703 if (copy_in_user(ifr, ifr32, sizeof(struct compat_ifreq)))
2704 return -EFAULT;
2705 ifr++;
2706 ifr32++;
2707 }
2708 }
2709 if (copy_to_user(uifc, &ifc, sizeof(struct ifconf)))
2710 return -EFAULT;
2711 2699
2712 err = dev_ioctl(net, SIOCGIFCONF, uifc); 2700 rtnl_lock();
2701 err = dev_ifconf(net, &ifc, sizeof(struct compat_ifreq));
2702 rtnl_unlock();
2713 if (err) 2703 if (err)
2714 return err; 2704 return err;
2715 2705
2716 if (copy_from_user(&ifc, uifc, sizeof(struct ifconf))) 2706 ifc32.ifc_len = ifc.ifc_len;
2717 return -EFAULT;
2718
2719 ifr = ifc.ifc_req;
2720 ifr32 = compat_ptr(ifc32.ifcbuf);
2721 for (i = 0, j = 0;
2722 i + sizeof(struct compat_ifreq) <= ifc32.ifc_len && j < ifc.ifc_len;
2723 i += sizeof(struct compat_ifreq), j += sizeof(struct ifreq)) {
2724 if (copy_in_user(ifr32, ifr, sizeof(struct compat_ifreq)))
2725 return -EFAULT;
2726 ifr32++;
2727 ifr++;
2728 }
2729
2730 if (ifc32.ifcbuf == 0) {
2731 /* Translate from 64-bit structure multiple to
2732 * a 32-bit one.
2733 */
2734 i = ifc.ifc_len;
2735 i = ((i / sizeof(struct ifreq)) * sizeof(struct compat_ifreq));
2736 ifc32.ifc_len = i;
2737 } else {
2738 ifc32.ifc_len = i;
2739 }
2740 if (copy_to_user(uifc32, &ifc32, sizeof(struct compat_ifconf))) 2707 if (copy_to_user(uifc32, &ifc32, sizeof(struct compat_ifconf)))
2741 return -EFAULT; 2708 return -EFAULT;
2742 2709
@@ -3133,7 +3100,7 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock,
3133 case SIOCGIFNAME: 3100 case SIOCGIFNAME:
3134 return dev_ifname32(net, argp); 3101 return dev_ifname32(net, argp);
3135 case SIOCGIFCONF: 3102 case SIOCGIFCONF:
3136 return dev_ifconf(net, argp); 3103 return compat_dev_ifconf(net, argp);
3137 case SIOCETHTOOL: 3104 case SIOCETHTOOL:
3138 return ethtool_ioctl(net, argp); 3105 return ethtool_ioctl(net, argp);
3139 case SIOCWANDEV: 3106 case SIOCWANDEV: