diff options
| author | Pauli Virtanen <pav@iki.fi> | 2026-08-29 17:20:02 +0300 |
|---|---|---|
| committer | Luiz Augusto von Dentz <luiz.von.dentz@intel.com> | 2026-10-14 13:25:55 -0400 |
| commit | 32d86e048d8efedb7aa6cbbd3c36c3c435401c92 (patch) | |
| tree | 04ac1f23cd03b20f45a2a56095959f8aadf33e25 /net/bluetooth | |
| parent | df5175dfcf4879f6bcdadee614ac089e5627b721 (diff) | |
Bluetooth: L2CAP: hold conn->lock for __l2cap_ecred_conn_rsp_defer
__l2cap_ecred_conn_rsp_defer() > __l2cap_chan_list_id() accesses
conn->chan_l which is guarded by conn->lock. The lock fails to be held
when calling from l2cap_sock.c.
Fix by using l2cap_chan_conn_lock(), and taking the locks in required
order l2cap_conn::lock > l2cap_chan::lock > sk. Leave fast path with
sk->sk_state precheck. Move the L2CAP defer handling to
l2cap_sock_defer().
The code should also take l2cap_chan_lock() for sibling channels, but
that needs separate fix due to lock nesting.
Fixes: ab4eedb790ca ("Bluetooth: L2CAP: Fix corrupted list in hci_chan_del")
Signed-off-by: Pauli Virtanen <pav@iki.fi>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Diffstat (limited to 'net/bluetooth')
| -rw-r--r-- | net/bluetooth/l2cap_sock.c | 68 |
1 files changed, 48 insertions, 20 deletions
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 0265b6508682..dee3025f0ec2 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -1243,40 +1243,68 @@ static void l2cap_publish_rx_avail(struct l2cap_chan *chan) l2cap_chan_rx_avail(chan, -1); } -static int l2cap_sock_recvmsg(struct socket *sock, struct msghdr *msg, - size_t len, int flags) +static int l2cap_sock_defer(struct sock *sk) { - struct sock *sk = sock->sk; - struct l2cap_pinfo *pi = l2cap_pi(sk); - int err; + struct l2cap_chan *chan = l2cap_pi(sk)->chan; + bool have_conn; + int err = 0; - if (unlikely(flags & MSG_ERRQUEUE)) - return sock_recv_errqueue(sk, msg, len, SOL_BLUETOOTH, - BT_SCM_ERROR); + /* Fast path check */ + lock_sock(sk); + if (sk->sk_state != BT_CONNECT2) { + release_sock(sk); + return 0; + } + release_sock(sk); + have_conn = l2cap_chan_lock_conn(chan); lock_sock(sk); if (sk->sk_state == BT_CONNECT2 && test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) { - if (pi->chan->mode == L2CAP_MODE_EXT_FLOWCTL) { + err = 1; + + if (!have_conn) { + release_sock(sk); + err = -ENOTCONN; + } else if (chan->mode == L2CAP_MODE_EXT_FLOWCTL) { sk->sk_state = BT_CONNECTED; - pi->chan->state = BT_CONNECTED; - __l2cap_ecred_conn_rsp_defer(pi->chan); - } else if (bdaddr_type_is_le(pi->chan->src_type)) { + chan->state = BT_CONNECTED; + release_sock(sk); + __l2cap_ecred_conn_rsp_defer(chan); + } else if (bdaddr_type_is_le(chan->src_type)) { sk->sk_state = BT_CONNECTED; - pi->chan->state = BT_CONNECTED; - __l2cap_le_connect_rsp_defer(pi->chan); + chan->state = BT_CONNECTED; + release_sock(sk); + __l2cap_le_connect_rsp_defer(chan); } else { sk->sk_state = BT_CONFIG; - pi->chan->state = BT_CONFIG; - __l2cap_connect_rsp_defer(pi->chan); + chan->state = BT_CONFIG; + release_sock(sk); + __l2cap_connect_rsp_defer(chan); } - - err = 0; - goto done; + } else { + release_sock(sk); } - release_sock(sk); + l2cap_chan_unlock_conn(chan, have_conn); + return err; +} + +static int l2cap_sock_recvmsg(struct socket *sock, struct msghdr *msg, + size_t len, int flags) +{ + struct sock *sk = sock->sk; + struct l2cap_pinfo *pi = l2cap_pi(sk); + int err; + + if (unlikely(flags & MSG_ERRQUEUE)) + return sock_recv_errqueue(sk, msg, len, SOL_BLUETOOTH, + BT_SCM_ERROR); + + err = l2cap_sock_defer(sk); + if (err) + return err < 0 ? err : 0; if (sock->type == SOCK_STREAM) err = bt_sock_stream_recvmsg(sock, msg, len, flags); |
