Merge tag 'net-next-5.15' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev...
[linux-2.6-microblaze.git] / net / socket.c
index 532fff5..7f64a6e 100644 (file)
@@ -212,6 +212,7 @@ static const char * const pf_family_names[] = {
        [PF_QIPCRTR]    = "PF_QIPCRTR",
        [PF_SMC]        = "PF_SMC",
        [PF_XDP]        = "PF_XDP",
+       [PF_MCTP]       = "PF_MCTP",
 };
 
 /*
@@ -1064,9 +1065,13 @@ static ssize_t sock_write_iter(struct kiocb *iocb, struct iov_iter *from)
  */
 
 static DEFINE_MUTEX(br_ioctl_mutex);
-static int (*br_ioctl_hook) (struct net *, unsigned int cmd, void __user *arg);
+static int (*br_ioctl_hook)(struct net *net, struct net_bridge *br,
+                           unsigned int cmd, struct ifreq *ifr,
+                           void __user *uarg);
 
-void brioctl_set(int (*hook) (struct net *, unsigned int, void __user *))
+void brioctl_set(int (*hook)(struct net *net, struct net_bridge *br,
+                            unsigned int cmd, struct ifreq *ifr,
+                            void __user *uarg))
 {
        mutex_lock(&br_ioctl_mutex);
        br_ioctl_hook = hook;
@@ -1074,6 +1079,22 @@ void brioctl_set(int (*hook) (struct net *, unsigned int, void __user *))
 }
 EXPORT_SYMBOL(brioctl_set);
 
+int br_ioctl_call(struct net *net, struct net_bridge *br, unsigned int cmd,
+                 struct ifreq *ifr, void __user *uarg)
+{
+       int err = -ENOPKG;
+
+       if (!br_ioctl_hook)
+               request_module("bridge");
+
+       mutex_lock(&br_ioctl_mutex);
+       if (br_ioctl_hook)
+               err = br_ioctl_hook(net, br, cmd, ifr, uarg);
+       mutex_unlock(&br_ioctl_mutex);
+
+       return err;
+}
+
 static DEFINE_MUTEX(vlan_ioctl_mutex);
 static int (*vlan_ioctl_hook) (struct net *, void __user *arg);
 
@@ -1088,8 +1109,11 @@ EXPORT_SYMBOL(vlan_ioctl_set);
 static long sock_do_ioctl(struct net *net, struct socket *sock,
                          unsigned int cmd, unsigned long arg)
 {
+       struct ifreq ifr;
+       bool need_copyout;
        int err;
        void __user *argp = (void __user *)arg;
+       void __user *data;
 
        err = sock->ops->ioctl(sock, cmd, arg);
 
@@ -1100,25 +1124,16 @@ static long sock_do_ioctl(struct net *net, struct socket *sock,
        if (err != -ENOIOCTLCMD)
                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;
-       } else {
-               struct ifreq ifr;
-               bool need_copyout;
-               if (copy_from_user(&ifr, argp, sizeof(struct ifreq)))
+       if (!is_socket_ioctl_cmd(cmd))
+               return -ENOTTY;
+
+       if (get_user_ifreq(&ifr, &data, argp))
+               return -EFAULT;
+       err = dev_ioctl(net, cmd, &ifr, data, &need_copyout);
+       if (!err && need_copyout)
+               if (put_user_ifreq(&ifr, argp))
                        return -EFAULT;
-               err = dev_ioctl(net, cmd, &ifr, &need_copyout);
-               if (!err && need_copyout)
-                       if (copy_to_user(argp, &ifr, sizeof(struct ifreq)))
-                               return -EFAULT;
-       }
+
        return err;
 }
 
@@ -1140,12 +1155,13 @@ static long sock_ioctl(struct file *file, unsigned cmd, unsigned long arg)
        net = sock_net(sk);
        if (unlikely(cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15))) {
                struct ifreq ifr;
+               void __user *data;
                bool need_copyout;
-               if (copy_from_user(&ifr, argp, sizeof(struct ifreq)))
+               if (get_user_ifreq(&ifr, &data, argp))
                        return -EFAULT;
-               err = dev_ioctl(net, cmd, &ifr, &need_copyout);
+               err = dev_ioctl(net, cmd, &ifr, data, &need_copyout);
                if (!err && need_copyout)
-                       if (copy_to_user(argp, &ifr, sizeof(struct ifreq)))
+                       if (put_user_ifreq(&ifr, argp))
                                return -EFAULT;
        } else
 #ifdef CONFIG_WEXT_CORE
