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

tcp: do not release socket ownership in tcp_close()

syzkaller was able to hit the WARN_ON(sock_owned_by_user(sk));
in tcp_close()

While a socket is being closed, it is very possible other
threads find it in rtnetlink dump.

tcp_get_info() will acquire the socket lock for a short amount
of time (slow = lock_sock_fast(sk)/unlock_sock_fast(sk, slow);),
enough to trigger the warning.

Fixes: 67db3e4bfbc9 ("tcp: no longer hold ehash lock while calling tcp_get_info()")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reported-by: syzbot <syzkaller@googlegroups.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Eric Dumazet and committed by
David S. Miller
8873c064 744486d4

+5 -9
+1
include/net/sock.h
··· 1492 1492 lock_sock_nested(sk, 0); 1493 1493 } 1494 1494 1495 + void __release_sock(struct sock *sk); 1495 1496 void release_sock(struct sock *sk); 1496 1497 1497 1498 /* BH context may only use the following locking interface. */
+1 -1
net/core/sock.c
··· 2317 2317 finish_wait(&sk->sk_lock.wq, &wait); 2318 2318 } 2319 2319 2320 - static void __release_sock(struct sock *sk) 2320 + void __release_sock(struct sock *sk) 2321 2321 __releases(&sk->sk_lock.slock) 2322 2322 __acquires(&sk->sk_lock.slock) 2323 2323 {
+3 -8
net/ipv4/tcp.c
··· 2416 2416 sock_hold(sk); 2417 2417 sock_orphan(sk); 2418 2418 2419 - /* It is the last release_sock in its life. It will remove backlog. */ 2420 - release_sock(sk); 2421 - 2422 - 2423 - /* Now socket is owned by kernel and we acquire BH lock 2424 - * to finish close. No need to check for user refs. 2425 - */ 2426 2419 local_bh_disable(); 2427 2420 bh_lock_sock(sk); 2428 - WARN_ON(sock_owned_by_user(sk)); 2421 + /* remove backlog if any, without releasing ownership. */ 2422 + __release_sock(sk); 2429 2423 2430 2424 percpu_counter_inc(sk->sk_prot->orphan_count); 2431 2425 ··· 2488 2494 out: 2489 2495 bh_unlock_sock(sk); 2490 2496 local_bh_enable(); 2497 + release_sock(sk); 2491 2498 sock_put(sk); 2492 2499 } 2493 2500 EXPORT_SYMBOL(tcp_close);