public inbox for stable@vger.kernel.org
 help / color / mirror / Atom feed
* [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