@@ -1170,14 +1186,7 @@ static long sock_ioctl(struct file *file, unsigned cmd, unsigned long arg)
                case SIOCSIFBR:
                case SIOCBRADDBR:
                case SIOCBRDELBR:
-                       err = -ENOPKG;
-                       if (!br_ioctl_hook)
-                               request_module("bridge");
-
-                       mutex_lock(&br_ioctl_mutex);
-                       if (br_ioctl_hook)
-                               err = br_ioctl_hook(net, cmd, argp);
-                       mutex_unlock(&br_ioctl_mutex);
+                       err = br_ioctl_call(net, NULL, cmd, NULL, argp);
                        break;
                case SIOCGIFVLAN:
                case SIOCSIFVLAN:
@@ -1217,6 +1226,11 @@ static long sock_ioctl(struct file *file, unsigned cmd, unsigned long arg)
                                                   cmd == SIOCGSTAMP_NEW,
                                                   false);
                        break;
+
+               case SIOCGIFCONF:
+                       err = dev_ifconf(net, argp);
+                       break;
+
                default:
                        err = sock_do_ioctl(net, sock, cmd, arg);
                        break;
@@ -3129,154 +3143,55 @@ void socket_seq_show(struct seq_file *seq)
 }
 #endif                         /* CONFIG_PROC_FS */
 
-#ifdef CONFIG_COMPAT
-static int compat_dev_ifconf(struct net *net, struct compat_ifconf __user *uifc32)
+/* Handle the fact that while struct ifreq has the same *layout* on
+ * 32/64 for everything but ifreq::ifru_ifmap and ifreq::ifru_data,
+ * which are handled elsewhere, it still has different *size* due to
+ * ifreq::ifru_ifmap (which is 16 bytes on 32 bit, 24 bytes on 64-bit,
+ * resulting in struct ifreq being 32 and 40 bytes respectively).
+ * As a result, if the struct happens to be at the end of a page and
+ * the next page isn't readable/writable, we get a fault. To prevent
+ * that, copy back and forth to the full size.
+ */
+int get_user_ifreq(struct ifreq *ifr, void __user **ifrdata, void __user *arg)
 {
-       struct compat_ifconf ifc32;
-       struct ifconf ifc;
-       int err;
+       if (in_compat_syscall()) {
+               struct compat_ifreq *ifr32 = (struct compat_ifreq *)ifr;
 
-       if (copy_from_user(&ifc32, uifc32, sizeof(struct compat_ifconf)))
-               return -EFAULT;
+               memset(ifr, 0, sizeof(*ifr));
+               if (copy_from_user(ifr32, arg, sizeof(*ifr32)))
+                       return -EFAULT;
 
-       ifc.ifc_len = ifc32.ifc_len;
-       ifc.ifc_req = compat_ptr(ifc32.ifcbuf);
+               if (ifrdata)
+                       *ifrdata = compat_ptr(ifr32->ifr_data);
 
-       rtnl_lock();
-       err = dev_ifconf(net, &ifc, sizeof(struct compat_ifreq));
-       rtnl_unlock();
-       if (err)
-               return err;
+               return 0;
+       }
 
-       ifc32.ifc_len = ifc.ifc_len;
-       if (copy_to_user(uifc32, &ifc32, sizeof(struct compat_ifconf)))
+       if (copy_from_user(ifr, arg, sizeof(*ifr)))
                return -EFAULT;
 
+       if (ifrdata)
+               *ifrdata = ifr->ifr_data;
+
        return 0;
 }
+EXPORT_SYMBOL(get_user_ifreq);
 
