Commit 4d295e54 authored by Christoph Hellwig's avatar Christoph Hellwig Committed by David S. Miller

net: simplify cBPF setsockopt compat handling

Add a helper that copies either a native or compat bpf_fprog from
userspace after verifying the length, and remove the compat setsockopt
handlers that now aren't required.
Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent d8a9b38f
...@@ -502,13 +502,11 @@ static inline bool insn_is_zext(const struct bpf_insn *insn) ...@@ -502,13 +502,11 @@ static inline bool insn_is_zext(const struct bpf_insn *insn)
offsetof(TYPE, MEMBER); \ offsetof(TYPE, MEMBER); \
}) })
#ifdef CONFIG_COMPAT
/* A struct sock_filter is architecture independent. */ /* A struct sock_filter is architecture independent. */
struct compat_sock_fprog { struct compat_sock_fprog {
u16 len; u16 len;
compat_uptr_t filter; /* struct sock_filter * */ compat_uptr_t filter; /* struct sock_filter * */
}; };
#endif
struct sock_fprog_kern { struct sock_fprog_kern {
u16 len; u16 len;
...@@ -1278,4 +1276,6 @@ struct bpf_sockopt_kern { ...@@ -1278,4 +1276,6 @@ struct bpf_sockopt_kern {
s32 retval; s32 retval;
}; };
int copy_bpf_fprog_from_user(struct sock_fprog *dst, void __user *src, int len);
#endif /* __LINUX_FILTER_H__ */ #endif /* __LINUX_FILTER_H__ */
...@@ -61,7 +61,6 @@ int __get_compat_msghdr(struct msghdr *kmsg, struct compat_msghdr __user *umsg, ...@@ -61,7 +61,6 @@ int __get_compat_msghdr(struct msghdr *kmsg, struct compat_msghdr __user *umsg,
compat_size_t *len); compat_size_t *len);
int get_compat_msghdr(struct msghdr *, struct compat_msghdr __user *, int get_compat_msghdr(struct msghdr *, struct compat_msghdr __user *,
struct sockaddr __user **, struct iovec **); struct sockaddr __user **, struct iovec **);
struct sock_fprog __user *get_compat_bpf_fprog(char __user *optval);
int put_cmsg_compat(struct msghdr*, int, int, int, void *); int put_cmsg_compat(struct msghdr*, int, int, int, void *);
int cmsghdr_from_user_compat_to_kern(struct msghdr *, struct sock *, int cmsghdr_from_user_compat_to_kern(struct msghdr *, struct sock *,
......
...@@ -335,49 +335,6 @@ void scm_detach_fds_compat(struct msghdr *kmsg, struct scm_cookie *scm) ...@@ -335,49 +335,6 @@ void scm_detach_fds_compat(struct msghdr *kmsg, struct scm_cookie *scm)
__scm_destroy(scm); __scm_destroy(scm);
} }
/* allocate a 64-bit sock_fprog on the user stack for duration of syscall. */
struct sock_fprog __user *get_compat_bpf_fprog(char __user *optval)
{
struct compat_sock_fprog __user *fprog32 = (struct compat_sock_fprog __user *)optval;
struct sock_fprog __user *kfprog = compat_alloc_user_space(sizeof(struct sock_fprog));
struct compat_sock_fprog f32;
struct sock_fprog f;
if (copy_from_user(&f32, fprog32, sizeof(*fprog32)))
return NULL;
memset(&f, 0, sizeof(f));
f.len = f32.len;
f.filter = compat_ptr(f32.filter);
if (copy_to_user(kfprog, &f, sizeof(struct sock_fprog)))
return NULL;
return kfprog;
}
EXPORT_SYMBOL_GPL(get_compat_bpf_fprog);
static int do_set_attach_filter(struct socket *sock, int level, int optname,
char __user *optval, unsigned int optlen)
{
struct sock_fprog __user *kfprog;
kfprog = get_compat_bpf_fprog(optval);
if (!kfprog)
return -EFAULT;
return sock_setsockopt(sock, level, optname, (char __user *)kfprog,
sizeof(struct sock_fprog));
}
static int compat_sock_setsockopt(struct socket *sock, int level, int optname,
char __user *optval, unsigned int optlen)
{
if (optname == SO_ATTACH_FILTER ||
optname == SO_ATTACH_REUSEPORT_CBPF)
return do_set_attach_filter(sock, level, optname,
optval, optlen);
return sock_setsockopt(sock, level, optname, optval, optlen);
}
static int __compat_sys_setsockopt(int fd, int level, int optname, static int __compat_sys_setsockopt(int fd, int level, int optname,
char __user *optval, unsigned int optlen) char __user *optval, unsigned int optlen)
{ {
...@@ -396,7 +353,7 @@ static int __compat_sys_setsockopt(int fd, int level, int optname, ...@@ -396,7 +353,7 @@ static int __compat_sys_setsockopt(int fd, int level, int optname,
} }
if (level == SOL_SOCKET) if (level == SOL_SOCKET)
err = compat_sock_setsockopt(sock, level, err = sock_setsockopt(sock, level,
optname, optval, optlen); optname, optval, optlen);
else if (sock->ops->compat_setsockopt) else if (sock->ops->compat_setsockopt)
err = sock->ops->compat_setsockopt(sock, level, err = sock->ops->compat_setsockopt(sock, level,
......
...@@ -77,6 +77,29 @@ ...@@ -77,6 +77,29 @@
#include <net/transp_v6.h> #include <net/transp_v6.h>
#include <linux/btf_ids.h> #include <linux/btf_ids.h>
int copy_bpf_fprog_from_user(struct sock_fprog *dst, void __user *src, int len)
{
if (in_compat_syscall()) {
struct compat_sock_fprog f32;
if (len != sizeof(f32))
return -EINVAL;
if (copy_from_user(&f32, src, sizeof(f32)))
return -EFAULT;
memset(dst, 0, sizeof(*dst));
dst->len = f32.len;
dst->filter = compat_ptr(f32.filter);
} else {
if (len != sizeof(*dst))
return -EINVAL;
if (copy_from_user(dst, src, sizeof(*dst)))
return -EFAULT;
}
return 0;
}
EXPORT_SYMBOL_GPL(copy_bpf_fprog_from_user);
/** /**
* sk_filter_trim_cap - run a packet through a socket filter * sk_filter_trim_cap - run a packet through a socket filter
* @sk: sock associated with &sk_buff * @sk: sock associated with &sk_buff
......
...@@ -1059,19 +1059,14 @@ int sock_setsockopt(struct socket *sock, int level, int optname, ...@@ -1059,19 +1059,14 @@ int sock_setsockopt(struct socket *sock, int level, int optname,
ret = sock_set_timeout(&sk->sk_sndtimeo, optval, optlen, optname == SO_SNDTIMEO_OLD); ret = sock_set_timeout(&sk->sk_sndtimeo, optval, optlen, optname == SO_SNDTIMEO_OLD);
break; break;
case SO_ATTACH_FILTER: case SO_ATTACH_FILTER: {
ret = -EINVAL; struct sock_fprog fprog;
if (optlen == sizeof(struct sock_fprog)) {
struct sock_fprog fprog;
ret = -EFAULT;
if (copy_from_user(&fprog, optval, sizeof(fprog)))
break;
ret = copy_bpf_fprog_from_user(&fprog, optval, optlen);
if (!ret)
ret = sk_attach_filter(&fprog, sk); ret = sk_attach_filter(&fprog, sk);
}
break; break;
}
case SO_ATTACH_BPF: case SO_ATTACH_BPF:
ret = -EINVAL; ret = -EINVAL;
if (optlen == sizeof(u32)) { if (optlen == sizeof(u32)) {
...@@ -1085,19 +1080,14 @@ int sock_setsockopt(struct socket *sock, int level, int optname, ...@@ -1085,19 +1080,14 @@ int sock_setsockopt(struct socket *sock, int level, int optname,
} }
break; break;
case SO_ATTACH_REUSEPORT_CBPF: case SO_ATTACH_REUSEPORT_CBPF: {
ret = -EINVAL; struct sock_fprog fprog;
if (optlen == sizeof(struct sock_fprog)) {
struct sock_fprog fprog;
ret = -EFAULT;
if (copy_from_user(&fprog, optval, sizeof(fprog)))
break;
ret = copy_bpf_fprog_from_user(&fprog, optval, optlen);
if (!ret)
ret = sk_reuseport_attach_filter(&fprog, sk); ret = sk_reuseport_attach_filter(&fprog, sk);
}
break; break;
}
case SO_ATTACH_REUSEPORT_EBPF: case SO_ATTACH_REUSEPORT_EBPF:
ret = -EINVAL; ret = -EINVAL;
if (optlen == sizeof(u32)) { if (optlen == sizeof(u32)) {
......
...@@ -1545,10 +1545,10 @@ static int fanout_set_data_cbpf(struct packet_sock *po, char __user *data, ...@@ -1545,10 +1545,10 @@ static int fanout_set_data_cbpf(struct packet_sock *po, char __user *data,
if (sock_flag(&po->sk, SOCK_FILTER_LOCKED)) if (sock_flag(&po->sk, SOCK_FILTER_LOCKED))
return -EPERM; return -EPERM;
if (len != sizeof(fprog))
return -EINVAL; ret = copy_bpf_fprog_from_user(&fprog, data, len);
if (copy_from_user(&fprog, data, len)) if (ret)
return -EFAULT; return ret;
ret = bpf_prog_create_from_user(&new, &fprog, NULL, false); ret = bpf_prog_create_from_user(&new, &fprog, NULL, false);
if (ret) if (ret)
...@@ -4040,28 +4040,6 @@ static int packet_getsockopt(struct socket *sock, int level, int optname, ...@@ -4040,28 +4040,6 @@ static int packet_getsockopt(struct socket *sock, int level, int optname,
return 0; return 0;
} }
#ifdef CONFIG_COMPAT
static int compat_packet_setsockopt(struct socket *sock, int level, int optname,
char __user *optval, unsigned int optlen)
{
struct packet_sock *po = pkt_sk(sock->sk);
if (level != SOL_PACKET)
return -ENOPROTOOPT;
if (optname == PACKET_FANOUT_DATA &&
po->fanout && po->fanout->type == PACKET_FANOUT_CBPF) {
optval = (char __user *)get_compat_bpf_fprog(optval);
if (!optval)
return -EFAULT;
optlen = sizeof(struct sock_fprog);
}
return packet_setsockopt(sock, level, optname, optval, optlen);
}
#endif
static int packet_notifier(struct notifier_block *this, static int packet_notifier(struct notifier_block *this,
unsigned long msg, void *ptr) unsigned long msg, void *ptr)
{ {
...@@ -4549,9 +4527,6 @@ static const struct proto_ops packet_ops = { ...@@ -4549,9 +4527,6 @@ static const struct proto_ops packet_ops = {
.shutdown = sock_no_shutdown, .shutdown = sock_no_shutdown,
.setsockopt = packet_setsockopt, .setsockopt = packet_setsockopt,
.getsockopt = packet_getsockopt, .getsockopt = packet_getsockopt,
#ifdef CONFIG_COMPAT
.compat_setsockopt = compat_packet_setsockopt,
#endif
.sendmsg = packet_sendmsg, .sendmsg = packet_sendmsg,
.recvmsg = packet_recvmsg, .recvmsg = packet_recvmsg,
.mmap = packet_mmap, .mmap = packet_mmap,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment