diff options
| author | Juan Perdomo <jcperdomo100@gmail.com> | 2026-09-12 23:09:45 -0400 |
|---|---|---|
| committer | Luiz Augusto von Dentz <luiz.von.dentz@intel.com> | 2026-09-15 14:55:41 -0400 |
| commit | 801fb950cae7048eb7d83b18857d1ca37b8cd5a4 (patch) | |
| tree | 8d2780c739c3ddc9422303e688f41791f6f1dc2c | |
| parent | 555cd2bd860e7c4bdc3f4e4405b05515b0d9bc87 (diff) | |
| download | linux-next-801fb950cae7048eb7d83b18857d1ca37b8cd5a4.tar.gz linux-next-801fb950cae7048eb7d83b18857d1ca37b8cd5a4.zip | |
Bluetooth: RFCOMM: avoid socket lock inversion in listener cleanup
rfcomm_sock_cleanup_listen() closes unaccepted child sockets through
rfcomm_sock_close(), which takes the child socket lock before
rfcomm_dlc_close() acquires rfcomm_mutex. The RFCOMM worker takes these
locks in reverse order while handling connections and DLC state changes,
so lockdep reports a possible deadlock.
Close dequeued children without taking their socket lock. The accept queue
owns a reference to each child, and bt_accept_dequeue() locks the child
while unlinking it and clearing its parent pointer.
Dropping the child lock makes it important to prevent a concurrent
rfcomm_connect_ind() from enqueueing a new child after cleanup observes an
empty queue. Set a listening socket to BT_CLOSED while its lock is still
held, before dropping the lock and draining the queue. The state check in
rfcomm_connect_ind() then rejects new children once cleanup starts.
Reported-by: syzbot+0cece8fa7d83523f47a3@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=0cece8fa7d83523f47a3
Fixes: b7ce436a5d79 ("Bluetooth: switch to lock_sock in RFCOMM")
Signed-off-by: Juan Perdomo <jcperdomo100@gmail.com>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
| -rw-r--r-- | net/bluetooth/rfcomm/sock.c | 13 |
1 files changed, 10 insertions, 3 deletions
diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c index 958081adb9b5..e2486bc11cbc 100644 --- a/net/bluetooth/rfcomm/sock.c +++ b/net/bluetooth/rfcomm/sock.c @@ -242,9 +242,7 @@ static void __rfcomm_sock_close(struct sock *sk) */ static void rfcomm_sock_close(struct sock *sk) { - lock_sock(sk); __rfcomm_sock_close(sk); - release_sock(sk); } static void rfcomm_sock_init(struct sock *sk, struct sock *parent) @@ -905,6 +903,7 @@ static int rfcomm_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsig static int rfcomm_sock_shutdown(struct socket *sock, int how) { struct sock *sk = sock->sk; + bool cleanup_listen = false; int err = 0; BT_DBG("sock %p, sk %p", sock, sk); @@ -915,9 +914,17 @@ static int rfcomm_sock_shutdown(struct socket *sock, int how) lock_sock(sk); if (!sk->sk_shutdown) { sk->sk_shutdown = SHUTDOWN_MASK; + if (sk->sk_state == BT_LISTEN) { + /* Block new children before cleaning up without sk lock. */ + sk->sk_state = BT_CLOSED; + cleanup_listen = true; + } release_sock(sk); - __rfcomm_sock_close(sk); + if (cleanup_listen) + rfcomm_sock_cleanup_listen(sk); + else + __rfcomm_sock_close(sk); lock_sock(sk); if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime && |
