mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Cen Zhang <zzzccc427@gmail.com>
To: marcel@holtmann.org, luiz.dentz@gmail.com
Cc: linux-bluetooth@vger.kernel.org, linux-kernel@vger.kernel.org,
	baijiaju1990@gmail.com, jjzuming@gmail.com, zzzccc427@gmail.com
Subject: [PATCH] Bluetooth: 6LoWPAN: Serialize multicast sends with peer removal
Date: Thu,  8 Oct 2026 12:27:52 +0800	[thread overview]
Message-ID: <pm-bluetooth-objects-candidate-0001-v6-7494fb26092a1231b75b@gmail.com> (raw)

A peer's channel must remain alive until send_mcast_pkt() finishes using
it. The multicast walk protects the lowpan_peer with RCU, but peer_del()
defers only the peer allocation. On disconnect, l2cap_chan_del() drops
the connection-list reference, chan_close_cb() removes the peer and drops
the original channel reference, and l2cap_conn_del() drops its temporary
reference. A ready channel can then be freed while multicast transmit
still holds the peer, causing send_pkt() to write chan->data after free.
Another peer can keep the interface up, so the last-peer shutdown does
not drain this transmission.

Hold devices_lock across the multicast walk and send_pkt() calls so that
peer removal cannot release the channel reference until the send finishes.
Use the bottom-half-safe variants for all devices_lock critical sections
because transmit runs in softirq context. This prevents transmit from
interrupting a process-context lock holder on the same CPU and deadlocking.
The LE send path uses atomic allocations and does not acquire the channel
or connection mutexes, preserving the existing lock order and teardown
sequence.

The reported access and release path were:

        [Thu Oct  1 11:52:32 2026] BUG: KASAN: slab-use-after-free in
    send_pkt+0x2c5/0x300
        [Thu Oct  1 11:52:32 2026] Write of size 8 at addr ffff88810be0f4a0
     by task python3/535
    [...]
    [Thu Oct  1 11:52:32 2026] Freed by task 502:
    [...]
    [Thu Oct  1 11:52:32 2026]  l2cap_chan_put+0x273/0x3a0
    [Thu Oct  1 11:52:32 2026]  l2cap_conn_del+0x36d/0x770
    [Thu Oct  1 11:52:32 2026]  l2cap_disconn_cfm+0x87/0xd0
    [Thu Oct  1 11:52:32 2026]  hci_disconn_complete_evt+0x319/0xa30
    [...]

Fixes: 90305829635d ("Bluetooth: 6lowpan: Converting rwlocks to use RCU")
Assisted-by: LLM
Signed-off-by: Cen Zhang <zzzccc427@gmail.com>
---
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
index 836add41f5..8732132c47 100644
--- a/net/bluetooth/6lowpan.c
+++ b/net/bluetooth/6lowpan.c
@@ -471,6 +471,11 @@
 	struct lowpan_btle_dev *entry;
 	int err = 0;
 
+	/*
+	 * Peer removal drops the channel reference, so RCU alone is not
+	 * enough.
+	 */
+	spin_lock_bh(&devices_lock);
 	rcu_read_lock();
 
 	list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
@@ -502,6 +507,7 @@
 	}
 
 	rcu_read_unlock();
+	spin_unlock_bh(&devices_lock);
 
 	return err;
 }
@@ -657,10 +663,10 @@
 
 	lowpan_iphc_uncompress_eui48_lladdr(&peer->peer_addr, peer->lladdr);
 
-	spin_lock(&devices_lock);
+	spin_lock_bh(&devices_lock);
 	INIT_LIST_HEAD(&peer->list);
 	peer_add(dev, peer);
-	spin_unlock(&devices_lock);
+	spin_unlock_bh(&devices_lock);
 
 	/* Notifying peers about us needs to be done without locks held */
 	if (new_netdev)
@@ -695,17 +701,17 @@
 	(*dev)->hdev = chan->conn->hcon->hdev;
 	INIT_LIST_HEAD(&(*dev)->peers);
 
-	spin_lock(&devices_lock);
+	spin_lock_bh(&devices_lock);
 	INIT_LIST_HEAD(&(*dev)->list);
 	list_add_rcu(&(*dev)->list, &bt_6lowpan_devices);
