Received: by 10.223.148.5 with SMTP id 5csp7954797wrq; Thu, 18 Jan 2018 11:40:39 -0800 (PST) X-Google-Smtp-Source: ACJfBosmFlu2RjsD8r4uNo5HhusBGqNw//Th+aMcuhuRJLSyQOBfc2OX8mukGOOzwt+4EYKQIePK X-Received: by 10.98.213.130 with SMTP id d124mr36627356pfg.112.1516304439851; Thu, 18 Jan 2018 11:40:39 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1516304439; cv=none; d=google.com; s=arc-20160816; b=LJcrUknolPeVsNQb5iDk4JN6OIPN4Fxd5k3Xy0CHOsc2DLUr6YiYYHAwaixYOIwTw5 y0teOnKmIeXza4xYAsMziQYXntWtKuNFRyRvPE2O8J79s4Du14U3b5F8jnBaNn4GFYV3 hUyPQYG2o9h1YdCMTX29cVEehMsJzrrd35xELnmZtIUB8gZ/YkgIyLQm1tuqRbHt9nn3 GLEjOmLeVM6iog4L1vL+us4gwJTjbgUX9YoNa/oiBgsQlCKKgcCMDxJWVa+5ECZFtGcq VYFpX6Bmy10z5vokSmUxBbkDcqVK4OCu9pFmlb6+9eiG0nYBIC4QMIX/7i0gvGFOxm4t pX1w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:arc-authentication-results; bh=NrJIRBvrBniMeeNxoNqKnNrxMRHf4ACs9GBQJ8EO+9Q=; b=SF/CwtYlZmMc9Sj5Dfa6u02ZnGLOF8vUZzURCRtI15wvBEQzYptOEnP/RSFDYieVOx ptjhyOYI3589JXqNTLEWfaQpqOHpsmwWNNrR0ybFR/K/EaOkKShf4o8aG5489aXzIzs8 gI+oyWmIwawDXJuJ+Wwn+Q5dYbLEGo94a+XrlB2Qks/cWd+cBl3PgtZ7A+ADJCcaVLEe AD2fk72EmME9+KILbi/4UiTEC+JKt2AhF2JRoG7XQFswvv+UwJfej6R4+o9inWOvyved khFDTV0sUoczFHVeP310PHqC36ixSUrnHzQ3TtsHDsIvMrUDN76wQS2ziB9Z16ZRul55 jtUg== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id b9si7455741pff.120.2018.01.18.11.40.25; Thu, 18 Jan 2018 11:40:39 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755995AbeARTig (ORCPT + 99 others); Thu, 18 Jan 2018 14:38:36 -0500 Received: from zeniv.linux.org.uk ([195.92.253.2]:55660 "EHLO ZenIV.linux.org.uk" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755956AbeARTic (ORCPT ); Thu, 18 Jan 2018 14:38:32 -0500 Received: from viro by ZenIV.linux.org.uk with local (Exim 4.87 #1 (Red Hat Linux)) id 1ecG0t-0005Dx-Cn; Thu, 18 Jan 2018 19:38:15 +0000 From: Al Viro To: netdev@vger.kernel.org Cc: "David S. Miller" , Linus Torvalds , linux-kernel@vger.kernel.org, Christoph Hellwig , Al Viro Subject: [PATCH 01/10] net: separate SIOCGIFCONF handling from dev_ioctl() Date: Thu, 18 Jan 2018 19:37:46 +0000 Message-Id: <20180118193755.19997-1-viro@ZenIV.linux.org.uk> X-Mailer: git-send-email 2.9.5 In-Reply-To: <20180118193156.GC13338@ZenIV.linux.org.uk> References: <20180118193156.GC13338@ZenIV.linux.org.uk> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Al Viro 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. Signed-off-by: Al Viro --- include/linux/netdevice.h | 4 ++- net/core/dev_ioctl.c | 29 +++++------------ net/ipv4/devinet.c | 16 +++++----- net/socket.c | 79 ++++++++++++++--------------------------------- 4 files changed, 42 insertions(+), 86 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index ed0799a12bf2..221487f5b214 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2759,7 +2759,8 @@ static inline bool dev_validate_header(const struct net_device *dev, return false; } -typedef int gifconf_func_t(struct net_device * dev, char __user * bufptr, int len); +typedef int gifconf_func_t(struct net_device * dev, char __user * bufptr, + int len, int size); int register_gifconf(unsigned int family, gifconf_func_t *gifconf); static inline int unregister_gifconf(unsigned int family) { @@ -3313,6 +3314,7 @@ void netdev_rx_handler_unregister(struct net_device *dev); bool dev_valid_name(const char *name); int dev_ioctl(struct net *net, unsigned int cmd, void __user *); +int dev_ifconf(struct net *net, struct ifconf *, int); int dev_ethtool(struct net *net, struct ifreq *); unsigned int dev_get_flags(const struct net_device *); int __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); * Thus we will need a 'compatibility mode'. */ -static int dev_ifconf(struct net *net, char __user *arg) +int dev_ifconf(struct net *net, struct ifconf *ifc, int size) { - struct ifconf ifc; struct net_device *dev; char __user *pos; int len; @@ -79,11 +78,8 @@ static int dev_ifconf(struct net *net, char __user *arg) * Fetch the caller's info block. */ - if (copy_from_user(&ifc, arg, sizeof(struct ifconf))) - return -EFAULT; - - pos = ifc.ifc_buf; - len = ifc.ifc_len; + pos = ifc->ifc_buf; + len = ifc->ifc_len; /* * 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) if (gifconf_list[i]) { int done; if (!pos) - done = gifconf_list[i](dev, NULL, 0); + done = gifconf_list[i](dev, NULL, 0, size); else done = gifconf_list[i](dev, pos + total, - len - total); + len - total, size); if (done < 0) return -EFAULT; total += done; @@ -109,12 +105,12 @@ static int dev_ifconf(struct net *net, char __user *arg) /* * All done. Write the updated control block back to the caller. */ - ifc.ifc_len = total; + ifc->ifc_len = total; /* * Both BSD and Solaris return 0 here, so we do too. */ - return copy_to_user(arg, &ifc, sizeof(struct ifconf)) ? -EFAULT : 0; + return 0; } /* @@ -412,17 +408,6 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg) int ret; char *colon; - /* One special case: SIOCGIFCONF takes ifconf argument - and requires shared lock, because it sleeps writing - to user space. - */ - - if (cmd == SIOCGIFCONF) { - rtnl_lock(); - ret = dev_ifconf(net, (char __user *) arg); - rtnl_unlock(); - return ret; - } if (cmd == SIOCGIFNAME) return dev_ifname(net, (struct ifreq __user *)arg); 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 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg) goto out; } -static int inet_gifconf(struct net_device *dev, char __user *buf, int len) +static int inet_gifconf(struct net_device *dev, char __user *buf, int len, int size) { struct in_device *in_dev = __in_dev_get_rtnl(dev); struct in_ifaddr *ifa; struct ifreq ifr; int done = 0; + if (WARN_ON(size > sizeof(struct ifreq))) + goto out; + if (!in_dev) goto out; for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { if (!buf) { - done += sizeof(ifr); + done += size; continue; } - if (len < (int) sizeof(ifr)) + if (len < size) break; memset(&ifr, 0, sizeof(struct ifreq)); strcpy(ifr.ifr_name, ifa->ifa_label); @@ -1212,13 +1215,12 @@ static int inet_gifconf(struct net_device *dev, char __user *buf, int len) (*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr = ifa->ifa_local; - if (copy_to_user(buf, &ifr, sizeof(struct ifreq))) { + if (copy_to_user(buf + done, &ifr, size)) { done = -EFAULT; break; } - buf += sizeof(struct ifreq); - len -= sizeof(struct ifreq); - done += sizeof(struct ifreq); + len -= size; + done += size; } out: return done; diff --git a/net/socket.c b/net/socket.c index fbfae1ed3ff5..112216c537e7 100644 --- a/net/socket.c +++ b/net/socket.c @@ -961,10 +961,22 @@ static long sock_do_ioctl(struct net *net, struct socket *sock, * If this ioctl is unknown try to hand it down * to the NIC driver. */ - if (err == -ENOIOCTLCMD) - err = dev_ioctl(net, cmd, argp); + if (err != -ENOIOCTLCMD) + return err; - return err; + if (cmd == SIOCGIFCONF) { + struct ifconf ifc; + if (copy_from_user(&ifc, argp, sizeof(struct ifconf))) + return -EFAULT; + rtnl_lock(); + err = dev_ifconf(net, &ifc, sizeof(struct ifreq)); + rtnl_unlock(); + if (!err && copy_to_user(argp, &ifc, sizeof(struct ifconf))) + err = -EFAULT; + return err; + } + + return dev_ioctl(net, cmd, argp); } /* @@ -2682,70 +2694,25 @@ static int dev_ifname32(struct net *net, struct compat_ifreq __user *uifr32) return 0; } -static int dev_ifconf(struct net *net, struct compat_ifconf __user *uifc32) +static int compat_dev_ifconf(struct net *net, struct compat_ifconf __user *uifc32) { struct compat_ifconf ifc32; struct ifconf ifc; - struct ifconf __user *uifc; - struct compat_ifreq __user *ifr32; - struct ifreq __user *ifr; - unsigned int i, j; int err; if (copy_from_user(&ifc32, uifc32, sizeof(struct compat_ifconf))) return -EFAULT; - memset(&ifc, 0, sizeof(ifc)); - if (ifc32.ifcbuf == 0) { - ifc32.ifc_len = 0; - ifc.ifc_len = 0; - ifc.ifc_req = NULL; - uifc = compat_alloc_user_space(sizeof(struct ifconf)); - } else { - size_t len = ((ifc32.ifc_len / sizeof(struct compat_ifreq)) + 1) * - sizeof(struct ifreq); - uifc = compat_alloc_user_space(sizeof(struct ifconf) + len); - ifc.ifc_len = len; - ifr = ifc.ifc_req = (void __user *)(uifc + 1); - ifr32 = compat_ptr(ifc32.ifcbuf); - for (i = 0; i < ifc32.ifc_len; i += sizeof(struct compat_ifreq)) { - if (copy_in_user(ifr, ifr32, sizeof(struct compat_ifreq))) - return -EFAULT; - ifr++; - ifr32++; - } - } - if (copy_to_user(uifc, &ifc, sizeof(struct ifconf))) - return -EFAULT; + ifc.ifc_len = ifc32.ifc_len; + ifc.ifc_req = compat_ptr(ifc32.ifcbuf); - err = dev_ioctl(net, SIOCGIFCONF, uifc); + rtnl_lock(); + err = dev_ifconf(net, &ifc, sizeof(struct compat_ifreq)); + rtnl_unlock(); if (err) return err; - if (copy_from_user(&ifc, uifc, sizeof(struct ifconf))) - return -EFAULT; - - ifr = ifc.ifc_req; - ifr32 = compat_ptr(ifc32.ifcbuf); - for (i = 0, j = 0; - i + sizeof(struct compat_ifreq) <= ifc32.ifc_len && j < ifc.ifc_len; - i += sizeof(struct compat_ifreq), j += sizeof(struct ifreq)) { - if (copy_in_user(ifr32, ifr, sizeof(struct compat_ifreq))) - return -EFAULT; - ifr32++; - ifr++; - } - - if (ifc32.ifcbuf == 0) { - /* Translate from 64-bit structure multiple to - * a 32-bit one. - */ - i = ifc.ifc_len; - i = ((i / sizeof(struct ifreq)) * sizeof(struct compat_ifreq)); - ifc32.ifc_len = i; - } else { - ifc32.ifc_len = i; - } + ifc32.ifc_len = ifc.ifc_len; if (copy_to_user(uifc32, &ifc32, sizeof(struct compat_ifconf))) return -EFAULT; @@ -3142,7 +3109,7 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock, case SIOCGIFNAME: return dev_ifname32(net, argp); case SIOCGIFCONF: - return dev_ifconf(net, argp); + return compat_dev_ifconf(net, argp); case SIOCETHTOOL: return ethtool_ioctl(net, argp); case SIOCWANDEV: -- 2.11.0