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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.