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.

net/rds: fix recursive lock in rds_tcp_conn_slots_available

syzbot reported a recursive lock warning in rds_tcp_get_peer_sport() as
it calls inet6_getname() which acquires the socket lock that was already
held by __release_sock().

kworker/u8:6/2985 is trying to acquire lock:
ffff88807a07aa20 (k-sk_lock-AF_INET6){+.+.}-{0:0}, at: lock_sock include/net/sock.h:1709 [inline]
ffff88807a07aa20 (k-sk_lock-AF_INET6){+.+.}-{0:0}, at: inet6_getname+0x15d/0x650 net/ipv6/af_inet6.c:533

but task is already holding lock:
ffff88807a07aa20 (k-sk_lock-AF_INET6){+.+.}-{0:0}, at: lock_sock include/net/sock.h:1709 [inline]
ffff88807a07aa20 (k-sk_lock-AF_INET6){+.+.}-{0:0}, at: tcp_sock_set_cork+0x2c/0x2e0 net/ipv4/tcp.c:3694
lock_sock_nested+0x48/0x100 net/core/sock.c:3780
lock_sock include/net/sock.h:1709 [inline]
inet6_getname+0x15d/0x650 net/ipv6/af_inet6.c:533
rds_tcp_get_peer_sport net/rds/tcp_listen.c:70 [inline]
rds_tcp_conn_slots_available+0x288/0x470 net/rds/tcp_listen.c:149
rds_recv_hs_exthdrs+0x60f/0x7c0 net/rds/recv.c:265
rds_recv_incoming+0x9f6/0x12d0 net/rds/recv.c:389
rds_tcp_data_recv+0x7f1/0xa40 net/rds/tcp_recv.c:243
__tcp_read_sock+0x196/0x970 net/ipv4/tcp.c:1702
rds_tcp_read_sock net/rds/tcp_recv.c:277 [inline]
rds_tcp_data_ready+0x369/0x950 net/rds/tcp_recv.c:331
tcp_rcv_established+0x19e9/0x2670 net/ipv4/tcp_input.c:6675
tcp_v6_do_rcv+0x8eb/0x1ba0 net/ipv6/tcp_ipv6.c:1609
sk_backlog_rcv include/net/sock.h:1185 [inline]
__release_sock+0x1b8/0x3a0 net/core/sock.c:3213

Reading from the socket struct directly is safe from possible paths. For
rds_tcp_accept_one(), the socket has just been accepted and is not yet
exposed to concurrent access. For rds_tcp_conn_slots_available(), direct
access avoids the recursive deadlock seen during backlog processing
where the socket lock is already held from the __release_sock().

However, rds_tcp_conn_slots_available() is also called from the normal
softirq path via tcp_data_ready() where the lock is not held. This is
also safe because inet_dport is a stable 16 bits field. A READ_ONCE()
annotation as the value might be accessed lockless in a concurrent
access context.

Note that it is also safe to call rds_tcp_conn_slots_available() from
rds_conn_shutdown() because the fan-out is disabled.

Fixes: 9d27a0fb122f ("net/rds: Trigger rds_send_ping() more than once")
Reported-by: syzbot+5efae91f60932839f0a5@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=5efae91f60932839f0a5
Signed-off-by: Fernando Fernandez Mancera <fmancera@suse.de>
Reviewed-by: Allison Henderson <achender@kernel.org>
Link: https://patch.msgid.link/20260219075738.4403-1-fmancera@suse.de
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

authored by

Fernando Fernandez Mancera and committed by
Paolo Abeni
021fd0f8 3aa67762

+7 -22
+3
net/rds/connection.c
··· 455 455 rcu_read_unlock(); 456 456 } 457 457 458 + /* we do not hold the socket lock here but it is safe because 459 + * fan-out is disabled when calling conn_slots_available() 460 + */ 458 461 if (conn->c_trans->conn_slots_available) 459 462 conn->c_trans->conn_slots_available(conn, false); 460 463 }
+4 -22
net/rds/tcp_listen.c
··· 59 59 static int 60 60 rds_tcp_get_peer_sport(struct socket *sock) 61 61 { 62 - union { 63 - struct sockaddr_storage storage; 64 - struct sockaddr addr; 65 - struct sockaddr_in sin; 66 - struct sockaddr_in6 sin6; 67 - } saddr; 68 - int sport; 62 + struct sock *sk = sock->sk; 69 63 70 - if (kernel_getpeername(sock, &saddr.addr) >= 0) { 71 - switch (saddr.addr.sa_family) { 72 - case AF_INET: 73 - sport = ntohs(saddr.sin.sin_port); 74 - break; 75 - case AF_INET6: 76 - sport = ntohs(saddr.sin6.sin6_port); 77 - break; 78 - default: 79 - sport = -1; 80 - } 81 - } else { 82 - sport = -1; 83 - } 64 + if (!sk) 65 + return -1; 84 66 85 - return sport; 67 + return ntohs(READ_ONCE(inet_sk(sk)->inet_dport)); 86 68 } 87 69 88 70 /* rds_tcp_accept_one_path(): if accepting on cp_index > 0, make sure the