From: Siwei Zhang <oss@fourdim.xyz>
To: stable@vger.kernel.org
Cc: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Subject: [PATCH 5.15.y v2 2/3] Bluetooth: L2CAP: Only accept connection responses to outstanding requests
Date: Sun, 8 Nov 2026 20:58:44 -0500 [thread overview]
Message-ID: <20261109015902.3034747-3-oss@fourdim.xyz> (raw)
In-Reply-To: <20261109015902.3034747-1-oss@fourdim.xyz>
commit a518b9a9ef6ee3b18ecdf6ba5360c608aaf969e9 upstream.
Adjusted context as stable does not have the lockdep_assert_held()
call and the __must_hold() context analysis annotations, and
l2cap_connect_create_rsp() still holds conn->chan_lock and returns
through the unlock label when the channel lookup fails.
Stable still has AMP and A2MP support, which upstream removed in
commit e7b02296fb40 ("Bluetooth: Remove BT_HS"). Close the same holes
there:
l2cap_do_create() sends an L2CAP_CREATE_CHAN_REQ whose response is also
handled by l2cap_connect_create_rsp(). Set CONF_CONNECT_PEND in
l2cap_send_create_chan_req() as l2cap_send_conn_req() does, so that
the response to it is still accepted. This also makes
__l2cap_no_conn_pending() see that request as outstanding, so
l2cap_do_start(), l2cap_conn_start() and l2cap_security_cfm() leave
the channel alone until it is answered, as they already do for a
pending connection request. l2cap_do_create() itself now does the same
instead of sending a second request.
When an A2MP Discover Response lists no AMP controller,
a2mp_discover_rsp() falls back to sending a connection request for
every channel in BT_CONNECT, including channels still waiting for
security or the feature mask exchange, channels that are not
connection oriented and channels already setting up an AMP link. A
peer can send that response unsolicited, and the request it triggers
sets CONF_CONNECT_PEND, so the peer's connection response would still
be accepted before security completes. Only handle a Discover Response
while our Discover Request is outstanding. Move the checks
l2cap_do_start() makes before sending the connection request into
l2cap_chan_conn_req_allowed(), and add l2cap_amp_fallback_conn_req(),
which only sends the fallback request for a connection oriented channel
that has no request outstanding and passes those checks.
l2cap_send_conn_req() is no longer used outside l2cap_core.c, so make
it static.
An outgoing channel waits in BT_CONNECT until security
and the feature mask exchange complete, and only then
sends its connection request. A peer can answer it
before that with a connection response matching our
source CID. The channel then moves to BT_CONFIG,
completes configuration and becomes connected before
security has completed. If security then fails,
l2cap_security_cfm() leaves the connected channel
alone, so it stays up on a link that did not reach
the required security level.
A connection response with a zero source CID is
matched by identifier. Another channel can hold the
same identifier for a response it still has to send,
so the lookup can pick the wrong channel.
Only accept a connection response while our request
is outstanding, and only look at such channels when
matching by identifier.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Siwei Zhang <oss@fourdim.xyz>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
v2: Pull the payload of an unsolicited A2MP Discover Response before
returning from a2mp_discover_rsp().
https://lore.kernel.org/stable/2026-11-08-1-daily-reply-0003-l2cap-a2mp-5-15@kernel.org/
include/net/bluetooth/l2cap.h | 2 +-
net/bluetooth/a2mp.c | 12 ++++--
net/bluetooth/a2mp.h | 1 +
net/bluetooth/l2cap_core.c | 81 +++++++++++++++++++++++++++++------
4 files changed, 79 insertions(+), 17 deletions(-)
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index 57315b9dc059..47156c773362 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -998,7 +998,7 @@ 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);
void l2cap_chan_del(struct l2cap_chan *chan, int err);
-void l2cap_send_conn_req(struct l2cap_chan *chan);
+void l2cap_amp_fallback_conn_req(struct l2cap_chan *chan);
void l2cap_move_start(struct l2cap_chan *chan);
void l2cap_logical_cfm(struct l2cap_chan *chan, struct hci_chan *hchan,
u8 status);
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index e7adb8a98cf9..2ef7e3280325 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -196,6 +196,12 @@ static int a2mp_discover_rsp(struct amp_mgr *mgr, struct sk_buff *skb,
u16 ext_feat;
bool found = false;
+ /* Only handle the response to our own Discover Request */
+ if (!test_and_clear_bit(DISCOVER_REQ_SENT, &mgr->state)) {
+ skb_pull(skb, le16_to_cpu(hdr->len));
+ return 0;
+ }
+
if (len < sizeof(*rsp))
return -EINVAL;
@@ -254,10 +260,7 @@ static int a2mp_discover_rsp(struct amp_mgr *mgr, struct sk_buff *skb,
continue;
l2cap_chan_lock(chan);
-
- if (chan->state == BT_CONNECT)
- l2cap_send_conn_req(chan);
-
+ l2cap_amp_fallback_conn_req(chan);
l2cap_chan_unlock(chan);
}
@@ -1050,5 +1053,6 @@ void a2mp_discover_amp(struct l2cap_chan *chan)
req.mtu = cpu_to_le16(L2CAP_A2MP_DEFAULT_MTU);
req.ext_feat = 0;
+ set_bit(DISCOVER_REQ_SENT, &mgr->state);
a2mp_send(mgr, A2MP_DISCOVER_REQ, 1, sizeof(req), &req);
}
diff --git a/net/bluetooth/a2mp.h b/net/bluetooth/a2mp.h
index 2fd253a61a2a..1bf31aed8547 100644
--- a/net/bluetooth/a2mp.h
+++ b/net/bluetooth/a2mp.h
@@ -15,6 +15,7 @@ enum amp_mgr_state {
READ_LOC_AMP_ASSOC,
READ_LOC_AMP_ASSOC_FINAL,
WRITE_REMOTE_AMP_ASSOC,
+ DISCOVER_REQ_SENT,
};
struct amp_mgr {
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 3204b95742a8..227b1c8f35ca 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -185,6 +185,28 @@ static struct l2cap_chan *l2cap_get_chan_by_ident(struct l2cap_conn *conn,
return c;
}
+/* Our connection request has been sent and is still outstanding */
+static bool l2cap_chan_connect_pending(struct l2cap_chan *chan)
+{
+ return chan->state == BT_CONNECT &&
+ test_bit(CONF_CONNECT_PEND, &chan->conf_state);
+}
+
+/* Other channels may hold the same ident for a response they still have
+ * to send, so only look at channels waiting for a connection response.
+ */
+static struct l2cap_chan *__l2cap_get_connect_chan_by_ident(struct l2cap_conn *conn,
+ u8 ident)
+{
+ struct l2cap_chan *c;
+
+ list_for_each_entry(c, &conn->chan_l, list) {
+ if (c->ident == ident && l2cap_chan_connect_pending(c))
+ return c;
+ }
+ return NULL;
+}
+
static struct l2cap_chan *__l2cap_global_chan_by_addr(__le16 psm, bdaddr_t *src,
u8 src_type)
{
@@ -1305,7 +1327,7 @@ static bool l2cap_check_efs(struct l2cap_chan *chan)
return true;
}
-void l2cap_send_conn_req(struct l2cap_chan *chan)
+static void l2cap_send_conn_req(struct l2cap_chan *chan)
{
struct l2cap_conn *conn = chan->conn;
struct l2cap_conn_req req;
@@ -1329,6 +1351,8 @@ static void l2cap_send_create_chan_req(struct l2cap_chan *chan, u8 amp_id)
chan->ident = l2cap_get_ident(chan->conn);
+ set_bit(CONF_CONNECT_PEND, &chan->conf_state);
+
l2cap_send_cmd(chan->conn, chan->ident, L2CAP_CREATE_CHAN_REQ,
sizeof(req), &req);
}
@@ -1592,6 +1616,26 @@ static bool l2cap_check_enc_key_size(struct hci_conn *hcon,
hcon->enc_key_size >= min_key_size);
}
+/* Check that a BR/EDR channel may send its connection request now */
+static bool l2cap_chan_conn_req_allowed(struct l2cap_chan *chan)
+{
+ struct l2cap_conn *conn = chan->conn;
+
+ if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE))
+ return false;
+
+ if (!l2cap_chan_check_security(chan, true) ||
+ !__l2cap_no_conn_pending(chan))
+ return false;
+
+ if (!l2cap_check_enc_key_size(conn->hcon, chan)) {
+ __set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
+ return false;
+ }
+
+ return true;
+}
+
static void l2cap_do_start(struct l2cap_chan *chan)
{
struct l2cap_conn *conn = chan->conn;
@@ -1606,17 +1650,23 @@ static void l2cap_do_start(struct l2cap_chan *chan)
return;
}
- if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE))
- return;
+ if (l2cap_chan_conn_req_allowed(chan))
+ l2cap_start_connection(chan);
+}
- if (!l2cap_chan_check_security(chan, true) ||
- !__l2cap_no_conn_pending(chan))
+/* A2MP found no AMP controller to use, so send the BR/EDR connection
+ * request instead, if the channel is still waiting to send one and
+ * passes the same checks as in l2cap_do_start(). Test for an outstanding
+ * request first, as the security check may start authentication.
+ */
+void l2cap_amp_fallback_conn_req(struct l2cap_chan *chan)
+{
+ if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED ||
+ chan->state != BT_CONNECT || !__l2cap_no_conn_pending(chan))
return;
- if (l2cap_check_enc_key_size(conn->hcon, chan))
- l2cap_start_connection(chan);
- else
- __set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
+ if (l2cap_chan_conn_req_allowed(chan))
+ l2cap_send_conn_req(chan);
}
static inline int l2cap_mode_supported(__u8 mode, __u32 feat_mask)
@@ -4426,7 +4476,7 @@ static int l2cap_connect_create_rsp(struct l2cap_conn *conn,
goto unlock;
}
} else {
- chan = __l2cap_get_chan_by_ident(conn, cmd->ident);
+ chan = __l2cap_get_connect_chan_by_ident(conn, cmd->ident);
if (!chan) {
err = -EBADSLT;
goto unlock;
@@ -4443,8 +4493,11 @@ static int l2cap_connect_create_rsp(struct l2cap_conn *conn,
l2cap_chan_lock(chan);
- /* Ignore a late response after the channel has left BT_CONNECT. */
- if (chan->state != BT_CONNECT)
+ /* Only accept a response to a connection request we sent and that
+ * is still outstanding. A channel waiting in BT_CONNECT for security
+ * or the feature mask has not sent one yet.
+ */
+ if (!l2cap_chan_connect_pending(chan))
goto done;
switch (result) {
@@ -5269,6 +5322,10 @@ static void l2cap_do_create(struct l2cap_chan *chan, int result,
/* Outgoing channel on AMP */
if (chan->state == BT_CONNECT) {
+ /* Never send a second request while one is outstanding */
+ if (!__l2cap_no_conn_pending(chan))
+ return;
+
if (result == L2CAP_CR_SUCCESS) {
chan->local_amp_id = local_amp_id;
l2cap_send_create_chan_req(chan, remote_amp_id);
--
2.56.0
next prev parent reply other threads:[~2026-11-09 1:59 UTC|newest]
Thread overview: 24+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-07 11:06 FAILED: patch "[PATCH] Bluetooth: L2CAP: Reject signalling responses in invalid states" failed to apply to 5.15-stable tree Sasha Levin
2026-11-08 0:05 ` [PATCH 5.15.y 0/3] Bluetooth: L2CAP: signalling response fixes Siwei Zhang
2026-11-08 0:05 ` [PATCH 5.15.y 1/3] Bluetooth: L2CAP: Reject signalling responses in invalid states Siwei Zhang
2026-11-08 0:05 ` [PATCH 5.15.y 2/3] Bluetooth: L2CAP: Only accept connection responses to outstanding requests Siwei Zhang
2026-11-09 0:34 ` Sasha Levin
2026-11-09 2:03 ` Siwei Zhang
2026-11-08 0:05 ` [PATCH 5.15.y 3/3] Bluetooth: L2CAP: Ignore connection responses with wrong ident Siwei Zhang
2026-11-09 1:58 ` [PATCH 5.15.y v2 0/3] Bluetooth: L2CAP: signalling response fixes Siwei Zhang
2026-11-09 1:58 ` [PATCH 5.15.y v2 1/3] Bluetooth: L2CAP: Reject signalling responses in invalid states Siwei Zhang
2026-11-09 1:58 ` Siwei Zhang [this message]
2026-11-09 1:58 ` [PATCH 5.15.y v2 3/3] Bluetooth: L2CAP: Ignore connection responses with wrong ident Siwei Zhang
2026-11-09 15:53 ` [PATCH 5.15.y v2 0/3] Bluetooth: L2CAP: signalling response fixes Sasha Levin
2026-11-10 15:08 ` Siwei Zhang
2026-11-10 19:13 ` Siwei Zhang
2026-11-10 19:10 ` [PATCH 5.15.y v3 " Siwei Zhang
2026-11-10 19:10 ` [PATCH 5.15.y v3 1/3] Bluetooth: L2CAP: Reject signalling responses in invalid states Siwei Zhang
2026-11-10 19:10 ` [PATCH 5.15.y v3 2/3] Bluetooth: L2CAP: Only accept connection responses to outstanding requests Siwei Zhang
2026-11-11 21:54 ` Sasha Levin
2026-11-10 19:10 ` [PATCH 5.15.y v3 3/3] Bluetooth: L2CAP: Ignore connection responses with wrong ident Siwei Zhang
2026-11-11 22:07 ` [PATCH 5.15.y v4 0/3] Bluetooth: L2CAP: signalling response fixes Siwei Zhang
2026-11-11 22:07 ` [PATCH 5.15.y v4 1/3] Bluetooth: L2CAP: Reject signalling responses in invalid states Siwei Zhang
2026-11-11 22:07 ` [PATCH 5.15.y v4 2/3] Bluetooth: L2CAP: Only accept connection responses to outstanding requests Siwei Zhang
2026-11-11 22:07 ` [PATCH 5.15.y v4 3/3] Bluetooth: L2CAP: Ignore connection responses with wrong ident Siwei Zhang
2026-11-13 2:33 ` [PATCH 5.15.y v4 0/3] Bluetooth: L2CAP: signalling response fixes Sasha Levin
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=20261109015902.3034747-3-oss@fourdim.xyz \
--to=oss@fourdim.xyz \
--cc=luiz.von.dentz@intel.com \
--cc=stable@vger.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