* [PATCH] Bluetooth: L2CAP: handle zero txwin_size in ERTM RFC option
@ 2026-04-17 22:16 Michael Bommarito
2026-04-20 19:06 ` Luiz Augusto von Dentz
0 siblings, 1 reply; 5+ messages in thread
From: Michael Bommarito @ 2026-04-17 22:16 UTC (permalink / raw)
To: Marcel Holtmann, Luiz Augusto von Dentz
Cc: Mat Martineau, Hyunwoo Kim, linux-bluetooth, linux-kernel, stable
Bluetooth L2CAP ERTM configuration (RFC option, type 0x04) carries an
unsigned 8-bit txwin_size field. Core Spec v5.3, Vol 3 Part A, section
5.4 specifies a valid range of 1..63 (or 1..0x3fff under the Extended
Window Size extension). A peer-supplied value of zero is out of spec
but the current l2cap_parse_conf_req() path stores it into
chan->remote_tx_win unchanged whenever CONF_EWS_RECV is not set.
The zero then reaches l2cap_seq_list_init(size = 0), which computes
alloc_size = roundup_pow_of_two(size). Per include/linux/log2.h the
result is undefined for size == 0. The runtime behaviour is
architecture-dependent:
* x86, arm64, RISC-V, MIPS, s390x, LoongArch: the ISA shift
instruction masks the shift count by (word_bits - 1), so
1UL << word_bits evaluates to 1 rather than 0.
kmalloc_array(1, sizeof(u16)) returns a valid 2-byte slab
allocation with seq_list->mask == 0. ERTM retransmission
silently collapses every reqseq onto slot 0 (correctness bug,
no memory corruption).
* ARMv7 (AArch32), PowerPC 32-bit (slw), PowerPC 64-bit (sld):
the shift instruction returns 0 for shift counts >= word
width. 1UL << word_bits therefore evaluates to 0,
kmalloc_array(0, sizeof(u16)) returns ZERO_SIZE_PTR, and
seq_list->mask becomes ULONG_MAX. Any subsequent access to
seq_list->list dereferences ZERO_SIZE_PTR (0x10), which is
always an unmapped low-memory address, and the kernel Oopses.
This is a remote kernel panic driven by a single peer-sent
CONFIG_REQ; it is not a demonstrated code-execution primitive.
Verified on qemu-system-arm -M virt -cpu cortex-a15 (ARMv7-A, same
LSL register-shift semantics as the Cortex-A9 class still shipping
in automotive infotainment on NXP i.MX6 with long-term availability
through 2028). A KASAN-inline kernel built from mainline panics in
l2cap_seq_list_init on the first peer CONFIG_REQ carrying
mode = L2CAP_MODE_ERTM and txwin_size = 0:
Unable to handle kernel paging request at virtual address
9f000002 when read
Internal error: Oops: 5 [#1] SMP ARM
PC is at l2cap_seq_list_init+0x140/0x28c
r2 : 00000010 r1 : ffffffff
Register r2 information: zero-size pointer
Mode SVC_32 ISA ARM
Call trace:
l2cap_seq_list_init from l2cap_ertm_init+0x588/0x758
l2cap_ertm_init from l2cap_config_rsp+0xeac/0x1158
l2cap_config_rsp from l2cap_recv_frame+0x1260/0x8000
l2cap_recv_frame from l2cap_recv_acldata+0xb78/0xdb0
l2cap_recv_acldata from hci_rx_work
r2 = 0x10 is ZERO_SIZE_PTR (the kernel's own decoder annotates it
as such). r1 = 0xFFFFFFFF is seq_list->mask after the 0 - 1
underflow. Faulting address 0x9f000002 is the KASAN shadow for
pointer 0x10 (shadow_offset 0x9f000000 + (0x10 >> 3)).
Trigger is one peer-supplied CONFIG_REQ on an L2CAP ERTM channel;
no local privileges required, no pairing required, no local
interaction beyond being within BR/EDR radio range of an affected
host.
Fix in two places:
* l2cap_parse_conf_req(): when the peer sends txwin_size = 0 in
the RFC option, clamp it up to L2CAP_DEFAULT_TX_WINDOW before
the chan->remote_tx_win assignment. This matches the existing
clamp on the CONF_EWS_RECV branch in the same function and
mirrors the shape of commit 25f420a0d4cf ("Bluetooth: L2CAP:
Fix ERTM re-init and zero pdu_len infinite loop") for the
sibling RFC max_pdu_size field. This is the primary fix: it
prevents zero from ever reaching l2cap_seq_list_init() on the
normal config path.
* l2cap_seq_list_init(): return -EINVAL on size == 0 as a
defence-in-depth check for any current or future caller that
might pass an unclamped value. The existing error-propagation
in l2cap_ertm_init() and its callers already tears the channel
down on error, so the peer simply loses the ERTM channel
rather than silently corrupting an unmapped ZERO_SIZE_PTR
allocation.
Related but distinct from commit 25f420a0d4cf ("Bluetooth: L2CAP:
Fix ERTM re-init and zero pdu_len infinite loop") which addressed
the sibling zero-value issue on the RFC max_pdu_size field.
Fixes: 3c588192b5e5 ("Bluetooth: Add the l2cap_seq_list structure for tracking frames")
Cc: stable@vger.kernel.org
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
Assisted-by: Claude:claude-opus-4-7
---
net/bluetooth/l2cap_core.c | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 95c65fece39b..b2fe094263ca 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -323,6 +323,17 @@ static int l2cap_seq_list_init(struct l2cap_seq_list *seq_list, u16 size)
{
size_t alloc_size, i;
+ /*
+ * A peer may send an ERTM RFC option with txwin_size = 0, which
+ * propagates here as size = 0. roundup_pow_of_two(0) is
+ * documented UB (see include/linux/log2.h) and produces a
+ * semantically broken seq_list that silently drops every
+ * retransmission slot. Reject size = 0 explicitly so the caller
+ * (l2cap_ertm_init) tears the channel down cleanly instead.
+ */
+ if (!size)
+ return -EINVAL;
+
/* Allocated size is a power of 2 to map sequence numbers
* (which may be up to 14 bits) in to a smaller array that is
* sized for the negotiated ERTM transmit windows.
@@ -3593,6 +3604,17 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data, size_t data
break;
case L2CAP_MODE_ERTM:
+ /*
+ * Peer-supplied RFC txwin_size = 0 is out of spec
+ * (Core Spec v5.3 Vol 3 Part A 5.4: ERTM tx window
+ * range is 1..63, or 1..0x3fff with EWS). Clamp up
+ * to the default window so the subsequent
+ * l2cap_seq_list_init(remote_tx_win) does not
+ * receive a zero size.
+ */
+ if (!rfc.txwin_size)
+ rfc.txwin_size = L2CAP_DEFAULT_TX_WINDOW;
+
if (!test_bit(CONF_EWS_RECV, &chan->conf_state))
chan->remote_tx_win = rfc.txwin_size;
else
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH] Bluetooth: L2CAP: handle zero txwin_size in ERTM RFC option
2026-04-17 22:16 [PATCH] Bluetooth: L2CAP: handle zero txwin_size in ERTM RFC option Michael Bommarito
@ 2026-04-20 19:06 ` Luiz Augusto von Dentz
2026-04-21 13:56 ` [PATCH v2 0/2] Bluetooth: L2CAP: fix zero txwin_size handling and repeated CONFIG_RSP re-init Michael Bommarito
` (2 more replies)
0 siblings, 3 replies; 5+ messages in thread
From: Luiz Augusto von Dentz @ 2026-04-20 19:06 UTC (permalink / raw)
To: Michael Bommarito
Cc: Marcel Holtmann, Mat Martineau, Hyunwoo Kim, linux-bluetooth,
linux-kernel, stable
Hi Michael,
On Fri, Apr 17, 2026 at 6:16 PM Michael Bommarito
<michael.bommarito@gmail.com> wrote:
>
> Bluetooth L2CAP ERTM configuration (RFC option, type 0x04) carries an
> unsigned 8-bit txwin_size field. Core Spec v5.3, Vol 3 Part A, section
> 5.4 specifies a valid range of 1..63 (or 1..0x3fff under the Extended
> Window Size extension). A peer-supplied value of zero is out of spec
> but the current l2cap_parse_conf_req() path stores it into
> chan->remote_tx_win unchanged whenever CONF_EWS_RECV is not set.
>
> The zero then reaches l2cap_seq_list_init(size = 0), which computes
> alloc_size = roundup_pow_of_two(size). Per include/linux/log2.h the
> result is undefined for size == 0. The runtime behaviour is
> architecture-dependent:
>
> * x86, arm64, RISC-V, MIPS, s390x, LoongArch: the ISA shift
> instruction masks the shift count by (word_bits - 1), so
> 1UL << word_bits evaluates to 1 rather than 0.
> kmalloc_array(1, sizeof(u16)) returns a valid 2-byte slab
> allocation with seq_list->mask == 0. ERTM retransmission
> silently collapses every reqseq onto slot 0 (correctness bug,
> no memory corruption).
>
> * ARMv7 (AArch32), PowerPC 32-bit (slw), PowerPC 64-bit (sld):
> the shift instruction returns 0 for shift counts >= word
> width. 1UL << word_bits therefore evaluates to 0,
> kmalloc_array(0, sizeof(u16)) returns ZERO_SIZE_PTR, and
> seq_list->mask becomes ULONG_MAX. Any subsequent access to
> seq_list->list dereferences ZERO_SIZE_PTR (0x10), which is
> always an unmapped low-memory address, and the kernel Oopses.
> This is a remote kernel panic driven by a single peer-sent
> CONFIG_REQ; it is not a demonstrated code-execution primitive.
>
> Verified on qemu-system-arm -M virt -cpu cortex-a15 (ARMv7-A, same
> LSL register-shift semantics as the Cortex-A9 class still shipping
> in automotive infotainment on NXP i.MX6 with long-term availability
> through 2028). A KASAN-inline kernel built from mainline panics in
> l2cap_seq_list_init on the first peer CONFIG_REQ carrying
> mode = L2CAP_MODE_ERTM and txwin_size = 0:
>
> Unable to handle kernel paging request at virtual address
> 9f000002 when read
> Internal error: Oops: 5 [#1] SMP ARM
> PC is at l2cap_seq_list_init+0x140/0x28c
> r2 : 00000010 r1 : ffffffff
> Register r2 information: zero-size pointer
> Mode SVC_32 ISA ARM
> Call trace:
> l2cap_seq_list_init from l2cap_ertm_init+0x588/0x758
> l2cap_ertm_init from l2cap_config_rsp+0xeac/0x1158
> l2cap_config_rsp from l2cap_recv_frame+0x1260/0x8000
> l2cap_recv_frame from l2cap_recv_acldata+0xb78/0xdb0
> l2cap_recv_acldata from hci_rx_work
>
> r2 = 0x10 is ZERO_SIZE_PTR (the kernel's own decoder annotates it
> as such). r1 = 0xFFFFFFFF is seq_list->mask after the 0 - 1
> underflow. Faulting address 0x9f000002 is the KASAN shadow for
> pointer 0x10 (shadow_offset 0x9f000000 + (0x10 >> 3)).
>
> Trigger is one peer-supplied CONFIG_REQ on an L2CAP ERTM channel;
> no local privileges required, no pairing required, no local
> interaction beyond being within BR/EDR radio range of an affected
> host.
>
> Fix in two places:
>
> * l2cap_parse_conf_req(): when the peer sends txwin_size = 0 in
> the RFC option, clamp it up to L2CAP_DEFAULT_TX_WINDOW before
> the chan->remote_tx_win assignment. This matches the existing
> clamp on the CONF_EWS_RECV branch in the same function and
> mirrors the shape of commit 25f420a0d4cf ("Bluetooth: L2CAP:
> Fix ERTM re-init and zero pdu_len infinite loop") for the
> sibling RFC max_pdu_size field. This is the primary fix: it
> prevents zero from ever reaching l2cap_seq_list_init() on the
> normal config path.
>
> * l2cap_seq_list_init(): return -EINVAL on size == 0 as a
> defence-in-depth check for any current or future caller that
> might pass an unclamped value. The existing error-propagation
> in l2cap_ertm_init() and its callers already tears the channel
> down on error, so the peer simply loses the ERTM channel
> rather than silently corrupting an unmapped ZERO_SIZE_PTR
> allocation.
>
> Related but distinct from commit 25f420a0d4cf ("Bluetooth: L2CAP:
> Fix ERTM re-init and zero pdu_len infinite loop") which addressed
> the sibling zero-value issue on the RFC max_pdu_size field.
>
> Fixes: 3c588192b5e5 ("Bluetooth: Add the l2cap_seq_list structure for tracking frames")
> Cc: stable@vger.kernel.org
> Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
> Assisted-by: Claude:claude-opus-4-7
> ---
> net/bluetooth/l2cap_core.c | 22 ++++++++++++++++++++++
> 1 file changed, 22 insertions(+)
>
> diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
> index 95c65fece39b..b2fe094263ca 100644
> --- a/net/bluetooth/l2cap_core.c
> +++ b/net/bluetooth/l2cap_core.c
> @@ -323,6 +323,17 @@ static int l2cap_seq_list_init(struct l2cap_seq_list *seq_list, u16 size)
> {
> size_t alloc_size, i;
>
> + /*
> + * A peer may send an ERTM RFC option with txwin_size = 0, which
> + * propagates here as size = 0. roundup_pow_of_two(0) is
> + * documented UB (see include/linux/log2.h) and produces a
> + * semantically broken seq_list that silently drops every
> + * retransmission slot. Reject size = 0 explicitly so the caller
> + * (l2cap_ertm_init) tears the channel down cleanly instead.
> + */
> + if (!size)
> + return -EINVAL;
> +
> /* Allocated size is a power of 2 to map sequence numbers
> * (which may be up to 14 bits) in to a smaller array that is
> * sized for the negotiated ERTM transmit windows.
> @@ -3593,6 +3604,17 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data, size_t data
> break;
>
> case L2CAP_MODE_ERTM:
> + /*
> + * Peer-supplied RFC txwin_size = 0 is out of spec
> + * (Core Spec v5.3 Vol 3 Part A 5.4: ERTM tx window
> + * range is 1..63, or 1..0x3fff with EWS). Clamp up
> + * to the default window so the subsequent
> + * l2cap_seq_list_init(remote_tx_win) does not
> + * receive a zero size.
> + */
> + if (!rfc.txwin_size)
> + rfc.txwin_size = L2CAP_DEFAULT_TX_WINDOW;
> +
> if (!test_bit(CONF_EWS_RECV, &chan->conf_state))
> chan->remote_tx_win = rfc.txwin_size;
> else
> --
> 2.53.0
https://sashiko.dev/#/patchset/20260417221628.1674866-1-michael.bommarito%40gmail.com
We should probably fix the double free if it can occur; perhaps the
others should be addressed in different patches.
--
Luiz Augusto von Dentz
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v2 0/2] Bluetooth: L2CAP: fix zero txwin_size handling and repeated CONFIG_RSP re-init
2026-04-20 19:06 ` Luiz Augusto von Dentz
@ 2026-04-21 13:56 ` Michael Bommarito
2026-04-21 13:56 ` [PATCH v2 1/2] Bluetooth: L2CAP: handle zero txwin_size in ERTM RFC option Michael Bommarito
2026-04-21 13:56 ` [PATCH v2 2/2] Bluetooth: L2CAP: skip ERTM re-init on repeated CONFIG_RSP Michael Bommarito
2 siblings, 0 replies; 5+ messages in thread
From: Michael Bommarito @ 2026-04-21 13:56 UTC (permalink / raw)
To: Marcel Holtmann, Luiz Augusto von Dentz
Cc: Mat Martineau, Hyunwoo Kim, linux-bluetooth, linux-kernel, stable
Hi Luiz,
Thanks for the review.
v1 mixed the original zero-txwin fix with a defensive
l2cap_seq_list_init(size == 0) error return. Re-checking that path
made it clear the reroll should be split:
1. keep the original report focused on zero txwin_size, but fix it by
normalizing zero window values at the actual inputs to the ERTM
state machine and by making sequence-list teardown idempotent,
rather than by introducing a new init-time failure path
2. handle the separate repeated-CONFIG_RSP ERTM re-init bug in its
own patch, matching the BT_CONNECTED guard that commit 25f420a0d4cf
already added on the CONFIG_REQ side
While auditing the Sashiko comments, I also checked the claimed
CONF_EWS_RECV bypass. I did not carry that theory into v2: in this tree
CONF_EWS_RECV is declared but never set, so the concrete zero-window
paths are the plain RFC option, the local L2CAP_OPTIONS socket setting,
and the CONFIG_RSP / conf_rfc_get negotiation state.
To make sure the reroll covered all the zero-entry sites (and only the
zero-entry sites), I enumerated every assignment to tx_win / ack_win /
remote_tx_win / txwin_size in net/bluetooth/l2cap_core.c and confirmed
patch 1 normalizes the four reachable input paths -- the local
L2CAP_OPTIONS setsockopt, l2cap_txwin_setup() on the outgoing RFC, the
L2CAP_MODE_ERTM branch of l2cap_parse_conf_req(), and the EWS / RFC
branches of l2cap_parse_conf_rsp() and l2cap_conf_rfc_get(). The only
other writers are the channel-create defaults in l2cap_chan_create()
(hard-coded to L2CAP_DEFAULT_TX_WINDOW) and the outgoing rfc.txwin_size
= 0 assignments in L2CAP_MODE_BASIC / L2CAP_MODE_STREAMING branches,
where a zero is spec-correct and never feeds ack_win.
l2cap_seq_list_free()'s callers are l2cap_chan_del() (channel teardown)
and l2cap_ertm_init()'s error path; clearing the list metadata after
kfree() is safe for both and is what makes the partially-initialized
re-entry in the failure path no longer a double-free source.
For patch 2, the BT_CONNECTED guard added to l2cap_config_rsp() mirrors
the existing one in l2cap_config_req() (lines 4339..4348 in this tree)
so both sides of the CONFIG exchange now refuse to re-enter
l2cap_ertm_init() once the channel is already connected. I did not
pin down a single introducing commit for the CONFIG_RSP side during
this pass, so patch 2 keeps Cc: stable@ without a speculative Fixes:
tag; happy to add one if you'd prefer a specific reference.
Changes since v1
----------------
- Split the reroll into two patches: the zero-txwin fix and the
separate repeated-CONFIG_RSP ERTM re-init fix.
- Dropped the v1 l2cap_seq_list_init(size == 0) -> -EINVAL defence and
instead normalize zero tx window values where they enter negotiated
ERTM state.
- Clamp the local L2CAP_OPTIONS txwin_size = 0 case back to
L2CAP_DEFAULT_TX_WINDOW.
- Normalize zero tx window values seen during CONFIG_RSP / RFC parsing
so ack_win does not collapse to 0.
- Make l2cap_seq_list_free() clear list metadata after kfree so later
teardown cannot trip over a previously freed list.
Michael Bommarito (2):
Bluetooth: L2CAP: handle zero txwin_size in ERTM RFC option
Bluetooth: L2CAP: skip ERTM re-init on repeated CONFIG_RSP
net/bluetooth/l2cap_core.c | 39 +++++++++++++++++++++++++++-----------
net/bluetooth/l2cap_sock.c | 3 +++
2 files changed, 31 insertions(+), 11 deletions(-)
--
2.53.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v2 1/2] Bluetooth: L2CAP: handle zero txwin_size in ERTM RFC option
2026-04-20 19:06 ` Luiz Augusto von Dentz
2026-04-21 13:56 ` [PATCH v2 0/2] Bluetooth: L2CAP: fix zero txwin_size handling and repeated CONFIG_RSP re-init Michael Bommarito
@ 2026-04-21 13:56 ` Michael Bommarito
2026-04-21 13:56 ` [PATCH v2 2/2] Bluetooth: L2CAP: skip ERTM re-init on repeated CONFIG_RSP Michael Bommarito
2 siblings, 0 replies; 5+ messages in thread
From: Michael Bommarito @ 2026-04-21 13:56 UTC (permalink / raw)
To: Marcel Holtmann, Luiz Augusto von Dentz
Cc: Mat Martineau, Hyunwoo Kim, linux-bluetooth, linux-kernel, stable
Peer-supplied ERTM RFC txwin_size = 0 can still propagate into the
ERTM transmit-window state, and the same invalid value can be
introduced locally through L2CAP_OPTIONS. In the request path that zero
reaches l2cap_seq_list_init(..., 0); in the response path it can shrink
ack_win to 0 and leave ERTM sequencing in a nonsensical state.
Normalize zero tx window values back to L2CAP_DEFAULT_TX_WINDOW
wherever they enter the ERTM state machine: local socket options,
outgoing tx_win setup, incoming config requests, and config-response
parsing. Also make l2cap_seq_list_free() clear its metadata after kfree
so an init failure after freeing srej_list cannot be freed a second time
during later channel teardown.
Fixes: 3c588192b5e5 ("Bluetooth: Add the l2cap_seq_list structure for tracking frames")
Cc: stable@vger.kernel.org
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
Assisted-by: Claude:claude-opus-4-7
---
Changes in v2:
- drop the v1 `l2cap_seq_list_init(size == 0) -> -EINVAL` approach
and instead normalize zero tx window values at the socket /
request / response inputs
- clamp the local `L2CAP_OPTIONS` txwin_size = 0 case back to
`L2CAP_DEFAULT_TX_WINDOW`
- make `l2cap_seq_list_free()` clear its metadata after `kfree()` so
later teardown cannot trip over a previously freed list
- split the repeated `CONFIG_RSP` ERTM re-init fix into patch 2
net/bluetooth/l2cap_core.c | 23 +++++++++++++++++++----
net/bluetooth/l2cap_sock.c | 3 +++
2 files changed, 22 insertions(+), 4 deletions(-)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 95c65fece39b..7ffafd117817 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -345,6 +345,10 @@ static int l2cap_seq_list_init(struct l2cap_seq_list *seq_list, u16 size)
static inline void l2cap_seq_list_free(struct l2cap_seq_list *seq_list)
{
kfree(seq_list->list);
+ seq_list->list = NULL;
+ seq_list->mask = 0;
+ seq_list->head = L2CAP_SEQ_LIST_CLEAR;
+ seq_list->tail = L2CAP_SEQ_LIST_CLEAR;
}
static inline bool l2cap_seq_list_contains(struct l2cap_seq_list *seq_list,
@@ -3234,8 +3238,15 @@ static void __l2cap_set_ertm_timeouts(struct l2cap_chan *chan,
rfc->monitor_timeout = cpu_to_le16(L2CAP_DEFAULT_MONITOR_TO);
}
+static inline u16 l2cap_txwin_default(u16 txwin)
+{
+ return txwin ? txwin : L2CAP_DEFAULT_TX_WINDOW;
+}
+
static inline void l2cap_txwin_setup(struct l2cap_chan *chan)
{
+ chan->tx_win = l2cap_txwin_default(chan->tx_win);
+
if (chan->tx_win > L2CAP_DEFAULT_TX_WINDOW &&
__l2cap_ews_supported(chan->conn)) {
/* use extended control field */
@@ -3593,6 +3604,8 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data, size_t data
break;
case L2CAP_MODE_ERTM:
+ rfc.txwin_size = l2cap_txwin_default(rfc.txwin_size);
+
if (!test_bit(CONF_EWS_RECV, &chan->conf_state))
chan->remote_tx_win = rfc.txwin_size;
else
@@ -3715,7 +3728,8 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len,
case L2CAP_CONF_EWS:
if (olen != 2)
break;
- chan->ack_win = min_t(u16, val, chan->ack_win);
+ chan->ack_win = min_t(u16, l2cap_txwin_default(val),
+ chan->ack_win);
l2cap_add_conf_opt(&ptr, L2CAP_CONF_EWS, 2,
chan->tx_win, endptr - ptr);
break;
@@ -3756,7 +3770,7 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len,
chan->mps = le16_to_cpu(rfc.max_pdu_size);
if (!test_bit(FLAG_EXT_CTRL, &chan->flags))
chan->ack_win = min_t(u16, chan->ack_win,
- rfc.txwin_size);
+ l2cap_txwin_default(rfc.txwin_size));
if (test_bit(FLAG_EFS_ENABLE, &chan->flags)) {
chan->local_msdu = le16_to_cpu(efs.msdu);
@@ -3970,10 +3984,11 @@ static void l2cap_conf_rfc_get(struct l2cap_chan *chan, void *rsp, int len)
chan->monitor_timeout = le16_to_cpu(rfc.monitor_timeout);
chan->mps = le16_to_cpu(rfc.max_pdu_size);
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
- chan->ack_win = min_t(u16, chan->ack_win, txwin_ext);
+ chan->ack_win = min_t(u16, chan->ack_win,
+ l2cap_txwin_default(txwin_ext));
else
chan->ack_win = min_t(u16, chan->ack_win,
- rfc.txwin_size);
+ l2cap_txwin_default(rfc.txwin_size));
break;
case L2CAP_MODE_STREAMING:
chan->mps = le16_to_cpu(rfc.max_pdu_size);
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 71e8c1b45bce..3b53e967bf40 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -765,6 +765,9 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname,
break;
}
+ if (!opts.txwin_size)
+ opts.txwin_size = L2CAP_DEFAULT_TX_WINDOW;
+
if (!l2cap_valid_mtu(chan, opts.imtu)) {
err = -EINVAL;
break;
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v2 2/2] Bluetooth: L2CAP: skip ERTM re-init on repeated CONFIG_RSP
2026-04-20 19:06 ` Luiz Augusto von Dentz
2026-04-21 13:56 ` [PATCH v2 0/2] Bluetooth: L2CAP: fix zero txwin_size handling and repeated CONFIG_RSP re-init Michael Bommarito
2026-04-21 13:56 ` [PATCH v2 1/2] Bluetooth: L2CAP: handle zero txwin_size in ERTM RFC option Michael Bommarito
@ 2026-04-21 13:56 ` Michael Bommarito
2 siblings, 0 replies; 5+ messages in thread
From: Michael Bommarito @ 2026-04-21 13:56 UTC (permalink / raw)
To: Marcel Holtmann, Luiz Augusto von Dentz
Cc: Mat Martineau, Hyunwoo Kim, linux-bluetooth, linux-kernel, stable
commit 25f420a0d4cf ("Bluetooth: L2CAP: Fix ERTM re-init and zero
pdu_len infinite loop") taught l2cap_config_req() not to call
l2cap_ertm_init() again once the channel is already in BT_CONNECTED.
l2cap_config_rsp() still lacks the same guard. After the initial ERTM
setup, any extra successful CONFIG_RSP re-enters l2cap_ertm_init(),
reinitializes tx_q and srej_q, and allocates fresh sequence lists over
the existing channel state.
Mirror the existing BT_CONNECTED check in l2cap_config_rsp() so response
parsing can still update negotiated parameters without reinitializing
ERTM state or leaking the old resources.
Cc: stable@vger.kernel.org
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
Assisted-by: Claude:claude-opus-4-7
---
Changes in v2:
- split out of the original zero-txwin patch so the repeated
`CONFIG_RSP` ERTM re-init bug is reviewed as a distinct issue
- mirror the existing `BT_CONNECTED` guard already present on the
`CONFIG_REQ` side after commit 25f420a0d4cf
net/bluetooth/l2cap_core.c | 16 +++++++++-------
1 file changed, 9 insertions(+), 7 deletions(-)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 7ffafd117817..fe98f4821a90 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -4480,14 +4480,16 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn,
if (test_bit(CONF_OUTPUT_DONE, &chan->conf_state)) {
set_default_fcs(chan);
- if (chan->mode == L2CAP_MODE_ERTM ||
- chan->mode == L2CAP_MODE_STREAMING)
- err = l2cap_ertm_init(chan);
+ if (chan->state != BT_CONNECTED) {
+ if (chan->mode == L2CAP_MODE_ERTM ||
+ chan->mode == L2CAP_MODE_STREAMING)
+ err = l2cap_ertm_init(chan);
- if (err < 0)
- l2cap_send_disconn_req(chan, -err);
- else
- l2cap_chan_ready(chan);
+ if (err < 0)
+ l2cap_send_disconn_req(chan, -err);
+ else
+ l2cap_chan_ready(chan);
+ }
}
done:
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-04-21 13:56 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-17 22:16 [PATCH] Bluetooth: L2CAP: handle zero txwin_size in ERTM RFC option Michael Bommarito
2026-04-20 19:06 ` Luiz Augusto von Dentz
2026-04-21 13:56 ` [PATCH v2 0/2] Bluetooth: L2CAP: fix zero txwin_size handling and repeated CONFIG_RSP re-init Michael Bommarito
2026-04-21 13:56 ` [PATCH v2 1/2] Bluetooth: L2CAP: handle zero txwin_size in ERTM RFC option Michael Bommarito
2026-04-21 13:56 ` [PATCH v2 2/2] Bluetooth: L2CAP: skip ERTM re-init on repeated CONFIG_RSP Michael Bommarito
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox