[PATCH 01/16] Bluetooth: L2CAP: take chan->lock for l2cap_chan_add/ready/del

From: Pauli Virtanen

Date: Sat Aug 29 2026 - 10:21:49 EST


chan->lock must be held for __l2cap_chan_add as eg. calls to
l2cap_chan_close assume chan->conn writes are guarded by it.

It must be held for l2cap_chan_del() due to
l2cap_sock.c:l2cap_chan_conn, l2cap_monitor_timeout, etc.

Similarly it should be held for l2cap_ops::ready (assumed in 6lowpan.c).
Also teardown usually has chan->lock held, it should always have it held
to have the same locking context.

The lock is not correctly held by l2cap_core in several places.

Add the missing locks for l2cap_chan_del/add/ready(), except in
l2cap_ecred_rsp_defer() which needs separate fix as it needs lock
nesting.

Fixes: 6fef032af009 ("Bluetooth: L2CAP: Fix use-after-free in l2cap_sock_new_connection_cb()")
Signed-off-by: Pauli Virtanen <pav@xxxxxx>
---
include/net/bluetooth/l2cap.h | 3 ++-
net/bluetooth/l2cap_core.c | 15 +++++++++++++++
2 files changed, 17 insertions(+), 1 deletion(-)

diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index 69d193fee351..43a67562b238 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -973,7 +973,8 @@ int l2cap_chan_check_security(struct l2cap_chan *chan, bool initiator);
void l2cap_chan_set_defaults(struct l2cap_chan *chan, struct l2cap_chan *pchan);
int l2cap_ertm_init(struct l2cap_chan *chan);
void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan);
-void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan);
+void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
+ __must_hold(&chan->lock);
typedef void (*l2cap_chan_func_t)(struct l2cap_chan *chan, void *data);
void l2cap_chan_list(struct l2cap_conn *conn, l2cap_chan_func_t func,
void *data);
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 358b11eabd4f..adcf714ec1ed 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -665,7 +665,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);
+ l2cap_chan_lock(chan);
__l2cap_chan_add(conn, chan);
+ l2cap_chan_unlock(chan);
mutex_unlock(&conn->lock);
}

@@ -4065,6 +4067,8 @@ static struct l2cap_chan *l2cap_new_connection(struct l2cap_conn *conn,
if (!chan)
return NULL;

+ l2cap_chan_lock(chan);
+
l2cap_chan_set_defaults(chan, pchan);
chan->ops = pchan->ops;

@@ -4073,10 +4077,13 @@ static struct l2cap_chan *l2cap_new_connection(struct l2cap_conn *conn,
if (pchan->ops->new_connection &&
pchan->ops->new_connection(pchan, chan) < 0) {
l2cap_chan_del(chan, 0);
+ l2cap_chan_unlock(chan);
l2cap_chan_put(chan);
return NULL;
}

+ l2cap_chan_unlock(chan);
+
return chan;
}

@@ -5047,6 +5054,8 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
goto response_unlock;
}

+ l2cap_chan_lock(chan);
+
bacpy(&chan->src, &conn->hcon->src);
bacpy(&chan->dst, &conn->hcon->dst);
chan->src_type = bdaddr_src_type(conn->hcon);
@@ -5079,6 +5088,8 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
result = L2CAP_CR_LE_SUCCESS;
}

+ l2cap_chan_unlock(chan);
+
response_unlock:
l2cap_chan_unlock(pchan);
l2cap_chan_put(pchan);
@@ -5271,6 +5282,8 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn,
continue;
}

+ l2cap_chan_lock(chan);
+
bacpy(&chan->src, &conn->hcon->src);
bacpy(&chan->dst, &conn->hcon->dst);
chan->src_type = bdaddr_src_type(conn->hcon);
@@ -5303,6 +5316,8 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn,
} else {
l2cap_chan_ready(chan);
}
+
+ l2cap_chan_unlock(chan);
}

unlock:
--
2.55.0