-static int ethtool_ioctl(struct net *net, struct compat_ifreq __user *ifr32)
+int put_user_ifreq(struct ifreq *ifr, void __user *arg)
 {
-       struct compat_ethtool_rxnfc __user *compat_rxnfc;
-       bool convert_in = false, convert_out = false;
-       size_t buf_size = 0;
-       struct ethtool_rxnfc __user *rxnfc = NULL;
-       struct ifreq ifr;
-       u32 rule_cnt = 0, actual_rule_cnt;
-       u32 ethcmd;
-       u32 data;
-       int ret;
+       size_t size = sizeof(*ifr);
 
-       if (get_user(data, &ifr32->ifr_ifru.ifru_data))
-               return -EFAULT;
+       if (in_compat_syscall())
+               size = sizeof(struct compat_ifreq);
 
-       compat_rxnfc = compat_ptr(data);
-
-       if (get_user(ethcmd, &compat_rxnfc->cmd))
+       if (copy_to_user(arg, ifr, size))
                return -EFAULT;
 
-       /* Most ethtool structures are defined without padding.
-        * Unfortunately struct ethtool_rxnfc is an exception.
-        */
-       switch (ethcmd) {
-       default:
-               break;
-       case ETHTOOL_GRXCLSRLALL:
-               /* Buffer size is variable */
-               if (get_user(rule_cnt, &compat_rxnfc->rule_cnt))
-                       return -EFAULT;
-               if (rule_cnt > KMALLOC_MAX_SIZE / sizeof(u32))
-                       return -ENOMEM;
-               buf_size += rule_cnt * sizeof(u32);
-               fallthrough;
-       case ETHTOOL_GRXRINGS:
-       case ETHTOOL_GRXCLSRLCNT:
-       case ETHTOOL_GRXCLSRULE:
-       case ETHTOOL_SRXCLSRLINS:
-               convert_out = true;
-               fallthrough;
-       case ETHTOOL_SRXCLSRLDEL:
-               buf_size += sizeof(struct ethtool_rxnfc);
-               convert_in = true;
-               rxnfc = compat_alloc_user_space(buf_size);
-               break;
-       }
-
-       if (copy_from_user(&ifr.ifr_name, &ifr32->ifr_name, IFNAMSIZ))
-               return -EFAULT;
-
-       ifr.ifr_data = convert_in ? rxnfc : (void __user *)compat_rxnfc;
-
-       if (convert_in) {
-               /* We expect there to be holes between fs.m_ext and
-                * fs.ring_cookie and at the end of fs, but nowhere else.
-                */
-               BUILD_BUG_ON(offsetof(struct compat_ethtool_rxnfc, fs.m_ext) +
-                            sizeof(compat_rxnfc->fs.m_ext) !=
-                            offsetof(struct ethtool_rxnfc, fs.m_ext) +
-                            sizeof(rxnfc->fs.m_ext));
-               BUILD_BUG_ON(
-                       offsetof(struct compat_ethtool_rxnfc, fs.location) -
-                       offsetof(struct compat_ethtool_rxnfc, fs.ring_cookie) !=
-                       offsetof(struct ethtool_rxnfc, fs.location) -
-                       offsetof(struct ethtool_rxnfc, fs.ring_cookie));
-
-               if (copy_in_user(rxnfc, compat_rxnfc,
-                                (void __user *)(&rxnfc->fs.m_ext + 1) -
-                                (void __user *)rxnfc) ||
-                   copy_in_user(&rxnfc->fs.ring_cookie,
-                                &compat_rxnfc->fs.ring_cookie,
-                                (void __user *)(&rxnfc->fs.location + 1) -
-                                (void __user *)&rxnfc->fs.ring_cookie))
-                       return -EFAULT;
-               if (ethcmd == ETHTOOL_GRXCLSRLALL) {
-                       if (put_user(rule_cnt, &rxnfc->rule_cnt))
-                               return -EFAULT;
-               } else if (copy_in_user(&rxnfc->rule_cnt,
-                                       &compat_rxnfc->rule_cnt,
-                                       sizeof(rxnfc->rule_cnt)))
-                       return -EFAULT;
-       }
-
-       ret = dev_ioctl(net, SIOCETHTOOL, &ifr, NULL);
-       if (ret)
-               return ret;
-
-       if (convert_out) {
-               if (copy_in_user(compat_rxnfc, rxnfc,
-                                (const void __user *)(&rxnfc->fs.m_ext + 1) -
-                                (const void __user *)rxnfc) ||
-                   copy_in_user(&compat_rxnfc->fs.ring_cookie,
-                                &rxnfc->fs.ring_cookie,
-                                (const void __user *)(&rxnfc->fs.location + 1) -
-                                (const void __user *)&rxnfc->fs.ring_cookie) ||
-                   copy_in_user(&compat_rxnfc->rule_cnt, &rxnfc->rule_cnt,
-                                sizeof(rxnfc->rule_cnt)))
-                       return -EFAULT;
-
-               if (ethcmd == ETHTOOL_GRXCLSRLALL) {
-                       /* As an optimisation, we only copy the actual
-                        * number of rules that the underlying
-                        * function returned.  Since Mallory might
-                        * change the rule count in user memory, we
-                        * check that it is less than the rule count
-                        * originally given (as the user buffer size),
-                        * which has been range-checked.
-                        */
-                       if (get_user(actual_rule_cnt, &rxnfc->rule_cnt))
-                               return -EFAULT;
-                       if (actual_rule_cnt < rule_cnt)
-                               rule_cnt = actual_rule_cnt;
-                       if (copy_in_user(&compat_rxnfc->rule_locs[0],
-                                        &rxnfc->rule_locs[0],
-                                        rule_cnt * sizeof(u32)))
-                               return -EFAULT;
-               }
-       }
-
        return 0;
 }
