Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Willem de Bruijn and committed by
David S. Miller
719c44d3 ca8bdaf1

+41 -2
+1
include/net/compat.h
··· 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 *,
+15 -2
net/compat.c
··· 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 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,
+25
net/packet/af_packet.c
··· 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 ··· 3941 3940 } 3942 3941 3943 3942 3943 + #ifdef CONFIG_COMPAT 3944 + static int compat_packet_setsockopt(struct socket *sock, int level, int optname, 3945 + char __user *optval, unsigned int optlen) 3946 + { 3947 + struct packet_sock *po = pkt_sk(sock->sk); 3948 + 3949 + if (level != SOL_PACKET) 3950 + return -ENOPROTOOPT; 3951 + 3952 + if (optname == PACKET_FANOUT_DATA && 3953 + po->fanout && po->fanout->type == PACKET_FANOUT_CBPF) { 3954 + optval = (char __user *)get_compat_bpf_fprog(optval); 3955 + if (!optval) 3956 + return -EFAULT; 3957 + optlen = sizeof(struct sock_fprog); 3958 + } 3959 + 3960 + return packet_setsockopt(sock, level, optname, optval, optlen); 3961 + } 3962 + #endif 3963 + 3944 3964 static int packet_notifier(struct notifier_block *this, 3945 3965 unsigned long msg, void *ptr) 3946 3966 { ··· 4438 4416 .shutdown = sock_no_shutdown, 4439 4417 .setsockopt = packet_setsockopt, 4440 4418 .getsockopt = packet_getsockopt, 4419 + #ifdef CONFIG_COMPAT 4420 + .compat_setsockopt = compat_packet_setsockopt, 4421 + #endif 4441 4422 .sendmsg = packet_sendmsg, 4442 4423 .recvmsg = packet_recvmsg, 4443 4424 .mmap = packet_mmap,