diff mbox series

udp6: Fix race condition in udp6_sendmsg & connect

Message ID 20230526150806.1457828-1-VEfanov@ispras.ru (mailing list archive)
State Superseded
Delegated to: Netdev Maintainers
Headers show
Series udp6: Fix race condition in udp6_sendmsg & connect | expand

Checks

Context Check Description
netdev/series_format warning Single patches do not need cover letters; Target tree name not specified in the subject
netdev/tree_selection success Guessed tree name to be net-next
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 8 this patch: 8
netdev/cc_maintainers success CCed 7 of 7 maintainers
netdev/build_clang success Errors and warnings before: 8 this patch: 8
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success Fixes tag looks correct
netdev/build_allmodconfig_warn success Errors and warnings before: 8 this patch: 8
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 15 lines checked
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Vlad Efanov May 26, 2023, 3:08 p.m. UTC
Syzkaller got the following report:
BUG: KASAN: use-after-free in sk_setup_caps+0x621/0x690 net/core/sock.c:2018
Read of size 8 at addr ffff888027f82780 by task syz-executor276/3255

The function sk_setup_caps (called by ip6_sk_dst_store_flow->
ip6_dst_store) referenced already freed memory as this memory was
freed by parallel task in udpv6_sendmsg->ip6_sk_dst_lookup_flow->
sk_dst_check.

          task1 (connect)              task2 (udp6_sendmsg)
        sk_setup_caps->sk_dst_set |
                                  |  sk_dst_check->
                                  |      sk_dst_set
                                  |      dst_release
        sk_setup_caps references  |
        to already freed dst_entry|

The reason for this race condition is: udp6_sendmsg() calls
ip6_sk_dst_lookup() without lock for sock structure and tries to
allocate/add dst_entry structure to sock structure in parallel with
"connect" task.

Found by Linux Verification Center (linuxtesting.org) with syzkaller.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Vladislav Efanov <VEfanov@ispras.ru>
---
 net/ipv6/udp.c | 3 +++
 1 file changed, 3 insertions(+)

Comments

Eric Dumazet May 26, 2023, 3:29 p.m. UTC | #1
On Fri, May 26, 2023 at 5:08 PM Vladislav Efanov <VEfanov@ispras.ru> wrote:
>
> Syzkaller got the following report:
> BUG: KASAN: use-after-free in sk_setup_caps+0x621/0x690 net/core/sock.c:2018
> Read of size 8 at addr ffff888027f82780 by task syz-executor276/3255

Please include a full report.

>
> The function sk_setup_caps (called by ip6_sk_dst_store_flow->
> ip6_dst_store) referenced already freed memory as this memory was
> freed by parallel task in udpv6_sendmsg->ip6_sk_dst_lookup_flow->
> sk_dst_check.
>
>           task1 (connect)              task2 (udp6_sendmsg)
>         sk_setup_caps->sk_dst_set |
>                                   |  sk_dst_check->
>                                   |      sk_dst_set
>                                   |      dst_release
>         sk_setup_caps references  |
>         to already freed dst_entry|


>
> The reason for this race condition is: udp6_sendmsg() calls
> ip6_sk_dst_lookup() without lock for sock structure and tries to
> allocate/add dst_entry structure to sock structure in parallel with
> "connect" task.
>
> Found by Linux Verification Center (linuxtesting.org) with syzkaller.
>
> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")

This is a bogus Fixes: tag

In old times, UDP sendmsg() was using the socket lock.

Then, in linux-4.0 Vlad Yasevich made UDP v6 sendmsg() lockless (and
racy in many points)


> Signed-off-by: Vladislav Efanov <VEfanov@ispras.ru>
> ---
>  net/ipv6/udp.c | 3 +++
>  1 file changed, 3 insertions(+)
>
> diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
> index e5a337e6b970..a5ecd5d93b0a 100644
> --- a/net/ipv6/udp.c
> +++ b/net/ipv6/udp.c
> @@ -1563,12 +1563,15 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
>
>         fl6->flowlabel = ip6_make_flowinfo(ipc6.tclass, fl6->flowlabel);
>
> +       lock_sock(sk);
>         dst = ip6_sk_dst_lookup_flow(sk, fl6, final_p, connected);
>         if (IS_ERR(dst)) {
>                 err = PTR_ERR(dst);
>                 dst = NULL;
> +               release_sock(sk);
>                 goto out;
>         }
> +       release_sock(sk);
>
>         if (ipc6.hlimit < 0)
>                 ipc6.hlimit = ip6_sk_dst_hoplimit(np, fl6, dst);
> --
> 2.34.1
>

There must be another way really.
You just killed UDP performance.
Paolo Abeni May 26, 2023, 3:33 p.m. UTC | #2
On Fri, 2023-05-26 at 18:08 +0300, Vladislav Efanov wrote:
> Syzkaller got the following report:
> BUG: KASAN: use-after-free in sk_setup_caps+0x621/0x690 net/core/sock.c:2018
> Read of size 8 at addr ffff888027f82780 by task syz-executor276/3255
> 
> The function sk_setup_caps (called by ip6_sk_dst_store_flow->
> ip6_dst_store) referenced already freed memory as this memory was
> freed by parallel task in udpv6_sendmsg->ip6_sk_dst_lookup_flow->
> sk_dst_check.
> 
>           task1 (connect)              task2 (udp6_sendmsg)
>         sk_setup_caps->sk_dst_set |
>                                   |  sk_dst_check->
>                                   |      sk_dst_set
>                                   |      dst_release
>         sk_setup_caps references  |
>         to already freed dst_entry|
> 
> The reason for this race condition is: udp6_sendmsg() calls
> ip6_sk_dst_lookup() without lock for sock structure and tries to
> allocate/add dst_entry structure to sock structure in parallel with
> "connect" task.
> 
> Found by Linux Verification Center (linuxtesting.org) with syzkaller.
> 
> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
> Signed-off-by: Vladislav Efanov <VEfanov@ispras.ru>

Thank you for the detailed report!

> ---
>  net/ipv6/udp.c | 3 +++
>  1 file changed, 3 insertions(+)
> 
> diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
> index e5a337e6b970..a5ecd5d93b0a 100644
> --- a/net/ipv6/udp.c
> +++ b/net/ipv6/udp.c
> @@ -1563,12 +1563,15 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
>  
>  	fl6->flowlabel = ip6_make_flowinfo(ipc6.tclass, fl6->flowlabel);
>  
> +	lock_sock(sk);

Acquiring the socket lock in this fast-path is going to kill the xmit
performances, I think we can't do that.

What about something like the following instead? Does that addresses
the UaF? (completely untested, not even built ;) If so, feel free to
take it over.

Thanks.

Paolo
---
diff --git a/net/core/sock.c b/net/core/sock.c
index 5440e67bcfe3..24f2761bdb1d 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -2381,7 +2381,6 @@ void sk_setup_caps(struct sock *sk, struct dst_entry *dst)
 {
 	u32 max_segs = 1;
 
-	sk_dst_set(sk, dst);
 	sk->sk_route_caps = dst->dev->features;
 	if (sk_is_tcp(sk))
 		sk->sk_route_caps |= NETIF_F_GSO;
@@ -2400,6 +2399,7 @@ void sk_setup_caps(struct sock *sk, struct dst_entry *dst)
 		}
 	}
 	sk->sk_gso_max_segs = max_segs;
+	sk_dst_set(sk, dst);
 }
 EXPORT_SYMBOL_GPL(sk_setup_caps);
Vlad Efanov May 26, 2023, 3:49 p.m. UTC | #3
Eric,

Here is the full report:

==================================================================
BUG: KASAN: use-after-free in sk_setup_caps+0x621/0x690 net/core/sock.c:2018
Read of size 8 at addr ffff88814c2cc8c0 by task syz-executor.5/22717

CPU: 1 PID: 22717 Comm: syz-executor.5 Not tainted 5.10.179-syzkaller #0
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.12.0-1 04/01/2014
Call Trace:
  __dump_stack lib/dump_stack.c:77 [inline]
  dump_stack+0x107/0x167 lib/dump_stack.c:118
  print_address_description.constprop.0+0x1e/0x250 mm/kasan/report.c:384
  __kasan_report mm/kasan/report.c:584 [inline]
  kasan_report.cold+0x1f/0x37 mm/kasan/report.c:601
  sk_setup_caps+0x621/0x690 net/core/sock.c:2018
  ip6_dst_store include/net/ip6_route.h:234 [inline]
  ip6_sk_dst_store_flow+0x2c9/0x7b0 net/ipv6/route.c:2852
  ip6_datagram_dst_update+0x801/0xe30 net/ipv6/datagram.c:107
  __ip6_datagram_connect+0x5f2/0x1360 net/ipv6/datagram.c:248
  ip6_datagram_connect+0x2b/0x50 net/ipv6/datagram.c:272
  inet_dgram_connect+0x150/0x2e0 net/ipv4/af_inet.c:577
  __sys_connect_file+0x15c/0x1a0 net/socket.c:1846
  __sys_connect+0x165/0x1a0 net/socket.c:1863
  __do_sys_connect net/socket.c:1873 [inline]
  __se_sys_connect net/socket.c:1870 [inline]
  __x64_sys_connect+0x6e/0xb0 net/socket.c:1870
  do_syscall_64+0x33/0x40 arch/x86/entry/common.c:46
  entry_SYSCALL_64_after_hwframe+0x61/0xc6
RIP: 0033:0x469fe9
Code: ff ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 40 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 b8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007f6b6e7e0c08 EFLAGS: 00000246 ORIG_RAX: 000000000000002a
RAX: ffffffffffffffda RBX: 000000000056c030 RCX: 0000000000469fe9
RDX: 000000000000001c RSI: 0000000020000080 RDI: 0000000000000004
RBP: 000000000056c030 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007ffdd98ee44f R14: 00007f6b6e7e1700 R15: 0000000000000001

