bpf: net: Change do_ip_getsockopt() to take the sockptr_t argument
authorMartin KaFai Lau <martin.lau@kernel.org>
Fri, 2 Sep 2022 00:28:28 +0000 (17:28 -0700)
committerAlexei Starovoitov <ast@kernel.org>
Sat, 3 Sep 2022 03:34:31 +0000 (20:34 -0700)
Similar to the earlier patch that changes sk_getsockopt() to
take the sockptr_t argument.  This patch also changes
do_ip_getsockopt() to take the sockptr_t argument such that
a latter patch can make bpf_getsockopt(SOL_IP) to reuse
do_ip_getsockopt().

Note on the change in ip_mc_gsfget().  This function is to
return an array of sockaddr_storage in optval.  This function
is shared between ip_get_mcast_msfilter() and
compat_ip_get_mcast_msfilter().  However, the sockaddr_storage
is stored at different offset of the optval because of
the difference between group_filter and compat_group_filter.
Thus, a new 'ss_offset' argument is added to ip_mc_gsfget().

Signed-off-by: Martin KaFai Lau <martin.lau@kernel.org>
Link: https://lore.kernel.org/r/20220902002828.2890585-1-kafai@fb.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
include/linux/igmp.h
include/linux/mroute.h
net/ipv4/igmp.c
net/ipv4/ip_sockglue.c
net/ipv4/ipmr.c

index 93c262e..7889014 100644 (file)
@@ -118,9 +118,9 @@ extern int ip_mc_source(int add, int omode, struct sock *sk,
                struct ip_mreq_source *mreqs, int ifindex);
 extern int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf,int ifindex);
 extern int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf,
-               struct ip_msfilter __user *optval, int __user *optlen);
+                       sockptr_t optval, sockptr_t optlen);
 extern int ip_mc_gsfget(struct sock *sk, struct group_filter *gsf,
-                       struct sockaddr_storage __user *p);
+                       sockptr_t optval, size_t offset);
 extern int ip_mc_sf_allow(struct sock *sk, __be32 local, __be32 rmt,
                          int dif, int sdif);
 extern void ip_mc_init_dev(struct in_device *);
index 6cbbfe9..80b8400 100644 (file)
@@ -17,7 +17,7 @@ static inline int ip_mroute_opt(int opt)
 }
 
 int ip_mroute_setsockopt(struct sock *, int, sockptr_t, unsigned int);
-int ip_mroute_getsockopt(struct sock *, int, char __user *, int __user *);
+int ip_mroute_getsockopt(struct sock *, int, sockptr_t, sockptr_t);
 int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg);
 int ipmr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg);
 int ip_mr_init(void);
@@ -29,8 +29,8 @@ static inline int ip_mroute_setsockopt(struct sock *sock, int optname,
        return -ENOPROTOOPT;
 }
 
-static inline int ip_mroute_getsockopt(struct sock *sock, int optname,
-                                      char __user *optval, int __user *optlen)
+static inline int ip_mroute_getsockopt(struct sock *sk, int optname,
+                                      sockptr_t optval, sockptr_t optlen)
 {
        return -ENOPROTOOPT;
 }
index e3ab0cb..df0660d 100644 (file)
@@ -2529,11 +2529,10 @@ done:
                err = ip_mc_leave_group(sk, &imr);
        return err;
 }
-
 int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf,
