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

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
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -502,13 +502,11 @@ static inline bool insn_is_zext(const struct bpf_insn *insn)
		offsetof(TYPE, MEMBER);						\
	})

#ifdef CONFIG_COMPAT
/* A struct sock_filter is architecture independent. */
struct compat_sock_fprog {
	u16		len;
	compat_uptr_t	filter;	/* struct sock_filter * */
};
#endif

struct sock_fprog_kern {
	u16			len;
@@ -1278,4 +1276,6 @@ struct bpf_sockopt_kern {
	s32		retval;
};

int copy_bpf_fprog_from_user(struct sock_fprog *dst, void __user *src, int len);

#endif /* __LINUX_FILTER_H__ */
+0 −1
Original line number Diff line number Diff line
@@ -61,7 +61,6 @@ int __get_compat_msghdr(struct msghdr *kmsg, struct compat_msghdr __user *umsg,
			compat_size_t *len);
int get_compat_msghdr(struct msghdr *, struct compat_msghdr __user *,
		      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 cmsghdr_from_user_compat_to_kern(struct msghdr *, struct sock *,
+1 −44
Original line number Diff line number Diff line
@@ -335,49 +335,6 @@ void scm_detach_fds_compat(struct msghdr *kmsg, struct scm_cookie *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,
				   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)
			err = compat_sock_setsockopt(sock, level,
			err = sock_setsockopt(sock, level,
					optname, optval, optlen);
		else if (sock->ops->compat_setsockopt)
			err = sock->ops->compat_setsockopt(sock, level,
+23 −0
Original line number Diff line number Diff line
@@ -77,6 +77,29 @@
#include <net/transp_v6.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: sock associated with &sk_buff
+10 −20
Original line number Diff line number Diff line
@@ -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);
		break;

	case SO_ATTACH_FILTER:
		ret = -EINVAL;
		if (optlen == sizeof(struct sock_fprog)) {
	case SO_ATTACH_FILTER: {
		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);
		}
		break;

	}
	case SO_ATTACH_BPF:
		ret = -EINVAL;
		if (optlen == sizeof(u32)) {
@@ -1085,19 +1080,14 @@ int sock_setsockopt(struct socket *sock, int level, int optname,
		}
		break;

	case SO_ATTACH_REUSEPORT_CBPF:
		ret = -EINVAL;
		if (optlen == sizeof(struct sock_fprog)) {
	case SO_ATTACH_REUSEPORT_CBPF: {
		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);
		}
		break;

	}
	case SO_ATTACH_REUSEPORT_EBPF:
		ret = -EINVAL;
		if (optlen == sizeof(u32)) {
Loading