From: Kyle Pepper <cochcoder@proton.me>
To: linux-bluetooth@vger.kernel.org
Cc: luiz.dentz@gmail.com, marcel@holtmann.org
Subject: [RFC PATCH v5 1/2] Bluetooth: L2CAP: Keep credits from completing security upgrades
Date: Fri, 06 Nov 2026 04:55:40 +0000 [thread overview]
Message-ID: <20261106045515.853897-1-cochcoder@proton.me> (raw)
In-Reply-To: <20261106021723.631339-1-cochcoder@proton.me>
Retain LE and ECRED credits without sending or resuming sockets while a
security upgrade is pending. Check the shared send path so a send that
crossed socket readiness or allocation lock gaps cannot drain queued
data at the old security level.
Reject deferred acceptance with EAGAIN while security is pending. Keep
the requested level unchanged and return EBUSY for another BT_SECURITY
request until completion or teardown, including the interval before
failed-upgrade timeout work closes the channel.
Validate encryption and key size before completing security, preserve
deferred setup, and resume connected sockets only when credits remain.
Schedule failed-upgrade teardown outside the channel-list traversal.
Assisted-by: LLM
Signed-off-by: Kyle Pepper <cochcoder@proton.me>
---
Changes in v5:
- Guard the shared send path across socket and allocation lock gaps.
- Keep deferred acceptance blocked while security is pending.
- Reject another BT_SECURITY request until completion or teardown.
AI review:
https://sashiko.dev/#/patchset/20261106021723.631339-1-cochcoder%40proton.me
GCC/Clang sanitizer fixtures, negative controls and W=1 object builds pass.
The local full kernel image builds with KASAN and lockdep enabled.
Native L2CAP and hardware behavior remain unverified. PR1075's BlueZ timeout
use-after-free and checkpatch attribution compatibility need separate CI fixes.
AI assistance: Codex prepared source revisions, descriptions and local tests
in response to the v4 automated review. Attribution is retained.
net/bluetooth/l2cap_core.c | 53 ++++++++++++++++++++++++++++++++++++--
net/bluetooth/l2cap_sock.c | 22 ++++++++++++++--
2 files changed, 71 insertions(+), 4 deletions(-)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 7dcb0586a..ae6659fab 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -2647,6 +2647,9 @@ static void l2cap_le_flowctl_send(struct l2cap_chan *chan)
BT_DBG("chan %p", chan);
+ if (test_bit(FLAG_PENDING_SECURITY, &chan->flags))
+ return;
+
while (chan->tx_credits && !skb_queue_empty(&chan->tx_q)) {
l2cap_do_send(chan, skb_dequeue(&chan->tx_q));
chan->tx_credits--;
@@ -5380,6 +5383,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");
@@ -5393,6 +5401,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);
@@ -7976,6 +7987,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 le_flowctl, pending_le;
+
l2cap_chan_lock(chan);
lockdep_assert_held(&chan->conn->lock);
@@ -7983,7 +7996,13 @@ 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)
+ le_flowctl = hcon->type == LE_LINK &&
+ (chan->mode == L2CAP_MODE_LE_FLOWCTL ||
+ chan->mode == L2CAP_MODE_EXT_FLOWCTL);
+ pending_le = le_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)) {
@@ -7991,9 +8010,39 @@ static void l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
continue;
}
+ if (pending_le && (chan->state == BT_CONNECTED ||
+ chan->state == BT_CONFIG ||
+ chan->state == BT_CONNECT2)) {
+ if (status || !encrypt ||
+ !smp_sufficient_security(hcon, chan->sec_level,
+ SMP_ALLOW_STK) ||
+ !l2cap_check_enc_key_size(hcon, chan)) {
+ /* Keep traffic suspended until timeout work closes
+ * the channel outside this list traversal.
+ */
+ __set_chan_timer(chan, 0);
+ l2cap_chan_unlock(chan);
+ continue;
+ }
+
+ clear_bit(FLAG_PENDING_SECURITY, &chan->flags);
+ chan->sec_level = hcon->sec_level;
+
+ /* Security completion must not accept deferred setup. */
+ if (chan->state == BT_CONNECT2) {
+ if (chan->tx_credits)
+ chan->ops->resume(chan);
+ l2cap_chan_unlock(chan);
+ continue;
+ }
+
+ l2cap_le_flowctl_send(chan);
+ }
+
if (!status && (chan->state == BT_CONNECTED ||
chan->state == BT_CONFIG)) {
- chan->ops->resume(chan);
+ if (!le_flowctl || 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 7a2551ef7..cc00ef688 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -972,6 +972,14 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
break;
}
+ /* Preserve an outstanding LE credit-based security request. */
+ if ((chan->mode == L2CAP_MODE_LE_FLOWCTL ||
+ chan->mode == L2CAP_MODE_EXT_FLOWCTL) &&
+ test_bit(FLAG_PENDING_SECURITY, &chan->flags)) {
+ err = -EBUSY;
+ break;
+ }
+
chan->sec_level = sec.level;
conn = l2cap_chan_conn(chan);
@@ -993,10 +1001,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;
}
@@ -1263,6 +1276,11 @@ static int l2cap_sock_defer(struct sock *sk)
if (!have_conn) {
release_sock(sk);
err = -ENOTCONN;
+ } else if ((chan->mode == L2CAP_MODE_LE_FLOWCTL ||
+ chan->mode == L2CAP_MODE_EXT_FLOWCTL) &&
+ test_bit(FLAG_PENDING_SECURITY, &chan->flags)) {
+ release_sock(sk);
+ err = -EAGAIN;
} else if (chan->mode == L2CAP_MODE_EXT_FLOWCTL) {
sk->sk_state = BT_CONNECTED;
chan->state = BT_CONNECTED;
base-commit: a063a00e6cd9e354aa6d67a154a0527951d7f881
next prev parent reply other threads:[~2026-11-06 4:55 UTC|newest]
Thread overview: 15+ 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 ` [RFC PATCH v3 1/2] Bluetooth: L2CAP: Keep credits from completing security upgrades Kyle Pepper
2026-10-15 18:17 ` [RFC PATCH v3 2/2] Bluetooth: L2CAP: tolerate zero-ident LE credit indications Kyle Pepper
2026-10-15 20:17 ` [RFC,v3,1/2] Bluetooth: L2CAP: Keep credits from completing security upgrades bluez.test.bot
2026-10-31 18:09 ` [RFC PATCH v3 1/2] " cochcoder
2026-11-06 2:17 ` [RFC PATCH v4 " Kyle Pepper
2026-11-06 2:18 ` [RFC PATCH v4 2/2] Bluetooth: L2CAP: tolerate zero-ident LE credit indications Kyle Pepper
2026-11-06 3:17 ` [RFC,v4,1/2] Bluetooth: L2CAP: Keep credits from completing security upgrades bluez.test.bot
2026-11-06 4:55 ` Kyle Pepper [this message]
2026-11-06 4:56 ` [RFC PATCH v5 2/2] Bluetooth: L2CAP: tolerate zero-ident LE credit indications 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=20261106045515.853897-1-cochcoder@proton.me \
--to=cochcoder@proton.me \
--cc=linux-bluetooth@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