-       struct ip_msfilter __user *optval, int __user *optlen)
+                sockptr_t optval, sockptr_t optlen)
 {
-       int err, len, count, copycount;
+       int err, len, count, copycount, msf_size;
        struct ip_mreqn imr;
        __be32 addr = msf->imsf_multiaddr;
        struct ip_mc_socklist *pmc;
@@ -2575,12 +2574,15 @@ int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf,
        copycount = count < msf->imsf_numsrc ? count : msf->imsf_numsrc;
        len = flex_array_size(psl, sl_addr, copycount);
        msf->imsf_numsrc = count;
-       if (put_user(IP_MSFILTER_SIZE(copycount), optlen) ||
-           copy_to_user(optval, msf, IP_MSFILTER_SIZE(0))) {
+       msf_size = IP_MSFILTER_SIZE(copycount);
+       if (copy_to_sockptr(optlen, &msf_size, sizeof(int)) ||
+           copy_to_sockptr(optval, msf, IP_MSFILTER_SIZE(0))) {
                return -EFAULT;
        }
        if (len &&
-           copy_to_user(&optval->imsf_slist_flex[0], psl->sl_addr, len))
+           copy_to_sockptr_offset(optval,
+                                  offsetof(struct ip_msfilter, imsf_slist_flex),
+                                  psl->sl_addr, len))
                return -EFAULT;
        return 0;
 done:
@@ -2588,7 +2590,7 @@ done:
 }
 
 int ip_mc_gsfget(struct sock *sk, struct group_filter *gsf,
-       struct sockaddr_storage __user *p)
+                sockptr_t optval, size_t ss_offset)
 {
        int i, count, copycount;
        struct sockaddr_in *psin;
@@ -2618,15 +2620,17 @@ int ip_mc_gsfget(struct sock *sk, struct group_filter *gsf,
        count = psl ? psl->sl_count : 0;
        copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc;
        gsf->gf_numsrc = count;
-       for (i = 0; i < copycount; i++, p++) {
+       for (i = 0; i < copycount; i++) {
                struct sockaddr_storage ss;
 
                psin = (struct sockaddr_in *)&ss;
                memset(&ss, 0, sizeof(ss));
                psin->sin_family = AF_INET;
                psin->sin_addr.s_addr = psl->sl_addr[i];
-               if (copy_to_user(p, &ss, sizeof(ss)))
+               if (copy_to_sockptr_offset(optval, ss_offset,
+                                          &ss, sizeof(ss)))
                        return -EFAULT;
+               ss_offset += sizeof(ss);
        }
        return 0;
 }
index 751fa69..5310def 100644 (file)
@@ -1462,37 +1462,37 @@ static bool getsockopt_needs_rtnl(int optname)
        return false;
 }
 
-static int ip_get_mcast_msfilter(struct sock *sk, void __user *optval,
-               int __user *optlen, int len)
+static int ip_get_mcast_msfilter(struct sock *sk, sockptr_t optval,
+                                sockptr_t optlen, int len)
 {
        const int size0 = offsetof(struct group_filter, gf_slist_flex);
-       struct group_filter __user *p = optval;
        struct group_filter gsf;
-       int num;
+       int num, gsf_size;
        int err;
 
        if (len < size0)
                return -EINVAL;
-       if (copy_from_user(&gsf, p, size0))
+       if (copy_from_sockptr(&gsf, optval, size0))
                return -EFAULT;
 
        num = gsf.gf_numsrc;
-       err = ip_mc_gsfget(sk, &gsf, p->gf_slist_flex);
+       err = ip_mc_gsfget(sk, &gsf, optval,
+                          offsetof(struct group_filter, gf_slist_flex));
        if (err)
                return err;
        if (gsf.gf_numsrc < num)
                num = gsf.gf_numsrc;
-       if (put_user(GROUP_FILTER_SIZE(num), optlen) ||
-           copy_to_user(p, &gsf, size0))
+       gsf_size = GROUP_FILTER_SIZE(num);
+       if (copy_to_sockptr(optlen, &gsf_size, sizeof(int)) ||
+           copy_to_sockptr(optval, &gsf, size0))
                return -EFAULT;
        return 0;
 }
 
-static int compat_ip_get_mcast_msfilter(struct sock *sk, void __user *optval,
-               int __user *optlen, int len)
+static int compat_ip_get_mcast_msfilter(struct sock *sk, sockptr_t optval,
+                                       sockptr_t optlen, int len)
 {
        const int size0 = offsetof(struct compat_group_filter, gf_slist_flex);
-       struct compat_group_filter __user *p = optval;
        struct compat_group_filter gf32;
        struct group_filter gf;
        int num;
@@ -1500,7 +1500,7 @@ static int compat_ip_get_mcast_msfilter(struct sock *sk, void __user *optval,
 
        if (len < size0)
                return -EINVAL;
-       if (copy_from_user(&gf32, p, size0))
+       if (copy_from_sockptr(&gf32, optval, size0))
                return -EFAULT;
 
        gf.gf_interface = gf32.gf_interface;
@@ -1508,21 +1508,24 @@ static int compat_ip_get_mcast_msfilter(struct sock *sk, void __user *optval,
        num = gf.gf_numsrc = gf32.gf_numsrc;
        gf.gf_group = gf32.gf_group;
 
-       err = ip_mc_gsfget(sk, &gf, p->gf_slist_flex);
+       err = ip_mc_gsfget(sk, &gf, optval,
+                          offsetof(struct compat_group_filter, gf_slist_flex));
        if (err)
                return err;
        if (gf.gf_numsrc < num)
                num = gf.gf_numsrc;
        len = GROUP_FILTER_SIZE(num) - (sizeof(gf) - sizeof(gf32));
-       if (put_user(len, optlen) ||
-           put_user(gf.gf_fmode, &p->gf_fmode) ||
-           put_user(gf.gf_numsrc, &p->gf_numsrc))
+       if (copy_to_sockptr(optlen, &len, sizeof(int)) ||
+           copy_to_sockptr_offset(optval, offsetof(struct compat_group_filter, gf_fmode),
+                                  &gf.gf_fmode, sizeof(gf.gf_fmode)) ||
+           copy_to_sockptr_offset(optval, offsetof(struct compat_group_filter, gf_numsrc),
+                                  &gf.gf_numsrc, sizeof(gf.gf_numsrc)))
                return -EFAULT;
        return 0;
 }
 
 static int do_ip_getsockopt(struct sock *sk, int level, int optname,
-                           char __user *optval, int __user *optlen)
+                           sockptr_t optval, sockptr_t optlen)
 {
        struct inet_sock *inet = inet_sk(sk);
        bool needs_rtnl = getsockopt_needs_rtnl(optname);
@@ -1535,7 +1538,7 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname,
        if (ip_mroute_opt(optname))
                return ip_mroute_getsockopt(sk, optname, optval, optlen);
 
-       if (get_user(len, optlen))
+       if (copy_from_sockptr(&len, optlen, sizeof(int)))
                return -EFAULT;
        if (len < 0)
                return -EINVAL;
@@ -1560,15 +1563,17 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname,
                               inet_opt->opt.optlen);
                release_sock(sk);
 
-               if (opt->optlen == 0)
-                       return put_user(0, optlen);
+               if (opt->optlen == 0) {
+                       len = 0;
+                       return copy_to_sockptr(optlen, &len, sizeof(int));
+               }
 
                ip_options_undo(opt);
 
                len = min_t(unsigned int, len, opt->optlen);
-               if (put_user(len, optlen))
+               if (copy_to_sockptr(optlen, &len, sizeof(int)))
                        return -EFAULT;
-               if (copy_to_user(optval, opt->__data, len))
+               if (copy_to_sockptr(optval, opt->__data, len))
                        return -EFAULT;
                return 0;
        }
@@ -1659,9 +1664,9 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname,
                addr.s_addr = inet->mc_addr;
                release_sock(sk);
 
-               if (put_user(len, optlen))
+               if (copy_to_sockptr(optlen, &len, sizeof(int)))
                        return -EFAULT;
-               if (copy_to_user(optval, &addr, len))
+               if (copy_to_sockptr(optval, &addr, len))
                        return -EFAULT;
                return 0;
        }