-	spin_unlock(&devices_lock);
+	spin_unlock_bh(&devices_lock);
 
 	err = lowpan_register_netdev(netdev, LOWPAN_LLTYPE_BTLE);
 	if (err < 0) {
 		BT_INFO("register_netdev failed %d", err);
-		spin_lock(&devices_lock);
+		spin_lock_bh(&devices_lock);
 		list_del_rcu(&(*dev)->list);
-		spin_unlock(&devices_lock);
+		spin_unlock_bh(&devices_lock);
 		free_netdev(netdev);
 		goto out;
 	}
@@ -788,7 +794,7 @@
 
 	BT_DBG("chan %p conn %p", chan, chan->conn);
 
-	spin_lock(&devices_lock);
+	spin_lock_bh(&devices_lock);
 
 	list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
 		dev = lowpan_btle_dev(entry->netdev);
@@ -808,7 +814,7 @@
 	}
 
 	if (!err && last && dev && !atomic_read(&dev->peer_count)) {
-		spin_unlock(&devices_lock);
+		spin_unlock_bh(&devices_lock);
 
 		cancel_delayed_work_sync(&dev->notify_peers);
 
@@ -817,7 +823,7 @@
 		INIT_WORK(&entry->delete_netdev, delete_netdev);
 		schedule_work(&entry->delete_netdev);
 	} else {
-		spin_unlock(&devices_lock);
+		spin_unlock_bh(&devices_lock);
 	}
 }
 
@@ -918,18 +924,18 @@
 
 	BT_DBG("conn %p dst type %u", conn, dst_type);
 
-	spin_lock(&devices_lock);
+	spin_lock_bh(&devices_lock);
 
 	peer = lookup_peer(conn);
 	if (!peer) {
-		spin_unlock(&devices_lock);
+		spin_unlock_bh(&devices_lock);
 		return -ENOENT;
 	}
 
 	chan = peer->chan;
 	l2cap_chan_hold(chan);
 
-	spin_unlock(&devices_lock);
+	spin_unlock_bh(&devices_lock);
 
 	BT_DBG("peer %p chan %p", peer, chan);
 
@@ -1053,7 +1059,7 @@
 
 		nchans = 0;
 
-		spin_lock(&devices_lock);
+		spin_lock_bh(&devices_lock);
 
 		list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
 			list_for_each_entry_rcu(peer, &entry->peers, list) {
@@ -1070,7 +1076,7 @@
 		}
 
 done:
-		spin_unlock(&devices_lock);
+		spin_unlock_bh(&devices_lock);
 
 		for (i = 0; i < nchans; ++i) {
 			l2cap_chan_close_unlocked(chans[i], ENOENT);
@@ -1195,7 +1201,7 @@
 	struct lowpan_btle_dev *entry;
 	struct lowpan_peer *peer;
 
-	spin_lock(&devices_lock);
+	spin_lock_bh(&devices_lock);
 
 	list_for_each_entry(entry, &bt_6lowpan_devices, list) {
 		list_for_each_entry(peer, &entry->peers, list)
@@ -1203,7 +1209,7 @@
 				   &peer->chan->dst, peer->chan->dst_type);
 	}
 
-	spin_unlock(&devices_lock);
+	spin_unlock_bh(&devices_lock);
 
 	return 0;
 }
@@ -1269,7 +1275,7 @@
 
 	switch (event) {
 	case NETDEV_UNREGISTER:
-		spin_lock(&devices_lock);
+		spin_lock_bh(&devices_lock);
 		list_for_each_entry(entry, &bt_6lowpan_devices, list) {
 			if (entry->netdev == netdev) {
 				BT_DBG("Unregistered netdev %s %p",
@@ -1278,7 +1284,7 @@
 				break;
 			}
 		}
-		spin_unlock(&devices_lock);
+		spin_unlock_bh(&devices_lock);
 		break;
 	}
 

                 reply	other threads:[~2026-10-08  4:28 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=pm-bluetooth-objects-candidate-0001-v6-7494fb26092a1231b75b@gmail.com \
    --to=zzzccc427@gmail.com \
    --cc=baijiaju1990@gmail.com \
    --cc=jjzuming@gmail.com \
    --cc=linux-bluetooth@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=luiz.dentz@gmail.com \
    --cc=marcel@holtmann.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®