Skip to content

Commit

Permalink
Bluetooth: invert locking order in connect path
Browse files Browse the repository at this point in the history
This move some checking code that was in l2cap_sock_connect() to
l2cap_chan_connect(). Thus we can invert the lock calls, i.e., call
lock_sock() before hci_dev_lock() to avoid a deadlock scenario.

Acked-by: Marcel Holtmann <marcel@holtmann.org>
Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
  • Loading branch information
Gustavo F. Padovan committed Dec 18, 2011
1 parent f878fca commit 03a0019
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 61 deletions.
3 changes: 2 additions & 1 deletion include/net/bluetooth/l2cap.h
Original file line number Diff line number Diff line change
Expand Up @@ -806,7 +806,8 @@ int l2cap_add_scid(struct l2cap_chan *chan, __u16 scid);
struct l2cap_chan *l2cap_chan_create(struct sock *sk);
void l2cap_chan_close(struct l2cap_chan *chan, int reason);
void l2cap_chan_destroy(struct l2cap_chan *chan);
int l2cap_chan_connect(struct l2cap_chan *chan);
inline int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
bdaddr_t *dst);
int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
u32 priority);
void l2cap_chan_busy(struct l2cap_chan *chan, int busy);
Expand Down
58 changes: 56 additions & 2 deletions net/bluetooth/l2cap_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -1144,11 +1144,10 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm, bdaddr
return c1;
}

int l2cap_chan_connect(struct l2cap_chan *chan)
inline int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdaddr_t *dst)
{
struct sock *sk = chan->sk;
bdaddr_t *src = &bt_sk(sk)->src;
bdaddr_t *dst = &bt_sk(sk)->dst;
struct l2cap_conn *conn;
struct hci_conn *hcon;
struct hci_dev *hdev;
Expand All @@ -1164,6 +1163,61 @@ int l2cap_chan_connect(struct l2cap_chan *chan)

hci_dev_lock(hdev);

lock_sock(sk);

/* PSM must be odd and lsb of upper byte must be 0 */
if ((__le16_to_cpu(psm) & 0x0101) != 0x0001 && !cid &&
chan->chan_type != L2CAP_CHAN_RAW) {
err = -EINVAL;
goto done;
}

if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && !(psm || cid)) {
err = -EINVAL;
goto done;
}

switch (chan->mode) {
case L2CAP_MODE_BASIC:
break;
case L2CAP_MODE_ERTM:
case L2CAP_MODE_STREAMING:
if (!disable_ertm)
break;
/* fall through */
default:
err = -ENOTSUPP;
goto done;
}

switch (sk->sk_state) {
case BT_CONNECT:
case BT_CONNECT2:
case BT_CONFIG:
/* Already connecting */
err = 0;
goto done;

case BT_CONNECTED:
/* Already connected */
err = -EISCONN;
goto done;

case BT_OPEN:
case BT_BOUND:
/* Can connect */
break;

default:
err = -EBADFD;
goto done;
}

/* Set destination address and psm */
bacpy(&bt_sk(sk)->dst, src);
chan->psm = psm;
chan->dcid = cid;

auth_type = l2cap_get_auth_type(chan);

if (chan->dcid == L2CAP_CID_LE_DATA)
Expand Down
61 changes: 3 additions & 58 deletions net/bluetooth/l2cap_sock.c
Original file line number Diff line number Diff line change
Expand Up @@ -121,70 +121,15 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al
if (la.l2_cid && la.l2_psm)
return -EINVAL;

lock_sock(sk);

if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED
&& !(la.l2_psm || la.l2_cid)) {
err = -EINVAL;
goto done;
}

switch (chan->mode) {
case L2CAP_MODE_BASIC:
break;
case L2CAP_MODE_ERTM:
case L2CAP_MODE_STREAMING:
if (!disable_ertm)
break;
/* fall through */
default:
err = -ENOTSUPP;
goto done;
}

switch (sk->sk_state) {
case BT_CONNECT:
case BT_CONNECT2:
case BT_CONFIG:
/* Already connecting */
goto wait;

case BT_CONNECTED:
/* Already connected */
err = -EISCONN;
goto done;

case BT_OPEN:
case BT_BOUND:
/* Can connect */
break;

default:
err = -EBADFD;
goto done;
}

/* PSM must be odd and lsb of upper byte must be 0 */
if ((__le16_to_cpu(la.l2_psm) & 0x0101) != 0x0001 && !la.l2_cid &&
chan->chan_type != L2CAP_CHAN_RAW) {
err = -EINVAL;
goto done;
}

/* Set destination address and psm */
bacpy(&bt_sk(sk)->dst, &la.l2_bdaddr);
chan->psm = la.l2_psm;
chan->dcid = la.l2_cid;

err = l2cap_chan_connect(chan);
err = l2cap_chan_connect(chan, la.l2_psm, la.l2_cid, &la.l2_bdaddr);
if (err)
goto done;

wait:
err = bt_sock_wait_state(sk, BT_CONNECTED,
sock_sndtimeo(sk, flags & O_NONBLOCK));
done:
release_sock(sk);
if (sock_owned_by_user(sk))
release_sock(sk);
return err;
}

Expand Down

0 comments on commit 03a0019

Please sign in to comment.