+EXPORT_SYMBOL(put_user_ifreq);
 
+#ifdef CONFIG_COMPAT
 static int compat_siocwandev(struct net *net, struct compat_ifreq __user *uifr32)
 {
        compat_uptr_t uptr32;
@@ -3284,7 +3199,7 @@ static int compat_siocwandev(struct net *net, struct compat_ifreq __user *uifr32
        void __user *saved;
        int err;
 
-       if (copy_from_user(&ifr, uifr32, sizeof(struct compat_ifreq)))
+       if (get_user_ifreq(&ifr, NULL, uifr32))
                return -EFAULT;
 
        if (get_user(uptr32, &uifr32->ifr_settings.ifs_ifsu))
@@ -3293,10 +3208,10 @@ static int compat_siocwandev(struct net *net, struct compat_ifreq __user *uifr32
        saved = ifr.ifr_settings.ifs_ifsu.raw_hdlc;
        ifr.ifr_settings.ifs_ifsu.raw_hdlc = compat_ptr(uptr32);
 
-       err = dev_ioctl(net, SIOCWANDEV, &ifr, NULL);
+       err = dev_ioctl(net, SIOCWANDEV, &ifr, NULL, NULL);
        if (!err) {
                ifr.ifr_settings.ifs_ifsu.raw_hdlc = saved;
-               if (copy_to_user(uifr32, &ifr, sizeof(struct compat_ifreq)))
+               if (put_user_ifreq(&ifr, uifr32))
                        err = -EFAULT;
        }
        return err;
@@ -3307,97 +3222,15 @@ static int compat_ifr_data_ioctl(struct net *net, unsigned int cmd,
                                 struct compat_ifreq __user *u_ifreq32)
 {
        struct ifreq ifreq;
-       u32 data32;
+       void __user *data;
 
-       if (copy_from_user(ifreq.ifr_name, u_ifreq32->ifr_name, IFNAMSIZ))
-               return -EFAULT;
-       if (get_user(data32, &u_ifreq32->ifr_data))
+       if (!is_socket_ioctl_cmd(cmd))
+               return -ENOTTY;
+       if (get_user_ifreq(&ifreq, &data, u_ifreq32))
                return -EFAULT;
-       ifreq.ifr_data = compat_ptr(data32);
+       ifreq.ifr_data = data;
 
-       return dev_ioctl(net, cmd, &ifreq, NULL);
-}
-
-static int compat_ifreq_ioctl(struct net *net, struct socket *sock,
-                             unsigned int cmd,
-                             struct compat_ifreq __user *uifr32)
-{
-       struct ifreq __user *uifr;
-       int err;
-
-       /* Handle the fact that while struct ifreq has the same *layout* on
-        * 32/64 for everything but ifreq::ifru_ifmap and ifreq::ifru_data,
-        * which are handled elsewhere, it still has different *size* due to
-        * ifreq::ifru_ifmap (which is 16 bytes on 32 bit, 24 bytes on 64-bit,
-        * resulting in struct ifreq being 32 and 40 bytes respectively).
-        * As a result, if the struct happens to be at the end of a page and
-        * the next page isn't readable/writable, we get a fault. To prevent
-        * that, copy back and forth to the full size.
-        */
-
-       uifr = compat_alloc_user_space(sizeof(*uifr));
-       if (copy_in_user(uifr, uifr32, sizeof(*uifr32)))
-               return -EFAULT;
-
-       err = sock_do_ioctl(net, sock, cmd, (unsigned long)uifr);
-
-       if (!err) {
-               switch (cmd) {
-               case SIOCGIFFLAGS:
-               case SIOCGIFMETRIC:
-               case SIOCGIFMTU:
-               case SIOCGIFMEM:
-               case SIOCGIFHWADDR:
-               case SIOCGIFINDEX:
-               case SIOCGIFADDR:
-               case SIOCGIFBRDADDR:
-               case SIOCGIFDSTADDR:
-               case SIOCGIFNETMASK:
-               case SIOCGIFPFLAGS:
-               case SIOCGIFTXQLEN:
-               case SIOCGMIIPHY:
-               case SIOCGMIIREG:
-               case SIOCGIFNAME:
-                       if (copy_in_user(uifr32, uifr, sizeof(*uifr32)))
-                               err = -EFAULT;
-                       break;
-               }
-       }
-       return err;
-}
-
-static int compat_sioc_ifmap(struct net *net, unsigned int cmd,
-                       struct compat_ifreq __user *uifr32)
-{
-       struct ifreq ifr;
-       struct compat_ifmap __user *uifmap32;
-       int err;
-
-       uifmap32 = &uifr32->ifr_ifru.ifru_map;
-       err = copy_from_user(&ifr, uifr32, sizeof(ifr.ifr_name));
-       err |= get_user(ifr.ifr_map.mem_start, &uifmap32->mem_start);
-       err |= get_user(ifr.ifr_map.mem_end, &uifmap32->mem_end);
-       err |= get_user(ifr.ifr_map.base_addr, &uifmap32->base_addr);
-       err |= get_user(ifr.ifr_map.irq, &uifmap32->irq);
-       err |= get_user(ifr.ifr_map.dma, &uifmap32->dma);
-       err |= get_user(ifr.ifr_map.port, &uifmap32->port);
-       if (err)
-               return -EFAULT;
-
-       err = dev_ioctl(net, cmd, &ifr, NULL);
-
-       if (cmd == SIOCGIFMAP && !err) {
-               err = copy_to_user(uifr32, &ifr, sizeof(ifr.ifr_name));
-               err |= put_user(ifr.ifr_map.mem_start, &uifmap32->mem_start);
-               err |= put_user(ifr.ifr_map.mem_end, &uifmap32->mem_end);
-               err |= put_user(ifr.ifr_map.base_addr, &uifmap32->base_addr);
-               err |= put_user(ifr.ifr_map.irq, &uifmap32->irq);
-               err |= put_user(ifr.ifr_map.dma, &uifmap32->dma);
-               err |= put_user(ifr.ifr_map.port, &uifmap32->port);
-               if (err)
-                       err = -EFAULT;
-       }
-       return err;
+       return dev_ioctl(net, cmd, &ifreq, data, NULL);
 }
 
 /* Since old style bridge ioctl's endup using SIOCDEVPRIVATE
@@ -3423,21 +3256,14 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock,
        struct net *net = sock_net(sk);
 
        if (cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15))
-               return compat_ifr_data_ioctl(net, cmd, argp);
+               return sock_ioctl(file, cmd, (unsigned long)argp);
 
        switch (cmd) {
        case SIOCSIFBR:
        case SIOCGIFBR:
                return old_bridge_ioctl(argp);
-       case SIOCGIFCONF:
-               return compat_dev_ifconf(net, argp);
-       case SIOCETHTOOL:
-               return ethtool_ioctl(net, argp);
        case SIOCWANDEV:
                return compat_siocwandev(net, argp);
-       case SIOCGIFMAP:
-       case SIOCSIFMAP:
-               return compat_sioc_ifmap(net, cmd, argp);
        case SIOCGSTAMP_OLD:
        case SIOCGSTAMPNS_OLD:
                if (!sock->ops->gettstamp)
@@ -3445,6 +3271,7 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock,
                return sock->ops->gettstamp(sock, argp, cmd == SIOCGSTAMP_OLD,
                                            !COMPAT_USE_64BIT_TIME);
 
+       case SIOCETHTOOL:
        case SIOCBONDSLAVEINFOQUERY:
        case SIOCBONDINFOQUERY:
        case SIOCSHWTSTAMP:
@@ -3462,10 +3289,13 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock,
        case SIOCGSKNS:
        case SIOCGSTAMP_NEW:
        case SIOCGSTAMPNS_NEW:
+       case SIOCGIFCONF:
                return sock_ioctl(file, cmd, arg);
 
        case SIOCGIFFLAGS:
        case SIOCSIFFLAGS:
+       case SIOCGIFMAP:
+       case SIOCSIFMAP:
        case SIOCGIFMETRIC:
        case SIOCSIFMETRIC:
        case SIOCGIFMTU:
@@ -3502,8 +3332,6 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock,
        case SIOCBONDRELEASE:
        case SIOCBONDSETHWADDR:
        case SIOCBONDCHANGEACTIVE:
-               return compat_ifreq_ioctl(net, sock, cmd, argp);
-
        case SIOCSARP:
        case SIOCGARP:
        case SIOCDARP: