]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
bpf: net: Change sk_getsockopt() to take the sockptr_t argument
authorMartin KaFai Lau <martin.lau@kernel.org>
Fri, 2 Sep 2022 00:28:02 +0000 (17:28 -0700)
committerSasha Levin <sashal@kernel.org>
Fri, 15 Mar 2024 14:48:20 +0000 (10:48 -0400)
[ Upstream commit 4ff09db1b79b98b4a2a7511571c640b76cab3beb ]

This patch changes sk_getsockopt() to take the sockptr_t argument
such that it can be used by bpf_getsockopt(SOL_SOCKET) in a
latter patch.

security_socket_getpeersec_stream() is not changed.  It stays
with the __user ptr (optval.user and optlen.user) to avoid changes
to other security hooks.  bpf_getsockopt(SOL_SOCKET) also does not
support SO_PEERSEC.

Signed-off-by: Martin KaFai Lau <martin.lau@kernel.org>
Link: https://lore.kernel.org/r/20220902002802.2888419-1-kafai@fb.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Stable-dep-of: 5a287d3d2b9d ("lsm: fix default return value of the socket_getpeersec_*() hooks")
Signed-off-by: Sasha Levin <sashal@kernel.org>
include/linux/filter.h
include/linux/sockptr.h
net/core/filter.c
net/core/sock.c

index bc6ce4b202a809820f5446c6b92bd6a4042b2fbb..cd56e53bd42e2e73c3a69d7dcc2c47b28d63aae1 100644 (file)
@@ -892,8 +892,7 @@ int sk_reuseport_attach_filter(struct sock_fprog *fprog, struct sock *sk);
 int sk_reuseport_attach_bpf(u32 ufd, struct sock *sk);
 void sk_reuseport_prog_free(struct bpf_prog *prog);
 int sk_detach_filter(struct sock *sk);
-int sk_get_filter(struct sock *sk, struct sock_filter __user *filter,
-                 unsigned int len);
+int sk_get_filter(struct sock *sk, sockptr_t optval, unsigned int len);
 
 bool sk_filter_charge(struct sock *sk, struct sk_filter *fp);
 void sk_filter_uncharge(struct sock *sk, struct sk_filter *fp);
index ea193414298b7f9fe24906a4c5ae57cde6e6af19..38862819e77a1dcae119027ea3885a8aea352ce8 100644 (file)
@@ -64,6 +64,11 @@ static inline int copy_to_sockptr_offset(sockptr_t dst, size_t offset,
        return 0;
 }
 
+static inline int copy_to_sockptr(sockptr_t dst, const void *src, size_t size)
+{
+       return copy_to_sockptr_offset(dst, 0, src, size);
+}
+
 static inline void *memdup_sockptr(sockptr_t src, size_t len)
 {
        void *p = kmalloc_track_caller(len, GFP_USER | __GFP_NOWARN);
index 6cfc8fb0562a276dd6b20b5c738abc5331c3614a..49e4d1535cc8236a174398bf89c52e28f4c74405 100644 (file)
@@ -9903,8 +9903,7 @@ int sk_detach_filter(struct sock *sk)
 }
 EXPORT_SYMBOL_GPL(sk_detach_filter);
 
