Commit 719c44d3 authored by Willem de Bruijn's avatar Willem de Bruijn Committed by David S. Miller

packet: compat support for sock_fprog

Socket option PACKET_FANOUT_DATA takes a struct sock_fprog as argument
if PACKET_FANOUT has mode PACKET_FANOUT_CBPF. This structure contains
a pointer into user memory. If userland is 32-bit and kernel is 64-bit
the two disagree about the layout of struct sock_fprog.

Add compat setsockopt support to convert a 32-bit compat_sock_fprog to
a 64-bit sock_fprog. This is analogous to compat_sock_fprog support for
SO_REUSEPORT added in commit 19575988 ("soreuseport: add compat
case for setsockopt SO_ATTACH_REUSEPORT_CBPF").
Reported-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
Signed-off-by: default avatarWillem de Bruijn <willemb@google.com>
Acked-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent ca8bdaf1
...@@ -42,6 +42,7 @@ int compat_sock_get_timestampns(struct sock *, struct timespec __user *); ...@@ -42,6 +42,7 @@ int compat_sock_get_timestampns(struct sock *, struct timespec __user *);
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);
asmlinkage long compat_sys_sendmsg(int, struct compat_msghdr __user *, asmlinkage long compat_sys_sendmsg(int, struct compat_msghdr __user *,
unsigned int); unsigned int);
asmlinkage long compat_sys_sendmmsg(int, struct compat_mmsghdr __user *, asmlinkage long compat_sys_sendmmsg(int, struct compat_mmsghdr __user *,
......
...@@ -309,8 +309,8 @@ void scm_detach_fds_compat(struct msghdr *kmsg, struct scm_cookie *scm) ...@@ -309,8 +309,8 @@ void scm_detach_fds_compat(struct msghdr *kmsg, struct scm_cookie *scm)
__scm_destroy(scm); __scm_destroy(scm);
} }
static int do_set_attach_filter(struct socket *sock, int level, int optname, /* allocate a 64-bit sock_fprog on the user stack for duration of syscall. */
char __user *optval, unsigned int optlen) struct sock_fprog __user *get_compat_bpf_fprog(char __user *optval)
{ {
struct compat_sock_fprog __user *fprog32 = (struct compat_sock_fprog __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 sock_fprog __user *kfprog = compat_alloc_user_space(sizeof(struct sock_fprog));
...@@ -323,6 +323,19 @@ static int do_set_attach_filter(struct socket *sock, int level, int optname, ...@@ -323,6 +323,19 @@ static int do_set_attach_filter(struct socket *sock, int level, int optname,
__get_user(ptr, &fprog32->filter) || __get_user(ptr, &fprog32->filter) ||
__put_user(len, &kfprog->len) || __put_user(len, &kfprog->len) ||
__put_user(compat_ptr(ptr), &kfprog->filter)) __put_user(compat_ptr(ptr), &kfprog->filter))
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 -EFAULT;
return sock_setsockopt(sock, level, optname, (char __user *)kfprog, return sock_setsockopt(sock, level, optname, (char __user *)kfprog,
......
...@@ -93,6 +93,7 @@ ...@@ -93,6 +93,7 @@
#include <net/inet_common.h> #include <net/inet_common.h>
#endif #endif
#include <linux/bpf.h> #include <linux/bpf.h>
#include <net/compat.h>
#include "internal.h" #include "internal.h"
...@@ -3940,6 +3941,27 @@ static int packet_getsockopt(struct socket *sock, int level, int optname, ...@@ -3940,6 +3941,27 @@ static int packet_getsockopt(struct socket *sock, int level, int optname,
} }
#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)
{ {
...@@ -4416,6 +4438,9 @@ static const struct proto_ops packet_ops = { ...@@ -4416,6 +4438,9 @@ 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