diff options
author | Willem de Bruijn <willemb@google.com> | 2016-06-07 12:06:34 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2016-06-10 02:41:03 -0400 |
commit | 719c44d340beeecd22cbda91b00ef55585b3c1a0 (patch) | |
tree | 5e53d5a92c7548ee9464b2133ad4651c23871297 /net | |
parent | ca8bdaf13abbdcbb12ff12164aa2d5b522ec524d (diff) |
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 1957598840f4 ("soreuseport: add compat
case for setsockopt SO_ATTACH_REUSEPORT_CBPF").
Reported-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: Willem de Bruijn <willemb@google.com>
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/compat.c | 17 | ||||
-rw-r--r-- | net/packet/af_packet.c | 25 |
2 files changed, 40 insertions, 2 deletions
diff --git a/net/compat.c b/net/compat.c index 1373947efb50..1cd2ec046164 100644 --- a/net/compat.c +++ b/net/compat.c | |||
@@ -309,8 +309,8 @@ void scm_detach_fds_compat(struct msghdr *kmsg, struct scm_cookie *scm) | |||
309 | __scm_destroy(scm); | 309 | __scm_destroy(scm); |
310 | } | 310 | } |
311 | 311 | ||
312 | static int do_set_attach_filter(struct socket *sock, int level, int optname, | 312 | /* allocate a 64-bit sock_fprog on the user stack for duration of syscall. */ |
313 | char __user *optval, unsigned int optlen) | 313 | struct sock_fprog __user *get_compat_bpf_fprog(char __user *optval) |
314 | { | 314 | { |
315 | struct compat_sock_fprog __user *fprog32 = (struct compat_sock_fprog __user *)optval; | 315 | struct compat_sock_fprog __user *fprog32 = (struct compat_sock_fprog __user *)optval; |
316 | struct sock_fprog __user *kfprog = compat_alloc_user_space(sizeof(struct sock_fprog)); | 316 | 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 | __get_user(ptr, &fprog32->filter) || | 323 | __get_user(ptr, &fprog32->filter) || |
324 | __put_user(len, &kfprog->len) || | 324 | __put_user(len, &kfprog->len) || |
325 | __put_user(compat_ptr(ptr), &kfprog->filter)) | 325 | __put_user(compat_ptr(ptr), &kfprog->filter)) |
326 | return NULL; | ||
327 | |||
328 | return kfprog; | ||
329 | } | ||
330 | EXPORT_SYMBOL_GPL(get_compat_bpf_fprog); | ||
331 | |||
332 | static int do_set_attach_filter(struct socket *sock, int level, int optname, | ||
333 | char __user *optval, unsigned int optlen) | ||
334 | { | ||
335 | struct sock_fprog __user *kfprog; | ||
336 | |||
337 | kfprog = get_compat_bpf_fprog(optval); | ||
338 | if (!kfprog) | ||
326 | return -EFAULT; | 339 | return -EFAULT; |
327 | 340 | ||
328 | return sock_setsockopt(sock, level, optname, (char __user *)kfprog, | 341 | return sock_setsockopt(sock, level, optname, (char __user *)kfprog, |
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 4040eb92d9c9..9bff6ef16fa7 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c | |||
@@ -93,6 +93,7 @@ | |||
93 | #include <net/inet_common.h> | 93 | #include <net/inet_common.h> |
94 | #endif | 94 | #endif |
95 | #include <linux/bpf.h> | 95 | #include <linux/bpf.h> |
96 | #include <net/compat.h> | ||
96 | 97 | ||
97 | #include "internal.h" | 98 | #include "internal.h" |
98 | 99 | ||
@@ -3940,6 +3941,27 @@ static int packet_getsockopt(struct socket *sock, int level, int optname, | |||
3940 | } | 3941 | } |
3941 | 3942 | ||
3942 | 3943 | ||
3944 | #ifdef CONFIG_COMPAT | ||
3945 | static int compat_packet_setsockopt(struct socket *sock, int level, int optname, | ||
3946 | char __user *optval, unsigned int optlen) | ||
3947 | { | ||
3948 | struct packet_sock *po = pkt_sk(sock->sk); | ||
3949 | |||
3950 | if (level != SOL_PACKET) | ||
3951 | return -ENOPROTOOPT; | ||
3952 | |||
3953 | if (optname == PACKET_FANOUT_DATA && | ||
3954 | po->fanout && po->fanout->type == PACKET_FANOUT_CBPF) { | ||
3955 | optval = (char __user *)get_compat_bpf_fprog(optval); | ||
3956 | if (!optval) | ||
3957 | return -EFAULT; | ||
3958 | optlen = sizeof(struct sock_fprog); | ||
3959 | } | ||
3960 | |||
3961 | return packet_setsockopt(sock, level, optname, optval, optlen); | ||
3962 | } | ||
3963 | #endif | ||
3964 | |||
3943 | static int packet_notifier(struct notifier_block *this, | 3965 | static int packet_notifier(struct notifier_block *this, |
3944 | unsigned long msg, void *ptr) | 3966 | unsigned long msg, void *ptr) |
3945 | { | 3967 | { |
@@ -4416,6 +4438,9 @@ static const struct proto_ops packet_ops = { | |||
4416 | .shutdown = sock_no_shutdown, | 4438 | .shutdown = sock_no_shutdown, |
4417 | .setsockopt = packet_setsockopt, | 4439 | .setsockopt = packet_setsockopt, |
4418 | .getsockopt = packet_getsockopt, | 4440 | .getsockopt = packet_getsockopt, |
4441 | #ifdef CONFIG_COMPAT | ||
4442 | .compat_setsockopt = compat_packet_setsockopt, | ||
4443 | #endif | ||
4419 | .sendmsg = packet_sendmsg, | 4444 | .sendmsg = packet_sendmsg, |
4420 | .recvmsg = packet_recvmsg, | 4445 | .recvmsg = packet_recvmsg, |
4421 | .mmap = packet_mmap, | 4446 | .mmap = packet_mmap, |