@@ -1673,12 +1678,11 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname,
                        err = -EINVAL;
                        goto out;
                }
-               if (copy_from_user(&msf, optval, IP_MSFILTER_SIZE(0))) {
+               if (copy_from_sockptr(&msf, optval, IP_MSFILTER_SIZE(0))) {
                        err = -EFAULT;
                        goto out;
                }
-               err = ip_mc_msfget(sk, &msf,
-                                  (struct ip_msfilter __user *)optval, optlen);
+               err = ip_mc_msfget(sk, &msf, optval, optlen);
                goto out;
        }
        case MCAST_MSFILTER:
@@ -1700,8 +1704,13 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname,
                if (sk->sk_type != SOCK_STREAM)
                        return -ENOPROTOOPT;
 
-               msg.msg_control_is_user = true;
-               msg.msg_control_user = optval;
+               if (optval.is_kernel) {
+                       msg.msg_control_is_user = false;
+                       msg.msg_control = optval.kernel;
+               } else {
+                       msg.msg_control_is_user = true;
+                       msg.msg_control_user = optval.user;
+               }
                msg.msg_controllen = len;
                msg.msg_flags = in_compat_syscall() ? MSG_CMSG_COMPAT : 0;
 
@@ -1722,7 +1731,7 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname,
                        put_cmsg(&msg, SOL_IP, IP_TOS, sizeof(tos), &tos);
                }
                len -= msg.msg_controllen;