Allocated by task 22717:
  kasan_save_stack+0x1b/0x40 mm/kasan/common.c:48
  kasan_set_track mm/kasan/common.c:56 [inline]
  __kasan_kmalloc.constprop.0+0xc9/0xd0 mm/kasan/common.c:461
  slab_post_alloc_hook mm/slab.h:532 [inline]
  slab_alloc_node mm/slub.c:2896 [inline]
  slab_alloc mm/slub.c:2904 [inline]
  kmem_cache_alloc+0x146/0x2e0 mm/slub.c:2909
  dst_alloc+0xa0/0x660 net/core/dst.c:93
  ip6_blackhole_route+0x61/0x550 net/ipv6/route.c:2535
  make_blackhole net/xfrm/xfrm_policy.c:3019 [inline]
  xfrm_lookup_route net/xfrm/xfrm_policy.c:3212 [inline]
  xfrm_lookup_route+0x109/0x200 net/xfrm/xfrm_policy.c:3203
  ip6_dst_lookup_flow+0x159/0x1d0 net/ipv6/ip6_output.c:1235
  ip6_datagram_dst_update+0x5d5/0xe30 net/ipv6/datagram.c:89
  __ip6_datagram_connect+0x5f2/0x1360 net/ipv6/datagram.c:248
  ip6_datagram_connect+0x2b/0x50 net/ipv6/datagram.c:272
  inet_dgram_connect+0x150/0x2e0 net/ipv4/af_inet.c:577
  __sys_connect_file+0x15c/0x1a0 net/socket.c:1846
  __sys_connect+0x165/0x1a0 net/socket.c:1863
  __do_sys_connect net/socket.c:1873 [inline]
  __se_sys_connect net/socket.c:1870 [inline]
  __x64_sys_connect+0x6e/0xb0 net/socket.c:1870
  do_syscall_64+0x33/0x40 arch/x86/entry/common.c:46
  entry_SYSCALL_64_after_hwframe+0x61/0xc6

Freed by task 5512:
  kasan_save_stack+0x1b/0x40 mm/kasan/common.c:48
  kasan_set_track+0x1c/0x30 mm/kasan/common.c:56
  kasan_set_free_info+0x1b/0x30 mm/kasan/generic.c:355
  __kasan_slab_free+0x112/0x170 mm/kasan/common.c:422
  slab_free_hook mm/slub.c:1542 [inline]
  slab_free_freelist_hook+0xb8/0x1b0 mm/slub.c:1576
  slab_free mm/slub.c:3149 [inline]
  kmem_cache_free+0xaa/0x2e0 mm/slub.c:3165
  dst_destroy+0x2c1/0x3c0 net/core/dst.c:129
  rcu_do_batch kernel/rcu/tree.c:2492 [inline]
  rcu_core+0x649/0x1310 kernel/rcu/tree.c:2733
  __do_softirq+0x1d4/0x8d3 kernel/softirq.c:298

Last call_rcu():
  kasan_save_stack+0x1b/0x40 mm/kasan/common.c:48
  kasan_record_aux_stack+0xad/0xc0 mm/kasan/generic.c:346
  __call_rcu kernel/rcu/tree.c:2974 [inline]
  call_rcu+0xb6/0x950 kernel/rcu/tree.c:3048
  dst_release net/core/dst.c:179 [inline]
  dst_release+0x7e/0xe0 net/core/dst.c:169
  sk_dst_set include/net/sock.h:2024 [inline]
  sk_setup_caps+0x95/0x690 net/core/sock.c:2017
  ip6_dst_store include/net/ip6_route.h:234 [inline]
  ip6_sk_dst_store_flow+0x2c9/0x7b0 net/ipv6/route.c:2852
  ip6_sk_dst_lookup_flow+0x641/0x9a0 net/ipv6/ip6_output.c:1269
  udpv6_sendmsg+0x183f/0x2d10 net/ipv6/udp.c:1522
  inet6_sendmsg+0x105/0x140 net/ipv6/af_inet6.c:651
  sock_sendmsg_nosec net/socket.c:651 [inline]
  sock_sendmsg+0xf2/0x190 net/socket.c:671
  ____sys_sendmsg+0x32e/0x870 net/socket.c:2356
  ___sys_sendmsg+0x100/0x170 net/socket.c:2410
  __sys_sendmmsg+0x192/0x460 net/socket.c:2496
  __do_sys_sendmmsg net/socket.c:2525 [inline]
  __se_sys_sendmmsg net/socket.c:2522 [inline]
  __x64_sys_sendmmsg+0x98/0x100 net/socket.c:2522
  do_syscall_64+0x33/0x40 arch/x86/entry/common.c:46
  entry_SYSCALL_64_after_hwframe+0x61/0xc6

