From: Kyle Pepper <cochcoder@proton.me>
To: linux-bluetooth@vger.kernel.org
Cc: Marcel Holtmann <marcel@holtmann.org>,
Luiz Augusto von Dentz <luiz.dentz@gmail.com>,
Johan Hedberg <johan.hedberg@intel.com>,
linux-kernel@vger.kernel.org
Subject: [RFC PATCH v3 1/2] Bluetooth: L2CAP: Keep credits from completing security upgrades
Date: Thu, 15 Oct 2026 18:16:53 +0000 [thread overview]
Message-ID: <20261015181624.48318-1-cochcoder@proton.me> (raw)
In-Reply-To: <20261015052751.137705-1-cochcoder@proton.me>
Credit indications can invoke resume on fixed or unready channels.
Connected LE credit-based sockets can also resume during a security
upgrade because credits and security share the socket suspend flag.
Validate the target mode and state for every identifier. Track pending
LE/ECRED security upgrades and retain credits without sending queued
data or resuming the socket until encryption meets the requested level.
Drain queued data after security completes and keep credit-starved
sockets suspended.
Fixes: fad5fc89594d ("Bluetooth: Add LE flow control discipline")
Assisted-by: OpenCode:gpt-6.1-sol
Signed-off-by: Kyle Pepper <cochcoder@proton.me>
---
Changes since the standalone security submission:
- Wrap test notes to fix the GitLint B1 failures in series 1186119.
- Refresh onto bluetooth-next ff8b09806b7b without a source delta.
- Include this prerequisite as patch 1/2 of the credit RFC v3 series.
This security fix is independently useful. It does not accept identifier
zero and can be considered without patch 2. The series uses an RFC prefix
because patch 2 still requires agreement on a specification exception.
Do not queue the dependent patch alone before this prerequisite lands.
The saved standalone CI report lists every job except GitLint passing,
including the native L2CAP tester. This is not a CI result for this series.
Earlier local runs on 397464709a20: nine focused security controls pass
on security-only and combined guests. The 14 public credit controls pass
13/14 security-only (expected positive zero-ID failure), 14/14 combined.
There were no skips or captured kernel alerts in these focused runs.
GCC W=1 changed objects and source-extracted fixtures also passed there.
That base's full local attempt was incomplete: SCI Fast Mode timed out
after an invalid HCI response length for opcode 0x20a3. The same failure
reproduced unpatched; this patch does not claim to fix it. Initial spaced
VM filters selected broader tests; corrected space-free focused runs are
the results above. Historical older-base results of 133/134 and 134/134
are not fresh full-suite results for this series.
The combined Arch trial is reported working, not isolated security or
exhaustive lifetime coverage. No severity/CVE claim is made. The private
reproducer and detailed private regression logs remain excluded.
net/bluetooth/l2cap_core.c | 33 +++++++++++++++++++++++++++++++--
net/bluetooth/l2cap_sock.c | 9 +++++++--
2 files changed, 38 insertions(+), 4 deletions(-)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 4dd56e007..7a7f30531 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -5391,6 +5391,11 @@ static inline int l2cap_le_credits(struct l2cap_conn *conn,
l2cap_chan_lock(chan);
+ if (chan->state != BT_CONNECTED ||
+ (chan->mode != L2CAP_MODE_LE_FLOWCTL &&
+ chan->mode != L2CAP_MODE_EXT_FLOWCTL))
+ goto unlock;
+
max_credits = LE_FLOWCTL_MAX_CREDITS - chan->tx_credits;
if (credits > max_credits) {
BT_ERR("LE credits overflow");
@@ -5404,6 +5409,9 @@ static inline int l2cap_le_credits(struct l2cap_conn *conn,
chan->tx_credits += credits;
+ if (test_bit(FLAG_PENDING_SECURITY, &chan->flags))
+ goto unlock;
+
/* Resume sending */
l2cap_le_flowctl_send(chan);
@@ -7967,6 +7975,8 @@ static void l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
mutex_lock(&conn->lock);
list_for_each_entry(chan, &conn->chan_l, list) {
+ bool pending_le;
+
l2cap_chan_lock(chan);
lockdep_assert_held(&chan->conn->lock);
@@ -7974,7 +7984,12 @@ static void l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
BT_DBG("chan %p scid 0x%4.4x state %s", chan, chan->scid,
state_to_string(chan->state));
- if (!status && encrypt)
+ pending_le = hcon->type == LE_LINK &&
+ (chan->mode == L2CAP_MODE_LE_FLOWCTL ||
+ chan->mode == L2CAP_MODE_EXT_FLOWCTL) &&
+ test_bit(FLAG_PENDING_SECURITY, &chan->flags);
+
+ if (!status && encrypt && !pending_le)
chan->sec_level = hcon->sec_level;
if (!__l2cap_no_conn_pending(chan)) {
@@ -7984,7 +7999,21 @@ static void l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
if (!status && (chan->state == BT_CONNECTED ||
chan->state == BT_CONFIG)) {
- chan->ops->resume(chan);
+ if (pending_le) {
+ if (!encrypt ||
+ !smp_sufficient_security(hcon, chan->sec_level,
+ SMP_ALLOW_STK)) {
+ l2cap_chan_unlock(chan);
+ continue;
+ }
+
+ clear_bit(FLAG_PENDING_SECURITY, &chan->flags);
+ chan->sec_level = hcon->sec_level;
+ l2cap_le_flowctl_send(chan);
+ }
+
+ if (!pending_le || chan->tx_credits)
+ chan->ops->resume(chan);
l2cap_check_encryption(chan, encrypt);
l2cap_chan_unlock(chan);
continue;
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index c396987c5..2d7f2935c 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -993,10 +993,15 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
} else if ((sk->sk_state == BT_CONNECT2 &&
test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) ||
sk->sk_state == BT_CONNECTED) {
- if (!l2cap_chan_check_security(chan, true))
+ if (!l2cap_chan_check_security(chan, true)) {
+ if (conn->hcon->type == LE_LINK &&
+ (chan->mode == L2CAP_MODE_LE_FLOWCTL ||
+ chan->mode == L2CAP_MODE_EXT_FLOWCTL))
+ set_bit(FLAG_PENDING_SECURITY, &chan->flags);
set_bit(BT_SK_SUSPEND, &bt_sk(sk)->flags);
- else
+ } else {
sk->sk_state_change(sk);
+ }
} else {
err = -EINVAL;
}
base-commit: ff8b09806b7b18884ad8d3a2b3d702da534a4e21
next prev parent reply other threads:[~2026-10-15 18:17 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-13 3:19 [RFC PATCH] Bluetooth: L2CAP: tolerate zero-ident LE credit indications Kyle Pepper
2026-10-13 4:20 ` [RFC] " bluez.test.bot
2026-10-13 14:43 ` [RFC PATCH] " Luiz Augusto von Dentz
2026-10-15 18:31 ` cochcoder
2026-10-15 5:28 ` [RFC PATCH v2] " Kyle Pepper
2026-10-15 6:42 ` [RFC,v2] " bluez.test.bot
2026-10-15 18:16 ` Kyle Pepper [this message]
2026-10-15 18:17 ` [RFC PATCH v3 2/2] " Kyle Pepper
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=20261015181624.48318-1-cochcoder@proton.me \
--to=cochcoder@proton.me \
--cc=johan.hedberg@intel.com \
--cc=linux-bluetooth@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=luiz.dentz@gmail.com \
--cc=marcel@holtmann.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