]> Pileus Git - ~andy/linux/blobdiff - net/bluetooth/l2cap_sock.c
Bluetooth: Remove sk member from struct l2cap_chan
[~andy/linux] / net / bluetooth / l2cap_sock.c
index 34e5a58ffca87df4df67d57b8f3df77ed69c92ce..7cc24d263caaab45af9d8bbe5be09ebda20e87a4 100644 (file)
@@ -159,8 +159,31 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr,
        if (!bdaddr_type_is_valid(la.l2_bdaddr_type))
                return -EINVAL;
 
-       if (chan->src_type == BDADDR_BREDR && la.l2_bdaddr_type != BDADDR_BREDR)
-               return -EINVAL;
+       /* Check that the socket wasn't bound to something that
+        * conflicts with the address given to connect(). If chan->src
+        * is BDADDR_ANY it means bind() was never used, in which case
+        * chan->src_type and la.l2_bdaddr_type do not need to match.
+        */
+       if (chan->src_type == BDADDR_BREDR && bacmp(&chan->src, BDADDR_ANY) &&
+           bdaddr_type_is_le(la.l2_bdaddr_type)) {
+               /* Old user space versions will try to incorrectly bind
+                * the ATT socket using BDADDR_BREDR. We need to accept
+                * this and fix up the source address type only when
+                * both the source CID and destination CID indicate
+                * ATT. Anything else is an invalid combination.
+                */
+               if (chan->scid != L2CAP_CID_ATT ||
+                   la.l2_cid != __constant_cpu_to_le16(L2CAP_CID_ATT))
+                       return -EINVAL;
+
+               /* We don't have the hdev available here to make a
+                * better decision on random vs public, but since all
+                * user space versions that exhibit this issue anyway do
+                * not support random local addresses assuming public
+                * here is good enough.
+                */
+               chan->src_type = BDADDR_LE_PUBLIC;
+       }
 
        if (chan->src_type != BDADDR_BREDR && la.l2_bdaddr_type == BDADDR_BREDR)
                return -EINVAL;
@@ -1019,6 +1042,8 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan)
 {
        struct sock *sk, *parent = chan->data;
 
+       lock_sock(parent);
+
        /* Check for backlog size */
        if (sk_acceptq_is_full(parent)) {
                BT_DBG("backlog full %d", parent->sk_ack_backlog);
@@ -1036,6 +1061,8 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan)
 
        bt_accept_enqueue(parent, sk);
 
+       release_sock(parent);
+
        return l2cap_pi(sk)->chan;
 }
 
@@ -1136,16 +1163,19 @@ static void l2cap_sock_state_change_cb(struct l2cap_chan *chan, int state,
 static struct sk_buff *l2cap_sock_alloc_skb_cb(struct l2cap_chan *chan,
                                               unsigned long len, int nb)
 {
+       struct sock *sk = chan->data;
        struct sk_buff *skb;
        int err;
 
        l2cap_chan_unlock(chan);
-       skb = bt_skb_send_alloc(chan->sk, len, nb, &err);
+       skb = bt_skb_send_alloc(sk, len, nb, &err);
        l2cap_chan_lock(chan);
 
        if (!skb)
                return ERR_PTR(err);
 
+       bt_cb(skb)->chan = chan;
+
        return skb;
 }
 
@@ -1171,11 +1201,15 @@ static void l2cap_sock_ready_cb(struct l2cap_chan *chan)
 
 static void l2cap_sock_defer_cb(struct l2cap_chan *chan)
 {
-       struct sock *sk = chan->data;
-       struct sock *parent = bt_sk(sk)->parent;
+       struct sock *parent, *sk = chan->data;
+
+       lock_sock(sk);
 
+       parent = bt_sk(sk)->parent;
        if (parent)
                parent->sk_data_ready(parent, 0);
+
+       release_sock(sk);
 }
 
 static void l2cap_sock_resume_cb(struct l2cap_chan *chan)
@@ -1340,8 +1374,6 @@ static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
 
        l2cap_chan_hold(chan);
 
-       chan->sk = sk;
-
        l2cap_pi(sk)->chan = chan;
 
        return sk;