Second to last call_rcu():
  kasan_save_stack+0x1b/0x40 mm/kasan/common.c:48
  kasan_record_aux_stack+0xad/0xc0 mm/kasan/generic.c:346
  __call_rcu kernel/rcu/tree.c:2974 [inline]
  call_rcu+0xb6/0x950 kernel/rcu/tree.c:3048
  dst_release net/core/dst.c:179 [inline]
  dst_release+0x7e/0xe0 net/core/dst.c:169
  rawv6_sendmsg+0xf73/0x3cf0 net/ipv6/raw.c:964
  inet_sendmsg+0x11d/0x140 net/ipv4/af_inet.c:817
  sock_sendmsg_nosec net/socket.c:651 [inline]
  sock_sendmsg+0x13c/0x190 net/socket.c:671
  ____sys_sendmsg+0x32e/0x870 net/socket.c:2356
  ___sys_sendmsg+0x100/0x170 net/socket.c:2410
  __sys_sendmmsg+0x192/0x460 net/socket.c:2496
  __do_sys_sendmmsg net/socket.c:2525 [inline]
  __se_sys_sendmmsg net/socket.c:2522 [inline]
  __x64_sys_sendmmsg+0x98/0x100 net/socket.c:2522
  do_syscall_64+0x33/0x40 arch/x86/entry/common.c:46
  entry_SYSCALL_64_after_hwframe+0x61/0xc6

The buggy address belongs to the object at ffff88814c2cc8c0
  which belongs to the cache ip6_dst_cache of size 232
The buggy address is located 0 bytes inside of
  232-byte region [ffff88814c2cc8c0, ffff88814c2cc9a8)
The buggy address belongs to the page:
page:000000009e9a5247 refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x14c2cc
head:000000009e9a5247 order:1 compound_mapcount:0
flags: 0x57ffe0000010200(slab|head)
raw: 057ffe0000010200 dead000000000100 dead000000000122 ffff888019cc8dc0
raw: 0000000000000000 0000000080190019 00000001ffffffff 0000000000000000
page dumped because: kasan: bad access detected

Memory state around the buggy address:
  ffff88814c2cc780: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  ffff88814c2cc800: 00 00 00 00 00 00 00 00 00 00 00 00 00 fc fc fc
>ffff88814c2cc880: fc fc fc fc fc fc fc fc fa fb fb fb fb fb fb fb
                                            ^
  ffff88814c2cc900: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
  ffff88814c2cc980: fb fb fb fb fb fc fc fc fc fc fc fc fc fc fc fc
==================================================================


Best regards,

Vlad.


On 26.05.2023 18:29, Eric Dumazet wrote:
> On Fri, May 26, 2023 at 5:08 PM Vladislav Efanov <VEfanov@ispras.ru> wrote:
>> Syzkaller got the following report:
>> BUG: KASAN: use-after-free in sk_setup_caps+0x621/0x690 net/core/sock.c:2018
>> Read of size 8 at addr ffff888027f82780 by task syz-executor276/3255
> Please include a full report.
>
>> The function sk_setup_caps (called by ip6_sk_dst_store_flow->
>> ip6_dst_store) referenced already freed memory as this memory was
>> freed by parallel task in udpv6_sendmsg->ip6_sk_dst_lookup_flow->
>> sk_dst_check.
>>
>>            task1 (connect)              task2 (udp6_sendmsg)
>>          sk_setup_caps->sk_dst_set |
>>                                    |  sk_dst_check->
>>                                    |      sk_dst_set
>>                                    |      dst_release
>>          sk_setup_caps references  |
>>          to already freed dst_entry|
>
>> The reason for this race condition is: udp6_sendmsg() calls
>> ip6_sk_dst_lookup() without lock for sock structure and tries to
>> allocate/add dst_entry structure to sock structure in parallel with
>> "connect" task.
>>
>> Found by Linux Verification Center (linuxtesting.org) with syzkaller.
>>
>> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
> This is a bogus Fixes: tag
>
> In old times, UDP sendmsg() was using the socket lock.
>
> Then, in linux-4.0 Vlad Yasevich made UDP v6 sendmsg() lockless (and
> racy in many points)
>
>
>> Signed-off-by: Vladislav Efanov <VEfanov@ispras.ru>
>> ---
>>   net/ipv6/udp.c | 3 +++
>>   1 file changed, 3 insertions(+)
>>
>> diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
>> index e5a337e6b970..a5ecd5d93b0a 100644
>> --- a/net/ipv6/udp.c
>> +++ b/net/ipv6/udp.c
>> @@ -1563,12 +1563,15 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
>>
>>          fl6->flowlabel = ip6_make_flowinfo(ipc6.tclass, fl6->flowlabel);
>>
>> +       lock_sock(sk);
>>          dst = ip6_sk_dst_lookup_flow(sk, fl6, final_p, connected);
>>          if (IS_ERR(dst)) {
>>                  err = PTR_ERR(dst);
>>                  dst = NULL;
>> +               release_sock(sk);
>>                  goto out;
>>          }
>> +       release_sock(sk);
>>
>>          if (ipc6.hlimit < 0)
>>                  ipc6.hlimit = ip6_sk_dst_hoplimit(np, fl6, dst);
>> --
>> 2.34.1
>>
> There must be another way really.
> You just killed UDP performance.
Vlad Efanov May 26, 2023, 3:58 p.m. UTC | #4
Paolo,


I don't think that we can just move sk_dst_set() call.

I think we can destroy dst of sendmsg task in this case.


Best regards,

Vlad.


On 26.05.2023 18:33, Paolo Abeni wrote:
> On Fri, 2023-05-26 at 18:08 +0300, Vladislav Efanov wrote:
>> Syzkaller got the following report:
>> BUG: KASAN: use-after-free in sk_setup_caps+0x621/0x690 net/core/sock.c:2018
>> Read of size 8 at addr ffff888027f82780 by task syz-executor276/3255
>>
>> The function sk_setup_caps (called by ip6_sk_dst_store_flow->
>> ip6_dst_store) referenced already freed memory as this memory was
>> freed by parallel task in udpv6_sendmsg->ip6_sk_dst_lookup_flow->
>> sk_dst_check.
>>
>>            task1 (connect)              task2 (udp6_sendmsg)
>>          sk_setup_caps->sk_dst_set |
>>                                    |  sk_dst_check->
>>                                    |      sk_dst_set
>>                                    |      dst_release
>>          sk_setup_caps references  |
>>          to already freed dst_entry|
>>
>> The reason for this race condition is: udp6_sendmsg() calls
>> ip6_sk_dst_lookup() without lock for sock structure and tries to
>> allocate/add dst_entry structure to sock structure in parallel with
>> "connect" task.
>>
>> Found by Linux Verification Center (linuxtesting.org) with syzkaller.
>>
>> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
>> Signed-off-by: Vladislav Efanov <VEfanov@ispras.ru>
> Thank you for the detailed report!
>
>> ---
>>   net/ipv6/udp.c | 3 +++
>>   1 file changed, 3 insertions(+)
>>
>> diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
>> index e5a337e6b970..a5ecd5d93b0a 100644
>> --- a/net/ipv6/udp.c
>> +++ b/net/ipv6/udp.c
>> @@ -1563,12 +1563,15 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
>>   
>>   	fl6->flowlabel = ip6_make_flowinfo(ipc6.tclass, fl6->flowlabel);
>>   
>> +	lock_sock(sk);
> Acquiring the socket lock in this fast-path is going to kill the xmit
> performances, I think we can't do that.
>
> What about something like the following instead? Does that addresses
> the UaF? (completely untested, not even built ;) If so, feel free to
> take it over.
>
> Thanks.
>
> Paolo
> ---
> diff --git a/net/core/sock.c b/net/core/sock.c
> index 5440e67bcfe3..24f2761bdb1d 100644
> --- a/net/core/sock.c
> +++ b/net/core/sock.c
> @@ -2381,7 +2381,6 @@ void sk_setup_caps(struct sock *sk, struct dst_entry *dst)
>   {
>   	u32 max_segs = 1;
>   
> -	sk_dst_set(sk, dst);
>   	sk->sk_route_caps = dst->dev->features;
>   	if (sk_is_tcp(sk))
>   		sk->sk_route_caps |= NETIF_F_GSO;
> @@ -2400,6 +2399,7 @@ void sk_setup_caps(struct sock *sk, struct dst_entry *dst)
>   		}
>   	}
>   	sk->sk_gso_max_segs = max_segs;
> +	sk_dst_set(sk, dst);
>   }
>   EXPORT_SYMBOL_GPL(sk_setup_caps);
>   
>
Eric Dumazet May 26, 2023, 4 p.m. UTC | #5
On Fri, May 26, 2023 at 5:58 PM Ефанов Владислав Александрович
<vefanov@ispras.ru> wrote:
>
> Paolo,
>
>
> I don't think that we can just move sk_dst_set() call.
>
> I think we can destroy dst of sendmsg task in this case.
>

dst are RCU protected, it should be easy to make sure we respect all the rules.
Vlad Efanov May 26, 2023, 4:09 p.m. UTC | #6
Eric,


udp6_sendmsg() currently still locks the socket (on line 1595).


Best regards,

Vlad.


On 26.05.2023 18:29, Eric Dumazet wrote:
> On Fri, May 26, 2023 at 5:08 PM Vladislav Efanov <VEfanov@ispras.ru> wrote:
>> Syzkaller got the following report:
>> BUG: KASAN: use-after-free in sk_setup_caps+0x621/0x690 net/core/sock.c:2018
>> Read of size 8 at addr ffff888027f82780 by task syz-executor276/3255
> Please include a full report.
>
>> The function sk_setup_caps (called by ip6_sk_dst_store_flow->
>> ip6_dst_store) referenced already freed memory as this memory was
>> freed by parallel task in udpv6_sendmsg->ip6_sk_dst_lookup_flow->
>> sk_dst_check.
>>
>>            task1 (connect)              task2 (udp6_sendmsg)
>>          sk_setup_caps->sk_dst_set |
>>                                    |  sk_dst_check->
>>                                    |      sk_dst_set
>>                                    |      dst_release
>>          sk_setup_caps references  |
>>          to already freed dst_entry|
>
>> The reason for this race condition is: udp6_sendmsg() calls
>> ip6_sk_dst_lookup() without lock for sock structure and tries to
>> allocate/add dst_entry structure to sock structure in parallel with
>> "connect" task.
>>
>> Found by Linux Verification Center (linuxtesting.org) with syzkaller.
>>
>> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
> This is a bogus Fixes: tag
>
> In old times, UDP sendmsg() was using the socket lock.
>
> Then, in linux-4.0 Vlad Yasevich made UDP v6 sendmsg() lockless (and
> racy in many points)
>
>
>> Signed-off-by: Vladislav Efanov <VEfanov@ispras.ru>
>> ---
>>   net/ipv6/udp.c | 3 +++
>>   1 file changed, 3 insertions(+)
>>
>> diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
>> index e5a337e6b970..a5ecd5d93b0a 100644
>> --- a/net/ipv6/udp.c
>> +++ b/net/ipv6/udp.c
>> @@ -1563,12 +1563,15 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
>>
>>          fl6->flowlabel = ip6_make_flowinfo(ipc6.tclass, fl6->flowlabel);
>>
>> +       lock_sock(sk);
>>          dst = ip6_sk_dst_lookup_flow(sk, fl6, final_p, connected);
>>          if (IS_ERR(dst)) {
>>                  err = PTR_ERR(dst);
>>                  dst = NULL;
>> +               release_sock(sk);
>>                  goto out;
>>          }
>> +       release_sock(sk);
>>
>>          if (ipc6.hlimit < 0)
>>                  ipc6.hlimit = ip6_sk_dst_hoplimit(np, fl6, dst);
>> --
>> 2.34.1
>>
> There must be another way really.
> You just killed UDP performance.
Vlad Efanov May 26, 2023, 4:41 p.m. UTC | #7
sk_dst_set() is called by sk_setup_caps().

sk_dst_set() replaces dst in socket using xchg() call and we still have 
two tasks use one socket but expect different dst in sk_dst_cache.


__sk_dst_set() is rcu protected, but it checks for socket lock.


static inline void
__sk_dst_set(struct sock *sk, struct dst_entry *dst)
{
     struct dst_entry *old_dst;

     sk_tx_queue_clear(sk);
     sk->sk_dst_pending_confirm = 0;
     old_dst = rcu_dereference_protected(sk->sk_dst_cache,
                         lockdep_sock_is_held(sk));
     rcu_assign_pointer(sk->sk_dst_cache, dst);
     dst_release(old_dst);
}


Best regards.

Vlad.


On 26.05.2023 19:00, Eric Dumazet wrote:
> On Fri, May 26, 2023 at 5:58 PM Ефанов Владислав Александрович
> <vefanov@ispras.ru> wrote:
>> Paolo,
>>
>>
>> I don't think that we can just move sk_dst_set() call.
>>
>> I think we can destroy dst of sendmsg task in this case.
>>
> dst are RCU protected, it should be easy to make sure we respect all the rules.
Eric Dumazet May 26, 2023, 4:46 p.m. UTC | #8
On Fri, May 26, 2023 at 6:09 PM Vlad Efanov <vefanov@ispras.ru> wrote:
>
> Eric,
>
>
> udp6_sendmsg() currently still locks the socket (on line 1595).
>

Not really, look more closely at lines 1580 -> 1594