-               return put_user(len, optlen);
+               return copy_to_sockptr(optlen, &len, sizeof(int));
        }
        case IP_FREEBIND:
                val = inet->freebind;
@@ -1742,15 +1751,15 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname,
        if (len < sizeof(int) && len > 0 && val >= 0 && val <= 255) {
                unsigned char ucval = (unsigned char)val;
                len = 1;
-               if (put_user(len, optlen))
+               if (copy_to_sockptr(optlen, &len, sizeof(int)))
                        return -EFAULT;
-               if (copy_to_user(optval, &ucval, 1))
+               if (copy_to_sockptr(optval, &ucval, 1))
                        return -EFAULT;
        } else {
                len = min_t(unsigned int, sizeof(int), len);
-               if (put_user(len, optlen))
+               if (copy_to_sockptr(optlen, &len, sizeof(int)))
                        return -EFAULT;
-               if (copy_to_user(optval, &val, len))
+               if (copy_to_sockptr(optval, &val, len))
                        return -EFAULT;
        }
        return 0;
@@ -1767,7 +1776,8 @@ int ip_getsockopt(struct sock *sk, int level,
 {
        int err;
 
-       err = do_ip_getsockopt(sk, level, optname, optval, optlen);
+       err = do_ip_getsockopt(sk, level, optname,
+                              USER_SOCKPTR(optval), USER_SOCKPTR(optlen));
 
 #if IS_ENABLED(CONFIG_BPFILTER_UMH)
        if (optname >= BPFILTER_IPT_SO_GET_INFO &&
index 73651d1..95eefbe 100644 (file)
@@ -1546,7 +1546,8 @@ out:
 }
 
 /* Getsock opt support for the multicast routing system. */
-int ip_mroute_getsockopt(struct sock *sk, int optname, char __user *optval, int __user *optlen)
+int ip_mroute_getsockopt(struct sock *sk, int optname, sockptr_t optval,
+                        sockptr_t optlen)
 {
        int olr;
        int val;
@@ -1577,14 +1578,14 @@ int ip_mroute_getsockopt(struct sock *sk, int optname, char __user *optval, int
                return -ENOPROTOOPT;
        }
 
-       if (get_user(olr, optlen))
+       if (copy_from_sockptr(&olr, optlen, sizeof(int)))
                return -EFAULT;
        olr = min_t(unsigned int, olr, sizeof(int));
        if (olr < 0)
                return -EINVAL;
-       if (put_user(olr, optlen))
+       if (copy_to_sockptr(optlen, &olr, sizeof(int)))
                return -EFAULT;
-       if (copy_to_user(optval, &val, olr))
+       if (copy_to_sockptr(optval, &val, olr))
                return -EFAULT;
        return 0;
 }