public inbox for linux-bluetooth@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-17 22:55 ` bluez.test.bot
  2026-04-20 19:06 ` [PATCH] " Luiz Augusto von Dentz
  0 siblings, 2 replies; 7+ 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] 7+ messages in thread

* RE: 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-17 22:55 ` bluez.test.bot
  2026-04-20 19:06 ` [PATCH] " Luiz Augusto von Dentz
  1 sibling, 0 replies; 7+ messages in thread
From: bluez.test.bot @ 2026-04-17 22:55 UTC (permalink / raw)
  To: linux-bluetooth, michael.bommarito

[-- Attachment #1: Type: text/plain, Size: 1746 bytes --]

This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=1082694

---Test result---

Test Summary:
CheckPatch                    FAIL      0.78 seconds
GitLint                       PASS      0.38 seconds
SubjectPrefix                 PASS      0.13 seconds
BuildKernel                   PASS      25.65 seconds
CheckAllWarning               PASS      28.56 seconds
CheckSparse                   PASS      26.63 seconds
BuildKernel32                 PASS      25.01 seconds
TestRunnerSetup               PASS      526.98 seconds
TestRunner_l2cap-tester       PASS      28.12 seconds
IncrementalBuild              PASS      24.83 seconds

Details
##############################
Test: CheckPatch - FAIL
Desc: Run checkpatch.pl script
Output:
Bluetooth: L2CAP: handle zero txwin_size in ERTM RFC option
WARNING: Non-standard signature: Assisted-by:
#191: 
Assisted-by: Claude:claude-opus-4-7

ERROR: Unrecognized email address: 'Claude:claude-opus-4-7'
#191: 
Assisted-by: Claude:claude-opus-4-7

total: 1 errors, 1 warnings, 0 checks, 34 lines checked

NOTE: For some of the reported defects, checkpatch may be able to
      mechanically convert to the typical style using --fix or --fix-inplace.

/github/workspace/src/patch/14528993.patch has style problems, please review.

NOTE: Ignored message types: UNKNOWN_COMMIT_ID

NOTE: If any of the errors are false positives, please report
      them to the maintainer, see CHECKPATCH in MAINTAINERS.




https://github.com/bluez/bluetooth-next/pull/100

---
Regards,
Linux Bluetooth


^ permalink raw reply	[flat|nested] 7+ 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-17 22:55 ` bluez.test.bot
@ 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)
  1 sibling, 3 replies; 7+ 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] 7+ 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 ` [PATCH] " 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; 7+ 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] 7+ messages in thread

* [PATCH v2 1/2] Bluetooth: L2CAP: handle zero txwin_size in ERTM RFC option
  2026-04-20 19:06 ` [PATCH] " 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 14:51     ` [v2,1/2] " bluez.test.bot
  2026-04-21 13:56   ` [PATCH v2 2/2] Bluetooth: L2CAP: skip ERTM re-init on repeated CONFIG_RSP Michael Bommarito
  2 siblings, 1 reply; 7+ 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] 7+ messages in thread

* [PATCH v2 2/2] Bluetooth: L2CAP: skip ERTM re-init on repeated CONFIG_RSP
  2026-04-20 19:06 ` [PATCH] " 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; 7+ 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] 7+ messages in thread

* RE: [v2,1/2] Bluetooth: L2CAP: handle zero txwin_size in ERTM RFC option
  2026-04-21 13:56   ` [PATCH v2 1/2] Bluetooth: L2CAP: handle zero txwin_size in ERTM RFC option Michael Bommarito
@ 2026-04-21 14:51     ` bluez.test.bot
  0 siblings, 0 replies; 7+ messages in thread
From: bluez.test.bot @ 2026-04-21 14:51 UTC (permalink / raw)
  To: linux-bluetooth, michael.bommarito

[-- Attachment #1: Type: text/plain, Size: 2553 bytes --]

This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=1083882

---Test result---

Test Summary:
CheckPatch                    FAIL      1.20 seconds
GitLint                       PASS      0.45 seconds
SubjectPrefix                 PASS      0.17 seconds
BuildKernel                   PASS      20.51 seconds
CheckAllWarning               PASS      21.04 seconds
CheckSparse                   PASS      23.68 seconds
BuildKernel32                 PASS      18.71 seconds
TestRunnerSetup               PASS      403.79 seconds
TestRunner_l2cap-tester       PASS      23.06 seconds
IncrementalBuild              PASS      21.36 seconds

Details
##############################
Test: CheckPatch - FAIL
Desc: Run checkpatch.pl script
Output:
[v2,1/2] Bluetooth: L2CAP: handle zero txwin_size in ERTM RFC option
WARNING: Non-standard signature: Assisted-by:
#125: 
Assisted-by: Claude:claude-opus-4-7

ERROR: Unrecognized email address: 'Claude:claude-opus-4-7'
#125: 
Assisted-by: Claude:claude-opus-4-7

total: 1 errors, 1 warnings, 0 checks, 72 lines checked

NOTE: For some of the reported defects, checkpatch may be able to
      mechanically convert to the typical style using --fix or --fix-inplace.

/github/workspace/src/patch/14532642.patch has style problems, please review.

NOTE: Ignored message types: UNKNOWN_COMMIT_ID

NOTE: If any of the errors are false positives, please report
      them to the maintainer, see CHECKPATCH in MAINTAINERS.


[v2,2/2] Bluetooth: L2CAP: skip ERTM re-init on repeated CONFIG_RSP
WARNING: Non-standard signature: Assisted-by:
#124: 
Assisted-by: Claude:claude-opus-4-7

ERROR: Unrecognized email address: 'Claude:claude-opus-4-7'
#124: 
Assisted-by: Claude:claude-opus-4-7

WARNING: The commit message has 'stable@', perhaps it also needs a 'Fixes:' tag?

total: 1 errors, 2 warnings, 0 checks, 23 lines checked

NOTE: For some of the reported defects, checkpatch may be able to
      mechanically convert to the typical style using --fix or --fix-inplace.

/github/workspace/src/patch/14532643.patch has style problems, please review.

NOTE: Ignored message types: UNKNOWN_COMMIT_ID

NOTE: If any of the errors are false positives, please report
      them to the maintainer, see CHECKPATCH in MAINTAINERS.




https://github.com/bluez/bluetooth-next/pull/111

---
Regards,
Linux Bluetooth


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-04-21 14:51 UTC | newest]

Thread overview: 7+ 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-17 22:55 ` bluez.test.bot
2026-04-20 19:06 ` [PATCH] " 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 14:51     ` [v2,1/2] " bluez.test.bot
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