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
next prev 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®