mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Cen Zhang <zzzccc427@gmail.com>
To: Tejun Heo <tj@kernel.org>, Lai Jiangshan <jiangshanlai@gmail.com>,
	Marcel Holtmann <marcel@holtmann.org>,
	Luiz Augusto von Dentz <luiz.dentz@gmail.com>,
	Marco Elver <elver@google.com>,
	Jukka Rissanen <jukka.rissanen@linux.intel.com>
Cc: linux-kernel@vger.kernel.org, linux-bluetooth@vger.kernel.org,
	baijiaju1990@gmail.com, jjzuming@gmail.com, zzzccc427@gmail.com
Subject: [PATCH 5/5] Bluetooth: 6lowpan: quiesce peers before channel deletion
Date: Mon, 21 Sep 2026 23:39:02 +0800	[thread overview]
Message-ID: <pm-series-6lowpan-lifecycle-82fab5876048c8c9503c-5@gmail.com> (raw)
In-Reply-To: <pm-series-6lowpan-lifecycle-82fab5876048c8c9503c-0@gmail.com>

6LoWPAN removes a peer and drops its channel reference from the close
callback. A transmitter which already observed the RCU-published peer
can still use the freed channel. Keeping the allocation alive alone is
insufficient: a transmitter which passed the deletion check can enqueue
a packet after L2CAP has purged the transmit queue.

Move peer cleanup to teardown, before FLAG_DEL and the transmit purge.
Mark the channel closed, unlink the peer under devices_lock, then drop
the lock and wait for network RCU readers with synchronize_net(). Free
the peer and release the initial channel reference only after those
transmitters have returned. Queue last-peer network-device deletion
before releasing the channel's ownership reference.

Disabling 6LoWPAN must also close the listener before sweeping existing
peers. Otherwise a request holding the old listener can publish a child
after the sweep. Serialize the transition with set_lock: requests which
complete admission before listener teardown are included in the sweep,
and requests which reach the closed listener are rejected.

The transmit/removal race produced this report:

    [   59.413897] BUG: KASAN: slab-use-after-free in send_pkt+0x3b1/0x3e0
    [   59.415014] Write of size 8 at addr ffff88810cf0e4a0 by task python3/583
    [ ... report excerpt omitted ... ]
    [   59.490444] Freed by task 504:
    [ ... report excerpt omitted ... ]
    [   59.493046]  kfree+0x307/0x580
    [   59.493497]  l2cap_chan_put+0x273/0x3a0
    [   59.494020]  l2cap_disconnect_req+0x613/0x890
    [ ... report excerpt omitted ... ]

Fixes: 6b8d4a6a0314 ("Bluetooth: 6LoWPAN: Use connected oriented channel instead of fixed one")
Assisted-by: LLM
Signed-off-by: Cen Zhang <zzzccc427@gmail.com>
---
 net/bluetooth/6lowpan.c | 64 +++++++++++++++++++++--------------------
 1 file changed, 33 insertions(+), 31 deletions(-)

diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
index 5c49dc146086..60d2c6d1be99 100644
--- a/net/bluetooth/6lowpan.c
+++ b/net/bluetooth/6lowpan.c
@@ -61,7 +61,6 @@ enum {
 
 struct lowpan_peer {
 	struct list_head list;
-	struct rcu_head rcu;
 	struct l2cap_chan *chan;
 
 	/* peer addresses in various formats */
@@ -100,7 +99,6 @@ static inline bool peer_del(struct lowpan_btle_dev *dev,
 			    struct lowpan_peer *peer)
 {
 	list_del_rcu(&peer->list);
-	kfree_rcu(peer, rcu);
 
 	if (atomic_dec_and_test(&dev->peer_count)) {
 		BT_DBG("last peer");
@@ -776,16 +774,15 @@ static void delete_netdev(struct work_struct *work)
 	/* The entry pointer is deleted by the netdev destructor. */
 }
 
-static void chan_close_cb(struct l2cap_chan *chan)
+static void chan_teardown_cb(struct l2cap_chan *chan, int err)
 {
 	struct lowpan_btle_dev *entry;
 	struct lowpan_btle_dev *dev = NULL;
-	struct lowpan_peer *peer;
-	int err = -ENOENT;
+	struct lowpan_peer *peer = NULL;
 	bool last = false;
-	bool queued;
 
 	BT_DBG("chan %p conn %p", chan, chan->conn);
+	chan->state = BT_CLOSED;
 
 	spin_lock(&devices_lock);
 
@@ -794,7 +791,6 @@ static void chan_close_cb(struct l2cap_chan *chan)
 		peer = __peer_lookup_chan(dev, chan);
 		if (peer) {
 			last = peer_del(dev, peer);
-			err = 0;
 
 			BT_DBG("dev %p removing %speer %p", dev,
 			       last ? "last " : "1 ", peer);
@@ -802,19 +798,28 @@ static void chan_close_cb(struct l2cap_chan *chan)
 		}
 	}
 
-	if (!err && last && dev && !atomic_read(&dev->peer_count)) {
-		spin_unlock(&devices_lock);
+	spin_unlock(&devices_lock);
 
-		cancel_delayed_work_sync(&dev->notify_peers);
+	if (peer) {
+		/* ndo_start_xmit() holds network RCU while using peer->chan. */
+		synchronize_net();
+		kfree(peer);
 
-		ifdown(dev->netdev);
+		if (last && dev) {
+			bool queued;
 
-		queued = schedule_module_work(&entry->delete_netdev,
-					      delete_netdev, THIS_MODULE);
-		WARN_ON_ONCE(!queued);
-	} else {
-		spin_unlock(&devices_lock);
+			cancel_delayed_work_sync(&dev->notify_peers);
+
+			ifdown(dev->netdev);
+
+			queued = schedule_module_work(&entry->delete_netdev,
+						      delete_netdev, THIS_MODULE);
+			WARN_ON_ONCE(!queued);
+		}
 	}
+
+	if (test_and_clear_bit(FLAG_RELEASE_CREATOR, &chan->flags))
+		l2cap_chan_put(chan);
 }
 
 static void chan_state_change_cb(struct l2cap_chan *chan, int state, int err)
@@ -873,6 +878,9 @@ static long chan_get_sndtimeo_cb(struct l2cap_chan *chan)
 static int chan_new_connection_cb(struct l2cap_chan *chan,
 				  struct l2cap_chan *new_chan)
 {
+	if (chan->state != BT_LISTEN)
+		return -EINVAL;
+
 	if (!l2cap_chan_set_ops(new_chan, &bt_6lowpan_chan_ops, THIS_MODULE))
 		return -ENODEV;
 
@@ -880,19 +888,11 @@ static int chan_new_connection_cb(struct l2cap_chan *chan,
 	return 0;
 }
 
-static void chan_teardown_cb(struct l2cap_chan *chan, int err)
-{
-	chan->state = BT_CLOSED;
-
-	if (test_and_clear_bit(FLAG_RELEASE_CREATOR, &chan->flags))
-		l2cap_chan_put(chan);
-}
-
 static const struct l2cap_ops bt_6lowpan_chan_ops = {
 	.name			= "L2CAP 6LoWPAN channel",
 	.new_connection		= chan_new_connection_cb,
 	.recv			= chan_recv_cb,
-	.close			= chan_close_cb,
+	.close			= l2cap_chan_no_close,
 	.state_change		= chan_state_change_cb,
 	.ready			= chan_ready_cb,
 	.resume			= chan_resume_cb,
@@ -1103,20 +1103,22 @@ static void disconnect_all_peers(void)
 
 static void do_enable_set(bool flag)
 {
-	if (!flag || enable_6lowpan != flag)
-		/* Disconnect existing connections if 6lowpan is
-		 * disabled
-		 */
-		disconnect_all_peers();
+	bool disconnect;
+
+	mutex_lock(&set_lock);
 
+	disconnect = !flag || enable_6lowpan != flag;
 	enable_6lowpan = flag;
 
-	mutex_lock(&set_lock);
 	if (listen_chan) {
 		l2cap_chan_close_unlocked(listen_chan, 0);
 		l2cap_chan_put(listen_chan);
+		listen_chan = NULL;
 	}
 
+	if (disconnect)
+		disconnect_all_peers();
+
 	listen_chan = bt_6lowpan_listen();
 	mutex_unlock(&set_lock);
 }
-- 
2.43.0


  parent reply	other threads:[~2026-09-21 15:39 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-21 15:38 [PATCH 0/5] Bluetooth: 6LoWPAN lifecycle fixes Cen Zhang
2026-09-21 15:38 ` [PATCH 1/5] Bluetooth: L2CAP: ignore close requests for deleted channels Cen Zhang
2026-09-21 16:56   ` Pauli Virtanen
2026-09-21 15:38 ` [PATCH 2/5] workqueue: add support for module-owned work Cen Zhang
2026-09-21 16:16   ` Tejun Heo
2026-09-21 17:03     ` Cen Zhang
2026-09-21 15:39 ` [PATCH 3/5] Bluetooth: L2CAP: drain channel timers on connection teardown Cen Zhang
2026-09-21 15:54   ` Luiz Augusto von Dentz
2026-09-21 15:39 ` [PATCH 4/5] Bluetooth: 6lowpan: handle channel setup failure and callback lifetime Cen Zhang
2026-09-21 15:39 ` Cen Zhang [this message]
2026-09-21 16:07 ` [PATCH 0/5] Bluetooth: 6LoWPAN lifecycle fixes Luiz Augusto von Dentz
2026-09-21 16:59   ` Cen Zhang

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-series-6lowpan-lifecycle-82fab5876048c8c9503c-5@gmail.com \
    --to=zzzccc427@gmail.com \
    --cc=baijiaju1990@gmail.com \
    --cc=elver@google.com \
    --cc=jiangshanlai@gmail.com \
    --cc=jjzuming@gmail.com \
    --cc=jukka.rissanen@linux.intel.com \
    --cc=linux-bluetooth@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=luiz.dentz@gmail.com \
    --cc=marcel@holtmann.org \
    --cc=tj@kernel.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®