@@ -951,11 +951,18 @@ static u8 l2cap_get_ident(struct l2cap_conn *conn)
static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len,
void *data)
{
- struct sk_buff *skb = l2cap_build_cmd(conn, code, ident, len, data);
+ struct sk_buff *skb;
u8 flags;
+ /* Check if HCI_CONN_DROP has been set since it means hci_chan_del has
+ * been called.
+ */
+ if (test_bit(HCI_CONN_DROP, &conn->hcon->flags))
+ return;
+
BT_DBG("code 0x%2.2x", code);
+ skb = l2cap_build_cmd(conn, code, ident, len, data);
if (!skb)
return;
@@ -6785,6 +6792,12 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
u16 cid, len;
__le16 psm;
+ /* Check if hcon has been dropped then drop its packets as well */
+ if (test_bit(HCI_CONN_DROP, &hcon->flags)) {
+ kfree_skb(skb);
+ return;
+ }
+
if (hcon->state != BT_CONNECTED) {
BT_DBG("queueing pending rx skb");
skb_queue_tail(&conn->pending_rx, skb);
@@ -7466,14 +7479,33 @@ static void l2cap_recv_reset(struct l2cap_conn *conn)
conn->rx_len = 0;
}
+static struct l2cap_conn *l2cap_conn_hold_unless_zero(struct l2cap_conn *c)
+{
+ BT_DBG("conn %p orig refcnt %u", c, kref_read(&c->ref));
+
+ if (!kref_get_unless_zero(&c->ref))
+ return NULL;
+
+ return c;
+}
+
void l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
{
- struct l2cap_conn *conn = hcon->l2cap_data;
+ struct l2cap_conn *conn;
int len;
+ /* Lock hdev to access l2cap_data to avoid race with l2cap_conn_del */
+ hci_dev_lock(hcon->hdev);
+
+ conn = hcon->l2cap_data;
+
if (!conn)
conn = l2cap_conn_add(hcon);
+ conn = l2cap_conn_hold_unless_zero(conn);
+
+ hci_dev_unlock(hcon->hdev);
+
if (!conn)
goto drop;
@@ -7565,6 +7597,8 @@ void l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
break;
}
+ l2cap_conn_put(conn);
+
drop:
kfree_skb(skb);
}