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

Configure Feed

Select the types of activity you want to include in your feed.

udp: properly deal with xfrm encap and ADDRFORM

UDP GRO accounting assumes that the GRO receive callback is always
set when the UDP tunnel is enabled, but syzkaller proved otherwise,
leading tot the following splat:

WARNING: CPU: 0 PID: 5837 at net/ipv4/udp_offload.c:123 udp_tunnel_update_gro_rcv+0x28d/0x4c0 net/ipv4/udp_offload.c:123
Modules linked in:
CPU: 0 UID: 0 PID: 5837 Comm: syz-executor850 Not tainted 6.14.0-syzkaller-13320-g420aabef3ab5 #0 PREEMPT(full)
Hardware name: Google Compute Engine/Google Compute Engine, BIOS Google 02/12/2025
RIP: 0010:udp_tunnel_update_gro_rcv+0x28d/0x4c0 net/ipv4/udp_offload.c:123
Code: 00 00 e8 c6 5a 2f f7 48 c1 e5 04 48 8d b5 20 53 c7 9a ba 10
00 00 00 4c 89 ff e8 ce 87 99 f7 e9 ce 00 00 00 e8 a4 5a 2f
f7 90 <0f> 0b 90 e9 de fd ff ff bf 01 00 00 00 89 ee e8 cf
5e 2f f7 85 ed
RSP: 0018:ffffc90003effa88 EFLAGS: 00010293
RAX: ffffffff8a93fc9c RBX: 0000000000000000 RCX: ffff8880306f9e00
RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000000
RBP: 0000000000000000 R08: ffffffff8a93fabe R09: 1ffffffff20bfb2e
R10: dffffc0000000000 R11: fffffbfff20bfb2f R12: ffff88814ef21738
R13: dffffc0000000000 R14: ffff88814ef21778 R15: 1ffff11029de42ef
FS: 0000000000000000(0000) GS:ffff888124f96000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007f04eec760d0 CR3: 000000000eb38000 CR4: 00000000003526f0
DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
Call Trace:
<TASK>
udp_tunnel_cleanup_gro include/net/udp_tunnel.h:205 [inline]
udpv6_destroy_sock+0x212/0x270 net/ipv6/udp.c:1829
sk_common_release+0x71/0x2e0 net/core/sock.c:3896
inet_release+0x17d/0x200 net/ipv4/af_inet.c:435
__sock_release net/socket.c:647 [inline]
sock_close+0xbc/0x240 net/socket.c:1391
__fput+0x3e9/0x9f0 fs/file_table.c:465
task_work_run+0x251/0x310 kernel/task_work.c:227
exit_task_work include/linux/task_work.h:40 [inline]
do_exit+0xa11/0x27f0 kernel/exit.c:953
do_group_exit+0x207/0x2c0 kernel/exit.c:1102
__do_sys_exit_group kernel/exit.c:1113 [inline]
__se_sys_exit_group kernel/exit.c:1111 [inline]
__x64_sys_exit_group+0x3f/0x40 kernel/exit.c:1111
x64_sys_call+0x26c3/0x26d0 arch/x86/include/generated/asm/syscalls_64.h:232
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0xf3/0x230 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f04eebfac79
Code: Unable to access opcode bytes at 0x7f04eebfac4f.
RSP: 002b:00007fffdcaa34a8 EFLAGS: 00000246 ORIG_RAX: 00000000000000e7
RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f04eebfac79
RDX: 000000000000003c RSI: 00000000000000e7 RDI: 0000000000000000
RBP: 00007f04eec75270 R08: ffffffffffffffb8 R09: 00007fffdcaa36c8
R10: 0000200000000000 R11: 0000000000000246 R12: 00007f04eec75270
R13: 0000000000000000 R14: 00007f04eec75cc0 R15: 00007f04eebcca70

Address the issue moving the accounting hook into
setup_udp_tunnel_sock() and set_xfrm_gro_udp_encap_rcv(), where
the GRO callback is actually set.

set_xfrm_gro_udp_encap_rcv() is prone to races with IPV6_ADDRFORM,
run the relevant setsockopt under the socket lock to ensure using
consistent values of sk_family and up->encap_type.

Refactor the GRO callback selection code, to make it clear that
the function pointer is always initialized.

