mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
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: ab4eedb790 ("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>
This commit is contained in:
parent
df5175dfcf
commit
32d86e048d
|
|
@ -1243,6 +1243,54 @@ static void l2cap_publish_rx_avail(struct l2cap_chan *chan)
|
|||
l2cap_chan_rx_avail(chan, -1);
|
||||
}
|
||||
|
||||
static int l2cap_sock_defer(struct sock *sk)
|
||||
{
|
||||
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
|
||||
bool have_conn;
|
||||
int err = 0;
|
||||
|
||||
/* 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)) {
|
||||
err = 1;
|
||||
|
||||
if (!have_conn) {
|
||||
release_sock(sk);
|
||||
err = -ENOTCONN;
|
||||
} else if (chan->mode == L2CAP_MODE_EXT_FLOWCTL) {
|
||||
sk->sk_state = BT_CONNECTED;
|
||||
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;
|
||||
chan->state = BT_CONNECTED;
|
||||
release_sock(sk);
|
||||
__l2cap_le_connect_rsp_defer(chan);
|
||||
} else {
|
||||
sk->sk_state = BT_CONFIG;
|
||||
chan->state = BT_CONFIG;
|
||||
release_sock(sk);
|
||||
__l2cap_connect_rsp_defer(chan);
|
||||
}
|
||||
} else {
|
||||
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)
|
||||
{
|
||||
|
|
@ -1254,29 +1302,9 @@ static int l2cap_sock_recvmsg(struct socket *sock, struct msghdr *msg,
|
|||
return sock_recv_errqueue(sk, msg, len, SOL_BLUETOOTH,
|
||||
BT_SCM_ERROR);
|
||||
|
||||
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) {
|
||||
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)) {
|
||||
sk->sk_state = BT_CONNECTED;
|
||||
pi->chan->state = BT_CONNECTED;
|
||||
__l2cap_le_connect_rsp_defer(pi->chan);
|
||||
} else {
|
||||
sk->sk_state = BT_CONFIG;
|
||||
pi->chan->state = BT_CONFIG;
|
||||
__l2cap_connect_rsp_defer(pi->chan);
|
||||
}
|
||||
|
||||
err = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
release_sock(sk);
|
||||
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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user