Merge https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next
[sfrench/cifs-2.6.git] / net / ipv6 / ipv6_sockglue.c
index e0dcc7a193df2a9f70350ec448f810c06a9e5330..2d2f4dd9e5dfa8278f5dbad0bfd5a2e16a77406d 100644 (file)
@@ -327,7 +327,7 @@ static int ipv6_set_opt_hdr(struct sock *sk, int optname, sockptr_t optval,
        int err;
 
        /* hop-by-hop / destination options are privileged option */
-       if (optname != IPV6_RTHDR && !ns_capable(net->user_ns, CAP_NET_RAW))
+       if (optname != IPV6_RTHDR && !sockopt_ns_capable(net->user_ns, CAP_NET_RAW))
                return -EPERM;
 
        /* remove any sticky options header with a zero option
@@ -391,8 +391,8 @@ sticky_done:
        return err;
 }
 
-static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
-                  sockptr_t optval, unsigned int optlen)
+int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
+                      sockptr_t optval, unsigned int optlen)
 {
        struct ipv6_pinfo *np = inet6_sk(sk);
        struct net *net = sock_net(sk);
@@ -417,7 +417,7 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
 
        if (needs_rtnl)
                rtnl_lock();
-       lock_sock(sk);
+       sockopt_lock_sock(sk);
 
        switch (optname) {
 
@@ -634,8 +634,8 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
                break;
 
        case IPV6_TRANSPARENT:
-               if (valbool && !ns_capable(net->user_ns, CAP_NET_RAW) &&
-                   !ns_capable(net->user_ns, CAP_NET_ADMIN)) {
+               if (valbool && !sockopt_ns_capable(net->user_ns, CAP_NET_RAW) &&
+                   !sockopt_ns_capable(net->user_ns, CAP_NET_ADMIN)) {
                        retv = -EPERM;
                        break;
                }
@@ -946,7 +946,7 @@ done:
        case IPV6_IPSEC_POLICY:
        case IPV6_XFRM_POLICY:
                retv = -EPERM;
-               if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
+               if (!sockopt_ns_capable(net->user_ns, CAP_NET_ADMIN))
                        break;
                retv = xfrm_user_policy(sk, optname, optval, optlen);
                break;
@@ -994,14 +994,14 @@ done:
                break;
        }
 
-       release_sock(sk);
+       sockopt_release_sock(sk);
        if (needs_rtnl)
                rtnl_unlock();
 
        return retv;
 
 e_inval:
-       release_sock(sk);
+       sockopt_release_sock(sk);
        if (needs_rtnl)
                rtnl_unlock();
        return -EINVAL;
@@ -1030,7 +1030,7 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval,
 EXPORT_SYMBOL(ipv6_setsockopt);
 
 static int ipv6_getsockopt_sticky(struct sock *sk, struct ipv6_txoptions *opt,
-                                 int optname, char __user *optval, int len)
+                                 int optname, sockptr_t optval, int len)
 {
        struct ipv6_opt_hdr *hdr;
 
@@ -1058,56 +1058,53 @@ static int ipv6_getsockopt_sticky(struct sock *sk, struct ipv6_txoptions *opt,
                return 0;
 
        len = min_t(unsigned int, len, ipv6_optlen(hdr));
-       if (copy_to_user(optval, hdr, len))
+       if (copy_to_sockptr(optval, hdr, len))
                return -EFAULT;
        return len;
 }
 
-static int ipv6_get_msfilter(struct sock *sk, void __user *optval,
-               int __user *optlen, int len)
+static int ipv6_get_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 err;
 
        if (len < size0)
                return -EINVAL;
-       if (copy_from_user(&gsf, p, size0))
+       if (copy_from_sockptr(&gsf, optval, size0))
                return -EFAULT;
        if (gsf.gf_group.ss_family != AF_INET6)
                return -EADDRNOTAVAIL;
        num = gsf.gf_numsrc;
-       lock_sock(sk);
-       err = ip6_mc_msfget(sk, &gsf, p->gf_slist_flex);
+       sockopt_lock_sock(sk);
+       err = ip6_mc_msfget(sk, &gsf, optval, size0);
        if (!err) {
                if (num > gsf.gf_numsrc)
                        num = gsf.gf_numsrc;
-               if (put_user(GROUP_FILTER_SIZE(num), optlen) ||
-                   copy_to_user(p, &gsf, size0))
+               len = GROUP_FILTER_SIZE(num);
+               if (copy_to_sockptr(optlen, &len, sizeof(int)) ||
+                   copy_to_sockptr(optval, &gsf, size0))
                        err = -EFAULT;
        }
-       release_sock(sk);
+       sockopt_release_sock(sk);
        return err;
 }
 
-static int compat_ipv6_get_msfilter(struct sock *sk, void __user *optval,
-               int __user *optlen)
+static int compat_ipv6_get_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 len, err;
+       int err;
        int num;
 
-       if (get_user(len, optlen))
-               return -EFAULT;
        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;
        gf.gf_fmode = gf32.gf_fmode;
@@ -1117,23 +1114,25 @@ static int compat_ipv6_get_msfilter(struct sock *sk, void __user *optval,
        if (gf.gf_group.ss_family != AF_INET6)
                return -EADDRNOTAVAIL;
 
-       lock_sock(sk);
-       err = ip6_mc_msfget(sk, &gf, p->gf_slist_flex);
-       release_sock(sk);
+       sockopt_lock_sock(sk);
+       err = ip6_mc_msfget(sk, &gf, optval, size0);
+       sockopt_release_sock(sk);
        if (err)
                return err;
        if (num > gf.gf_numsrc)
                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(gf32.gf_fmode)) ||
+           copy_to_sockptr_offset(optval, offsetof(struct compat_group_filter, gf_numsrc),
+                                  &gf.gf_numsrc, sizeof(gf32.gf_numsrc)))
                return -EFAULT;
        return 0;
 }
 
-static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
-                   char __user *optval, int __user *optlen, unsigned int flags)
+int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
+                      sockptr_t optval, sockptr_t optlen)
 {
        struct ipv6_pinfo *np = inet6_sk(sk);
        int len;
@@ -1142,7 +1141,7 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
        if (ip6_mroute_opt(optname))
                return ip6_mroute_getsockopt(sk, optname, optval, optlen);
 
-       if (get_user(len, optlen))
+       if (copy_from_sockptr(&len, optlen, sizeof(int)))
                return -EFAULT;
        switch (optname) {
        case IPV6_ADDRFORM:
@@ -1156,7 +1155,7 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
                break;
        case MCAST_MSFILTER:
                if (in_compat_syscall())
-                       return compat_ipv6_get_msfilter(sk, optval, optlen);
+                       return compat_ipv6_get_msfilter(sk, optval, optlen, len);
                return ipv6_get_msfilter(sk, optval, optlen, len);
        case IPV6_2292PKTOPTIONS:
        {
@@ -1166,16 +1165,21 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
                if (sk->sk_type != SOCK_STREAM)
                        return -ENOPROTOOPT;
 
-               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 = flags;
-               msg.msg_control_is_user = true;
+               msg.msg_flags = 0;
 
-               lock_sock(sk);
+               sockopt_lock_sock(sk);
                skb = np->pktoptions;
                if (skb)
                        ip6_datagram_recv_ctl(sk, &msg, skb);
-               release_sock(sk);
+               sockopt_release_sock(sk);
                if (!skb) {
                        if (np->rxopt.bits.rxinfo) {
                                struct in6_pktinfo src_info;
@@ -1212,7 +1216,7 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
                        }
                }
                len -= msg.msg_controllen;
-               return put_user(len, optlen);
+               return copy_to_sockptr(optlen, &len, sizeof(int));
        }
        case IPV6_MTU:
        {
@@ -1264,15 +1268,15 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
        {
                struct ipv6_txoptions *opt;
 
-               lock_sock(sk);
+               sockopt_lock_sock(sk);
                opt = rcu_dereference_protected(np->opt,
                                                lockdep_sock_is_held(sk));
                len = ipv6_getsockopt_sticky(sk, opt, optname, optval, len);
-               release_sock(sk);
+               sockopt_release_sock(sk);
                /* check if ipv6_getsockopt_sticky() returns err code */
                if (len < 0)
                        return len;
-               return put_user(len, optlen);
+               return copy_to_sockptr(optlen, &len, sizeof(int));
        }
 
        case IPV6_RECVHOPOPTS:
