summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPauli Virtanen <pav@iki.fi>2026-08-29 17:20:02 +0300
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>2026-10-14 13:25:55 -0400
commit32d86e048d8efedb7aa6cbbd3c36c3c435401c92 (patch)
tree04ac1f23cd03b20f45a2a56095959f8aadf33e25
parentdf5175dfcf4879f6bcdadee614ac089e5627b721 (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>
-rw-r--r--net/bluetooth/l2cap_sock.c68
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);