* [PATCH 1/1] Bluetooth: L2CAP: Fix use-after-free in l2cap_sock_new_connection_cb()
2026-05-11 3:18 [PATCH 0/1] Bluetooth: L2CAP: Fix use-after-free in l2cap_sock_new_connection_cb() Siwei Zhang
@ 2026-05-11 3:18 ` Siwei Zhang
2026-05-11 4:21 ` bluez.test.bot
0 siblings, 1 reply; 4+ messages in thread
From: Siwei Zhang @ 2026-05-11 3:18 UTC (permalink / raw)
To: Marcel Holtmann, Luiz Augusto von Dentz; +Cc: linux-bluetooth, Siwei Zhang
l2cap_sock_new_connection_cb() accesses l2cap_pi(sk)->chan after
release_sock(parent). Once the parent lock is released, the child
socket sk can be freed by another task.
Save the channel pointer into a local variable while the parent lock
is still held to prevent this.
Fixes: 8ffb929098a5 ("Bluetooth: Remove parent socket usage from l2cap_core.c")
Cc: stable@kernel.org
Assisted-by: Claude:claude-opus-4-7
Signed-off-by: Siwei Zhang <oss@fourdim.xyz>
---
net/bluetooth/6lowpan.c | 5 +++++
net/bluetooth/l2cap_core.c | 12 ++++++++++++
net/bluetooth/l2cap_sock.c | 13 ++++++++++++-
net/bluetooth/smp.c | 5 +++++
4 files changed, 34 insertions(+), 1 deletion(-)
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
index 2f03b780b40d..bbe67bd73f9c 100644
--- a/net/bluetooth/6lowpan.c
+++ b/net/bluetooth/6lowpan.c
@@ -755,6 +755,11 @@ static inline struct l2cap_chan *chan_new_conn_cb(struct l2cap_chan *pchan)
BT_DBG("chan %p pchan %p", chan, pchan);
+ /* Match the put that the caller of ops->new_connection() performs
+ * once it is done with the returned channel pointer.
+ */
+ l2cap_chan_hold(chan);
+
return chan;
}
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 95c65fece39b..fc663386872c 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -4071,6 +4071,9 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
__l2cap_chan_add(conn, chan);
+ /* Drop the ops->new_connection() ref; conn list now pins chan. */
+ l2cap_chan_put(chan);
+
dcid = chan->scid;
__set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
@@ -4978,6 +4981,9 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
__l2cap_chan_add(conn, chan);
+ /* Drop the ops->new_connection() ref; conn list now pins chan. */
+ l2cap_chan_put(chan);
+
l2cap_le_flowctl_init(chan, __le16_to_cpu(req->credits));
dcid = chan->scid;
@@ -5202,6 +5208,9 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn,
__l2cap_chan_add(conn, chan);
+ /* Drop the ops->new_connection() ref; conn list now pins chan. */
+ l2cap_chan_put(chan);
+
l2cap_ecred_init(chan, __le16_to_cpu(req->credits));
/* Init response */
@@ -7402,6 +7411,9 @@ static void l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
chan->dst_type = dst_type;
__l2cap_chan_add(conn, chan);
+
+ /* Drop the ops->new_connection() ref; conn list now pins chan. */
+ l2cap_chan_put(chan);
}
l2cap_chan_unlock(pchan);
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 71e8c1b45bce..355fad9e2955 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -1497,6 +1497,7 @@ static void l2cap_sock_cleanup_listen(struct sock *parent)
static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan)
{
struct sock *sk, *parent = chan->data;
+ struct l2cap_chan *child_chan;
lock_sock(parent);
@@ -1520,9 +1521,19 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan)
bt_accept_enqueue(parent, sk, false);
+ child_chan = l2cap_pi(sk)->chan;
+
+ /* Pin the channel for the caller. Once release_sock(parent) returns,
+ * userspace can accept(2) and immediately close(2) the child socket,
+ * which would drop the socket's references on the channel and free
+ * it before the caller (e.g. l2cap_connect_req()) is done using the
+ * returned pointer. The matching put is the caller's responsibility.
+ */
+ l2cap_chan_hold(child_chan);
+
release_sock(parent);
- return l2cap_pi(sk)->chan;
+ return child_chan;
}
static int l2cap_sock_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index 98f1da4f5f55..32761d3d252e 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -3261,6 +3261,11 @@ static inline struct l2cap_chan *smp_new_conn_cb(struct l2cap_chan *pchan)
BT_DBG("created chan %p", chan);
+ /* Match the put that the caller of ops->new_connection() performs
+ * once it is done with the returned channel pointer.
+ */
+ l2cap_chan_hold(chan);
+
return chan;
}
--
2.54.0
^ permalink raw reply related [flat|nested] 4+ messages in thread