>
> Best regards,
>
> Vlad.
>
>
> On 26.05.2023 18:29, Eric Dumazet wrote:
> > On Fri, May 26, 2023 at 5:08 PM Vladislav Efanov <VEfanov@ispras.ru> wrote:
> >> Syzkaller got the following report:
> >> BUG: KASAN: use-after-free in sk_setup_caps+0x621/0x690 net/core/sock.c:2018
> >> Read of size 8 at addr ffff888027f82780 by task syz-executor276/3255
> > Please include a full report.
> >
> >> The function sk_setup_caps (called by ip6_sk_dst_store_flow->
> >> ip6_dst_store) referenced already freed memory as this memory was
> >> freed by parallel task in udpv6_sendmsg->ip6_sk_dst_lookup_flow->
> >> sk_dst_check.
> >>
> >>            task1 (connect)              task2 (udp6_sendmsg)
> >>          sk_setup_caps->sk_dst_set |
> >>                                    |  sk_dst_check->
> >>                                    |      sk_dst_set
> >>                                    |      dst_release
> >>          sk_setup_caps references  |
> >>          to already freed dst_entry|
> >
> >> The reason for this race condition is: udp6_sendmsg() calls
> >> ip6_sk_dst_lookup() without lock for sock structure and tries to
> >> allocate/add dst_entry structure to sock structure in parallel with
> >> "connect" task.
> >>
> >> Found by Linux Verification Center (linuxtesting.org) with syzkaller.
> >>
> >> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
> > This is a bogus Fixes: tag
> >
> > In old times, UDP sendmsg() was using the socket lock.
> >
> > Then, in linux-4.0 Vlad Yasevich made UDP v6 sendmsg() lockless (and
> > racy in many points)
> >
> >
> >> Signed-off-by: Vladislav Efanov <VEfanov@ispras.ru>
> >> ---
> >>   net/ipv6/udp.c | 3 +++
> >>   1 file changed, 3 insertions(+)
> >>
> >> diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
> >> index e5a337e6b970..a5ecd5d93b0a 100644
> >> --- a/net/ipv6/udp.c
> >> +++ b/net/ipv6/udp.c
> >> @@ -1563,12 +1563,15 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
> >>
> >>          fl6->flowlabel = ip6_make_flowinfo(ipc6.tclass, fl6->flowlabel);
> >>
> >> +       lock_sock(sk);
> >>          dst = ip6_sk_dst_lookup_flow(sk, fl6, final_p, connected);
> >>          if (IS_ERR(dst)) {
> >>                  err = PTR_ERR(dst);
> >>                  dst = NULL;
> >> +               release_sock(sk);
> >>                  goto out;
> >>          }
> >> +       release_sock(sk);
> >>
> >>          if (ipc6.hlimit < 0)
> >>                  ipc6.hlimit = ip6_sk_dst_hoplimit(np, fl6, dst);
> >> --
> >> 2.34.1
> >>
> > There must be another way really.
> > You just killed UDP performance.
Eric Dumazet May 26, 2023, 4:47 p.m. UTC | #9
On Fri, May 26, 2023 at 6:41 PM Vlad Efanov <vefanov@ispras.ru> wrote:
>
> sk_dst_set() is called by sk_setup_caps().
>
> sk_dst_set() replaces dst in socket using xchg() call and we still have
> two tasks use one socket but expect different dst in sk_dst_cache.
>
>
> __sk_dst_set() is rcu protected, but it checks for socket lock.
>
>
> static inline void
> __sk_dst_set(struct sock *sk, struct dst_entry *dst)
> {
>      struct dst_entry *old_dst;
>
>      sk_tx_queue_clear(sk);
>      sk->sk_dst_pending_confirm = 0;
>      old_dst = rcu_dereference_protected(sk->sk_dst_cache,
>                          lockdep_sock_is_held(sk));
>      rcu_assign_pointer(sk->sk_dst_cache, dst);
>      dst_release(old_dst);
> }

I am quite familiar with this code.

What are you trying to say exactly ?

Please come with a V2 without grabbing the socket lock.
Vlad Efanov May 26, 2023, 4:57 p.m. UTC | #10
Yes.


There is no lock for this lines and my patch does not broken this logic.

I sugessted to set lock only for lines 1566-1571 
(ip6_sk_dst_lookup_flow() call).


Best regards,

Vlad.


On 26.05.2023 19:46, Eric Dumazet wrote:
> On Fri, May 26, 2023 at 6:09 PM Vlad Efanov <vefanov@ispras.ru> wrote:
>> Eric,
>>
>>
>> udp6_sendmsg() currently still locks the socket (on line 1595).
>>
> Not really, look more closely at lines 1580 -> 1594
>
>
>> Best regards,
>>
>> Vlad.
>>
>>
>> On 26.05.2023 18:29, Eric Dumazet wrote:
>>> On Fri, May 26, 2023 at 5:08 PM Vladislav Efanov <VEfanov@ispras.ru> wrote:
>>>> Syzkaller got the following report:
>>>> BUG: KASAN: use-after-free in sk_setup_caps+0x621/0x690 net/core/sock.c:2018
>>>> Read of size 8 at addr ffff888027f82780 by task syz-executor276/3255
>>> Please include a full report.
>>>
>>>> The function sk_setup_caps (called by ip6_sk_dst_store_flow->
>>>> ip6_dst_store) referenced already freed memory as this memory was
>>>> freed by parallel task in udpv6_sendmsg->ip6_sk_dst_lookup_flow->
>>>> sk_dst_check.
>>>>
>>>>             task1 (connect)              task2 (udp6_sendmsg)
>>>>           sk_setup_caps->sk_dst_set |
>>>>                                     |  sk_dst_check->
>>>>                                     |      sk_dst_set
>>>>                                     |      dst_release
>>>>           sk_setup_caps references  |
>>>>           to already freed dst_entry|
>>>> The reason for this race condition is: udp6_sendmsg() calls
>>>> ip6_sk_dst_lookup() without lock for sock structure and tries to
>>>> allocate/add dst_entry structure to sock structure in parallel with
>>>> "connect" task.
>>>>
>>>> Found by Linux Verification Center (linuxtesting.org) with syzkaller.
>>>>
>>>> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
>>> This is a bogus Fixes: tag
>>>
>>> In old times, UDP sendmsg() was using the socket lock.
>>>
>>> Then, in linux-4.0 Vlad Yasevich made UDP v6 sendmsg() lockless (and
>>> racy in many points)
>>>
>>>
>>>> Signed-off-by: Vladislav Efanov <VEfanov@ispras.ru>
>>>> ---
>>>>    net/ipv6/udp.c | 3 +++
>>>>    1 file changed, 3 insertions(+)
>>>>
>>>> diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
>>>> index e5a337e6b970..a5ecd5d93b0a 100644
>>>> --- a/net/ipv6/udp.c
>>>> +++ b/net/ipv6/udp.c
>>>> @@ -1563,12 +1563,15 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
>>>>
>>>>           fl6->flowlabel = ip6_make_flowinfo(ipc6.tclass, fl6->flowlabel);
>>>>
>>>> +       lock_sock(sk);
>>>>           dst = ip6_sk_dst_lookup_flow(sk, fl6, final_p, connected);
>>>>           if (IS_ERR(dst)) {
>>>>                   err = PTR_ERR(dst);
>>>>                   dst = NULL;
>>>> +               release_sock(sk);
>>>>                   goto out;
>>>>           }
>>>> +       release_sock(sk);
>>>>
>>>>           if (ipc6.hlimit < 0)
>>>>                   ipc6.hlimit = ip6_sk_dst_hoplimit(np, fl6, dst);
>>>> --
>>>> 2.34.1
>>>>
>>> There must be another way really.
>>> You just killed UDP performance.
Vlad Efanov May 26, 2023, 5 p.m. UTC | #11
I will rework the patch.

