bluetooth pull request for net:
- btintel_pcie: Fix a potential race condition - L2CAP: Fix slab-use-after-free Read in l2cap_send_cmd - L2CAP: Fix corrupted list in hci_chan_del -----BEGIN PGP SIGNATURE----- iQJNBAABCAA3FiEE7E6oRXp8w05ovYr/9JCA4xAyCykFAmeuG3IZHGx1aXoudm9u LmRlbnR6QGludGVsLmNvbQAKCRD0kIDjEDILKZuXD/98tptadcw3dNb8AkYT3txb 4Yd4Ksg+j0N0+Fgvs7HQIMYSRmmXkJkvqP1QO376KRDnQ1OLWAH825zh4Ut0G8BK cT5MkpYZ0QpOVqCTrBBcZY18NBjTYZcIfElLJvGC1dLeSnK4akYXIwydGTAZWhQx tqQHkPdfAmf26Aay4ESNESCaiiQdD0ZyvzlsmmnHzjJ42FDWQUrkMk7sdRXc4Xik TtYT96Ogf0kVebdMlG1lKknc7YueQdF1vYUmm8ItOutzRb2bPlYGtCh2nH0bZ2ZB k1ngijrrwR77wOqjs7pEP4sZ202y8ozqS1i+KL/TgMPoGiCoUcdvj+Z9h3SuWYq7 lfGC9W1fY9Guio031OPndvIyifEH6v+WsrQchHZ8OocjdVEJnzTd3RsB6bBadlNb 8szG72Qxhz0RNfqp9zhguzwxYSsa8tFBDeuHiIMt+/F/QoJITCN8kebf1h02PFES kKPUN+l4KBoD3A/qL2CKj041FEfKad5ItX8MVRZSc5IFfOEh9PyaBpmj4ZokCt9J otuzKJU7G7pMqBDvSi7qGWbd4OENV1+q0+gQPM7tgIZ75bauIkYhrvGvpB9AMGiE QxtslE9PIVR3Oq184EsRSGhzuISoC3nvwd+5bCls7YyNfNkplTZRtr/zhf0RoE/t +yUIc/hVkGWTIvPKXJDY/g== =LE1H -----END PGP SIGNATURE----- Merge tag 'for-net-2025-02-13' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth Luiz Augusto von Dentz says: ==================== bluetooth pull request for net: - btintel_pcie: Fix a potential race condition - L2CAP: Fix slab-use-after-free Read in l2cap_send_cmd - L2CAP: Fix corrupted list in hci_chan_del * tag 'for-net-2025-02-13' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth: Bluetooth: L2CAP: Fix corrupted list in hci_chan_del Bluetooth: btintel_pcie: Fix a potential race condition Bluetooth: L2CAP: Fix slab-use-after-free Read in l2cap_send_cmd ==================== Link: https://patch.msgid.link/20250213162446.617632-1-luiz.dentz@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
82c260c880
4 changed files with 92 additions and 100 deletions
|
@ -1320,6 +1320,10 @@ static int btintel_pcie_send_frame(struct hci_dev *hdev,
|
|||
if (opcode == 0xfc01)
|
||||
btintel_pcie_inject_cmd_complete(hdev, opcode);
|
||||
}
|
||||
/* Firmware raises alive interrupt on HCI_OP_RESET */
|
||||
if (opcode == HCI_OP_RESET)
|
||||
data->gp0_received = false;
|
||||
|
||||
hdev->stat.cmd_tx++;
|
||||
break;
|
||||
case HCI_ACLDATA_PKT:
|
||||
|
@ -1357,7 +1361,6 @@ static int btintel_pcie_send_frame(struct hci_dev *hdev,
|
|||
opcode, btintel_pcie_alivectxt_state2str(old_ctxt),
|
||||
btintel_pcie_alivectxt_state2str(data->alive_intr_ctxt));
|
||||
if (opcode == HCI_OP_RESET) {
|
||||
data->gp0_received = false;
|
||||
ret = wait_event_timeout(data->gp0_wait_q,
|
||||
data->gp0_received,
|
||||
msecs_to_jiffies(BTINTEL_DEFAULT_INTR_TIMEOUT_MS));
|
||||
|
|
|
@ -668,7 +668,7 @@ struct l2cap_conn {
|
|||
struct l2cap_chan *smp;
|
||||
|
||||
struct list_head chan_l;
|
||||
struct mutex chan_lock;
|
||||
struct mutex lock;
|
||||
struct kref ref;
|
||||
struct list_head users;
|
||||
};
|
||||
|
@ -970,6 +970,7 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err);
|
|||
void l2cap_send_conn_req(struct l2cap_chan *chan);
|
||||
|
||||
struct l2cap_conn *l2cap_conn_get(struct l2cap_conn *conn);
|
||||
struct l2cap_conn *l2cap_conn_hold_unless_zero(struct l2cap_conn *conn);
|
||||
void l2cap_conn_put(struct l2cap_conn *conn);
|
||||
|
||||
int l2cap_register_user(struct l2cap_conn *conn, struct l2cap_user *user);
|
||||
|
|
|
@ -119,7 +119,6 @@ static struct l2cap_chan *l2cap_get_chan_by_scid(struct l2cap_conn *conn,
|
|||
{
|
||||
struct l2cap_chan *c;
|
||||
|
||||
mutex_lock(&conn->chan_lock);
|
||||
c = __l2cap_get_chan_by_scid(conn, cid);
|
||||
if (c) {
|
||||
/* Only lock if chan reference is not 0 */
|
||||
|
@ -127,7 +126,6 @@ static struct l2cap_chan *l2cap_get_chan_by_scid(struct l2cap_conn *conn,
|
|||
if (c)
|
||||
l2cap_chan_lock(c);
|
||||
}
|
||||
mutex_unlock(&conn->chan_lock);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
@ -140,7 +138,6 @@ static struct l2cap_chan *l2cap_get_chan_by_dcid(struct l2cap_conn *conn,
|
|||
{
|
||||
struct l2cap_chan *c;
|
||||
|
||||
mutex_lock(&conn->chan_lock);
|
||||
c = __l2cap_get_chan_by_dcid(conn, cid);
|
||||
if (c) {
|
||||
/* Only lock if chan reference is not 0 */
|
||||
|
@ -148,7 +145,6 @@ static struct l2cap_chan *l2cap_get_chan_by_dcid(struct l2cap_conn *conn,
|
|||
if (c)
|
||||
l2cap_chan_lock(c);
|
||||
}
|
||||
mutex_unlock(&conn->chan_lock);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
@ -418,7 +414,7 @@ static void l2cap_chan_timeout(struct work_struct *work)
|
|||
if (!conn)
|
||||
return;
|
||||
|
||||
mutex_lock(&conn->chan_lock);
|
||||
mutex_lock(&conn->lock);
|
||||
/* __set_chan_timer() calls l2cap_chan_hold(chan) while scheduling
|
||||
* this work. No need to call l2cap_chan_hold(chan) here again.
|
||||
*/
|
||||
|
@ -439,7 +435,7 @@ static void l2cap_chan_timeout(struct work_struct *work)
|
|||
l2cap_chan_unlock(chan);
|
||||
l2cap_chan_put(chan);
|
||||
|
||||
mutex_unlock(&conn->chan_lock);
|
||||
mutex_unlock(&conn->lock);
|
||||
}
|
||||
|
||||
struct l2cap_chan *l2cap_chan_create(void)
|
||||
|
@ -641,9 +637,9 @@ void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
|
|||
|
||||
void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
|
||||
{
|
||||
mutex_lock(&conn->chan_lock);
|
||||
mutex_lock(&conn->lock);
|
||||
__l2cap_chan_add(conn, chan);
|
||||
mutex_unlock(&conn->chan_lock);
|
||||
mutex_unlock(&conn->lock);
|
||||
}
|
||||
|
||||
void l2cap_chan_del(struct l2cap_chan *chan, int err)
|
||||
|
@ -731,9 +727,9 @@ void l2cap_chan_list(struct l2cap_conn *conn, l2cap_chan_func_t func,
|
|||
if (!conn)
|
||||
return;
|
||||
|
||||
mutex_lock(&conn->chan_lock);
|
||||
mutex_lock(&conn->lock);
|
||||
__l2cap_chan_list(conn, func, data);
|
||||
mutex_unlock(&conn->chan_lock);
|
||||
mutex_unlock(&conn->lock);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(l2cap_chan_list);
|
||||
|
@ -745,7 +741,7 @@ static void l2cap_conn_update_id_addr(struct work_struct *work)
|
|||
struct hci_conn *hcon = conn->hcon;
|
||||
struct l2cap_chan *chan;
|
||||
|
||||
mutex_lock(&conn->chan_lock);
|
||||
mutex_lock(&conn->lock);
|
||||
|
||||
list_for_each_entry(chan, &conn->chan_l, list) {
|
||||
l2cap_chan_lock(chan);
|
||||
|
@ -754,7 +750,7 @@ static void l2cap_conn_update_id_addr(struct work_struct *work)
|
|||
l2cap_chan_unlock(chan);
|
||||
}
|
||||
|
||||
mutex_unlock(&conn->chan_lock);
|
||||
mutex_unlock(&conn->lock);
|
||||
}
|
||||
|
||||
static void l2cap_chan_le_connect_reject(struct l2cap_chan *chan)
|
||||
|
@ -948,6 +944,16 @@ static u8 l2cap_get_ident(struct l2cap_conn *conn)
|
|||
return id;
|
||||
}
|
||||
|
||||
static void l2cap_send_acl(struct l2cap_conn *conn, struct sk_buff *skb,
|
||||
u8 flags)
|
||||
{
|
||||
/* Check if the hcon still valid before attempting to send */
|
||||
if (hci_conn_valid(conn->hcon->hdev, conn->hcon))
|
||||
hci_send_acl(conn->hchan, skb, flags);
|
||||
else
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len,
|
||||
void *data)
|
||||
{
|
||||
|
@ -970,7 +976,7 @@ static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len,
|
|||
bt_cb(skb)->force_active = BT_POWER_FORCE_ACTIVE_ON;
|
||||
skb->priority = HCI_PRIO_MAX;
|
||||
|
||||
hci_send_acl(conn->hchan, skb, flags);
|
||||
l2cap_send_acl(conn, skb, flags);
|
||||
}
|
||||
|
||||
static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb)
|
||||
|
@ -1497,8 +1503,6 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
|
|||
|
||||
BT_DBG("conn %p", conn);
|
||||
|
||||
mutex_lock(&conn->chan_lock);
|
||||
|
||||
list_for_each_entry_safe(chan, tmp, &conn->chan_l, list) {
|
||||
l2cap_chan_lock(chan);
|
||||
|
||||
|
@ -1567,8 +1571,6 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
|
|||
|
||||
l2cap_chan_unlock(chan);
|
||||
}
|
||||
|
||||
mutex_unlock(&conn->chan_lock);
|
||||
}
|
||||
|
||||
static void l2cap_le_conn_ready(struct l2cap_conn *conn)
|
||||
|
@ -1614,7 +1616,7 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
|
|||
if (hcon->type == ACL_LINK)
|
||||
l2cap_request_info(conn);
|
||||
|
||||
mutex_lock(&conn->chan_lock);
|
||||
mutex_lock(&conn->lock);
|
||||
|
||||
list_for_each_entry(chan, &conn->chan_l, list) {
|
||||
|
||||
|
@ -1632,7 +1634,7 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
|
|||
l2cap_chan_unlock(chan);
|
||||
}
|
||||
|
||||
mutex_unlock(&conn->chan_lock);
|
||||
mutex_unlock(&conn->lock);
|
||||
|
||||
if (hcon->type == LE_LINK)
|
||||
l2cap_le_conn_ready(conn);
|
||||
|
@ -1647,14 +1649,10 @@ static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err)
|
|||
|
||||
BT_DBG("conn %p", conn);
|
||||
|
||||
mutex_lock(&conn->chan_lock);
|
||||
|
||||
list_for_each_entry(chan, &conn->chan_l, list) {
|
||||
if (test_bit(FLAG_FORCE_RELIABLE, &chan->flags))
|
||||
l2cap_chan_set_err(chan, err);
|
||||
}
|
||||
|
||||
mutex_unlock(&conn->chan_lock);
|
||||
}
|
||||
|
||||
static void l2cap_info_timeout(struct work_struct *work)
|
||||
|
@ -1665,7 +1663,9 @@ static void l2cap_info_timeout(struct work_struct *work)
|
|||
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
|
||||
conn->info_ident = 0;
|
||||
|
||||
mutex_lock(&conn->lock);
|
||||
l2cap_conn_start(conn);
|
||||
mutex_unlock(&conn->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1757,6 +1757,8 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
|
|||
|
||||
BT_DBG("hcon %p conn %p, err %d", hcon, conn, err);
|
||||
|
||||
mutex_lock(&conn->lock);
|
||||
|
||||
kfree_skb(conn->rx_skb);
|
||||
|
||||
skb_queue_purge(&conn->pending_rx);
|
||||
|
@ -1775,8 +1777,6 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
|
|||
/* Force the connection to be immediately dropped */
|
||||
hcon->disc_timeout = 0;
|
||||
|
||||
mutex_lock(&conn->chan_lock);
|
||||
|
||||
/* Kill channels */
|
||||
list_for_each_entry_safe(chan, l, &conn->chan_l, list) {
|
||||
l2cap_chan_hold(chan);
|
||||
|
@ -1790,15 +1790,14 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
|
|||
l2cap_chan_put(chan);
|
||||
}
|
||||
|
||||
mutex_unlock(&conn->chan_lock);
|
||||
|
||||
hci_chan_del(conn->hchan);
|
||||
|
||||
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT)
|
||||
cancel_delayed_work_sync(&conn->info_timer);
|
||||
|
||||
hcon->l2cap_data = NULL;
|
||||
hci_chan_del(conn->hchan);
|
||||
conn->hchan = NULL;
|
||||
|
||||
hcon->l2cap_data = NULL;
|
||||
mutex_unlock(&conn->lock);
|
||||
l2cap_conn_put(conn);
|
||||
}
|
||||
|
||||
|
@ -2916,8 +2915,6 @@ static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb)
|
|||
|
||||
BT_DBG("conn %p", conn);
|
||||
|
||||
mutex_lock(&conn->chan_lock);
|
||||
|
||||
list_for_each_entry(chan, &conn->chan_l, list) {
|
||||
if (chan->chan_type != L2CAP_CHAN_RAW)
|
||||
continue;
|
||||
|
@ -2932,8 +2929,6 @@ static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb)
|
|||
if (chan->ops->recv(chan, nskb))
|
||||
kfree_skb(nskb);
|
||||
}
|
||||
|
||||
mutex_unlock(&conn->chan_lock);
|
||||
}
|
||||
|
||||
/* ---- L2CAP signalling commands ---- */
|
||||
|
@ -3952,7 +3947,6 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
|
|||
goto response;
|
||||
}
|
||||
|
||||
mutex_lock(&conn->chan_lock);
|
||||
l2cap_chan_lock(pchan);
|
||||
|
||||
/* Check if the ACL is secure enough (if not SDP) */
|
||||
|
@ -4059,7 +4053,6 @@ response:
|
|||
}
|
||||
|
||||
l2cap_chan_unlock(pchan);
|
||||
mutex_unlock(&conn->chan_lock);
|
||||
l2cap_chan_put(pchan);
|
||||
}
|
||||
|
||||
|
@ -4098,27 +4091,19 @@ static int l2cap_connect_create_rsp(struct l2cap_conn *conn,
|
|||
BT_DBG("dcid 0x%4.4x scid 0x%4.4x result 0x%2.2x status 0x%2.2x",
|
||||
dcid, scid, result, status);
|
||||
|
||||
mutex_lock(&conn->chan_lock);
|
||||
|
||||
if (scid) {
|
||||
chan = __l2cap_get_chan_by_scid(conn, scid);
|
||||
if (!chan) {
|
||||
err = -EBADSLT;
|
||||
goto unlock;
|
||||
}
|
||||
if (!chan)
|
||||
return -EBADSLT;
|
||||
} else {
|
||||
chan = __l2cap_get_chan_by_ident(conn, cmd->ident);
|
||||
if (!chan) {
|
||||
err = -EBADSLT;
|
||||
goto unlock;
|
||||
}
|
||||
if (!chan)
|
||||
return -EBADSLT;
|
||||
}
|
||||
|
||||
chan = l2cap_chan_hold_unless_zero(chan);
|
||||
if (!chan) {
|
||||
err = -EBADSLT;
|
||||
goto unlock;
|
||||
}
|
||||
if (!chan)
|
||||
return -EBADSLT;
|
||||
|
||||
err = 0;
|
||||
|
||||
|
@ -4156,9 +4141,6 @@ static int l2cap_connect_create_rsp(struct l2cap_conn *conn,
|
|||
l2cap_chan_unlock(chan);
|
||||
l2cap_chan_put(chan);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&conn->chan_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -4446,11 +4428,7 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn,
|
|||
|
||||
chan->ops->set_shutdown(chan);
|
||||
|
||||
l2cap_chan_unlock(chan);
|
||||
mutex_lock(&conn->chan_lock);
|
||||
l2cap_chan_lock(chan);
|
||||
l2cap_chan_del(chan, ECONNRESET);
|
||||
mutex_unlock(&conn->chan_lock);
|
||||
|
||||
chan->ops->close(chan);
|
||||
|
||||
|
@ -4487,11 +4465,7 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn,
|
|||
return 0;
|
||||
}
|
||||
|
||||
l2cap_chan_unlock(chan);
|
||||
mutex_lock(&conn->chan_lock);
|
||||
l2cap_chan_lock(chan);
|
||||
l2cap_chan_del(chan, 0);
|
||||
mutex_unlock(&conn->chan_lock);
|
||||
|
||||
chan->ops->close(chan);
|
||||
|
||||
|
@ -4689,13 +4663,9 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn,
|
|||
BT_DBG("dcid 0x%4.4x mtu %u mps %u credits %u result 0x%2.2x",
|
||||
dcid, mtu, mps, credits, result);
|
||||
|
||||
mutex_lock(&conn->chan_lock);
|
||||
|
||||
chan = __l2cap_get_chan_by_ident(conn, cmd->ident);
|
||||
if (!chan) {
|
||||
err = -EBADSLT;
|
||||
goto unlock;
|
||||
}
|
||||
if (!chan)
|
||||
return -EBADSLT;
|
||||
|
||||
err = 0;
|
||||
|
||||
|
@ -4743,9 +4713,6 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn,
|
|||
|
||||
l2cap_chan_unlock(chan);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&conn->chan_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -4857,7 +4824,6 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
|
|||
goto response;
|
||||
}
|
||||
|
||||
mutex_lock(&conn->chan_lock);
|
||||
l2cap_chan_lock(pchan);
|
||||
|
||||
if (!smp_sufficient_security(conn->hcon, pchan->sec_level,
|
||||
|
@ -4923,7 +4889,6 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
|
|||
|
||||
response_unlock:
|
||||
l2cap_chan_unlock(pchan);
|
||||
mutex_unlock(&conn->chan_lock);
|
||||
l2cap_chan_put(pchan);
|
||||
|
||||
if (result == L2CAP_CR_PEND)
|
||||
|
@ -5057,7 +5022,6 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn,
|
|||
goto response;
|
||||
}
|
||||
|
||||
mutex_lock(&conn->chan_lock);
|
||||
l2cap_chan_lock(pchan);
|
||||
|
||||
if (!smp_sufficient_security(conn->hcon, pchan->sec_level,
|
||||
|
@ -5132,7 +5096,6 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn,
|
|||
|
||||
unlock:
|
||||
l2cap_chan_unlock(pchan);
|
||||
mutex_unlock(&conn->chan_lock);
|
||||
l2cap_chan_put(pchan);
|
||||
|
||||
response:
|
||||
|
@ -5169,8 +5132,6 @@ static inline int l2cap_ecred_conn_rsp(struct l2cap_conn *conn,
|
|||
BT_DBG("mtu %u mps %u credits %u result 0x%4.4x", mtu, mps, credits,
|
||||
result);
|
||||
|
||||
mutex_lock(&conn->chan_lock);
|
||||
|
||||
cmd_len -= sizeof(*rsp);
|
||||
|
||||
list_for_each_entry_safe(chan, tmp, &conn->chan_l, list) {
|
||||
|
@ -5256,8 +5217,6 @@ static inline int l2cap_ecred_conn_rsp(struct l2cap_conn *conn,
|
|||
l2cap_chan_unlock(chan);
|
||||
}
|
||||
|
||||
mutex_unlock(&conn->chan_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -5370,8 +5329,6 @@ static inline int l2cap_le_command_rej(struct l2cap_conn *conn,
|
|||
if (cmd_len < sizeof(*rej))
|
||||
return -EPROTO;
|
||||
|
||||
mutex_lock(&conn->chan_lock);
|
||||
|
||||
chan = __l2cap_get_chan_by_ident(conn, cmd->ident);
|
||||
if (!chan)
|
||||
goto done;
|
||||
|
@ -5386,7 +5343,6 @@ static inline int l2cap_le_command_rej(struct l2cap_conn *conn,
|
|||
l2cap_chan_put(chan);
|
||||
|
||||
done:
|
||||
mutex_unlock(&conn->chan_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -6841,8 +6797,12 @@ static void process_pending_rx(struct work_struct *work)
|
|||
|
||||
BT_DBG("");
|
||||
|
||||
mutex_lock(&conn->lock);
|
||||
|
||||
while ((skb = skb_dequeue(&conn->pending_rx)))
|
||||
l2cap_recv_frame(conn, skb);
|
||||
|
||||
mutex_unlock(&conn->lock);
|
||||
}
|
||||
|
||||
static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon)
|
||||
|
@ -6881,7 +6841,7 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon)
|
|||
conn->local_fixed_chan |= L2CAP_FC_SMP_BREDR;
|
||||
|
||||
mutex_init(&conn->ident_lock);
|
||||
mutex_init(&conn->chan_lock);
|
||||
mutex_init(&conn->lock);
|
||||
|
||||
INIT_LIST_HEAD(&conn->chan_l);
|
||||
INIT_LIST_HEAD(&conn->users);
|
||||
|
@ -7072,7 +7032,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
|
|||
}
|
||||
}
|
||||
|
||||
mutex_lock(&conn->chan_lock);
|
||||
mutex_lock(&conn->lock);
|
||||
l2cap_chan_lock(chan);
|
||||
|
||||
if (cid && __l2cap_get_chan_by_dcid(conn, cid)) {
|
||||
|
@ -7113,7 +7073,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
|
|||
|
||||
chan_unlock:
|
||||
l2cap_chan_unlock(chan);
|
||||
mutex_unlock(&conn->chan_lock);
|
||||
mutex_unlock(&conn->lock);
|
||||
done:
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
@ -7325,7 +7285,7 @@ static void l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
|
|||
|
||||
BT_DBG("conn %p status 0x%2.2x encrypt %u", conn, status, encrypt);
|
||||
|
||||
mutex_lock(&conn->chan_lock);
|
||||
mutex_lock(&conn->lock);
|
||||
|
||||
list_for_each_entry(chan, &conn->chan_l, list) {
|
||||
l2cap_chan_lock(chan);
|
||||
|
@ -7399,7 +7359,7 @@ static void l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
|
|||
l2cap_chan_unlock(chan);
|
||||
}
|
||||
|
||||
mutex_unlock(&conn->chan_lock);
|
||||
mutex_unlock(&conn->lock);
|
||||
}
|
||||
|
||||
/* Append fragment into frame respecting the maximum len of rx_skb */
|
||||
|
@ -7466,19 +7426,45 @@ static void l2cap_recv_reset(struct l2cap_conn *conn)
|
|||
conn->rx_len = 0;
|
||||
}
|
||||
|
||||
struct l2cap_conn *l2cap_conn_hold_unless_zero(struct l2cap_conn *c)
|
||||
{
|
||||
if (!c)
|
||||
return NULL;
|
||||
|
||||
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);
|
||||
|
||||
if (!conn)
|
||||
goto drop;
|
||||
conn = l2cap_conn_hold_unless_zero(conn);
|
||||
|
||||
hci_dev_unlock(hcon->hdev);
|
||||
|
||||
if (!conn) {
|
||||
kfree_skb(skb);
|
||||
return;
|
||||
}
|
||||
|
||||
BT_DBG("conn %p len %u flags 0x%x", conn, skb->len, flags);
|
||||
|
||||
mutex_lock(&conn->lock);
|
||||
|
||||
switch (flags) {
|
||||
case ACL_START:
|
||||
case ACL_START_NO_FLUSH:
|
||||
|
@ -7503,7 +7489,7 @@ void l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
|
|||
if (len == skb->len) {
|
||||
/* Complete frame received */
|
||||
l2cap_recv_frame(conn, skb);
|
||||
return;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
BT_DBG("Start: total len %d, frag len %u", len, skb->len);
|
||||
|
@ -7567,6 +7553,9 @@ void l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
|
|||
|
||||
drop:
|
||||
kfree_skb(skb);
|
||||
unlock:
|
||||
mutex_unlock(&conn->lock);
|
||||
l2cap_conn_put(conn);
|
||||
}
|
||||
|
||||
static struct hci_cb l2cap_cb = {
|
||||
|
|
|
@ -1326,9 +1326,10 @@ static int l2cap_sock_shutdown(struct socket *sock, int how)
|
|||
/* prevent sk structure from being freed whilst unlocked */
|
||||
sock_hold(sk);
|
||||
|
||||
chan = l2cap_pi(sk)->chan;
|
||||
/* prevent chan structure from being freed whilst unlocked */
|
||||
l2cap_chan_hold(chan);
|
||||
chan = l2cap_chan_hold_unless_zero(l2cap_pi(sk)->chan);
|
||||
if (!chan)
|
||||
goto shutdown_already;
|
||||
|
||||
BT_DBG("chan %p state %s", chan, state_to_string(chan->state));
|
||||
|
||||
|
@ -1358,22 +1359,20 @@ static int l2cap_sock_shutdown(struct socket *sock, int how)
|
|||
release_sock(sk);
|
||||
|
||||
l2cap_chan_lock(chan);
|
||||
conn = chan->conn;
|
||||
if (conn)
|
||||
/* prevent conn structure from being freed */
|
||||
l2cap_conn_get(conn);
|
||||
/* prevent conn structure from being freed */
|
||||
conn = l2cap_conn_hold_unless_zero(chan->conn);
|
||||
l2cap_chan_unlock(chan);
|
||||
|
||||
if (conn)
|
||||
/* mutex lock must be taken before l2cap_chan_lock() */
|
||||
mutex_lock(&conn->chan_lock);
|
||||
mutex_lock(&conn->lock);
|
||||
|
||||
l2cap_chan_lock(chan);
|
||||
l2cap_chan_close(chan, 0);
|
||||
l2cap_chan_unlock(chan);
|
||||
|
||||
if (conn) {
|
||||
mutex_unlock(&conn->chan_lock);
|
||||
mutex_unlock(&conn->lock);
|
||||
l2cap_conn_put(conn);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue