Bluetooth: Restore locking semantics when looking up L2CAP channels
authorMat Martineau <mathewm@codeaurora.org>
Wed, 2 May 2012 16:42:00 +0000 (09:42 -0700)
committerGustavo Padovan <gustavo.padovan@collabora.co.uk>
Wed, 16 May 2012 19:13:51 +0000 (16:13 -0300)
As the comment for l2cap_get_chan_by_scid indicated, the function used
to return a locked socket.  The lock for the socket was acquired while
the channel list was also locked.

When locking was moved over to the l2cap_chan structure, the channel
lock was no longer acquired with the channel list still locked.  This
made it possible for the l2cap_chan to be deleted after
conn->chan_lock was released but before l2cap_chan_lock was called.
Making the call to l2cap_chan_lock before releasing conn->chan_lock
makes it impossible for the l2cap_chan to be deleted at the wrong
time.

Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
Reviewed-by: Ulisses Furquim <ulisses@profusion.mobi>
Signed-off-by: Gustavo Padovan <gustavo@padovan.org>
net/bluetooth/l2cap_core.c

index aea54f55c41d68060f7d84e3d6e06fc5293ec28f..285c5e13c7d51807a9f3dc9789d4cffdeffff08a 100644 (file)
@@ -98,13 +98,15 @@ static struct l2cap_chan *__l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16
 }
 
 /* Find channel with given SCID.
- * Returns locked socket */
+ * Returns locked channel. */
 static struct l2cap_chan *l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 cid)
 {
        struct l2cap_chan *c;
 
        mutex_lock(&conn->chan_lock);
        c = __l2cap_get_chan_by_scid(conn, cid);
+       if (c)
+               l2cap_chan_lock(c);
        mutex_unlock(&conn->chan_lock);
 
        return c;
@@ -3183,8 +3185,6 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
        if (!chan)
                return -ENOENT;
 
-       l2cap_chan_lock(chan);
-
        if (chan->state != BT_CONFIG && chan->state != BT_CONNECT2) {
                struct l2cap_cmd_rej_cid rej;
 
@@ -3297,8 +3297,6 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
        if (!chan)
                return 0;
 
-       l2cap_chan_lock(chan);
-
        switch (result) {
        case L2CAP_CONF_SUCCESS:
                l2cap_conf_rfc_get(chan, rsp->data, len);
@@ -4631,8 +4629,6 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
                return 0;
        }
 
-       l2cap_chan_lock(chan);
-
        BT_DBG("chan %p, len %d", chan, skb->len);
 
        if (chan->state != BT_CONNECTED)