Best regards,

Vlad.


On 26.05.2023 19:47, Eric Dumazet wrote:
> On Fri, May 26, 2023 at 6:41 PM Vlad Efanov <vefanov@ispras.ru> wrote:
>> sk_dst_set() is called by sk_setup_caps().
>>
>> sk_dst_set() replaces dst in socket using xchg() call and we still have
>> two tasks use one socket but expect different dst in sk_dst_cache.
>>
>>
>> __sk_dst_set() is rcu protected, but it checks for socket lock.
>>
>>
>> static inline void
>> __sk_dst_set(struct sock *sk, struct dst_entry *dst)
>> {
>>       struct dst_entry *old_dst;
>>
>>       sk_tx_queue_clear(sk);
>>       sk->sk_dst_pending_confirm = 0;
>>       old_dst = rcu_dereference_protected(sk->sk_dst_cache,
>>                           lockdep_sock_is_held(sk));
>>       rcu_assign_pointer(sk->sk_dst_cache, dst);
>>       dst_release(old_dst);
>> }
> I am quite familiar with this code.
>
> What are you trying to say exactly ?
>
> Please come with a V2 without grabbing the socket lock.
Paolo Abeni May 26, 2023, 6:13 p.m. UTC | #12
On Fri, 2023-05-26 at 18:58 +0300, Ефанов Владислав Александрович
wrote:
> I don't think that we can just move sk_dst_set() call.
> 
> I think we can destroy dst of sendmsg task in this case.

AFAICS ip6_sk_dst_lookup_flow tries to acquire a reference to the
cached dst. If the connect() clears the cache, decreasing the refcnt,
the counter of the dst in use by sendmsg() must still be non zero.

IMHO the problem you see is that sk_setup_caps() keeps using the dst
after transferring the ownership to the dst cache, which is illegal.
The suggested patch addressed that.

If I'm wrong your syzkaller repro will keep splatting. Please have just
have a spin, thanks.

Paolo
Vlad Efanov May 29, 2023, 2:13 p.m. UTC | #13
Thank you for detail information.

The patch is reworked and being tested now.


Vlad.

On 26.05.2023 21:13, Paolo Abeni wrote:
> On Fri, 2023-05-26 at 18:58 +0300, Ефанов Владислав Александрович
> wrote:
>> I don't think that we can just move sk_dst_set() call.
>>
>> I think we can destroy dst of sendmsg task in this case.
> AFAICS ip6_sk_dst_lookup_flow tries to acquire a reference to the
> cached dst. If the connect() clears the cache, decreasing the refcnt,
> the counter of the dst in use by sendmsg() must still be non zero.
>
> IMHO the problem you see is that sk_setup_caps() keeps using the dst
> after transferring the ownership to the dst cache, which is illegal.
> The suggested patch addressed that.
>
> If I'm wrong your syzkaller repro will keep splatting. Please have just
> have a spin, thanks.
>
> Paolo
>
diff mbox series

Patch

diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index e5a337e6b970..a5ecd5d93b0a 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -1563,12 +1563,15 @@  int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
 
 	fl6->flowlabel = ip6_make_flowinfo(ipc6.tclass, fl6->flowlabel);
 
+	lock_sock(sk);
 	dst = ip6_sk_dst_lookup_flow(sk, fl6, final_p, connected);
 	if (IS_ERR(dst)) {
 		err = PTR_ERR(dst);
 		dst = NULL;
+		release_sock(sk);
 		goto out;
 	}
+	release_sock(sk);
 
 	if (ipc6.hlimit < 0)
 		ipc6.hlimit = ip6_sk_dst_hoplimit(np, fl6, dst);