Bluetooth: Fix reference counting of global L2CAP channels
When looking up entries from the global L2CAP channel list there needs
to be a guarantee that other code doesn't go and remove the entry after
a channel has been returned by the lookup function. This patch makes
sure that the channel reference is incremented before the read lock is
released in the global channel lookup functions. The patch also adds the
corresponding l2cap_chan_put() calls once the channels pointers are
no-longer needed.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 46547b9..a47a14e 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -1440,6 +1440,7 @@
src_match = !bacmp(&c->src, src);
dst_match = !bacmp(&c->dst, dst);
if (src_match && dst_match) {
+ l2cap_chan_hold(c);
read_unlock(&chan_list_lock);
return c;
}
@@ -1453,6 +1454,9 @@
}
}
+ if (c1)
+ l2cap_chan_hold(c1);
+
read_unlock(&chan_list_lock);
return c1;
@@ -1475,13 +1479,13 @@
/* Client ATT sockets should override the server one */
if (__l2cap_get_chan_by_dcid(conn, L2CAP_CID_ATT))
- return;
+ goto put;
dst_type = bdaddr_type(hcon, hcon->dst_type);
/* If device is blocked, do not create a channel for it */
if (hci_bdaddr_list_lookup(&hdev->blacklist, &hcon->dst, dst_type))
- return;
+ goto put;
/* For LE slave connections, make sure the connection interval
* is in the range of the minium and maximum interval that has
@@ -1517,6 +1521,8 @@
clean:
l2cap_chan_unlock(pchan);
+put:
+ l2cap_chan_put(pchan);
}
static void l2cap_conn_ready(struct l2cap_conn *conn)
@@ -1794,6 +1800,7 @@
src_match = !bacmp(&c->src, src);
dst_match = !bacmp(&c->dst, dst);
if (src_match && dst_match) {
+ l2cap_chan_hold(c);
read_unlock(&chan_list_lock);
return c;
}
@@ -1807,6 +1814,9 @@
}
}
+ if (c1)
+ l2cap_chan_hold(c1);
+
read_unlock(&chan_list_lock);
return c1;
@@ -3884,6 +3894,7 @@
response:
l2cap_chan_unlock(pchan);
mutex_unlock(&conn->chan_lock);
+ l2cap_chan_put(pchan);
sendresp:
rsp.scid = cpu_to_le16(scid);
@@ -5497,6 +5508,7 @@
response_unlock:
l2cap_chan_unlock(pchan);
mutex_unlock(&conn->chan_lock);
+ l2cap_chan_put(pchan);
if (result == L2CAP_CR_PEND)
return 0;
@@ -6845,12 +6857,12 @@
struct l2cap_chan *chan;
if (hcon->type != ACL_LINK)
- goto drop;
+ goto free_skb;
chan = l2cap_global_chan_by_psm(0, psm, &hcon->src, &hcon->dst,
ACL_LINK);
if (!chan)
- goto drop;
+ goto free_skb;
BT_DBG("chan %p, len %d", chan, skb->len);
@@ -6864,10 +6876,14 @@
bacpy(&bt_cb(skb)->bdaddr, &hcon->dst);
bt_cb(skb)->psm = psm;
- if (!chan->ops->recv(chan, skb))
+ if (!chan->ops->recv(chan, skb)) {
+ l2cap_chan_put(chan);
return;
+ }
drop:
+ l2cap_chan_put(chan);
+free_skb:
kfree_skb(skb);
}
@@ -6878,22 +6894,26 @@
struct l2cap_chan *chan;
if (hcon->type != LE_LINK)
- goto drop;
+ goto free_skb;
chan = l2cap_global_chan_by_scid(BT_CONNECTED, L2CAP_CID_ATT,
&hcon->src, &hcon->dst);
if (!chan)
- goto drop;
+ goto free_skb;
BT_DBG("chan %p, len %d", chan, skb->len);
if (chan->imtu < skb->len)
goto drop;
- if (!chan->ops->recv(chan, skb))
+ if (!chan->ops->recv(chan, skb)) {
+ l2cap_chan_put(chan);
return;
+ }
drop:
+ l2cap_chan_put(chan);
+free_skb:
kfree_skb(skb);
}