From nobody Tue Jun 16 10:10:31 2026 Received: from mail-qv1-f50.google.com (mail-qv1-f50.google.com [209.85.219.50]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C11623B1B3 for ; Fri, 17 Apr 2026 22:16:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.50 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776464205; cv=none; b=j+u3SV7J+08HQkMuBrTG7OX+rdxlQUAOnJDjcFsjrnkb5pztxlBNuDIAiAsJRy/1De+/ILz9rb3Mnp9do+HMNtlWg5Ij+HKjSWmLbtB9zn/vtkptXYSerMY5bC3qQbLXw6JprBaMu11iaA1+n6NQL4LKMqf1B4JjmolxANYkg2E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776464205; c=relaxed/simple; bh=xncuZyFph/BnvwQs00+eKWPa35xDllq9CDcHH0DKUyI=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=rMddyCST6aBZpR1X4FpdWePQjVeP5/E2xochDc1zrnkroeSsJcDeA03Ie5tfWb6PJadEOxhPvEN7ytWzc0BhBG+BjoodhimNDc0cYpQH8fT8kOScG7SvkcRLEGk2Qi4mqQVEd8yVmJMA2ZsQfcTY6mTa1K1sOeTqgHO3upGaz4A= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=HTsjPvYh; arc=none smtp.client-ip=209.85.219.50 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="HTsjPvYh" Received: by mail-qv1-f50.google.com with SMTP id 6a1803df08f44-8a3342d301aso11313716d6.2 for ; Fri, 17 Apr 2026 15:16:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1776464202; x=1777069002; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=xCouuL44gA5wAk0ILhGdoV/0EZfXmLvvGfbf/4hMjUs=; b=HTsjPvYh2Sp7divS7SrvLAQlIplmUaxlgHmrwkh/D81PbbCbfSDs/kAS19SxguIFMx 9crqCGz6QQvqFdM6owz3/DD994Yy7tqBeljgS3EWffOh+pxSuWAaiq6+Fu4jCIxW/fpA ZSHRT1gQk+mfqP68sDLAIL2BFSpEhG7DXHrlY97LSrf8/MYwOnDjl92m15QrbQvm150E OJBD6lQASW+MymPnLid2wYkpW5+KVNnRSzfEp2ziU1tw2mnaFQWozrDypB9Ab7jFHm5S qGuahaOwNu4vik417ouSsp38hjIOGTmtxEqfQP1UsuzGSBDWuNxIq/Y+wHwfsHz24ymz Devg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1776464202; x=1777069002; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=xCouuL44gA5wAk0ILhGdoV/0EZfXmLvvGfbf/4hMjUs=; b=jwigBfM7o1Na6pIUACdH79oc+CJz39KMotalgRunSPaQrkgRz3D1CknbzTMeRfcSz6 NxNEHE5YGcyDSxZzX7E0v3tIvwh3EdfiKQHgi/+29tOz+/kx8jjFT/vic2A7E732ww7+ Q3m+w0PjrC682PXAkxaDgAx95gtoVjOYYW6XkmeSjwqAu5d5OnxvocqrpyB/w8Ae26by 1QwehtRJoXEzp6dN4+wU3tOc26QBd+jLP2wvu/2VJS51GanHdeGQ8TOTXmO4T/bG2WgH 7dvRmVC9DOfYDGxe+MjNwMdok4JwzoR+HpT3iGNGixDp0FzaGHNSOWidTcO5asJEeANb qs2g== X-Forwarded-Encrypted: i=1; AFNElJ+4XcxpdvcbcOopdxwjJlMtR4QAXWVl70VnmdmrPvtSrFfC8l1V8d7fDMoUeDVnJnIT+DRenePx4lVXQ7o=@vger.kernel.org X-Gm-Message-State: AOJu0YyoWGFWCABnbv5we7LPLkgs9ElJ28uNDWDsBgfa00zBZtXRwbhs LXR+TiCMRJd8vImD7tsGFJ8qaMrg4DNrLCrTo+2WQxFGBtz2a550nfJe X-Gm-Gg: AeBDievRUO4n6lwdvONEbxZC0A/5A0zbH3VhMf06lhRweRydhigJfG49QQKPeqaGa9t wmJtkPpvtqz+UmwZ3q9jNUR0vIWTS/SAT05oXxITIPdYN+/nyxv4z8ShrSsw8jlktNrOAOfC0HA 5W8VkpxzQTheHdxLn9uLNa+e5Qn0w/LrqITiuUkLc8qAkSbO2WINTM/hNlwQfRAqEKVKu+MsTP/ 9g/KYIWvjJT0tbjldv74tfRj8s+7s3FpBNJDQhJJsNehKH6gAx5K5+AOBHfX/JYG2ECXpnaraVf Vfncm4m5vsPAo9Z+pWBM3wKY6DtO21KaFg9D183+qDMLE0y/5+9Bf+fs70mhZ7WluxmgB7pA0wJ JFCTvsiEfCVpgV1EY3Drma+gUFlcgPkGNS/uVE9sJh93CSV+mTAPGxr4/TdXg28Q3/R0/M0qkV/ JKxPkJ+3TjT+Vb6I+2DlL9fLo0AarKfZ9qU3Rop0xTqn8oyYD4wMIl8V6Vuj8cLHboXjAoCDF35 L7mzq9xZXTBEN1OUvikveM850LA+xa7LC/R+PwYA5ZcFhVrlHPdvA== X-Received: by 2002:a05:6214:cc8:b0:8ac:b70c:8a9b with SMTP id 6a1803df08f44-8b028156a6amr80785566d6.44.1776464201715; Fri, 17 Apr 2026 15:16:41 -0700 (PDT) Received: from server0.tail6e7dd.ts.net (c-68-48-65-54.hsd1.mi.comcast.net. [68.48.65.54]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-8b02aec3a3bsm20754196d6.49.2026.04.17.15.16.40 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 17 Apr 2026 15:16:41 -0700 (PDT) From: Michael Bommarito To: Marcel Holtmann , Luiz Augusto von Dentz Cc: Mat Martineau , Hyunwoo Kim , linux-bluetooth@vger.kernel.org, linux-kernel@vger.kernel.org, stable@vger.kernel.org Subject: [PATCH] Bluetooth: L2CAP: handle zero txwin_size in ERTM RFC option Date: Fri, 17 Apr 2026 18:16:28 -0400 Message-ID: <20260417221628.1674866-1-michael.bommarito@gmail.com> X-Mailer: git-send-email 2.53.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 =3D 0), which computes alloc_size =3D roundup_pow_of_two(size). Per include/linux/log2.h the result is undefined for size =3D=3D 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 =3D=3D 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 >=3D 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 =3D L2CAP_MODE_ERTM and txwin_size =3D 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 =3D 0x10 is ZERO_SIZE_PTR (the kernel's own decoder annotates it as such). r1 =3D 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 =3D 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 =3D=3D 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 track= ing frames") Cc: stable@vger.kernel.org Signed-off-by: Michael Bommarito 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 =3D 0, which + * propagates here as size =3D 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 =3D 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 *c= han, void *data, size_t data break; case L2CAP_MODE_ERTM: + /* + * Peer-supplied RFC txwin_size =3D 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 =3D L2CAP_DEFAULT_TX_WINDOW; + if (!test_bit(CONF_EWS_RECV, &chan->conf_state)) chan->remote_tx_win =3D rfc.txwin_size; else -- 2.53.0