-int sk_get_filter(struct sock *sk, struct sock_filter __user *ubuf,
-                 unsigned int len)
+int sk_get_filter(struct sock *sk, sockptr_t optval, unsigned int len)
 {
        struct sock_fprog_kern *fprog;
        struct sk_filter *filter;
@@ -9935,7 +9934,7 @@ int sk_get_filter(struct sock *sk, struct sock_filter __user *ubuf,
                goto out;
 
        ret = -EFAULT;
-       if (copy_to_user(ubuf, fprog->filter, bpf_classic_proglen(fprog)))
+       if (copy_to_sockptr(optval, fprog->filter, bpf_classic_proglen(fprog)))
                goto out;
 
        /* Instead of bytes, the API requests to return the number
index 95559d088a169df9cf9adfc0ab73665db9643826..42da46965b16f6294a55a0766a30ba1f9d32eda8 100644 (file)
@@ -644,8 +644,8 @@ out:
        return ret;
 }
 
-static int sock_getbindtodevice(struct sock *sk, char __user *optval,
-                               int __user *optlen, int len)
+static int sock_getbindtodevice(struct sock *sk, sockptr_t optval,
+                               sockptr_t optlen, int len)
 {
        int ret = -ENOPROTOOPT;
 #ifdef CONFIG_NETDEVICES
@@ -668,12 +668,12 @@ static int sock_getbindtodevice(struct sock *sk, char __user *optval,
        len = strlen(devname) + 1;
 
        ret = -EFAULT;
-       if (copy_to_user(optval, devname, len))
+       if (copy_to_sockptr(optval, devname, len))
                goto out;
 
 zero:
        ret = -EFAULT;
-       if (put_user(len, optlen))
+       if (copy_to_sockptr(optlen, &len, sizeof(int)))
                goto out;
 
        ret = 0;
@@ -1281,20 +1281,23 @@ static void cred_to_ucred(struct pid *pid, const struct cred *cred,
        }
 }
 
-static int groups_to_user(gid_t __user *dst, const struct group_info *src)
+static int groups_to_user(sockptr_t dst, const struct group_info *src)
 {
        struct user_namespace *user_ns = current_user_ns();
        int i;
 
-       for (i = 0; i < src->ngroups; i++)
-               if (put_user(from_kgid_munged(user_ns, src->gid[i]), dst + i))
+       for (i = 0; i < src->ngroups; i++) {
+               gid_t gid = from_kgid_munged(user_ns, src->gid[i]);
+
+               if (copy_to_sockptr_offset(dst, i * sizeof(gid), &gid, sizeof(gid)))
                        return -EFAULT;
+       }
 
        return 0;
 }
 
 static int sk_getsockopt(struct sock *sk, int level, int optname,
-                        char __user *optval, int __user *optlen)
+                        sockptr_t optval, sockptr_t optlen)
 {
        struct socket *sock = sk->sk_socket;
 
@@ -1312,7 +1315,7 @@ static int sk_getsockopt(struct sock *sk, int level, int optname,
        int lv = sizeof(int);
        int len;
 
-       if (get_user(len, optlen))
+       if (copy_from_sockptr(&len, optlen, sizeof(int)))
                return -EFAULT;
        if (len < 0)
                return -EINVAL;
@@ -1445,7 +1448,7 @@ static int sk_getsockopt(struct sock *sk, int level, int optname,
                cred_to_ucred(sk->sk_peer_pid, sk->sk_peer_cred, &peercred);
                spin_unlock(&sk->sk_peer_lock);
 
-               if (copy_to_user(optval, &peercred, len))
+               if (copy_to_sockptr(optval, &peercred, len))
                        return -EFAULT;
                goto lenout;
        }
@@ -1463,11 +1466,11 @@ static int sk_getsockopt(struct sock *sk, int level, int optname,
                if (len < n * sizeof(gid_t)) {
                        len = n * sizeof(gid_t);
                        put_cred(cred);
-                       return put_user(len, optlen) ? -EFAULT : -ERANGE;
+                       return copy_to_sockptr(optlen, &len, sizeof(int)) ? -EFAULT : -ERANGE;
                }
                len = n * sizeof(gid_t);
 
-               ret = groups_to_user((gid_t __user *)optval, cred->group_info);
+               ret = groups_to_user(optval, cred->group_info);
                put_cred(cred);
                if (ret)
                        return ret;
@@ -1483,7 +1486,7 @@ static int sk_getsockopt(struct sock *sk, int level, int optname,
                        return -ENOTCONN;
                if (lv < len)
                        return -EINVAL;
-               if (copy_to_user(optval, address, len))
+               if (copy_to_sockptr(optval, address, len))
                        return -EFAULT;
                goto lenout;
        }
@@ -1500,7 +1503,7 @@ static int sk_getsockopt(struct sock *sk, int level, int optname,
                break;
 
        case SO_PEERSEC:
-               return security_socket_getpeersec_stream(sock, optval, optlen, len);
+               return security_socket_getpeersec_stream(sock, optval.user, optlen.user, len);
 
        case SO_MARK:
                v.val = sk->sk_mark;
@@ -1528,7 +1531,7 @@ static int sk_getsockopt(struct sock *sk, int level, int optname,
                return sock_getbindtodevice(sk, optval, optlen, len);
 
        case SO_GET_FILTER:
-               len = sk_get_filter(sk, (struct sock_filter __user *)optval, len);
+               len = sk_get_filter(sk, optval, len);
                if (len < 0)
                        return len;
 
@@ -1575,7 +1578,7 @@ static int sk_getsockopt(struct sock *sk, int level, int optname,
                sk_get_meminfo(sk, meminfo);
 
                len = min_t(unsigned int, len, sizeof(meminfo));
-               if (copy_to_user(optval, &meminfo, len))
+               if (copy_to_sockptr(optval, &meminfo, len))
                        return -EFAULT;
 
                goto lenout;
@@ -1625,10 +1628,10 @@ static int sk_getsockopt(struct sock *sk, int level, int optname,
 
        if (len > lv)
                len = lv;
-       if (copy_to_user(optval, &v, len))
+       if (copy_to_sockptr(optval, &v, len))
                return -EFAULT;
 lenout:
-       if (put_user(len, optlen))
+       if (copy_to_sockptr(optlen, &len, sizeof(int)))
                return -EFAULT;
        return 0;
 }
@@ -1636,7 +1639,9 @@ lenout:
 int sock_getsockopt(struct socket *sock, int level, int optname,
                    char __user *optval, int __user *optlen)
 {
-       return sk_getsockopt(sock->sk, level, optname, optval, optlen);
+       return sk_getsockopt(sock->sk, level, optname,
+                            USER_SOCKPTR(optval),
+                            USER_SOCKPTR(optlen));
 }
 
 /*