@@ -1326,9 +1330,9 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
                if (!mtuinfo.ip6m_mtu)
                        return -ENOTCONN;
 
-               if (put_user(len, optlen))
+               if (copy_to_sockptr(optlen, &len, sizeof(int)))
                        return -EFAULT;
-               if (copy_to_user(optval, &mtuinfo, len))
+               if (copy_to_sockptr(optval, &mtuinfo, len))
                        return -EFAULT;
 
                return 0;
@@ -1405,7 +1409,7 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
                if (len < sizeof(freq))
                        return -EINVAL;
 
-               if (copy_from_user(&freq, optval, sizeof(freq)))
+               if (copy_from_sockptr(&freq, optval, sizeof(freq)))
                        return -EFAULT;
 
                if (freq.flr_action != IPV6_FL_A_GET)
@@ -1420,9 +1424,9 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
                if (val < 0)
                        return val;
 
-               if (put_user(len, optlen))
+               if (copy_to_sockptr(optlen, &len, sizeof(int)))
                        return -EFAULT;
-               if (copy_to_user(optval, &freq, len))
+               if (copy_to_sockptr(optval, &freq, len))
                        return -EFAULT;
 
                return 0;
@@ -1474,9 +1478,9 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
                return -ENOPROTOOPT;
        }
        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;
 }
@@ -1492,7 +1496,8 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname,
        if (level != SOL_IPV6)
                return -ENOPROTOOPT;
 
-       err = do_ipv6_getsockopt(sk, level, optname, optval, optlen, 0);
+       err = do_ipv6_getsockopt(sk, level, optname,
+                                USER_SOCKPTR(optval), USER_SOCKPTR(optlen));
 #ifdef CONFIG_NETFILTER
        /* we need to exclude all possible ENOPROTOOPTs except default case */
        if (err == -ENOPROTOOPT && optname != IPV6_2292PKTOPTIONS) {