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").
Unified · Split
| @@ -42,6 +42,7 @@ int compat_sock_get_timestampns(struct sock *, struct timespec __user *); · 41 unmodified lines | ||
| 42 | 42 | |
| 43 | 43 | int get_compat_msghdr(struct msghdr *, struct compat_msghdr __user *, |
| 44 | 44 | struct sockaddr __user **, struct iovec **); |
| 45 | +struct sock_fprog __user *get_compat_bpf_fprog(char __user *optval); | |
| 45 | 46 | asmlinkage long compat_sys_sendmsg(int, struct compat_msghdr __user *, |
| 46 | 47 | unsigned int); |
| 47 | 48 | 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) · 308 unmodified lines | ||
| 309 | 309 | __scm_destroy(scm); |
| 310 | 310 | } |
| 311 | 311 | |
| 312 | −static int do_set_attach_filter(struct socket *sock, int level, int optname, | |
| 313 | − char __user *optval, unsigned int optlen) | |
| 312 | +/* allocate a 64-bit sock_fprog on the user stack for duration of syscall. */ | |
| 313 | +struct sock_fprog __user *get_compat_bpf_fprog(char __user *optval) | |
| 314 | 314 | { |
| 315 | 315 | struct compat_sock_fprog __user *fprog32 = (struct compat_sock_fprog __user *)optval; |
| 316 | 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, · 6 unmodified lines | ||
| 323 | 323 | __get_user(ptr, &fprog32->filter) || |
| 324 | 324 | __put_user(len, &kfprog->len) || |
| 325 | 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 | 339 | return -EFAULT; |
| 327 | 340 | |
| 328 | 341 | return sock_setsockopt(sock, level, optname, (char __user *)kfprog, |
| @@ -93,6 +93,7 @@ · 92 unmodified lines | ||
| 93 | 93 | #include <net/inet_common.h> |
| 94 | 94 | #endif |
| 95 | 95 | #include <linux/bpf.h> |
| 96 | +#include <net/compat.h> | |
| 96 | 97 | |
| 97 | 98 | #include "internal.h" |
| 98 | 99 | |
| @@ -3940,6 +3941,27 @@ static int packet_getsockopt(struct socket *sock, int level, int optname, · 3841 unmodified lines | ||
| 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 | 3965 | static int packet_notifier(struct notifier_block *this, |
| 3944 | 3966 | unsigned long msg, void *ptr) |
| 3945 | 3967 | { |
| @@ -4416,6 +4438,9 @@ static const struct proto_ops packet_ops = { · 470 unmodified lines | ||
| 4416 | 4438 | .shutdown = sock_no_shutdown, |
| 4417 | 4439 | .setsockopt = packet_setsockopt, |
| 4418 | 4440 | .getsockopt = packet_getsockopt, |
| 4441 | +#ifdef CONFIG_COMPAT | |
| 4442 | + .compat_setsockopt = compat_packet_setsockopt, | |
| 4443 | +#endif | |
| 4419 | 4444 | .sendmsg = packet_sendmsg, |
| 4420 | 4445 | .recvmsg = packet_recvmsg, |
| 4421 | 4446 | .mmap = packet_mmap, |