* [PATCH net-next 0/7] net: convert remaining bluetooth socket families to getsockopt_iter
From: Breno Leitao @ 2026-05-11 10:41 UTC (permalink / raw)
To: Marcel Holtmann, Luiz Augusto von Dentz, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Shuah Khan
Cc: linux-bluetooth, linux-kernel, netdev, linux-kselftest,
Breno Leitao, kernel-team
Continue the conversion to .getsockopt_iter for the Bluetooth socket
families: hci_sock, ISO, RFCOMM, SCO and L2CAP. The first patch is a
small precursor that fixes a long-standing 1-byte put_user write in
hci_sock_getsockopt_old() so the subsequent conversion stays mechanical.
The riskiest change in this series is the SCO BT_CODEC conversion: it
is the only one that drops an open-coded ptr cursor in favour of
relying on iter_out advancing naturally on every copy_to_iter() call.
Every other socket option is a near-mechanical s/copy_to_user/
copy_to_iter/ rewrite, but BT_CODEC walks a variable-length list of
codecs + capabilities and previously tracked its own write offset by
hand. Getting the cursor semantics wrong here would silently truncate
or misalign user-visible codec data.
To guard against regressions in that specific path, a patch (previous to
the change) extends tools/testing/selftests/net/getsockopt_iter.c with
a focused BT_CODEC test that pre-fills the user buffer with a sentinel
and asserts:
- the buffer is unchanged on the early error returns (-EBADFD /
-EOPNOTSUPP, the common case in CI without a controller exposing
HCI_OFFLOAD_CODECS_ENABLED + a get_data_path_id op), and
- at least one byte changed on the success path (when a capable
controller is present), proving the converted copy_to_iter() walk
actually wrote into user memory.
Signed-off-by: Breno Leitao <leitao@debian.org>
---
Breno Leitao (7):
Bluetooth: hci_sock: write the full optval for getsockopt
Bluetooth: hci_sock: convert to getsockopt_iter
Bluetooth: ISO: convert to getsockopt_iter
Bluetooth: RFCOMM: convert to getsockopt_iter
Bluetooth: L2CAP: convert to getsockopt_iter
selftests: net: getsockopt_iter: cover SCO BT_CODEC conversion
Bluetooth: SCO: convert to getsockopt_iter
net/bluetooth/hci_sock.c | 26 ++++++-----
net/bluetooth/iso.c | 27 +++++------
net/bluetooth/l2cap_sock.c | 61 +++++++++++++++----------
net/bluetooth/rfcomm/sock.c | 30 +++++++------
net/bluetooth/sco.c | 59 ++++++++++++------------
tools/testing/selftests/net/getsockopt_iter.c | 65 +++++++++++++++++++++++++++
6 files changed, 179 insertions(+), 89 deletions(-)
---
base-commit: 63751099502d10f0aa6bb35273e56c5800cc4e3a
change-id: 20260511-getsock_three-d0d7f1b2629e
Best regards,
--
Breno Leitao <leitao@debian.org>
^ permalink raw reply
* [PATCH net-next 1/7] Bluetooth: hci_sock: write the full optval for getsockopt
From: Breno Leitao @ 2026-05-11 10:41 UTC (permalink / raw)
To: Marcel Holtmann, Luiz Augusto von Dentz, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Shuah Khan
Cc: linux-bluetooth, linux-kernel, netdev, linux-kselftest,
Breno Leitao, kernel-team
In-Reply-To: <20260511-getsock_three-v1-0-1461fa8786ab@debian.org>
In hci_sock_getsockopt_old(), HCI_DATA_DIR and HCI_TIME_STAMP both store
their value into a local int and then call put_user(opt, optval). Because
optval is the function parameter typed char __user *, put_user sizes the
write from sizeof(*optval), so only the low byte of the int is copied to
userspace.
The matching setsockopt path reads sizeof(int) via copy_safe_from_sockptr,
so userspace passes a 4-byte buffer in both directions but previously got
back only one initialized byte on the read side.
Not sending this through 'net' tree given this bug is mostly invisble,
given opt is 0/1, and the last byte is being properly copied.
With this change, the upcoming translation to .getsockopt_iter becomes
mechanical.
FWIW: This behavior appeared in commit 1da177e4c3f4 ("Linux-2.6.12-rc2").
Signed-off-by: Breno Leitao <leitao@debian.org>
---
net/bluetooth/hci_sock.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index 0290dea081f62..1823c06ba8940 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -2088,7 +2088,7 @@ static int hci_sock_getsockopt_old(struct socket *sock, int level, int optname,
else
opt = 0;
- if (put_user(opt, optval))
+ if (put_user(opt, (int __user *)optval))
err = -EFAULT;
break;
@@ -2098,7 +2098,7 @@ static int hci_sock_getsockopt_old(struct socket *sock, int level, int optname,
else
opt = 0;
- if (put_user(opt, optval))
+ if (put_user(opt, (int __user *)optval))
err = -EFAULT;
break;
--
2.53.0-Meta
^ permalink raw reply related
* Re: [PATCH v2 5/8] net: of_net: Add of_get_nvmem_eui48() helper for EUI-48 lookup
From: Bartosz Golaszewski @ 2026-05-11 10:36 UTC (permalink / raw)
To: Loic Poulain
Cc: linux-mmc, devicetree, linux-kernel, linux-arm-msm, linux-block,
linux-wireless, ath10k, linux-bluetooth, netdev, daniel,
Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Bjorn Andersson, Konrad Dybcio, Jens Axboe, Johannes Berg,
Jeff Johnson, Bartosz Golaszewski, Marcel Holtmann,
Luiz Augusto von Dentz, Balakrishna Godavarthi, Rocky Liao,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Srinivas Kandagatla, Andrew Lunn, Heiner Kallweit,
Russell King, Saravana Kannan
In-Reply-To: <20260507-block-as-nvmem-v2-5-bf17edd5134e@oss.qualcomm.com>
On Thu, 7 May 2026 17:24:40 +0200, Loic Poulain
<loic.poulain@oss.qualcomm.com> said:
> Factor out the common NVMEM EUI-48 retrieval logic from
> of_get_mac_address_nvmem() into a new of_get_nvmem_eui48() helper that
> accepts the NVMEM cell name as a parameter. This allows other subsystems
> (e.g. Bluetooth) to reuse the same lookup-validate-copy pattern with a
> different cell name, without duplicating code.
>
> of_get_mac_address_nvmem() is updated to call of_get_nvmem_eui48() with
> "mac-address", preserving its existing behavior.
>
> Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
> ---
Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
^ permalink raw reply
* RE: Bluetooth: L2CAP: Fix use-after-free in l2cap_sock_new_connection_cb()
From: bluez.test.bot @ 2026-05-11 8:32 UTC (permalink / raw)
To: linux-bluetooth, oss
In-Reply-To: <20260511045124.298319-2-oss@fourdim.xyz>
[-- Attachment #1: Type: text/plain, Size: 1045 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=1092493
---Test result---
Test Summary:
CheckPatch PASS 2.09 seconds
GitLint PASS 0.32 seconds
SubjectPrefix PASS 0.12 seconds
BuildKernel PASS 25.70 seconds
CheckAllWarning PASS 28.70 seconds
CheckSparse PASS 27.32 seconds
BuildKernel32 PASS 25.31 seconds
TestRunnerSetup PASS 561.40 seconds
TestRunner_l2cap-tester PASS 378.21 seconds
TestRunner_smp-tester PASS 18.06 seconds
TestRunner_6lowpan-tester PASS 51.06 seconds
IncrementalBuild PASS 24.97 seconds
https://github.com/bluez/bluetooth-next/pull/163
---
Regards,
Linux Bluetooth
^ permalink raw reply
* RE: [6.1.y,1/2] Bluetooth: hci_sync: Remove remaining dependencies of hci_request
From: bluez.test.bot @ 2026-05-11 7:59 UTC (permalink / raw)
To: linux-bluetooth, 32840572
In-Reply-To: <tencent_CA96C7486F1CF8F0C72A2274E56AD1766708@qq.com>
[-- Attachment #1: Type: text/plain, Size: 727 bytes --]
This is an automated email and please do not reply to this email.
Dear Submitter,
Thank you for submitting the patches to the linux bluetooth mailing list.
While preparing the CI tests, the patches you submitted couldn't be applied to the current HEAD of the repository.
----- Output -----
error: patch failed: include/net/bluetooth/hci_sync.h:5
error: include/net/bluetooth/hci_sync.h: patch does not apply
error: net/bluetooth/hci_request.h: does not exist in index
error: patch failed: net/bluetooth/hci_sync.c:11
error: net/bluetooth/hci_sync.c: patch does not apply
hint: Use 'git am --show-current-patch' to see the failed patch
Please resolve the issue and submit the patches again.
---
Regards,
Linux Bluetooth
^ permalink raw reply
* RE: support libical 4.0
From: bluez.test.bot @ 2026-05-11 7:54 UTC (permalink / raw)
To: linux-bluetooth, fundawang
In-Reply-To: <1445e90e.ded.19e159db7d2.Coremail.fundawang@yeah.net>
[-- Attachment #1: Type: text/plain, Size: 382 bytes --]
This is an automated email and please do not reply to this email.
Dear Submitter,
Thank you for submitting the patches to the linux bluetooth mailing list.
While preparing the CI tests, the patches you submitted couldn't be applied to the current HEAD of the repository.
----- Output -----
Please resolve the issue and submit the patches again.
---
Regards,
Linux Bluetooth
^ permalink raw reply
* Re: [Patch] support libical 4.0
From: Bastien Nocera @ 2026-05-11 7:44 UTC (permalink / raw)
To: Funda Wang, linux-bluetooth
In-Reply-To: <1445e90e.ded.19e159db7d2.Coremail.fundawang@yeah.net>
On Mon, 2026-05-11 at 13:58 +0800, Funda Wang wrote:
> Hello,
>
> Since libical 4.0 [1], pkgconfig files of libical have been splitted
> into several files according to libraries. The libical pkgconfig
> check need to be tweaked to check libicalvcal. Patch attached.
That breaks libical < 4.0, which is still the current version on most
stable distributions.
And please follow the instructions at:
https://github.com/bluez/bluez/blob/master/HACKING#L98
for how to send patches.
Regards
>
> Thanks.
>
> [1]: https://github.com/libical/libical/pull/988
^ permalink raw reply
* [PATCH 6.1.y 2/2] Bluetooth: btintel: serialize btintel_hw_error() with hci_req_sync_lock
From: Fang Wang @ 2026-05-11 6:35 UTC (permalink / raw)
To: gregkh, stable, zzzccc427
Cc: patches, linux-kernel, marcel, johan.hedberg, luiz.dentz,
linux-bluetooth, luiz.von.dentz
From: Cen Zhang <zzzccc427@gmail.com>
[ Upstream commit 94d8e6fe5d0818e9300e514e095a200bd5ff93ae ]
btintel_hw_error() issues two __hci_cmd_sync() calls (HCI_OP_RESET
and Intel exception-info retrieval) without holding
hci_req_sync_lock(). This lets it race against
hci_dev_do_close() -> btintel_shutdown_combined(), which also runs
__hci_cmd_sync() under the same lock. When both paths manipulate
hdev->req_status/req_rsp concurrently, the close path may free the
response skb first, and the still-running hw_error path hits a
slab-use-after-free in kfree_skb().
Wrap the whole recovery sequence in hci_req_sync_lock/unlock so it
is serialized with every other synchronous HCI command issuer.
Below is the data race report and the kasan report:
BUG: data-race in __hci_cmd_sync_sk / btintel_shutdown_combined
read of hdev->req_rsp at net/bluetooth/hci_sync.c:199
by task kworker/u17:1/83:
__hci_cmd_sync_sk+0x12f2/0x1c30 net/bluetooth/hci_sync.c:200
__hci_cmd_sync+0x55/0x80 net/bluetooth/hci_sync.c:223
btintel_hw_error+0x114/0x670 drivers/bluetooth/btintel.c:254
hci_error_reset+0x348/0xa30 net/bluetooth/hci_core.c:1030
write/free by task ioctl/22580:
btintel_shutdown_combined+0xd0/0x360
drivers/bluetooth/btintel.c:3648
hci_dev_close_sync+0x9ae/0x2c10 net/bluetooth/hci_sync.c:5246
hci_dev_do_close+0x232/0x460 net/bluetooth/hci_core.c:526
BUG: KASAN: slab-use-after-free in
sk_skb_reason_drop+0x43/0x380 net/core/skbuff.c:1202
Read of size 4 at addr ffff888144a738dc
by task kworker/u17:1/83:
__hci_cmd_sync_sk+0x12f2/0x1c30 net/bluetooth/hci_sync.c:200
__hci_cmd_sync+0x55/0x80 net/bluetooth/hci_sync.c:223
btintel_hw_error+0x186/0x670 drivers/bluetooth/btintel.c:260
Fixes: 973bb97e5aee ("Bluetooth: btintel: Add generic function for handling hardware errors")
Signed-off-by: Cen Zhang <zzzccc427@gmail.com>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Signed-off-by: Fang Wang <32840572@qq.com>
---
drivers/bluetooth/btintel.c | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c
index 7a9d2da3c814..1cba08e9403a 100644
--- a/drivers/bluetooth/btintel.c
+++ b/drivers/bluetooth/btintel.c
@@ -225,11 +225,13 @@ static void btintel_hw_error(struct hci_dev *hdev, u8 code)
bt_dev_err(hdev, "Hardware error 0x%2.2x", code);
+ hci_req_sync_lock(hdev);
+
skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
bt_dev_err(hdev, "Reset after hardware error failed (%ld)",
PTR_ERR(skb));
- return;
+ goto unlock;
}
kfree_skb(skb);
@@ -237,18 +239,21 @@ static void btintel_hw_error(struct hci_dev *hdev, u8 code)
if (IS_ERR(skb)) {
bt_dev_err(hdev, "Retrieving Intel exception info failed (%ld)",
PTR_ERR(skb));
- return;
+ goto unlock;
}
if (skb->len != 13) {
bt_dev_err(hdev, "Exception info size mismatch");
kfree_skb(skb);
- return;
+ goto unlock;
}
bt_dev_err(hdev, "Exception info %s", (char *)(skb->data + 1));
kfree_skb(skb);
+
+unlock:
+ hci_req_sync_unlock(hdev);
}
int btintel_version_info(struct hci_dev *hdev, struct intel_version *ver)
--
2.34.1
^ permalink raw reply related
* [PATCH 6.1.y 1/2] Bluetooth: hci_sync: Remove remaining dependencies of hci_request
From: Fang Wang @ 2026-05-11 6:34 UTC (permalink / raw)
To: gregkh, stable, luiz.von.dentz
Cc: patches, linux-kernel, marcel, johan.hedberg, luiz.dentz, davem,
edumazet, kuba, pabeni, linux-bluetooth, netdev
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
[ Upstream commit f2d89775358606c7ab6b6b6c4a02fe1e8cd270b1 ]
This removes the dependencies of hci_req_init and hci_request_cancel_all
from hci_sync.c.
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Signed-off-by: Fang Wang <32840572@qq.com>
---
include/net/bluetooth/hci_sync.h | 17 +++++++++++++++++
net/bluetooth/hci_request.h | 21 ---------------------
net/bluetooth/hci_sync.c | 14 +++++++++++---
3 files changed, 28 insertions(+), 24 deletions(-)
diff --git a/include/net/bluetooth/hci_sync.h b/include/net/bluetooth/hci_sync.h
index a8b106d884d4..a68ddf5c0228 100644
--- a/include/net/bluetooth/hci_sync.h
+++ b/include/net/bluetooth/hci_sync.h
@@ -5,6 +5,23 @@
* Copyright (C) 2021 Intel Corporation
*/
+#define HCI_REQ_DONE 0
+#define HCI_REQ_PEND 1
+#define HCI_REQ_CANCELED 2
+
+#define hci_req_sync_lock(hdev) mutex_lock(&hdev->req_lock)
+#define hci_req_sync_unlock(hdev) mutex_unlock(&hdev->req_lock)
+
+struct hci_request {
+ struct hci_dev *hdev;
+ struct sk_buff_head cmd_q;
+
+ /* If something goes wrong when building the HCI request, the error
+ * value is stored in this field.
+ */
+ int err;
+};
+
typedef int (*hci_cmd_sync_work_func_t)(struct hci_dev *hdev, void *data);
typedef void (*hci_cmd_sync_work_destroy_t)(struct hci_dev *hdev, void *data,
int err);
diff --git a/net/bluetooth/hci_request.h b/net/bluetooth/hci_request.h
index 0be75cf0efed..b730da4a8b47 100644
--- a/net/bluetooth/hci_request.h
+++ b/net/bluetooth/hci_request.h
@@ -22,27 +22,6 @@
#include <asm/unaligned.h>
-#define HCI_REQ_DONE 0
-#define HCI_REQ_PEND 1
-#define HCI_REQ_CANCELED 2
-
-#define hci_req_sync_lock(hdev) mutex_lock(&hdev->req_lock)
-#define hci_req_sync_unlock(hdev) mutex_unlock(&hdev->req_lock)
-
-#define HCI_REQ_DONE 0
-#define HCI_REQ_PEND 1
-#define HCI_REQ_CANCELED 2
-
-struct hci_request {
- struct hci_dev *hdev;
- struct sk_buff_head cmd_q;
-
- /* If something goes wrong when building the HCI request, the error
- * value is stored in this field.
- */
- int err;
-};
-
void hci_req_init(struct hci_request *req, struct hci_dev *hdev);
void hci_req_purge(struct hci_request *req);
bool hci_req_status_pend(struct hci_dev *hdev);
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index c6f9d07a4819..4d23455e90bb 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -11,7 +11,6 @@
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/mgmt.h>
-#include "hci_request.h"
#include "hci_codec.h"
#include "hci_debugfs.h"
#include "smp.h"
@@ -142,6 +141,13 @@ static int hci_cmd_sync_run(struct hci_request *req)
return 0;
}
+static void hci_request_init(struct hci_request *req, struct hci_dev *hdev)
+{
+ skb_queue_head_init(&req->cmd_q);
+ req->hdev = hdev;
+ req->err = 0;
+}
+
/* This function requires the caller holds hdev->req_lock. */
struct sk_buff *__hci_cmd_sync_sk(struct hci_dev *hdev, u16 opcode, u32 plen,
const void *param, u8 event, u32 timeout,
@@ -153,7 +159,7 @@ struct sk_buff *__hci_cmd_sync_sk(struct hci_dev *hdev, u16 opcode, u32 plen,
bt_dev_dbg(hdev, "Opcode 0x%4.4x", opcode);
- hci_req_init(&req, hdev);
+ hci_request_init(&req, hdev);
hci_cmd_sync_add(&req, opcode, plen, param, event, sk);
@@ -5188,7 +5194,9 @@ int hci_dev_close_sync(struct hci_dev *hdev)
cancel_delayed_work(&hdev->le_scan_disable);
cancel_delayed_work(&hdev->le_scan_restart);
- hci_request_cancel_all(hdev);
+ hci_cmd_sync_cancel_sync(hdev, ENODEV);
+
+ cancel_interleave_scan(hdev);
if (hdev->adv_instance_timeout) {
cancel_delayed_work_sync(&hdev->adv_instance_expire);
--
2.34.1
^ permalink raw reply related
* Re: Linux 7.1-rc3 regression (Bluetooth)
From: Thorsten Leemhuis @ 2026-05-11 6:30 UTC (permalink / raw)
To: markus.suvanto
Cc: linux-kernel, linux-bluetooth@vger.kernel.org,
Linux kernel regressions list, Luiz Augusto von Dentz,
Pauli Virtanen, Mikhail Gavrilov
In-Reply-To: <f652d5d9841a9b7c100dd19ee97c86099f580724.camel@gmail.com>
On 5/11/26 07:17, markus.suvanto@gmail.com wrote:
> Hello
>
> I upgrade 7.1-rc2 to 7.1-rc3. After that bluetooth didn't start
> hci0: Failed to send wmt func ctrl (-22)
> My fix was to revert commit 634a4408c0615c523cf7531790f4f14a422b9206
Thx for your report. FWIW, there are two proposed fixed for this change
floating around:
https://lore.kernel.org/all/20260508173121.27526-1-mikhail.v.gavrilov@gmail.com/
https://lore.kernel.org/all/770d36b07311bf88210c187923f243fb9f126f04.1777058551.git.pav@iki.fi/
Given that this is the third revert within a short time-frame I wonder
if we should fast-track a fix (once ready) to spare more users the pain
of bisecting & reporting.
Ciao, Thorsten
> -Markus Suvanto
>
>
> commit 994246ea1eed9029d648b28a9828359f5046173e (HEAD -> master)
> Author: Markus Suvanto <markus.suvanto@movesole.com>
> Date: Mon May 11 07:29:47 2026 +0300
>
> Revert "Bluetooth: btmtk: validate WMT event SKB length before struct access"
>
> This reverts commit 634a4408c0615c523cf7531790f4f14a422b9206.
>
> commit 5d6919055dec134de3c40167a490f33c74c12581 (tag: v7.1-rc3, origin/master, origin/HEAD)
> Author: Linus Torvalds <torvalds@linux-foundation.org>
> Date: Sun May 10 14:08:09 2026 -0700
>
> Linux 7.1-rc3
>
>
> Lenovo ThinkPad T14s Gen 1 (20UJ) AMD Ryzen 5 PRO 4650U
>
> Not working dmesg:
> masu@t470 ~ % grep Bluetoot dmesg_7.1.0-rc3
> [ 4.229749] Bluetooth: Core ver 2.22
> [ 4.232923] Bluetooth: HCI device and connection manager initialized
> [ 4.233081] Bluetooth: HCI socket layer initialized
> [ 4.233227] Bluetooth: L2CAP socket layer initialized
> [ 4.233236] Bluetooth: SCO socket layer initialized
> [ 4.413060] Bluetooth: hci0: HW/SW Version: 0x008a008a, Build Time: 20251223091725
> [ 6.142372] Bluetooth: BNEP (Ethernet Emulation) ver 1.3
> [ 6.142384] Bluetooth: BNEP filters: protocol multicast
> [ 6.142393] Bluetooth: BNEP socket layer initialized
> [ 7.727087] Bluetooth: hci0: Failed to send wmt func ctrl (-22)
> [ 7.727140] Bluetooth: hci0: HCI Enhanced Setup Synchronous Connection command is advertised, but not supported.
>
>
> Working dmesg:
> masu@t470 ~ % grep Bluetoot dmesg_7.1.0-rc3-00001-g994246ea1eed
> [ 4.296358] Bluetooth: Core ver 2.22
> [ 4.296385] Bluetooth: HCI device and connection manager initialized
> [ 4.296393] Bluetooth: HCI socket layer initialized
> [ 4.296399] Bluetooth: L2CAP socket layer initialized
> [ 4.296404] Bluetooth: SCO socket layer initialized
> [ 4.391514] Bluetooth: hci0: HW/SW Version: 0x008a008a, Build Time: 20251223091725
> [ 6.087152] Bluetooth: BNEP (Ethernet Emulation) ver 1.3
> [ 6.087165] Bluetooth: BNEP filters: protocol multicast
> [ 6.087172] Bluetooth: BNEP socket layer initialized
> [ 7.591891] Bluetooth: hci0: Device setup in 3127955 usecs
> [ 7.591922] Bluetooth: hci0: HCI Enhanced Setup Synchronous Connection command is advertised, but not supported.
> [ 7.657339] Bluetooth: MGMT ver 1.23
> [ 25.883135] Bluetooth: RFCOMM TTY layer initialized
> [ 25.883150] Bluetooth: RFCOMM socket layer initialized
> [ 25.883154] Bluetooth: RFCOMM ver 1.11
^ permalink raw reply
* [Patch] support libical 4.0
From: Funda Wang @ 2026-05-11 5:58 UTC (permalink / raw)
To: linux-bluetooth
[-- Attachment #1.1: Type: text/plain, Size: 264 bytes --]
Hello,
Since libical 4.0 [1], pkgconfig files of libical have been splitted into several files according to libraries. The libical pkgconfig check need to be tweaked to check libicalvcal. Patch attached.
Thanks.
[1]: https://github.com/libical/libical/pull/988
[-- Attachment #1.2: Type: text/html, Size: 285 bytes --]
[-- Attachment #2: bluez-5.86-support-ical-4.0.patch.txt --]
[-- Type: text/plain, Size: 778 bytes --]
From e5128e1124efa814d8c65b4797025b1a15c6b250 Mon Sep 17 00:00:00 2001
From: Funda Wang <fundawang@yeah.net>
Date: Mon, 11 May 2026 12:37:35 +0800
Subject: [PATCH] support libical 4.0
---
configure.ac | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/configure.ac b/configure.ac
index 52de7d6..1cdd551 100644
--- a/configure.ac
+++ b/configure.ac
@@ -306,7 +306,9 @@ fi
AC_ARG_ENABLE(obex, AS_HELP_STRING([--disable-obex],
[disable OBEX profile support]), [enable_obex=${enableval}])
if (test "${enable_obex}" != "no"); then
- PKG_CHECK_MODULES(ICAL, libical)
+ PKG_CHECK_MODULES(ICAL, [libicalvcal >= 4.0.0], [],
+ [PKG_CHECK_MODULES(ICAL, [libical])]
+ )
fi
AM_CONDITIONAL(OBEX, test "${enable_obex}" != "no")
--
2.47.3
^ permalink raw reply related
* [PATCH 1/1] Bluetooth: L2CAP: Fix use-after-free in l2cap_sock_new_connection_cb()
From: Siwei Zhang @ 2026-05-11 4:51 UTC (permalink / raw)
To: Marcel Holtmann, Luiz Augusto von Dentz; +Cc: linux-bluetooth, Siwei Zhang
In-Reply-To: <20260511045124.298319-1-oss@fourdim.xyz>
l2cap_sock_new_connection_cb() accesses l2cap_pi(sk)->chan after
release_sock(parent). Once the parent lock is released, the child
socket sk can be freed by another task.
Save the channel pointer into a local variable while the parent lock
is still held to prevent this.
Fixes: 8ffb929098a5 ("Bluetooth: Remove parent socket usage from l2cap_core.c")
Cc: stable@kernel.org
Assisted-by: Claude:claude-opus-4-7
Signed-off-by: Siwei Zhang <oss@fourdim.xyz>
---
net/bluetooth/6lowpan.c | 5 +++++
net/bluetooth/l2cap_core.c | 12 ++++++++++++
net/bluetooth/l2cap_sock.c | 13 ++++++++++++-
net/bluetooth/smp.c | 5 +++++
4 files changed, 34 insertions(+), 1 deletion(-)
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
index 23a229ab6a33..71c1c04b61e5 100644
--- a/net/bluetooth/6lowpan.c
+++ b/net/bluetooth/6lowpan.c
@@ -755,6 +755,11 @@ static inline struct l2cap_chan *chan_new_conn_cb(struct l2cap_chan *pchan)
BT_DBG("chan %p pchan %p", chan, pchan);
+ /* Match the put that the caller of ops->new_connection() performs
+ * once it is done with the returned channel pointer.
+ */
+ l2cap_chan_hold(chan);
+
return chan;
}
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 7701528f1167..0f6c3c651207 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -4071,6 +4071,9 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
__l2cap_chan_add(conn, chan);
+ /* Drop the ops->new_connection() ref; conn list now pins chan. */
+ l2cap_chan_put(chan);
+
dcid = chan->scid;
__set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
@@ -4970,6 +4973,9 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
__l2cap_chan_add(conn, chan);
+ /* Drop the ops->new_connection() ref; conn list now pins chan. */
+ l2cap_chan_put(chan);
+
l2cap_le_flowctl_init(chan, __le16_to_cpu(req->credits));
dcid = chan->scid;
@@ -5194,6 +5200,9 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn,
__l2cap_chan_add(conn, chan);
+ /* Drop the ops->new_connection() ref; conn list now pins chan. */
+ l2cap_chan_put(chan);
+
l2cap_ecred_init(chan, __le16_to_cpu(req->credits));
/* Init response */
@@ -7407,6 +7416,9 @@ static void l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
chan->dst_type = dst_type;
__l2cap_chan_add(conn, chan);
+
+ /* Drop the ops->new_connection() ref; conn list now pins chan. */
+ l2cap_chan_put(chan);
}
l2cap_chan_unlock(pchan);
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index cf590a67d364..295c79cf5cf3 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -1497,6 +1497,7 @@ static void l2cap_sock_cleanup_listen(struct sock *parent)
static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan)
{
struct sock *sk, *parent = chan->data;
+ struct l2cap_chan *child_chan;
if (!parent)
return NULL;
@@ -1523,9 +1524,19 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan)
bt_accept_enqueue(parent, sk, false);
+ child_chan = l2cap_pi(sk)->chan;
+
+ /* Pin the channel for the caller. Once release_sock(parent) returns,
+ * userspace can accept(2) and immediately close(2) the child socket,
+ * which would drop the socket's references on the channel and free
+ * it before the caller (e.g. l2cap_connect_req()) is done using the
+ * returned pointer. The matching put is the caller's responsibility.
+ */
+ l2cap_chan_hold(child_chan);
+
release_sock(parent);
- return l2cap_pi(sk)->chan;
+ return child_chan;
}
static int l2cap_sock_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index 1739c1989dbd..9796c3030434 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -3231,6 +3231,11 @@ static inline struct l2cap_chan *smp_new_conn_cb(struct l2cap_chan *pchan)
BT_DBG("created chan %p", chan);
+ /* Match the put that the caller of ops->new_connection() performs
+ * once it is done with the returned channel pointer.
+ */
+ l2cap_chan_hold(chan);
+
return chan;
}
--
2.54.0
^ permalink raw reply related
* [PATCH 0/1] Bluetooth: L2CAP: Fix use-after-free in l2cap_sock_new_connection_cb()
From: Siwei Zhang @ 2026-05-11 4:51 UTC (permalink / raw)
To: Marcel Holtmann, Luiz Augusto von Dentz; +Cc: linux-bluetooth, Siwei Zhang
This addresses v2 comments on https://sashiko.dev/#/patchset/20260415204842.2363950-1-oss%40fourdim.xyz .
Compared to v3, rebase against bluetooth-next.
Siwei Zhang (1):
Bluetooth: L2CAP: Fix use-after-free in l2cap_sock_new_connection_cb()
net/bluetooth/6lowpan.c | 5 +++++
net/bluetooth/l2cap_core.c | 12 ++++++++++++
net/bluetooth/l2cap_sock.c | 13 ++++++++++++-
net/bluetooth/smp.c | 5 +++++
4 files changed, 34 insertions(+), 1 deletion(-)
--
2.54.0
^ permalink raw reply
* RE: Bluetooth: L2CAP: Fix use-after-free in l2cap_sock_new_connection_cb()
From: bluez.test.bot @ 2026-05-11 4:21 UTC (permalink / raw)
To: linux-bluetooth, oss
In-Reply-To: <20260511031837.221778-2-oss@fourdim.xyz>
[-- Attachment #1: Type: text/plain, Size: 555 bytes --]
This is an automated email and please do not reply to this email.
Dear Submitter,
Thank you for submitting the patches to the linux bluetooth mailing list.
While preparing the CI tests, the patches you submitted couldn't be applied to the current HEAD of the repository.
----- Output -----
error: patch failed: net/bluetooth/l2cap_sock.c:1497
error: net/bluetooth/l2cap_sock.c: patch does not apply
hint: Use 'git am --show-current-patch' to see the failed patch
Please resolve the issue and submit the patches again.
---
Regards,
Linux Bluetooth
^ permalink raw reply
* [PATCH 1/1] Bluetooth: L2CAP: Fix use-after-free in l2cap_sock_new_connection_cb()
From: Siwei Zhang @ 2026-05-11 3:18 UTC (permalink / raw)
To: Marcel Holtmann, Luiz Augusto von Dentz; +Cc: linux-bluetooth, Siwei Zhang
In-Reply-To: <20260511031837.221778-1-oss@fourdim.xyz>
l2cap_sock_new_connection_cb() accesses l2cap_pi(sk)->chan after
release_sock(parent). Once the parent lock is released, the child
socket sk can be freed by another task.
Save the channel pointer into a local variable while the parent lock
is still held to prevent this.
Fixes: 8ffb929098a5 ("Bluetooth: Remove parent socket usage from l2cap_core.c")
Cc: stable@kernel.org
Assisted-by: Claude:claude-opus-4-7
Signed-off-by: Siwei Zhang <oss@fourdim.xyz>
---
net/bluetooth/6lowpan.c | 5 +++++
net/bluetooth/l2cap_core.c | 12 ++++++++++++
net/bluetooth/l2cap_sock.c | 13 ++++++++++++-
net/bluetooth/smp.c | 5 +++++
4 files changed, 34 insertions(+), 1 deletion(-)
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
index 2f03b780b40d..bbe67bd73f9c 100644
--- a/net/bluetooth/6lowpan.c
+++ b/net/bluetooth/6lowpan.c
@@ -755,6 +755,11 @@ static inline struct l2cap_chan *chan_new_conn_cb(struct l2cap_chan *pchan)
BT_DBG("chan %p pchan %p", chan, pchan);
+ /* Match the put that the caller of ops->new_connection() performs
+ * once it is done with the returned channel pointer.
+ */
+ l2cap_chan_hold(chan);
+
return chan;
}
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 95c65fece39b..fc663386872c 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -4071,6 +4071,9 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
__l2cap_chan_add(conn, chan);
+ /* Drop the ops->new_connection() ref; conn list now pins chan. */
+ l2cap_chan_put(chan);
+
dcid = chan->scid;
__set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
@@ -4978,6 +4981,9 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
__l2cap_chan_add(conn, chan);
+ /* Drop the ops->new_connection() ref; conn list now pins chan. */
+ l2cap_chan_put(chan);
+
l2cap_le_flowctl_init(chan, __le16_to_cpu(req->credits));
dcid = chan->scid;
@@ -5202,6 +5208,9 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn,
__l2cap_chan_add(conn, chan);
+ /* Drop the ops->new_connection() ref; conn list now pins chan. */
+ l2cap_chan_put(chan);
+
l2cap_ecred_init(chan, __le16_to_cpu(req->credits));
/* Init response */
@@ -7402,6 +7411,9 @@ static void l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
chan->dst_type = dst_type;
__l2cap_chan_add(conn, chan);
+
+ /* Drop the ops->new_connection() ref; conn list now pins chan. */
+ l2cap_chan_put(chan);
}
l2cap_chan_unlock(pchan);
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 71e8c1b45bce..355fad9e2955 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -1497,6 +1497,7 @@ static void l2cap_sock_cleanup_listen(struct sock *parent)
static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan)
{
struct sock *sk, *parent = chan->data;
+ struct l2cap_chan *child_chan;
lock_sock(parent);
@@ -1520,9 +1521,19 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan)
bt_accept_enqueue(parent, sk, false);
+ child_chan = l2cap_pi(sk)->chan;
+
+ /* Pin the channel for the caller. Once release_sock(parent) returns,
+ * userspace can accept(2) and immediately close(2) the child socket,
+ * which would drop the socket's references on the channel and free
+ * it before the caller (e.g. l2cap_connect_req()) is done using the
+ * returned pointer. The matching put is the caller's responsibility.
+ */
+ l2cap_chan_hold(child_chan);
+
release_sock(parent);
- return l2cap_pi(sk)->chan;
+ return child_chan;
}
static int l2cap_sock_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index 98f1da4f5f55..32761d3d252e 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -3261,6 +3261,11 @@ static inline struct l2cap_chan *smp_new_conn_cb(struct l2cap_chan *pchan)
BT_DBG("created chan %p", chan);
+ /* Match the put that the caller of ops->new_connection() performs
+ * once it is done with the returned channel pointer.
+ */
+ l2cap_chan_hold(chan);
+
return chan;
}
--
2.54.0
^ permalink raw reply related
* [PATCH 0/1] Bluetooth: L2CAP: Fix use-after-free in l2cap_sock_new_connection_cb()
From: Siwei Zhang @ 2026-05-11 3:18 UTC (permalink / raw)
To: Marcel Holtmann, Luiz Augusto von Dentz; +Cc: linux-bluetooth, Siwei Zhang
This addresses v2 comments on https://sashiko.dev/#/patchset/20260415204842.2363950-1-oss%40fourdim.xyz .
Siwei Zhang (1):
Bluetooth: L2CAP: Fix use-after-free in l2cap_sock_new_connection_cb()
net/bluetooth/6lowpan.c | 5 +++++
net/bluetooth/l2cap_core.c | 12 ++++++++++++
net/bluetooth/l2cap_sock.c | 13 ++++++++++++-
net/bluetooth/smp.c | 5 +++++
4 files changed, 34 insertions(+), 1 deletion(-)
--
2.54.0
^ permalink raw reply
* [bluez/bluez]
From: BluezTestBot @ 2026-05-10 10:21 UTC (permalink / raw)
To: linux-bluetooth
Branch: refs/heads/1079646
Home: https://github.com/bluez/bluez
To unsubscribe from these emails, change your notification settings at https://github.com/bluez/bluez/settings/notifications
^ permalink raw reply
* RE: [v2] Bluetooth: 6lowpan: Fix peer and channel lifetime during teardown
From: bluez.test.bot @ 2026-05-10 4:38 UTC (permalink / raw)
To: linux-bluetooth, rollkingzzc
In-Reply-To: <20260510040424.2054614-1-rollkingzzc@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 2393 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=1092168
---Test result---
Test Summary:
CheckPatch FAIL 0.81 seconds
GitLint FAIL 0.33 seconds
SubjectPrefix PASS 0.13 seconds
BuildKernel PASS 27.24 seconds
CheckAllWarning PASS 29.84 seconds
CheckSparse PASS 28.98 seconds
BuildKernel32 PASS 26.59 seconds
TestRunnerSetup PASS 571.72 seconds
TestRunner_6lowpan-tester PASS 58.20 seconds
IncrementalBuild PASS 28.09 seconds
Details
##############################
Test: CheckPatch - FAIL
Desc: Run checkpatch.pl script
Output:
[v2] Bluetooth: 6lowpan: Fix peer and channel lifetime during teardown
WARNING: Prefer a maximum 75 chars per line (possible unwrapped commit description?)
#126:
The buggy scenario involves two paths, with each column showing the order within that path:
total: 0 errors, 1 warnings, 0 checks, 239 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/14563451.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.
##############################
Test: GitLint - FAIL
Desc: Run gitlint
Output:
[v2] Bluetooth: 6lowpan: Fix peer and channel lifetime during teardown
WARNING: I3 - ignore-body-lines: gitlint will be switching from using Python regex 'match' (match beginning) to 'search' (match anywhere) semantics. Please review your ignore-body-lines.regex option accordingly. To remove this warning, set general.regex-style-search=True. More details: https://jorisroovers.github.io/gitlint/configuration/#regex-style-search
24: B1 Line exceeds max length (91>80): "The buggy scenario involves two paths, with each column showing the order within that path:"
https://github.com/bluez/bluetooth-next/pull/162
---
Regards,
Linux Bluetooth
^ permalink raw reply
* [PATCH v2] Bluetooth: 6lowpan: Fix peer and channel lifetime during teardown
From: Zhang Cen @ 2026-05-10 4:04 UTC (permalink / raw)
To: Marcel Holtmann, Luiz Augusto von Dentz
Cc: linux-bluetooth, linux-kernel, Hillf Danton, zerocling0077,
hanguidong02, Zhang Cen
In-Reply-To: <20260509173745.413473-1-rollkingzzc@gmail.com>
6LoWPAN peers keep a protocol-owned L2CAP channel reference in
peer->chan. chan_close_cb() removes the peer from the RCU list when the
channel is closed, but the old code also dropped that channel reference
before the peer object stopped being observable by in-flight RCU readers.
That can leave peer readers with a live lowpan_peer that still points at
an l2cap_chan whose last reference has already been released. Some
lookup helpers also returned raw peer pointers after dropping RCU
protection, so keeping only the channel alive until the RCU callback
still leaves the peer lifetime rules implicit.
Make the peer lifetime explicit. Give each lowpan_peer a refcount, have
RCU lookups take a peer reference before returning a peer outside the
read-side critical section, and make peer_del() only unlink the peer and
schedule an RCU callback to drop the list owner reference after the grace
period. Release the peer-owned L2CAP channel reference and module
reference from the final peer put.
Keep temporary channel references in transmit and disconnect paths that
cache or send through peer->chan after lookup.
The buggy scenario involves two paths, with each column showing the order within that path:
L2CAP peer teardown: Concurrent peer reader:
1. A close path holds a 1. A reader such as
temporary reference to the lookup_peer(),
channel peer_lookup_dst(),
send_mcast_pkt(), or
bt_6lowpan_disconnect()
observes the peer
2. l2cap_chan_del() drops the 2. The reader keeps using the
connection-owned channel raw peer or peer->chan after
reference RCU lookup protection ends
3. chan_close_cb() removes the 3. The reader reads channel
lowpan_peer from the peer fields, locks the channel, or
list sends through the channel
pointer
4. The old chan_close_cb()
dropped the peer-owned
channel reference before the
peer RCU grace period ended
5. The close caller releases its
temporary channel reference
If the reader reaches its channel use after teardown releases the peer-
owned channel reference and the close caller releases its temporary
reference, it can use a freed channel; raw peer returns can also outlive
the peer RCU callback.
lowpan_peer objects remain RCU-observable after peer_del() unlinks them.
Signed-off-by: Zhang Cen <rollkingzzc@gmail.com>
---
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
index 2f03b780b40d..7334a78f5bfb 100644
--- a/net/bluetooth/6lowpan.c
+++ b/net/bluetooth/6lowpan.c
@@ -9,6 +9,7 @@
#include <linux/etherdevice.h>
#include <linux/module.h>
#include <linux/debugfs.h>
+#include <linux/refcount.h>
#include <net/ipv6.h>
#include <net/ip6_route.h>
@@ -61,6 +62,7 @@ enum {
struct lowpan_peer {
struct list_head list;
struct rcu_head rcu;
+ refcount_t refcnt;
struct l2cap_chan *chan;
/* peer addresses in various formats */
@@ -95,13 +97,33 @@ static inline void peer_add(struct lowpan_btle_dev *dev,
atomic_inc(&dev->peer_count);
}
+static inline bool lowpan_peer_get_unless_zero(struct lowpan_peer *peer)
+{
+ return refcount_inc_not_zero(&peer->refcnt);
+}
+
+static void lowpan_peer_put(struct lowpan_peer *peer)
+{
+ if (!refcount_dec_and_test(&peer->refcnt))
+ return;
+
+ l2cap_chan_put(peer->chan);
+ kfree(peer);
+ module_put(THIS_MODULE);
+}
+
+static void lowpan_peer_put_rcu(struct rcu_head *rcu)
+{
+ struct lowpan_peer *peer = container_of(rcu, struct lowpan_peer, rcu);
+
+ lowpan_peer_put(peer);
+}
+
static inline bool peer_del(struct lowpan_btle_dev *dev,
struct lowpan_peer *peer)
{
list_del_rcu(&peer->list);
- kfree_rcu(peer, rcu);
-
- module_put(THIS_MODULE);
+ call_rcu(&peer->rcu, lowpan_peer_put_rcu);
if (atomic_dec_and_test(&dev->peer_count)) {
BT_DBG("last peer");
@@ -179,7 +201,8 @@ static inline struct lowpan_peer *peer_lookup_dst(struct lowpan_btle_dev *dev,
&peer->chan->dst, peer->chan->dst_type,
&peer->peer_addr);
- if (!ipv6_addr_cmp(&peer->peer_addr, nexthop)) {
+ if (!ipv6_addr_cmp(&peer->peer_addr, nexthop) &&
+ lowpan_peer_get_unless_zero(peer)) {
rcu_read_unlock();
return peer;
}
@@ -189,7 +212,8 @@ static inline struct lowpan_peer *peer_lookup_dst(struct lowpan_btle_dev *dev,
neigh = __ipv6_neigh_lookup(dev->netdev, nexthop);
if (neigh) {
list_for_each_entry_rcu(peer, &dev->peers, list) {
- if (!memcmp(neigh->ha, peer->lladdr, ETH_ALEN)) {
+ if (!memcmp(neigh->ha, peer->lladdr, ETH_ALEN) &&
+ lowpan_peer_get_unless_zero(peer)) {
neigh_release(neigh);
rcu_read_unlock();
return peer;
@@ -212,8 +236,9 @@ static struct lowpan_peer *lookup_peer(struct l2cap_conn *conn)
list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
peer = __peer_lookup_conn(entry, conn);
- if (peer)
+ if (peer && lowpan_peer_get_unless_zero(peer))
break;
+ peer = NULL;
}
rcu_read_unlock();
@@ -361,8 +386,10 @@ static int chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
return -ENOENT;
dev = lookup_dev(chan->conn);
- if (!dev || !dev->netdev)
+ if (!dev || !dev->netdev) {
+ lowpan_peer_put(peer);
return -ENOENT;
+ }
err = recv_pkt(skb, dev->netdev, peer);
if (err) {
@@ -370,6 +397,8 @@ static int chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
err = -EAGAIN;
}
+ lowpan_peer_put(peer);
+
return err;
}
@@ -380,6 +409,8 @@ static int setup_header(struct sk_buff *skb, struct net_device *netdev,
struct ipv6hdr *hdr;
struct lowpan_btle_dev *dev;
struct lowpan_peer *peer;
+ struct l2cap_chan *chan;
+ u8 peer_lladdr[ETH_ALEN];
u8 *daddr;
int err, status = 0;
@@ -388,9 +419,9 @@ static int setup_header(struct sk_buff *skb, struct net_device *netdev,
dev = lowpan_btle_dev(netdev);
memcpy(&ipv6_daddr, &hdr->daddr, sizeof(ipv6_daddr));
+ lowpan_cb(skb)->chan = NULL;
if (ipv6_addr_is_multicast(&ipv6_daddr)) {
- lowpan_cb(skb)->chan = NULL;
daddr = NULL;
} else {
BT_DBG("dest IP %pI6c", &ipv6_daddr);
@@ -406,10 +437,15 @@ static int setup_header(struct sk_buff *skb, struct net_device *netdev,
return -ENOENT;
}
- daddr = peer->lladdr;
- *peer_addr = peer->chan->dst;
- *peer_addr_type = peer->chan->dst_type;
- lowpan_cb(skb)->chan = peer->chan;
+ chan = peer->chan;
+ l2cap_chan_hold(chan);
+ memcpy(peer_lladdr, peer->lladdr, ETH_ALEN);
+ *peer_addr = chan->dst;
+ *peer_addr_type = chan->dst_type;
+ lowpan_cb(skb)->chan = chan;
+ lowpan_peer_put(peer);
+
+ daddr = peer_lladdr;
status = 1;
}
@@ -417,8 +453,13 @@ static int setup_header(struct sk_buff *skb, struct net_device *netdev,
lowpan_header_compress(skb, netdev, daddr, dev->netdev->dev_addr);
err = dev_hard_header(skb, netdev, ETH_P_IPV6, NULL, NULL, 0);
- if (err < 0)
+ if (err < 0) {
+ if (lowpan_cb(skb)->chan) {
+ l2cap_chan_put(lowpan_cb(skb)->chan);
+ lowpan_cb(skb)->chan = NULL;
+ }
return err;
+ }
return status;
}
@@ -483,15 +524,23 @@ static int send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev)
dev = lowpan_btle_dev(entry->netdev);
list_for_each_entry_rcu(pentry, &dev->peers, list) {
+ struct l2cap_chan *chan = pentry->chan;
int ret;
local_skb = skb_clone(skb, GFP_ATOMIC);
+ if (!local_skb) {
+ err = -ENOMEM;
+ continue;
+ }
+
+ l2cap_chan_hold(chan);
BT_DBG("xmit %s to %pMR type %u IP %pI6c chan %p",
netdev->name,
- &pentry->chan->dst, pentry->chan->dst_type,
- &pentry->peer_addr, pentry->chan);
- ret = send_pkt(pentry->chan, local_skb, netdev);
+ &chan->dst, chan->dst_type,
+ &pentry->peer_addr, chan);
+ ret = send_pkt(chan, local_skb, netdev);
+ l2cap_chan_put(chan);
if (ret < 0)
err = ret;
@@ -530,10 +579,14 @@ static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *netdev)
if (err) {
if (lowpan_cb(skb)->chan) {
+ struct l2cap_chan *chan = lowpan_cb(skb)->chan;
+
BT_DBG("xmit %s to %pMR type %u IP %pI6c chan %p",
netdev->name, &addr, addr_type,
- &lowpan_cb(skb)->addr, lowpan_cb(skb)->chan);
- err = send_pkt(lowpan_cb(skb)->chan, skb, netdev);
+ &lowpan_cb(skb)->addr, chan);
+ err = send_pkt(chan, skb, netdev);
+ l2cap_chan_put(chan);
+ lowpan_cb(skb)->chan = NULL;
} else {
err = -ENOENT;
}
@@ -649,6 +702,7 @@ static struct l2cap_chan *add_peer_chan(struct l2cap_chan *chan,
if (!peer)
return NULL;
+ refcount_set(&peer->refcnt, 1);
peer->chan = chan;
baswap((void *)peer->lladdr, &chan->dst);
@@ -802,8 +856,6 @@ static void chan_close_cb(struct l2cap_chan *chan)
last ? "last " : "1 ", peer);
BT_DBG("chan %p orig refcnt %u", chan,
kref_read(&chan->kref));
-
- l2cap_chan_put(chan);
break;
}
}
@@ -918,6 +970,7 @@ static int bt_6lowpan_connect(bdaddr_t *addr, u8 dst_type)
static int bt_6lowpan_disconnect(struct l2cap_conn *conn, u8 dst_type)
{
struct lowpan_peer *peer;
+ struct l2cap_chan *chan;
BT_DBG("conn %p dst type %u", conn, dst_type);
@@ -925,11 +978,16 @@ static int bt_6lowpan_disconnect(struct l2cap_conn *conn, u8 dst_type)
if (!peer)
return -ENOENT;
- BT_DBG("peer %p chan %p", peer, peer->chan);
+ chan = peer->chan;
+ l2cap_chan_hold(chan);
+
+ BT_DBG("peer %p chan %p", peer, chan);
+ lowpan_peer_put(peer);
- l2cap_chan_lock(peer->chan);
- l2cap_chan_close(peer->chan, ENOENT);
- l2cap_chan_unlock(peer->chan);
+ l2cap_chan_lock(chan);
+ l2cap_chan_close(chan, ENOENT);
+ l2cap_chan_unlock(chan);
+ l2cap_chan_put(chan);
return 0;
}
@@ -1169,6 +1227,7 @@ static ssize_t lowpan_control_write(struct file *fp,
peer = lookup_peer(conn);
if (peer) {
BT_DBG("6LoWPAN connection already exists");
+ lowpan_peer_put(peer);
return -EALREADY;
}
^ permalink raw reply related
* Re: [PATCH] Bluetooth: 6lowpan: Defer peer channel release until RCU cleanup
From: c Z @ 2026-05-10 4:01 UTC (permalink / raw)
To: Hillf Danton
Cc: Marcel Holtmann, Luiz Augusto von Dentz, linux-bluetooth,
linux-kernel, zerocling0077
In-Reply-To: <20260510014359.472-1-hdanton@sina.com>
Hillf Danton <hdanton@sina.com> 于2026年5月10日周日 09:44写道:
>
> On Sun, 10 May 2026 01:37:45 +0800 Zhang Cen wrote:
> > A 6LoWPAN peer stores the protocol-owned L2CAP channel reference in
> > peer->chan. chan_close_cb() removes the peer from the RCU list, but it
> > also drops that reference immediately. The peer object can still be seen
> > by in-flight RCU readers, and some paths keep using peer->chan after the
> > lookup has finished.
> >
> > That lets transmit and disconnect paths dereference, lock, or send
> > through a channel whose last reference was released by the close path.
> >
> Sounds like the race between close and lookup paths
>
> chan_close_cb() setup_header()
> --- ---
> peer_del()
> list_del_rcu(&peer->list);
> kfree_rcu(peer, rcu);
> l2cap_chan_put(c); // free chan
>
> peer_lookup_dst(dev, &ipv6_daddr, skb);
> rcu_read_lock();
> peer = find peer;
> rcu_read_unlock();
> if (peer)
> addr = peer->chan->dst; //uaf
>
> and on the lookup side, a) peer is unsafe outside rcu lock and
> b) chan is used after put.
>
> To fix the race, in addition to move l2cap_chan_put() to the rcu
> callback as this patch does, refcount should be added to peer to
> make the lookup path safe.
>
> chan_close_cb() setup_header()
> --- ---
> peer_del()
> list_del_rcu(&peer->list);
> call_rcu(&peer->rcu, peer_free_rcu);
> if (peer->refcount-- == 0) {
> //alternatively schedule workqueue if task context needed
> l2cap_chan_put(c);
> kfree(peer);
> module_put(THIS_MODULE);
> }
> peer_lookup_dst(dev, &ipv6_daddr, skb);
> rcu_read_lock();
> peer = find peer;
> if (peer)
> peer->refcount++;
> rcu_read_unlock();
> if (peer) {
> addr = peer->chan->dst;
> if (peer->refcount-- == 0)
> schedule workqueue to free peer;
> }
>
Hi Hillf,
Thanks for looking at this and for spelling out the lookup-side lifetime
issue.
I agree that v2 should make the peer lifetime explicit. v1 moves the
peer-owned l2cap_chan_put() behind the RCU grace period, and it also
changes the unicast transmit lookup to copy the peer fields / take a
channel reference before dropping RCU. But the broader point still
stands: helpers such as lookup_peer() should not hand a raw lowpan_peer
to users that continue after rcu_read_unlock().
I'll prepare v2 so the peer object has its own refcount. The plan is to:
* add a refcount/kref to struct lowpan_peer and initialize it with the
peer-list owner reference;
* make RCU lookups that return a peer acquire a reference before
unlocking, using the usual non-zero refcount pattern;
* make peer_del() only unlink the peer and schedule an RCU callback
which drops the owner reference after the grace period;
* release the peer-owned L2CAP channel reference and module reference
from the final peer put; and
* audit the remaining lookup_peer()/peer_lookup_dst() users so every
post-lookup use either holds a peer reference or only uses data copied
while still under RCU.
Thanks,
Zhang Cen
^ permalink raw reply
* Re: [PATCH] Bluetooth: 6lowpan: Defer peer channel release until RCU cleanup
From: Hillf Danton @ 2026-05-10 1:43 UTC (permalink / raw)
To: Zhang Cen
Cc: Marcel Holtmann, Luiz Augusto von Dentz, linux-bluetooth,
linux-kernel, zerocling0077
In-Reply-To: <20260509173745.413473-1-rollkingzzc@gmail.com>
On Sun, 10 May 2026 01:37:45 +0800 Zhang Cen wrote:
> A 6LoWPAN peer stores the protocol-owned L2CAP channel reference in
> peer->chan. chan_close_cb() removes the peer from the RCU list, but it
> also drops that reference immediately. The peer object can still be seen
> by in-flight RCU readers, and some paths keep using peer->chan after the
> lookup has finished.
>
> That lets transmit and disconnect paths dereference, lock, or send
> through a channel whose last reference was released by the close path.
>
Sounds like the race between close and lookup paths
chan_close_cb() setup_header()
--- ---
peer_del()
list_del_rcu(&peer->list);
kfree_rcu(peer, rcu);
l2cap_chan_put(c); // free chan
peer_lookup_dst(dev, &ipv6_daddr, skb);
rcu_read_lock();
peer = find peer;
rcu_read_unlock();
if (peer)
addr = peer->chan->dst; //uaf
and on the lookup side, a) peer is unsafe outside rcu lock and
b) chan is used after put.
To fix the race, in addition to move l2cap_chan_put() to the rcu
callback as this patch does, refcount should be added to peer to
make the lookup path safe.
chan_close_cb() setup_header()
--- ---
peer_del()
list_del_rcu(&peer->list);
call_rcu(&peer->rcu, peer_free_rcu);
if (peer->refcount-- == 0) {
//alternatively schedule workqueue if task context needed
l2cap_chan_put(c);
kfree(peer);
module_put(THIS_MODULE);
}
peer_lookup_dst(dev, &ipv6_daddr, skb);
rcu_read_lock();
peer = find peer;
if (peer)
peer->refcount++;
rcu_read_unlock();
if (peer) {
addr = peer->chan->dst;
if (peer->refcount-- == 0)
schedule workqueue to free peer;
}
> Defer the peer-owned l2cap_chan_put() until the peer RCU callback so a
> peer that remains observable through RCU still carries a live channel
> reference. Then take temporary channel references in the unicast,
> multicast, and explicit disconnect paths before they keep using the
> channel after the lookup window closes.
>
> If the reader reaches step 3 after teardown reaches step 4, it can hit a
> freed l2cap_chan.
>
> The buggy scenario involves two paths, with each column showing the order within that path:
>
> L2CAP peer teardown: Concurrent peer reader:
> 1. l2cap_conn_del() or another 1. A reader such as
> close path takes a temporary __peer_lookup_conn(),
> hold on the channel setup_header(),
> send_mcast_pkt(), or
> bt_6lowpan_disconnect()
> traverses the peer list and
> reads peer->chan
> 2. l2cap_chan_del() drops the 2. The reader keeps using the
> connection-owned channel raw channel pointer after the
> reference before 6LoWPAN lookup or after only RCU
> close handling finishes protection remains
> 3. chan_close_cb() removes the 3. It dereferences channel
> matching lowpan_peer from the fields or calls send or close
> peer list operations through that
> pointer
> 4. The original chan_close_cb()
> also dropped the peer-owned
> l2cap_chan reference, and the
> close caller later released
> its temporary hold
>
> A peer reader can still observe the lowpan_peer while chan_close_cb()
> has already consumed the peer-owned channel reference and the close
> caller is releasing the last temporary hold, leaving peer->chan stale
> before the peer's RCU grace period ends.
>
> lowpan_peer objects stay RCU-visible after peer_del() removes them from
> the list.
>
> Signed-off-by: Zhang Cen <rollkingzzc@gmail.com>
>
> ---
> diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
> index 2f03b780b40d..ea3ee6929101 100644
> --- a/net/bluetooth/6lowpan.c
> +++ b/net/bluetooth/6lowpan.c
> @@ -95,13 +95,20 @@ static inline void peer_add(struct lowpan_btle_dev *dev,
> atomic_inc(&dev->peer_count);
> }
>
> +static void peer_free_rcu(struct rcu_head *rcu)
> +{
> + struct lowpan_peer *peer = container_of(rcu, struct lowpan_peer, rcu);
> +
> + l2cap_chan_put(peer->chan);
> + kfree(peer);
> + module_put(THIS_MODULE);
> +}
> +
> static inline bool peer_del(struct lowpan_btle_dev *dev,
> struct lowpan_peer *peer)
> {
> list_del_rcu(&peer->list);
> - kfree_rcu(peer, rcu);
> -
> - module_put(THIS_MODULE);
> + call_rcu(&peer->rcu, peer_free_rcu);
>
> if (atomic_dec_and_test(&dev->peer_count)) {
> BT_DBG("last peer");
> @@ -137,9 +144,32 @@ __peer_lookup_conn(struct lowpan_btle_dev *dev, struct l2cap_conn *conn)
> return NULL;
> }
>
> -static inline struct lowpan_peer *peer_lookup_dst(struct lowpan_btle_dev *dev,
> - struct in6_addr *daddr,
> - struct sk_buff *skb)
> +static struct l2cap_chan *lookup_peer_chan(struct l2cap_conn *conn)
> +{
> + struct lowpan_btle_dev *entry;
> + struct lowpan_peer *peer;
> + struct l2cap_chan *chan = NULL;
> +
> + rcu_read_lock();
> +
> + list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
> + peer = __peer_lookup_conn(entry, conn);
> + if (peer) {
> + chan = peer->chan;
> + l2cap_chan_hold(chan);
> + break;
> + }
> + }
> +
> + rcu_read_unlock();
> +
> + return chan;
> +}
> +
> +static int peer_lookup_dst(struct lowpan_btle_dev *dev, struct in6_addr *daddr,
> + struct sk_buff *skb, u8 *lladdr,
> + bdaddr_t *peer_addr, u8 *peer_addr_type,
> + struct l2cap_chan **chan)
> {
> struct rt6_info *rt = dst_rt6_info(skb_dst(skb));
> int count = atomic_read(&dev->peer_count);
> @@ -175,13 +205,20 @@ static inline struct lowpan_peer *peer_lookup_dst(struct lowpan_btle_dev *dev,
> rcu_read_lock();
>
> list_for_each_entry_rcu(peer, &dev->peers, list) {
> + struct l2cap_chan *pchan = peer->chan;
> +
> BT_DBG("dst addr %pMR dst type %u ip %pI6c",
> - &peer->chan->dst, peer->chan->dst_type,
> + &pchan->dst, pchan->dst_type,
> &peer->peer_addr);
>
> if (!ipv6_addr_cmp(&peer->peer_addr, nexthop)) {
> + memcpy(lladdr, peer->lladdr, ETH_ALEN);
> + *peer_addr = pchan->dst;
> + *peer_addr_type = pchan->dst_type;
> + l2cap_chan_hold(pchan);
> + *chan = pchan;
> rcu_read_unlock();
> - return peer;
> + return 0;
> }
> }
>
> @@ -190,9 +227,16 @@ static inline struct lowpan_peer *peer_lookup_dst(struct lowpan_btle_dev *dev,
> if (neigh) {
> list_for_each_entry_rcu(peer, &dev->peers, list) {
> if (!memcmp(neigh->ha, peer->lladdr, ETH_ALEN)) {
> + struct l2cap_chan *pchan = peer->chan;
> +
> + memcpy(lladdr, peer->lladdr, ETH_ALEN);
> + *peer_addr = pchan->dst;
> + *peer_addr_type = pchan->dst_type;
> + l2cap_chan_hold(pchan);
> + *chan = pchan;
> neigh_release(neigh);
> rcu_read_unlock();
> - return peer;
> + return 0;
> }
> }
> neigh_release(neigh);
> @@ -200,7 +244,7 @@ static inline struct lowpan_peer *peer_lookup_dst(struct lowpan_btle_dev *dev,
>
> rcu_read_unlock();
>
> - return NULL;
> + return -ENOENT;
> }
>
> static struct lowpan_peer *lookup_peer(struct l2cap_conn *conn)
> @@ -379,7 +423,7 @@ static int setup_header(struct sk_buff *skb, struct net_device *netdev,
> struct in6_addr ipv6_daddr;
> struct ipv6hdr *hdr;
> struct lowpan_btle_dev *dev;
> - struct lowpan_peer *peer;
> + u8 peer_lladdr[ETH_ALEN];
> u8 *daddr;
> int err, status = 0;
>
> @@ -388,9 +432,9 @@ static int setup_header(struct sk_buff *skb, struct net_device *netdev,
> dev = lowpan_btle_dev(netdev);
>
> memcpy(&ipv6_daddr, &hdr->daddr, sizeof(ipv6_daddr));
> + lowpan_cb(skb)->chan = NULL;
>
> if (ipv6_addr_is_multicast(&ipv6_daddr)) {
> - lowpan_cb(skb)->chan = NULL;
> daddr = NULL;
> } else {
> BT_DBG("dest IP %pI6c", &ipv6_daddr);
> @@ -400,16 +444,15 @@ static int setup_header(struct sk_buff *skb, struct net_device *netdev,
> * or user set route) so get peer according to
> * the destination address.
> */
> - peer = peer_lookup_dst(dev, &ipv6_daddr, skb);
> - if (!peer) {
> + err = peer_lookup_dst(dev, &ipv6_daddr, skb, peer_lladdr,
> + peer_addr, peer_addr_type,
> + &lowpan_cb(skb)->chan);
> + if (err) {
> BT_DBG("no such peer");
> - return -ENOENT;
> + return err;
> }
>
> - daddr = peer->lladdr;
> - *peer_addr = peer->chan->dst;
> - *peer_addr_type = peer->chan->dst_type;
> - lowpan_cb(skb)->chan = peer->chan;
> + daddr = peer_lladdr;
>
> status = 1;
> }
> @@ -417,8 +460,13 @@ static int setup_header(struct sk_buff *skb, struct net_device *netdev,
> lowpan_header_compress(skb, netdev, daddr, dev->netdev->dev_addr);
>
> err = dev_hard_header(skb, netdev, ETH_P_IPV6, NULL, NULL, 0);
> - if (err < 0)
> + if (err < 0) {
> + if (lowpan_cb(skb)->chan) {
> + l2cap_chan_put(lowpan_cb(skb)->chan);
> + lowpan_cb(skb)->chan = NULL;
> + }
> return err;
> + }
>
> return status;
> }
> @@ -483,15 +531,23 @@ static int send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev)
> dev = lowpan_btle_dev(entry->netdev);
>
> list_for_each_entry_rcu(pentry, &dev->peers, list) {
> + struct l2cap_chan *chan = pentry->chan;
> int ret;
>
> local_skb = skb_clone(skb, GFP_ATOMIC);
> + if (!local_skb) {
> + err = -ENOMEM;
> + continue;
> + }
> +
> + l2cap_chan_hold(chan);
>
> BT_DBG("xmit %s to %pMR type %u IP %pI6c chan %p",
> netdev->name,
> - &pentry->chan->dst, pentry->chan->dst_type,
> - &pentry->peer_addr, pentry->chan);
> - ret = send_pkt(pentry->chan, local_skb, netdev);
> + &chan->dst, chan->dst_type,
> + &pentry->peer_addr, chan);
> + ret = send_pkt(chan, local_skb, netdev);
> + l2cap_chan_put(chan);
> if (ret < 0)
> err = ret;
>
> @@ -530,10 +586,14 @@ static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *netdev)
>
> if (err) {
> if (lowpan_cb(skb)->chan) {
> + struct l2cap_chan *chan = lowpan_cb(skb)->chan;
> +
> BT_DBG("xmit %s to %pMR type %u IP %pI6c chan %p",
> netdev->name, &addr, addr_type,
> - &lowpan_cb(skb)->addr, lowpan_cb(skb)->chan);
> - err = send_pkt(lowpan_cb(skb)->chan, skb, netdev);
> + &lowpan_cb(skb)->addr, chan);
> + err = send_pkt(chan, skb, netdev);
> + l2cap_chan_put(chan);
> + lowpan_cb(skb)->chan = NULL;
> } else {
> err = -ENOENT;
> }
> @@ -802,8 +862,6 @@ static void chan_close_cb(struct l2cap_chan *chan)
> last ? "last " : "1 ", peer);
> BT_DBG("chan %p orig refcnt %u", chan,
> kref_read(&chan->kref));
> -
> - l2cap_chan_put(chan);
> break;
> }
> }
> @@ -917,19 +975,20 @@ static int bt_6lowpan_connect(bdaddr_t *addr, u8 dst_type)
>
> static int bt_6lowpan_disconnect(struct l2cap_conn *conn, u8 dst_type)
> {
> - struct lowpan_peer *peer;
> + struct l2cap_chan *chan;
>
> BT_DBG("conn %p dst type %u", conn, dst_type);
>
> - peer = lookup_peer(conn);
> - if (!peer)
> + chan = lookup_peer_chan(conn);
> + if (!chan)
> return -ENOENT;
>
> - BT_DBG("peer %p chan %p", peer, peer->chan);
> + BT_DBG("chan %p", chan);
>
> - l2cap_chan_lock(peer->chan);
> - l2cap_chan_close(peer->chan, ENOENT);
> - l2cap_chan_unlock(peer->chan);
> + l2cap_chan_lock(chan);
> + l2cap_chan_close(chan, ENOENT);
> + l2cap_chan_unlock(chan);
> + l2cap_chan_put(chan);
>
> return 0;
> }
>
^ permalink raw reply
* RE: Bluetooth: mgmt: validate advertising TLV envelopes before parsing
From: bluez.test.bot @ 2026-05-09 18:58 UTC (permalink / raw)
To: linux-bluetooth, rollkingzzc
In-Reply-To: <20260509173708.411850-1-rollkingzzc@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 1702 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=1092077
---Test result---
Test Summary:
CheckPatch FAIL 0.60 seconds
GitLint PASS 0.27 seconds
SubjectPrefix PASS 0.11 seconds
BuildKernel PASS 19.18 seconds
CheckAllWarning PASS 21.24 seconds
CheckSparse PASS 20.64 seconds
BuildKernel32 PASS 19.26 seconds
TestRunnerSetup PASS 404.83 seconds
TestRunner_mgmt-tester PASS 2008.13 seconds
TestRunner_mesh-tester PASS 55.00 seconds
IncrementalBuild PASS 19.11 seconds
Details
##############################
Test: CheckPatch - FAIL
Desc: Run checkpatch.pl script
Output:
Bluetooth: mgmt: validate advertising TLV envelopes before parsing
WARNING: The commit message has 'BUG: KASAN: ', perhaps it also needs a 'Fixes:' tag?
total: 0 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/14563149.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/159
---
Regards,
Linux Bluetooth
^ permalink raw reply
* RE: Bluetooth: 6lowpan: Defer peer channel release until RCU cleanup
From: bluez.test.bot @ 2026-05-09 18:29 UTC (permalink / raw)
To: linux-bluetooth, rollkingzzc
In-Reply-To: <20260509173745.413473-1-rollkingzzc@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 2381 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=1092079
---Test result---
Test Summary:
CheckPatch FAIL 0.80 seconds
GitLint FAIL 0.63 seconds
SubjectPrefix PASS 0.13 seconds
BuildKernel PASS 26.63 seconds
CheckAllWarning PASS 29.30 seconds
CheckSparse PASS 27.94 seconds
BuildKernel32 PASS 26.06 seconds
TestRunnerSetup PASS 563.40 seconds
TestRunner_6lowpan-tester PASS 51.05 seconds
IncrementalBuild PASS 24.62 seconds
Details
##############################
Test: CheckPatch - FAIL
Desc: Run checkpatch.pl script
Output:
Bluetooth: 6lowpan: Defer peer channel release until RCU cleanup
WARNING: Prefer a maximum 75 chars per line (possible unwrapped commit description?)
#117:
The buggy scenario involves two paths, with each column showing the order within that path:
total: 0 errors, 1 warnings, 0 checks, 236 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/14563151.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.
##############################
Test: GitLint - FAIL
Desc: Run gitlint
Output:
Bluetooth: 6lowpan: Defer peer channel release until RCU cleanup
WARNING: I3 - ignore-body-lines: gitlint will be switching from using Python regex 'match' (match beginning) to 'search' (match anywhere) semantics. Please review your ignore-body-lines.regex option accordingly. To remove this warning, set general.regex-style-search=True. More details: https://jorisroovers.github.io/gitlint/configuration/#regex-style-search
21: B1 Line exceeds max length (91>80): "The buggy scenario involves two paths, with each column showing the order within that path:"
https://github.com/bluez/bluetooth-next/pull/161
---
Regards,
Linux Bluetooth
^ permalink raw reply
* RE: Bluetooth: RFCOMM: hold listener socket in rfcomm_connect_ind()
From: bluez.test.bot @ 2026-05-09 18:29 UTC (permalink / raw)
To: linux-bluetooth, rollkingzzc
In-Reply-To: <20260509173727.412674-1-rollkingzzc@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 2465 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=1092078
---Test result---
Test Summary:
CheckPatch FAIL 0.77 seconds
GitLint FAIL 0.34 seconds
SubjectPrefix PASS 0.13 seconds
BuildKernel PASS 24.98 seconds
CheckAllWarning PASS 27.45 seconds
CheckSparse PASS 26.44 seconds
BuildKernel32 PASS 24.22 seconds
TestRunnerSetup PASS 518.00 seconds
TestRunner_rfcomm-tester PASS 63.49 seconds
IncrementalBuild PASS 23.77 seconds
Details
##############################
Test: CheckPatch - FAIL
Desc: Run checkpatch.pl script
Output:
Bluetooth: RFCOMM: hold listener socket in rfcomm_connect_ind()
WARNING: Prefer a maximum 75 chars per line (possible unwrapped commit description?)
#119:
The buggy scenario involves two paths, with each column showing the order within that path:
WARNING: The commit message has 'BUG: KASAN: ', perhaps it also needs a 'Fixes:' tag?
total: 0 errors, 2 warnings, 0 checks, 65 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/14563150.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.
##############################
Test: GitLint - FAIL
Desc: Run gitlint
Output:
Bluetooth: RFCOMM: hold listener socket in rfcomm_connect_ind()
WARNING: I3 - ignore-body-lines: gitlint will be switching from using Python regex 'match' (match beginning) to 'search' (match anywhere) semantics. Please review your ignore-body-lines.regex option accordingly. To remove this warning, set general.regex-style-search=True. More details: https://jorisroovers.github.io/gitlint/configuration/#regex-style-search
23: B1 Line exceeds max length (91>80): "The buggy scenario involves two paths, with each column showing the order within that path:"
https://github.com/bluez/bluetooth-next/pull/160
---
Regards,
Linux Bluetooth
^ permalink raw reply
* [PATCH] Bluetooth: 6lowpan: Defer peer channel release until RCU cleanup
From: Zhang Cen @ 2026-05-09 17:37 UTC (permalink / raw)
To: Marcel Holtmann, Luiz Augusto von Dentz
Cc: linux-bluetooth, linux-kernel, zerocling0077, Zhang Cen
A 6LoWPAN peer stores the protocol-owned L2CAP channel reference in
peer->chan. chan_close_cb() removes the peer from the RCU list, but it
also drops that reference immediately. The peer object can still be seen
by in-flight RCU readers, and some paths keep using peer->chan after the
lookup has finished.
That lets transmit and disconnect paths dereference, lock, or send
through a channel whose last reference was released by the close path.
Defer the peer-owned l2cap_chan_put() until the peer RCU callback so a
peer that remains observable through RCU still carries a live channel
reference. Then take temporary channel references in the unicast,
multicast, and explicit disconnect paths before they keep using the
channel after the lookup window closes.
If the reader reaches step 3 after teardown reaches step 4, it can hit a
freed l2cap_chan.
The buggy scenario involves two paths, with each column showing the order within that path:
L2CAP peer teardown: Concurrent peer reader:
1. l2cap_conn_del() or another 1. A reader such as
close path takes a temporary __peer_lookup_conn(),
hold on the channel setup_header(),
send_mcast_pkt(), or
bt_6lowpan_disconnect()
traverses the peer list and
reads peer->chan
2. l2cap_chan_del() drops the 2. The reader keeps using the
connection-owned channel raw channel pointer after the
reference before 6LoWPAN lookup or after only RCU
close handling finishes protection remains
3. chan_close_cb() removes the 3. It dereferences channel
matching lowpan_peer from the fields or calls send or close
peer list operations through that
pointer
4. The original chan_close_cb()
also dropped the peer-owned
l2cap_chan reference, and the
close caller later released
its temporary hold
A peer reader can still observe the lowpan_peer while chan_close_cb()
has already consumed the peer-owned channel reference and the close
caller is releasing the last temporary hold, leaving peer->chan stale
before the peer's RCU grace period ends.
lowpan_peer objects stay RCU-visible after peer_del() removes them from
the list.
Signed-off-by: Zhang Cen <rollkingzzc@gmail.com>
---
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
index 2f03b780b40d..ea3ee6929101 100644
--- a/net/bluetooth/6lowpan.c
+++ b/net/bluetooth/6lowpan.c
@@ -95,13 +95,20 @@ static inline void peer_add(struct lowpan_btle_dev *dev,
atomic_inc(&dev->peer_count);
}
+static void peer_free_rcu(struct rcu_head *rcu)
+{
+ struct lowpan_peer *peer = container_of(rcu, struct lowpan_peer, rcu);
+
+ l2cap_chan_put(peer->chan);
+ kfree(peer);
+ module_put(THIS_MODULE);
+}
+
static inline bool peer_del(struct lowpan_btle_dev *dev,
struct lowpan_peer *peer)
{
list_del_rcu(&peer->list);
- kfree_rcu(peer, rcu);
-
- module_put(THIS_MODULE);
+ call_rcu(&peer->rcu, peer_free_rcu);
if (atomic_dec_and_test(&dev->peer_count)) {
BT_DBG("last peer");
@@ -137,9 +144,32 @@ __peer_lookup_conn(struct lowpan_btle_dev *dev, struct l2cap_conn *conn)
return NULL;
}
-static inline struct lowpan_peer *peer_lookup_dst(struct lowpan_btle_dev *dev,
- struct in6_addr *daddr,
- struct sk_buff *skb)
+static struct l2cap_chan *lookup_peer_chan(struct l2cap_conn *conn)
+{
+ struct lowpan_btle_dev *entry;
+ struct lowpan_peer *peer;
+ struct l2cap_chan *chan = NULL;
+
+ rcu_read_lock();
+
+ list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
+ peer = __peer_lookup_conn(entry, conn);
+ if (peer) {
+ chan = peer->chan;
+ l2cap_chan_hold(chan);
+ break;
+ }
+ }
+
+ rcu_read_unlock();
+
+ return chan;
+}
+
+static int peer_lookup_dst(struct lowpan_btle_dev *dev, struct in6_addr *daddr,
+ struct sk_buff *skb, u8 *lladdr,
+ bdaddr_t *peer_addr, u8 *peer_addr_type,
+ struct l2cap_chan **chan)
{
struct rt6_info *rt = dst_rt6_info(skb_dst(skb));
int count = atomic_read(&dev->peer_count);
@@ -175,13 +205,20 @@ static inline struct lowpan_peer *peer_lookup_dst(struct lowpan_btle_dev *dev,
rcu_read_lock();
list_for_each_entry_rcu(peer, &dev->peers, list) {
+ struct l2cap_chan *pchan = peer->chan;
+
BT_DBG("dst addr %pMR dst type %u ip %pI6c",
- &peer->chan->dst, peer->chan->dst_type,
+ &pchan->dst, pchan->dst_type,
&peer->peer_addr);
if (!ipv6_addr_cmp(&peer->peer_addr, nexthop)) {
+ memcpy(lladdr, peer->lladdr, ETH_ALEN);
+ *peer_addr = pchan->dst;
+ *peer_addr_type = pchan->dst_type;
+ l2cap_chan_hold(pchan);
+ *chan = pchan;
rcu_read_unlock();
- return peer;
+ return 0;
}
}
@@ -190,9 +227,16 @@ static inline struct lowpan_peer *peer_lookup_dst(struct lowpan_btle_dev *dev,
if (neigh) {
list_for_each_entry_rcu(peer, &dev->peers, list) {
if (!memcmp(neigh->ha, peer->lladdr, ETH_ALEN)) {
+ struct l2cap_chan *pchan = peer->chan;
+
+ memcpy(lladdr, peer->lladdr, ETH_ALEN);
+ *peer_addr = pchan->dst;
+ *peer_addr_type = pchan->dst_type;
+ l2cap_chan_hold(pchan);
+ *chan = pchan;
neigh_release(neigh);
rcu_read_unlock();
- return peer;
+ return 0;
}
}
neigh_release(neigh);
@@ -200,7 +244,7 @@ static inline struct lowpan_peer *peer_lookup_dst(struct lowpan_btle_dev *dev,
rcu_read_unlock();
- return NULL;
+ return -ENOENT;
}
static struct lowpan_peer *lookup_peer(struct l2cap_conn *conn)
@@ -379,7 +423,7 @@ static int setup_header(struct sk_buff *skb, struct net_device *netdev,
struct in6_addr ipv6_daddr;
struct ipv6hdr *hdr;
struct lowpan_btle_dev *dev;
- struct lowpan_peer *peer;
+ u8 peer_lladdr[ETH_ALEN];
u8 *daddr;
int err, status = 0;
@@ -388,9 +432,9 @@ static int setup_header(struct sk_buff *skb, struct net_device *netdev,
dev = lowpan_btle_dev(netdev);
memcpy(&ipv6_daddr, &hdr->daddr, sizeof(ipv6_daddr));
+ lowpan_cb(skb)->chan = NULL;
if (ipv6_addr_is_multicast(&ipv6_daddr)) {
- lowpan_cb(skb)->chan = NULL;
daddr = NULL;
} else {
BT_DBG("dest IP %pI6c", &ipv6_daddr);
@@ -400,16 +444,15 @@ static int setup_header(struct sk_buff *skb, struct net_device *netdev,
* or user set route) so get peer according to
* the destination address.
*/
- peer = peer_lookup_dst(dev, &ipv6_daddr, skb);
- if (!peer) {
+ err = peer_lookup_dst(dev, &ipv6_daddr, skb, peer_lladdr,
+ peer_addr, peer_addr_type,
+ &lowpan_cb(skb)->chan);
+ if (err) {
BT_DBG("no such peer");
- return -ENOENT;
+ return err;
}
- daddr = peer->lladdr;
- *peer_addr = peer->chan->dst;
- *peer_addr_type = peer->chan->dst_type;
- lowpan_cb(skb)->chan = peer->chan;
+ daddr = peer_lladdr;
status = 1;
}
@@ -417,8 +460,13 @@ static int setup_header(struct sk_buff *skb, struct net_device *netdev,
lowpan_header_compress(skb, netdev, daddr, dev->netdev->dev_addr);
err = dev_hard_header(skb, netdev, ETH_P_IPV6, NULL, NULL, 0);
- if (err < 0)
+ if (err < 0) {
+ if (lowpan_cb(skb)->chan) {
+ l2cap_chan_put(lowpan_cb(skb)->chan);
+ lowpan_cb(skb)->chan = NULL;
+ }
return err;
+ }
return status;
}
@@ -483,15 +531,23 @@ static int send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev)
dev = lowpan_btle_dev(entry->netdev);
list_for_each_entry_rcu(pentry, &dev->peers, list) {
+ struct l2cap_chan *chan = pentry->chan;
int ret;
local_skb = skb_clone(skb, GFP_ATOMIC);
+ if (!local_skb) {
+ err = -ENOMEM;
+ continue;
+ }
+
+ l2cap_chan_hold(chan);
BT_DBG("xmit %s to %pMR type %u IP %pI6c chan %p",
netdev->name,
- &pentry->chan->dst, pentry->chan->dst_type,
- &pentry->peer_addr, pentry->chan);
- ret = send_pkt(pentry->chan, local_skb, netdev);
+ &chan->dst, chan->dst_type,
+ &pentry->peer_addr, chan);
+ ret = send_pkt(chan, local_skb, netdev);
+ l2cap_chan_put(chan);
if (ret < 0)
err = ret;
@@ -530,10 +586,14 @@ static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *netdev)
if (err) {
if (lowpan_cb(skb)->chan) {
+ struct l2cap_chan *chan = lowpan_cb(skb)->chan;
+
BT_DBG("xmit %s to %pMR type %u IP %pI6c chan %p",
netdev->name, &addr, addr_type,
- &lowpan_cb(skb)->addr, lowpan_cb(skb)->chan);
- err = send_pkt(lowpan_cb(skb)->chan, skb, netdev);
+ &lowpan_cb(skb)->addr, chan);
+ err = send_pkt(chan, skb, netdev);
+ l2cap_chan_put(chan);
+ lowpan_cb(skb)->chan = NULL;
} else {
err = -ENOENT;
}
@@ -802,8 +862,6 @@ static void chan_close_cb(struct l2cap_chan *chan)
last ? "last " : "1 ", peer);
BT_DBG("chan %p orig refcnt %u", chan,
kref_read(&chan->kref));
-
- l2cap_chan_put(chan);
break;
}
}
@@ -917,19 +975,20 @@ static int bt_6lowpan_connect(bdaddr_t *addr, u8 dst_type)
static int bt_6lowpan_disconnect(struct l2cap_conn *conn, u8 dst_type)
{
- struct lowpan_peer *peer;
+ struct l2cap_chan *chan;
BT_DBG("conn %p dst type %u", conn, dst_type);
- peer = lookup_peer(conn);
- if (!peer)
+ chan = lookup_peer_chan(conn);
+ if (!chan)
return -ENOENT;
- BT_DBG("peer %p chan %p", peer, peer->chan);
+ BT_DBG("chan %p", chan);
- l2cap_chan_lock(peer->chan);
- l2cap_chan_close(peer->chan, ENOENT);
- l2cap_chan_unlock(peer->chan);
+ l2cap_chan_lock(chan);
+ l2cap_chan_close(chan, ENOENT);
+ l2cap_chan_unlock(chan);
+ l2cap_chan_put(chan);
return 0;
}
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox