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: Christoph Hellwig <hch@lst.de> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
d8a9b38f83
commit
4d295e5461
|
@ -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 {
|
||||||
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,
|
||||||
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 *,
|
||||||
|
|
45
net/compat.c
45
net/compat.c
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
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 @@
|
||||||
#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 @@ set_sndbuf:
|
||||||
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;
|
|
||||||
if (optlen == sizeof(struct sock_fprog)) {
|
|
||||||
struct sock_fprog fprog;
|
struct sock_fprog fprog;
|
||||||
|
|
||||||
ret = -EFAULT;
|
ret = copy_bpf_fprog_from_user(&fprog, optval, optlen);
|
||||||
if (copy_from_user(&fprog, optval, sizeof(fprog)))
|
if (!ret)
|
||||||
break;
|
|
||||||
|
|
||||||
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 @@ set_sndbuf:
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SO_ATTACH_REUSEPORT_CBPF:
|
case SO_ATTACH_REUSEPORT_CBPF: {
|
||||||
ret = -EINVAL;
|
|
||||||
if (optlen == sizeof(struct sock_fprog)) {
|
|
||||||
struct sock_fprog fprog;
|
struct sock_fprog fprog;
|
||||||
|
|
||||||
ret = -EFAULT;
|
ret = copy_bpf_fprog_from_user(&fprog, optval, optlen);
|
||||||
if (copy_from_user(&fprog, optval, sizeof(fprog)))
|
if (!ret)
|
||||||
break;
|
|
||||||
|
|
||||||
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,
|
||||||
|
|
||||||
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,
|
||||||
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 = {
|
||||||
.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,
|
||||||
|
|
Loading…
Reference in New Issue