]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
udp: properly deal with xfrm encap and ADDRFORM
authorPaolo Abeni <pabeni@redhat.com>
Wed, 9 Apr 2025 20:00:56 +0000 (22:00 +0200)
committerJakub Kicinski <kuba@kernel.org>
Mon, 14 Apr 2025 21:29:01 +0000 (14:29 -0700)
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>
include/net/udp_tunnel.h
net/ipv4/udp.c
net/ipv4/udp_tunnel_core.c

index 288f06f23a804386d8485b86b12724dfa1423f29..2df3b8344eb52a25e6ea850795aaef02b4e47e46 100644 (file)
@@ -215,7 +215,6 @@ static inline void udp_tunnel_encap_enable(struct sock *sk)
        if (READ_ONCE(sk->sk_family) == PF_INET6)
                ipv6_stub->udpv6_encap_enable();
 #endif
-       udp_tunnel_update_gro_rcv(sk, true);
        udp_encap_enable();
 }
 
index 8867fe687888ce80038ea881a414835fb15c7c5b..f9f5b92cf4b6131ea7038727365de8566641a2de 100644 (file)
@@ -2904,15 +2904,33 @@ void udp_destroy_sock(struct sock *sk)
        }
 }
 
+typedef struct sk_buff *(*udp_gro_receive_t)(struct sock *sk,
+                                            struct list_head *head,
+                                            struct sk_buff *skb);
+
 static void set_xfrm_gro_udp_encap_rcv(__u16 encap_type, unsigned short family,
                                       struct sock *sk)
 {
 #ifdef CONFIG_XFRM
+       udp_gro_receive_t new_gro_receive;
+
        if (udp_test_bit(GRO_ENABLED, sk) && encap_type == UDP_ENCAP_ESPINUDP) {
-               if (family == AF_INET)
-                       WRITE_ONCE(udp_sk(sk)->gro_receive, xfrm4_gro_udp_encap_rcv);
-               else if (IS_ENABLED(CONFIG_IPV6) && family == AF_INET6)
-                       WRITE_ONCE(udp_sk(sk)->gro_receive, ipv6_stub->xfrm6_gro_udp_encap_rcv);
+               if (IS_ENABLED(CONFIG_IPV6) && family == AF_INET6)
+                       new_gro_receive = ipv6_stub->xfrm6_gro_udp_encap_rcv;
+               else
+                       new_gro_receive = xfrm4_gro_udp_encap_rcv;
+
+               if (udp_sk(sk)->gro_receive != new_gro_receive) {
+                       /*
+                        * With IPV6_ADDRFORM the gro callback could change
+                        * after being set, unregister the old one, if valid.
+                        */
+                       if (udp_sk(sk)->gro_receive)
+                               udp_tunnel_update_gro_rcv(sk, false);
+
+                       WRITE_ONCE(udp_sk(sk)->gro_receive, new_gro_receive);
+                       udp_tunnel_update_gro_rcv(sk, true);
+               }
        }
 #endif
 }
@@ -2962,6 +2980,7 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname,
                break;
 
        case UDP_ENCAP:
+               sockopt_lock_sock(sk);
                switch (val) {
                case 0:
 #ifdef CONFIG_XFRM
@@ -2985,6 +3004,7 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname,
                        err = -ENOPROTOOPT;
                        break;
                }
+               sockopt_release_sock(sk);
                break;
 
        case UDP_NO_CHECK6_TX:
@@ -3002,13 +3022,14 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname,
                break;
 
        case UDP_GRO:
-
+               sockopt_lock_sock(sk);
                /* when enabling GRO, accept the related GSO packet type */
                if (valbool)
                        udp_tunnel_encap_enable(sk);
                udp_assign_bit(GRO_ENABLED, sk, valbool);
                udp_assign_bit(ACCEPT_L4, sk, valbool);
                set_xfrm_gro_udp_encap_rcv(up->encap_type, sk->sk_family, sk);
+               sockopt_release_sock(sk);
                break;
 
        /*
index 3d1214e6df0ea71478d17573a0566304f2d8204a..2326548997d3f6eae03188a18eca0c13e391f6c4 100644 (file)
@@ -90,6 +90,8 @@ void setup_udp_tunnel_sock(struct net *net, struct socket *sock,
 
        udp_tunnel_encap_enable(sk);
 
+       udp_tunnel_update_gro_rcv(sk, true);
+
        if (!sk->sk_dport && !sk->sk_bound_dev_if && sk_saddr_any(sk) &&
            sk->sk_kern_sock)
                udp_tunnel_update_gro_lookup(net, sk, true);