summaryrefslogtreecommitdiffstats
path: root/net/core/dev_ioctl.c
diff options
context:
space:
mode:
authorArnd Bergmann <arnd@arndb.de>2021-07-22 16:29:02 +0200
committerDavid S. Miller <davem@davemloft.net>2021-07-23 15:20:25 +0200
commit876f0bf9d0d5189dca9341c8e8e8686b09db8398 (patch)
tree399c3cf96ae9b0dd4506cb1f3ca03a5458ce8a9a /net/core/dev_ioctl.c
parentnet: socket: remove register_gifconf (diff)
downloadlinux-876f0bf9d0d5189dca9341c8e8e8686b09db8398.tar.xz
linux-876f0bf9d0d5189dca9341c8e8e8686b09db8398.zip
net: socket: simplify dev_ifconf handling
The dev_ifconf() calling conventions make compat handling more complicated than necessary, simplify this by moving the in_compat_syscall() check into the function. Signed-off-by: Arnd Bergmann <arnd@arndb.de> Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/core/dev_ioctl.c')
-rw-r--r--net/core/dev_ioctl.c55
1 files changed, 29 insertions, 26 deletions
diff --git a/net/core/dev_ioctl.c b/net/core/dev_ioctl.c
index c22c3dc15ce9..950e2fe5d56a 100644
--- a/net/core/dev_ioctl.c
+++ b/net/core/dev_ioctl.c
@@ -31,48 +31,51 @@ static int dev_ifname(struct net *net, struct ifreq *ifr)
* size eventually, and there is nothing I can do about it.
* Thus we will need a 'compatibility mode'.
*/
-
-int dev_ifconf(struct net *net, struct ifconf *ifc, int size)
+int dev_ifconf(struct net *net, struct ifconf __user *uifc)
{
struct net_device *dev;
- char __user *pos;
- int len;
- int total;
- int i;
+ void __user *pos;
+ size_t size;
+ int len, total = 0, done;
- /*
- * Fetch the caller's info block.
- */
+ /* both the ifconf and the ifreq structures are slightly different */
+ if (in_compat_syscall()) {
+ struct compat_ifconf ifc32;
+
+ if (copy_from_user(&ifc32, uifc, sizeof(struct compat_ifconf)))
+ return -EFAULT;
- pos = ifc->ifc_buf;
- len = ifc->ifc_len;
+ pos = compat_ptr(ifc32.ifcbuf);
+ len = ifc32.ifc_len;
+ size = sizeof(struct compat_ifreq);
+ } else {
+ struct ifconf ifc;
- /*
- * Loop over the interfaces, and write an info block for each.
- */
+ if (copy_from_user(&ifc, uifc, sizeof(struct ifconf)))
+ return -EFAULT;
- total = 0;
+ pos = ifc.ifc_buf;
+ len = ifc.ifc_len;
+ size = sizeof(struct ifreq);
+ }
+
+ /* Loop over the interfaces, and write an info block for each. */
+ rtnl_lock();
for_each_netdev(net, dev) {
- int done;
if (!pos)
done = inet_gifconf(dev, NULL, 0, size);
else
done = inet_gifconf(dev, pos + total,
len - total, size);
- if (done < 0)
+ if (done < 0) {
+ rtnl_unlock();
return -EFAULT;
+ }
total += done;
}
+ rtnl_unlock();
- /*
- * All done. Write the updated control block back to the caller.
- */
- ifc->ifc_len = total;
-
- /*
- * Both BSD and Solaris return 0 here, so we do too.
- */
- return 0;
+ return put_user(total, &uifc->ifc_len);
}
static int dev_getifmap(struct net_device *dev, struct ifreq *ifr)