Reported-by: syzbot+8c469a2260132cd095c1@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=8c469a2260132cd095c1
Fixes: 172bf009c18d ("xfrm: Support GRO for IPv4 ESP in UDP encapsulation")
Fixes: 5d7f5b2f6b935 ("udp_tunnel: use static call for GRO hooks when possible")
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Reviewed-by: Sabrina Dubroca <sd@queasysnail.net>
Link: https://patch.msgid.link/92bcdb6899145a9a387c8fa9e3ca656642a43634.1744228733.git.pabeni@redhat.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Paolo Abeni and committed by
Jakub Kicinski
c26c192c b65999e7

+28 -6
-1
include/net/udp_tunnel.h
··· 215 215 if (READ_ONCE(sk->sk_family) == PF_INET6) 216 216 ipv6_stub->udpv6_encap_enable(); 217 217 #endif 218 - udp_tunnel_update_gro_rcv(sk, true); 219 218 udp_encap_enable(); 220 219 } 221 220
+26 -5
net/ipv4/udp.c
··· 2904 2904 } 2905 2905 } 2906 2906 2907 + typedef struct sk_buff *(*udp_gro_receive_t)(struct sock *sk, 2908 + struct list_head *head, 2909 + struct sk_buff *skb); 2910 + 2907 2911 static void set_xfrm_gro_udp_encap_rcv(__u16 encap_type, unsigned short family, 2908 2912 struct sock *sk) 2909 2913 { 2910 2914 #ifdef CONFIG_XFRM 2915 + udp_gro_receive_t new_gro_receive; 2916 + 2911 2917 if (udp_test_bit(GRO_ENABLED, sk) && encap_type == UDP_ENCAP_ESPINUDP) { 2912 - if (family == AF_INET) 2913 - WRITE_ONCE(udp_sk(sk)->gro_receive, xfrm4_gro_udp_encap_rcv); 2914 - else if (IS_ENABLED(CONFIG_IPV6) && family == AF_INET6) 2915 - WRITE_ONCE(udp_sk(sk)->gro_receive, ipv6_stub->xfrm6_gro_udp_encap_rcv); 2918 + if (IS_ENABLED(CONFIG_IPV6) && family == AF_INET6) 2919 + new_gro_receive = ipv6_stub->xfrm6_gro_udp_encap_rcv; 2920 + else 2921 + new_gro_receive = xfrm4_gro_udp_encap_rcv; 2922 + 2923 + if (udp_sk(sk)->gro_receive != new_gro_receive) { 2924 + /* 2925 + * With IPV6_ADDRFORM the gro callback could change 2926 + * after being set, unregister the old one, if valid. 2927 + */ 2928 + if (udp_sk(sk)->gro_receive) 2929 + udp_tunnel_update_gro_rcv(sk, false); 2930 + 2931 + WRITE_ONCE(udp_sk(sk)->gro_receive, new_gro_receive); 2932 + udp_tunnel_update_gro_rcv(sk, true); 2933 + } 2916 2934 } 2917 2935 #endif 2918 2936 } ··· 2980 2962 break; 2981 2963 2982 2964 case UDP_ENCAP: 2965 + sockopt_lock_sock(sk); 2983 2966 switch (val) { 2984 2967 case 0: 2985 2968 #ifdef CONFIG_XFRM ··· 3004 2985 err = -ENOPROTOOPT; 3005 2986 break; 3006 2987 } 2988 + sockopt_release_sock(sk); 3007 2989 break; 3008 2990 3009 2991 case UDP_NO_CHECK6_TX: ··· 3022 3002 break; 3023 3003 3024 3004 case UDP_GRO: 3025 - 3005 + sockopt_lock_sock(sk); 3026 3006 /* when enabling GRO, accept the related GSO packet type */ 3027 3007 if (valbool) 3028 3008 udp_tunnel_encap_enable(sk); 3029 3009 udp_assign_bit(GRO_ENABLED, sk, valbool); 3030 3010 udp_assign_bit(ACCEPT_L4, sk, valbool); 3031 3011 set_xfrm_gro_udp_encap_rcv(up->encap_type, sk->sk_family, sk); 3012 + sockopt_release_sock(sk); 3032 3013 break; 3033 3014 3034 3015 /*
+2
net/ipv4/udp_tunnel_core.c
··· 90 90 91 91 udp_tunnel_encap_enable(sk); 92 92 93 + udp_tunnel_update_gro_rcv(sk, true); 94 + 93 95 if (!sk->sk_dport && !sk->sk_bound_dev_if && sk_saddr_any(sk) && 94 96 sk->sk_kern_sock) 95 97 udp_tunnel_update_gro_lookup(net, sk, true);