Revert "Bluetooth: L2CAP: Fix corrupted list in hci_chan_del"

This reverts commit f87271d21d which is
commit ab4eedb790cae44313759b50fe47da285e2519d5 upstream.

It breaks the Android kernel abi and can be brought back in the future
in an abi-safe way if it is really needed.

Bug: 161946584
Change-Id: I3ee05b3e7584529cb41ab496d14e7391cbf73260
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
This commit is contained in:
Greg Kroah-Hartman
2025-04-22 15:50:15 +00:00
committed by Treehugger Robot
parent 5fe17b6460
commit 60712b3754
3 changed files with 98 additions and 58 deletions
+1 -2
View File
@@ -668,7 +668,7 @@ struct l2cap_conn {
struct l2cap_chan *smp;
struct list_head chan_l;
struct mutex lock;
struct mutex chan_lock;
struct kref ref;
struct list_head users;
};
@@ -970,7 +970,6 @@ 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);
+89 -49
View File
@@ -119,6 +119,7 @@ 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 */
@@ -126,6 +127,7 @@ 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;
}
@@ -138,6 +140,7 @@ 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 */
@@ -145,6 +148,7 @@ 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;
}
@@ -414,7 +418,7 @@ static void l2cap_chan_timeout(struct work_struct *work)
if (!conn)
return;
mutex_lock(&conn->lock);
mutex_lock(&conn->chan_lock);
/* __set_chan_timer() calls l2cap_chan_hold(chan) while scheduling
* this work. No need to call l2cap_chan_hold(chan) here again.
*/
@@ -435,7 +439,7 @@ static void l2cap_chan_timeout(struct work_struct *work)
l2cap_chan_unlock(chan);
l2cap_chan_put(chan);
mutex_unlock(&conn->lock);
mutex_unlock(&conn->chan_lock);
}
struct l2cap_chan *l2cap_chan_create(void)
@@ -638,9 +642,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->lock);
mutex_lock(&conn->chan_lock);
__l2cap_chan_add(conn, chan);
mutex_unlock(&conn->lock);
mutex_unlock(&conn->chan_lock);
}
void l2cap_chan_del(struct l2cap_chan *chan, int err)
@@ -728,9 +732,9 @@ void l2cap_chan_list(struct l2cap_conn *conn, l2cap_chan_func_t func,
if (!conn)
return;
mutex_lock(&conn->lock);
mutex_lock(&conn->chan_lock);
__l2cap_chan_list(conn, func, data);
mutex_unlock(&conn->lock);
mutex_unlock(&conn->chan_lock);
}
EXPORT_SYMBOL_GPL(l2cap_chan_list);
@@ -742,7 +746,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->lock);
mutex_lock(&conn->chan_lock);
list_for_each_entry(chan, &conn->chan_l, list) {
l2cap_chan_lock(chan);
@@ -751,7 +755,7 @@ static void l2cap_conn_update_id_addr(struct work_struct *work)
l2cap_chan_unlock(chan);
}
mutex_unlock(&conn->lock);
mutex_unlock(&conn->chan_lock);
}
static void l2cap_chan_le_connect_reject(struct l2cap_chan *chan)
@@ -1504,6 +1508,8 @@ 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);
@@ -1572,6 +1578,8 @@ 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)
@@ -1617,7 +1625,7 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
if (hcon->type == ACL_LINK)
l2cap_request_info(conn);
mutex_lock(&conn->lock);
mutex_lock(&conn->chan_lock);
list_for_each_entry(chan, &conn->chan_l, list) {
@@ -1635,7 +1643,7 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
l2cap_chan_unlock(chan);
}
mutex_unlock(&conn->lock);
mutex_unlock(&conn->chan_lock);
if (hcon->type == LE_LINK)
l2cap_le_conn_ready(conn);
@@ -1650,10 +1658,14 @@ 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)
@@ -1664,9 +1676,7 @@ 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);
}
/*
@@ -1758,8 +1768,6 @@ 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);
@@ -1778,6 +1786,8 @@ 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);
@@ -1791,14 +1801,12 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
l2cap_chan_put(chan);
}
mutex_unlock(&conn->chan_lock);
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT)
cancel_delayed_work_sync(&conn->info_timer);
hci_chan_del(conn->hchan);
conn->hchan = NULL;
hcon->l2cap_data = NULL;
mutex_unlock(&conn->lock);
l2cap_conn_put(conn);
}
@@ -1806,6 +1814,7 @@ static void l2cap_conn_free(struct kref *ref)
{
struct l2cap_conn *conn = container_of(ref, struct l2cap_conn, ref);
hci_chan_del(conn->hchan);
hci_conn_put(conn->hcon);
kfree(conn);
}
@@ -2916,6 +2925,8 @@ 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;
@@ -2930,6 +2941,8 @@ 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,6 +3965,7 @@ 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) */
@@ -4058,6 +4072,7 @@ response:
}
l2cap_chan_unlock(pchan);
mutex_unlock(&conn->chan_lock);
l2cap_chan_put(pchan);
}
@@ -4096,19 +4111,27 @@ 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)
return -EBADSLT;
if (!chan) {
err = -EBADSLT;
goto unlock;
}
} else {
chan = __l2cap_get_chan_by_ident(conn, cmd->ident);
if (!chan)
return -EBADSLT;
if (!chan) {
err = -EBADSLT;
goto unlock;
}
}
chan = l2cap_chan_hold_unless_zero(chan);
if (!chan)
return -EBADSLT;
if (!chan) {
err = -EBADSLT;
goto unlock;
}
err = 0;
@@ -4146,6 +4169,9 @@ 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;
}
@@ -4433,7 +4459,11 @@ 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);
@@ -4470,7 +4500,11 @@ 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);
@@ -4668,9 +4702,13 @@ 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)
return -EBADSLT;
if (!chan) {
err = -EBADSLT;
goto unlock;
}
err = 0;
@@ -4718,6 +4756,9 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn,
l2cap_chan_unlock(chan);
unlock:
mutex_unlock(&conn->chan_lock);
return err;
}
@@ -4829,6 +4870,7 @@ 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,
@@ -4894,6 +4936,7 @@ 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)
@@ -5027,6 +5070,7 @@ 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,
@@ -5101,6 +5145,7 @@ 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:
@@ -5137,6 +5182,8 @@ 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) {
@@ -5222,6 +5269,8 @@ static inline int l2cap_ecred_conn_rsp(struct l2cap_conn *conn,
l2cap_chan_unlock(chan);
}
mutex_unlock(&conn->chan_lock);
return err;
}
@@ -5334,6 +5383,8 @@ 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;
@@ -5348,6 +5399,7 @@ static inline int l2cap_le_command_rej(struct l2cap_conn *conn,
l2cap_chan_put(chan);
done:
mutex_unlock(&conn->chan_lock);
return 0;
}
@@ -6802,12 +6854,8 @@ 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)
@@ -6846,7 +6894,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->lock);
mutex_init(&conn->chan_lock);
INIT_LIST_HEAD(&conn->chan_l);
INIT_LIST_HEAD(&conn->users);
@@ -7037,7 +7085,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
}
}
mutex_lock(&conn->lock);
mutex_lock(&conn->chan_lock);
l2cap_chan_lock(chan);
if (cid && __l2cap_get_chan_by_dcid(conn, cid)) {
@@ -7078,7 +7126,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
chan_unlock:
l2cap_chan_unlock(chan);
mutex_unlock(&conn->lock);
mutex_unlock(&conn->chan_lock);
done:
hci_dev_unlock(hdev);
hci_dev_put(hdev);
@@ -7290,7 +7338,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->lock);
mutex_lock(&conn->chan_lock);
list_for_each_entry(chan, &conn->chan_l, list) {
l2cap_chan_lock(chan);
@@ -7364,7 +7412,7 @@ static void l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
l2cap_chan_unlock(chan);
}
mutex_unlock(&conn->lock);
mutex_unlock(&conn->chan_lock);
}
/* Append fragment into frame respecting the maximum len of rx_skb */
@@ -7431,11 +7479,8 @@ 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)
static 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))
@@ -7461,15 +7506,11 @@ void l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
hci_dev_unlock(hcon->hdev);
if (!conn) {
kfree_skb(skb);
return;
}
if (!conn)
goto drop;
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:
@@ -7494,7 +7535,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);
goto unlock;
return;
}
BT_DBG("Start: total len %d, frag len %u", len, skb->len);
@@ -7556,11 +7597,10 @@ void l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
break;
}
l2cap_conn_put(conn);
drop:
kfree_skb(skb);
unlock:
mutex_unlock(&conn->lock);
l2cap_conn_put(conn);
}
static struct hci_cb l2cap_cb = {
+8 -7
View File
@@ -1326,10 +1326,9 @@ 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 */
chan = l2cap_chan_hold_unless_zero(l2cap_pi(sk)->chan);
if (!chan)
goto shutdown_already;
l2cap_chan_hold(chan);
BT_DBG("chan %p state %s", chan, state_to_string(chan->state));
@@ -1359,20 +1358,22 @@ static int l2cap_sock_shutdown(struct socket *sock, int how)
release_sock(sk);
l2cap_chan_lock(chan);
/* prevent conn structure from being freed */
conn = l2cap_conn_hold_unless_zero(chan->conn);
conn = chan->conn;
if (conn)
/* prevent conn structure from being freed */
l2cap_conn_get(conn);
l2cap_chan_unlock(chan);
if (conn)
/* mutex lock must be taken before l2cap_chan_lock() */
mutex_lock(&conn->lock);
mutex_lock(&conn->chan_lock);
l2cap_chan_lock(chan);
l2cap_chan_close(chan, 0);
l2cap_chan_unlock(chan);
if (conn) {
mutex_unlock(&conn->lock);
mutex_unlock(&conn->chan_lock);
l2cap_conn_put(conn);
}