* RE: [5.10] Bluetooth: hci_core: Fix use-after-free in vhci_flush()
From: bluez.test.bot @ 2026-06-04 0:19 UTC (permalink / raw)
To: linux-bluetooth, vlad102nikolaev
In-Reply-To: <20260603234343.445-1-vlad102nikolaev@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 670 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_core.h:28
error: include/net/bluetooth/hci_core.h: patch does not apply
error: patch failed: net/bluetooth/hci_core.c:1040
error: net/bluetooth/hci_core.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 5.10] Bluetooth: hci_core: Fix use-after-free in vhci_flush()
From: Vladislav Nikolaev @ 2026-06-03 23:43 UTC (permalink / raw)
To: stable, Greg Kroah-Hartman
Cc: Vladislav Nikolaev, Marcel Holtmann, Johan Hedberg,
David S. Miller, Jakub Kicinski, David Herrmann, linux-bluetooth,
netdev, linux-kernel, Luiz Augusto von Dentz, David Rheinsberg,
Johan Hedberg, lvc-project, syzbot+2faa4825e556199361f9,
Kuniyuki Iwashima, Paul Menzel, Luiz Augusto von Dentz
From: Kuniyuki Iwashima <kuniyu@google.com>
commit 1d6123102e9fbedc8d25bf4731da6d513173e49e upstream.
syzbot reported use-after-free in vhci_flush() without repro. [0]
From the splat, a thread close()d a vhci file descriptor while
its device was being used by iotcl() on another thread.
Once the last fd refcnt is released, vhci_release() calls
hci_unregister_dev(), hci_free_dev(), and kfree() for struct
vhci_data, which is set to hci_dev->dev->driver_data.
The problem is that there is no synchronisation after unlinking
hdev from hci_dev_list in hci_unregister_dev(). There might be
another thread still accessing the hdev which was fetched before
the unlink operation.
We can use SRCU for such synchronisation.
Let's run hci_dev_reset() under SRCU and wait for its completion
in hci_unregister_dev().
Another option would be to restore hci_dev->destruct(), which was
removed in commit 587ae086f6e4 ("Bluetooth: Remove unused
hci-destruct cb"). However, this would not be a good solution, as
we should not run hci_unregister_dev() while there are in-flight
ioctl() requests, which could lead to another data-race KCSAN splat.
Note that other drivers seem to have the same problem, for exmaple,
virtbt_remove().
[0]:
BUG: KASAN: slab-use-after-free in skb_queue_empty_lockless include/linux/skbuff.h:1891 [inline]
BUG: KASAN: slab-use-after-free in skb_queue_purge_reason+0x99/0x360 net/core/skbuff.c:3937
Read of size 8 at addr ffff88807cb8d858 by task syz.1.219/6718
CPU: 1 UID: 0 PID: 6718 Comm: syz.1.219 Not tainted 6.16.0-rc1-syzkaller-00196-g08207f42d3ff #0 PREEMPT(full)
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 05/07/2025
Call Trace:
<TASK>
dump_stack_lvl+0x189/0x250 lib/dump_stack.c:120
print_address_description mm/kasan/report.c:408 [inline]
print_report+0xd2/0x2b0 mm/kasan/report.c:521
kasan_report+0x118/0x150 mm/kasan/report.c:634
skb_queue_empty_lockless include/linux/skbuff.h:1891 [inline]
skb_queue_purge_reason+0x99/0x360 net/core/skbuff.c:3937
skb_queue_purge include/linux/skbuff.h:3368 [inline]
vhci_flush+0x44/0x50 drivers/bluetooth/hci_vhci.c:69
hci_dev_do_reset net/bluetooth/hci_core.c:552 [inline]
hci_dev_reset+0x420/0x5c0 net/bluetooth/hci_core.c:592
sock_do_ioctl+0xd9/0x300 net/socket.c:1190
sock_ioctl+0x576/0x790 net/socket.c:1311
vfs_ioctl fs/ioctl.c:51 [inline]
__do_sys_ioctl fs/ioctl.c:907 [inline]
__se_sys_ioctl+0xf9/0x170 fs/ioctl.c:893
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0xfa/0x3b0 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7fcf5b98e929
Code: ff ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 40 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 a8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007fcf5c7b9038 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
RAX: ffffffffffffffda RBX: 00007fcf5bbb6160 RCX: 00007fcf5b98e929
RDX: 0000000000000000 RSI: 00000000400448cb RDI: 0000000000000009
RBP: 00007fcf5ba10b39 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 0000000000000000 R14: 00007fcf5bbb6160 R15: 00007ffd6353d528
</TASK>
Allocated by task 6535:
kasan_save_stack mm/kasan/common.c:47 [inline]
kasan_save_track+0x3e/0x80 mm/kasan/common.c:68
poison_kmalloc_redzone mm/kasan/common.c:377 [inline]
__kasan_kmalloc+0x93/0xb0 mm/kasan/common.c:394
kasan_kmalloc include/linux/kasan.h:260 [inline]
__kmalloc_cache_noprof+0x230/0x3d0 mm/slub.c:4359
kmalloc_noprof include/linux/slab.h:905 [inline]
kzalloc_noprof include/linux/slab.h:1039 [inline]
vhci_open+0x57/0x360 drivers/bluetooth/hci_vhci.c:635
misc_open+0x2bc/0x330 drivers/char/misc.c:161
chrdev_open+0x4c9/0x5e0 fs/char_dev.c:414
do_dentry_open+0xdf0/0x1970 fs/open.c:964
vfs_open+0x3b/0x340 fs/open.c:1094
do_open fs/namei.c:3887 [inline]
path_openat+0x2ee5/0x3830 fs/namei.c:4046
do_filp_open+0x1fa/0x410 fs/namei.c:4073
do_sys_openat2+0x121/0x1c0 fs/open.c:1437
do_sys_open fs/open.c:1452 [inline]
__do_sys_openat fs/open.c:1468 [inline]
__se_sys_openat fs/open.c:1463 [inline]
__x64_sys_openat+0x138/0x170 fs/open.c:1463
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0xfa/0x3b0 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
Freed by task 6535:
kasan_save_stack mm/kasan/common.c:47 [inline]
kasan_save_track+0x3e/0x80 mm/kasan/common.c:68
kasan_save_free_info+0x46/0x50 mm/kasan/generic.c:576
poison_slab_object mm/kasan/common.c:247 [inline]
__kasan_slab_free+0x62/0x70 mm/kasan/common.c:264
kasan_slab_free include/linux/kasan.h:233 [inline]
slab_free_hook mm/slub.c:2381 [inline]
slab_free mm/slub.c:4643 [inline]
kfree+0x18e/0x440 mm/slub.c:4842
vhci_release+0xbc/0xd0 drivers/bluetooth/hci_vhci.c:671
__fput+0x44c/0xa70 fs/file_table.c:465
task_work_run+0x1d1/0x260 kernel/task_work.c:227
exit_task_work include/linux/task_work.h:40 [inline]
do_exit+0x6ad/0x22e0 kernel/exit.c:955
do_group_exit+0x21c/0x2d0 kernel/exit.c:1104
__do_sys_exit_group kernel/exit.c:1115 [inline]
__se_sys_exit_group kernel/exit.c:1113 [inline]
__x64_sys_exit_group+0x3f/0x40 kernel/exit.c:1113
x64_sys_call+0x21ba/0x21c0 arch/x86/include/generated/asm/syscalls_64.h:232
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0xfa/0x3b0 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
The buggy address belongs to the object at ffff88807cb8d800
which belongs to the cache kmalloc-1k of size 1024
The buggy address is located 88 bytes inside of
freed 1024-byte region [ffff88807cb8d800, ffff88807cb8dc00)
Fixes: bf18c7118cf8 ("Bluetooth: vhci: Free driver_data on file release")
Reported-by: syzbot+2faa4825e556199361f9@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=f62d64848fc4c7c30cd6
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
Acked-by: Paul Menzel <pmenzel@molgen.mpg.de>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Signed-off-by: Vladislav Nikolaev <vlad102nikolaev@gmail.com>
---
Backport fix for CVE-2025-38250
include/net/bluetooth/hci_core.h | 2 ++
net/bluetooth/hci_core.c | 34 ++++++++++++++++++++++++++++----
2 files changed, 32 insertions(+), 4 deletions(-)
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index fe62943a35dd..bc4e2856f235 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -28,6 +28,7 @@
#include <linux/idr.h>
#include <linux/leds.h>
#include <linux/rculist.h>
+#include <linux/srcu.h>
#include <net/bluetooth/hci.h>
#include <net/bluetooth/hci_sock.h>
@@ -285,6 +286,7 @@ struct amp_assoc {
struct hci_dev {
struct list_head list;
+ struct srcu_struct srcu;
struct mutex lock;
const char *name;
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 9787a4c55113..a718e38f3da3 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -1040,7 +1040,7 @@ static int hci_linkpol_req(struct hci_request *req, unsigned long opt)
/* Get HCI device by index.
* Device is held on return. */
-struct hci_dev *hci_dev_get(int index)
+static struct hci_dev *__hci_dev_get(int index, int *srcu_index)
{
struct hci_dev *hdev = NULL, *d;
@@ -1053,6 +1053,8 @@ struct hci_dev *hci_dev_get(int index)
list_for_each_entry(d, &hci_dev_list, list) {
if (d->id == index) {
hdev = hci_dev_hold(d);
+ if (srcu_index)
+ *srcu_index = srcu_read_lock(&d->srcu);
break;
}
}
@@ -1060,6 +1062,22 @@ struct hci_dev *hci_dev_get(int index)
return hdev;
}
+struct hci_dev *hci_dev_get(int index)
+{
+ return __hci_dev_get(index, NULL);
+}
+
+static struct hci_dev *hci_dev_get_srcu(int index, int *srcu_index)
+{
+ return __hci_dev_get(index, srcu_index);
+}
+
+static void hci_dev_put_srcu(struct hci_dev *hdev, int srcu_index)
+{
+ srcu_read_unlock(&hdev->srcu, srcu_index);
+ hci_dev_put(hdev);
+}
+
/* ---- Inquiry support ---- */
bool hci_discovery_active(struct hci_dev *hdev)
@@ -1906,9 +1924,9 @@ static int hci_dev_do_reset(struct hci_dev *hdev)
int hci_dev_reset(__u16 dev)
{
struct hci_dev *hdev;
- int err;
+ int err, srcu_index;
- hdev = hci_dev_get(dev);
+ hdev = hci_dev_get_srcu(dev, &srcu_index);
if (!hdev)
return -ENODEV;
@@ -1930,7 +1948,7 @@ int hci_dev_reset(__u16 dev)
err = hci_dev_do_reset(hdev);
done:
- hci_dev_put(hdev);
+ hci_dev_put_srcu(hdev, srcu_index);
return err;
}
@@ -3596,6 +3614,11 @@ struct hci_dev *hci_alloc_dev(void)
if (!hdev)
return NULL;
+ if (init_srcu_struct(&hdev->srcu)) {
+ kfree(hdev);
+ return NULL;
+ }
+
hdev->pkt_type = (HCI_DM1 | HCI_DH1 | HCI_HV1);
hdev->esco_type = (ESCO_HV1);
hdev->link_mode = (HCI_LM_ACCEPT);
@@ -3839,6 +3862,9 @@ void hci_unregister_dev(struct hci_dev *hdev)
list_del(&hdev->list);
write_unlock(&hci_dev_list_lock);
+ synchronize_srcu(&hdev->srcu);
+ cleanup_srcu_struct(&hdev->srcu);
+
cancel_work_sync(&hdev->rx_work);
cancel_work_sync(&hdev->cmd_work);
cancel_work_sync(&hdev->tx_work);
--
2.47.3
^ permalink raw reply related
* [bluez/bluez]
From: BluezTestBot @ 2026-06-03 22:15 UTC (permalink / raw)
To: linux-bluetooth
Branch: refs/heads/1089467
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: [BlueZ,v1,1/2] client/mgmt: Add options to ltks command for loading entries
From: bluez.test.bot @ 2026-06-03 20:36 UTC (permalink / raw)
To: linux-bluetooth, luiz.dentz
In-Reply-To: <20260603190358.425835-1-luiz.dentz@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 989 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=1105509
---Test result---
Test Summary:
CheckPatch PASS 0.88 seconds
GitLint PASS 0.58 seconds
BuildEll PASS 17.95 seconds
BluezMake PASS 654.52 seconds
MakeCheck PASS 18.33 seconds
MakeDistcheck PASS 223.87 seconds
CheckValgrind PASS 277.86 seconds
CheckSmatch PASS 312.38 seconds
bluezmakeextell PASS 166.67 seconds
IncrementalBuild PASS 636.29 seconds
ScanBuild PASS 923.83 seconds
https://github.com/bluez/bluez/pull/2173
---
Regards,
Linux Bluetooth
^ permalink raw reply
* [bluez/bluez] 67058b: client/mgmt: Add options to ltks command for loadi...
From: Luiz Augusto von Dentz @ 2026-06-03 19:41 UTC (permalink / raw)
To: linux-bluetooth
Branch: refs/heads/1105509
Home: https://github.com/bluez/bluez
Commit: 67058b3f707cbd1acc867b598f471c744bd95f85
https://github.com/bluez/bluez/commit/67058b3f707cbd1acc867b598f471c744bd95f85
Author: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Date: 2026-06-03 (Wed, 03 Jun 2026)
Changed paths:
M client/mgmt.c
Log Message:
-----------
client/mgmt: Add options to ltks command for loading entries
Add support for optionally specifying a single LTK entry when using
the ltks command. When called without arguments it clears all LTKs
(existing behavior). When called with an address and key parameters
it loads exactly one LTK entry, useful for testing.
Options:
-a addr_type Address type (1=LE Public, 2=LE Random)
-t key_type Key type (0=Unauthenticated, 1=Authenticated)
-c central Central flag (0 or 1)
-e enc_size Encryption key size (7-16)
-d ediv Encrypted Diversifier
-r rand Random number (64-bit)
-k key 128-bit key as 32 hex characters
Commit: 14aeb5480c3b59ad83320d712a0290949d524baa
https://github.com/bluez/bluez/commit/14aeb5480c3b59ad83320d712a0290949d524baa
Author: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Date: 2026-06-03 (Wed, 03 Jun 2026)
Changed paths:
M doc/bluetoothctl-mgmt.rst
Log Message:
-----------
doc/bluetoothctl-mgmt: Update ltks command documentation
Document the new options for the ltks command including address type,
key type, central flag, encryption size, EDIV, random number and key
value parameters.
Compare: https://github.com/bluez/bluez/compare/67058b3f707c%5E...14aeb5480c3b
To unsubscribe from these emails, change your notification settings at https://github.com/bluez/bluez/settings/notifications
^ permalink raw reply
* [PATCH BlueZ v1 2/2] doc/bluetoothctl-mgmt: Update ltks command documentation
From: Luiz Augusto von Dentz @ 2026-06-03 19:03 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <20260603190358.425835-1-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Document the new options for the ltks command including address type,
key type, central flag, encryption size, EDIV, random number and key
value parameters.
---
doc/bluetoothctl-mgmt.rst | 19 ++++++++++++++++---
1 file changed, 16 insertions(+), 3 deletions(-)
diff --git a/doc/bluetoothctl-mgmt.rst b/doc/bluetoothctl-mgmt.rst
index 646c2dcb4365..da04f9c225b3 100644
--- a/doc/bluetoothctl-mgmt.rst
+++ b/doc/bluetoothctl-mgmt.rst
@@ -509,11 +509,24 @@ Load Link Keys
ltks
----
-Load Long Term Keys
+Load Long Term Keys. Note that loading keys replaces all previously loaded
+keys, so loading a single entry invalidates all previously loaded keys.
-:Usage: **> ltks**
-:Example Load stored LE long term keys:
+:Usage: **> ltks [-a addr_type] [-t key_type] [-c central] [-e enc_size] [-d ediv] [-r rand] [-k key] [address]**
+:[-a addr_type]: Address type (1=LE Public, 2=LE Random). Default: 1 (LE Public)
+:[-t key_type]: Key type (0=Unauthenticated, 1=Authenticated). Default: 0 (Unauthenticated)
+:[-c central]: Central flag (0 or 1). Default: 0
+:[-e enc_size]: Encryption key size (7-16). Default: 0
+:[-d ediv]: Encrypted Diversifier (16-bit value). Default: 0x0000
+:[-r rand]: Random number (64-bit value). Default: 0x0000000000000000
+:[-k key]: 128-bit key as 32 hex characters. Default: all zeros
+:[address]: Bluetooth address of the peer device
+:Example Clear all long term keys:
| **> ltks**
+:Example Load an authenticated LTK for a specific device:
+ | **> ltks -t 1 -c 1 -e 16 -d 0x1234 -r 0xABCD -k 0123456789abcdef0123456789abcdef 00:11:22:33:44:55**
+:Example Load an LTK with LE Random address type:
+ | **> ltks -a 2 -t 0 -c 1 -e 16 -d 0 -r 0 -k 0123456789abcdef0123456789abcdef AA:BB:CC:DD:EE:FF**
irks
----
--
2.54.0
^ permalink raw reply related
* [PATCH BlueZ v1 1/2] client/mgmt: Add options to ltks command for loading entries
From: Luiz Augusto von Dentz @ 2026-06-03 19:03 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Add support for optionally specifying a single LTK entry when using
the ltks command. When called without arguments it clears all LTKs
(existing behavior). When called with an address and key parameters
it loads exactly one LTK entry, useful for testing.
Options:
-a addr_type Address type (1=LE Public, 2=LE Random)
-t key_type Key type (0=Unauthenticated, 1=Authenticated)
-c central Central flag (0 or 1)
-e enc_size Encryption key size (7-16)
-d ediv Encrypted Diversifier
-r rand Random number (64-bit)
-k key 128-bit key as 32 hex characters
---
client/mgmt.c | 100 +++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 94 insertions(+), 6 deletions(-)
diff --git a/client/mgmt.c b/client/mgmt.c
index 50558a313866..6d6ada95f43d 100644
--- a/client/mgmt.c
+++ b/client/mgmt.c
@@ -3535,21 +3535,107 @@ static void ltks_rsp(uint8_t status, uint16_t len, const void *param,
bt_shell_noninteractive_quit(EXIT_SUCCESS);
}
+static const struct option ltks_options[] = {
+ { "help", 0, 0, 'h' },
+ { "address-type", 1, 0, 'a' },
+ { "type", 1, 0, 't' },
+ { "central", 1, 0, 'c' },
+ { "enc-size", 1, 0, 'e' },
+ { "ediv", 1, 0, 'd' },
+ { "rand", 1, 0, 'r' },
+ { "key", 1, 0, 'k' },
+ { 0, 0, 0, 0 }
+};
+
static void cmd_ltks(int argc, char **argv)
{
- struct mgmt_cp_load_long_term_keys cp;
+ struct mgmt_cp_load_long_term_keys *cp;
+ uint8_t buf[sizeof(*cp) + sizeof(struct mgmt_ltk_info)];
+ uint16_t count = 0;
+ struct mgmt_ltk_info *ltk;
+ uint8_t addr_type = BDADDR_LE_PUBLIC;
+ int opt;
uint16_t index;
index = mgmt_index;
if (index == MGMT_INDEX_NONE)
index = 0;
- memset(&cp, 0, sizeof(cp));
+ cp = (void *) buf;
+ memset(buf, 0, sizeof(buf));
+ ltk = &cp->keys[0];
- if (mgmt_send(mgmt, MGMT_OP_LOAD_LONG_TERM_KEYS, index, sizeof(cp), &cp,
- ltks_rsp, NULL, NULL) == 0) {
+ while ((opt = getopt_long(argc, argv, "+a:t:c:e:d:r:k:h",
+ ltks_options, NULL)) != -1) {
+ switch (opt) {
+ case 'a':
+ addr_type = strtol(optarg, NULL, 0);
+ if (addr_type != BDADDR_LE_PUBLIC &&
+ addr_type != BDADDR_LE_RANDOM) {
+ error("Invalid address type (expected 1=LE "
+ "Public, 2=LE Random)");
+ optind = 0;
+ return bt_shell_noninteractive_quit(
+ EXIT_FAILURE);
+ }
+ break;
+ case 't':
+ ltk->type = strtol(optarg, NULL, 0);
+ break;
+ case 'c':
+ ltk->central = strtol(optarg, NULL, 0);
+ break;
+ case 'e':
+ ltk->enc_size = strtol(optarg, NULL, 0);
+ break;
+ case 'd':
+ ltk->ediv = strtol(optarg, NULL, 0);
+ break;
+ case 'r':
+ ltk->rand = strtoull(optarg, NULL, 0);
+ break;
+ case 'k':
+ if (strlen(optarg) != 32) {
+ error("Invalid key length (expected 32 hex "
+ "chars)");
+ optind = 0;
+ return bt_shell_noninteractive_quit(
+ EXIT_FAILURE);
+ }
+ for (int i = 0; i < 16; i++) {
+ char byte[3] = { optarg[i * 2],
+ optarg[i * 2 + 1], 0 };
+ ltk->val[i] = strtol(byte, NULL, 16);
+ }
+ break;
+ case 'h':
+ bt_shell_usage();
+ optind = 0;
+ return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+ default:
+ bt_shell_usage();
+ optind = 0;
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+
+ if (argc > 0) {
+ str2ba(argv[0], <k->addr.bdaddr);
+ ltk->addr.type = addr_type;
+ count = 1;
+ }
+
+ cp->key_count = cpu_to_le16(count);
+
+ if (mgmt_send(mgmt, MGMT_OP_LOAD_LONG_TERM_KEYS, index,
+ sizeof(*cp) + count * sizeof(*ltk), cp,
+ ltks_rsp, NULL, NULL) == 0) {
error("Unable to send load_ltks cmd");
- return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
}
@@ -6136,7 +6222,9 @@ static const struct bt_shell_menu mgmt_menu = {
cmd_unpair, "Unpair device" },
{ "keys", NULL,
cmd_keys, "Load Link Keys" },
- { "ltks", NULL,
+ { "ltks", "[-a addr_type] [-t key_type] [-c central] "
+ "[-e enc_size] [-d ediv] [-r rand] [-k key] "
+ "[address]",
cmd_ltks, "Load Long Term Keys" },
{ "irks", "[--local index] [--file file path]",
cmd_irks, "Load Identity Resolving Keys" },
--
2.54.0
^ permalink raw reply related
* Re: [PATCH v9 1/1] Bluetooth: L2CAP: Fix use-after-free in l2cap_sock_new_connection_cb()
From: Luiz Augusto von Dentz @ 2026-06-03 18:17 UTC (permalink / raw)
To: Siwei Zhang; +Cc: Marcel Holtmann, linux-bluetooth
In-Reply-To: <20260603150835.3539963-2-oss@fourdim.xyz>
Hi Siwei,
On Wed, Jun 3, 2026 at 11:09 AM Siwei Zhang <oss@fourdim.xyz> wrote:
>
> 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.
>
> Allocate the channel outside the func to prevent this.
>
> Fixes: 8ffb929098a5 ("Bluetooth: Remove parent socket usage from l2cap_core.c")
> Cc: stable@kernel.org
> Assisted-by: Claude:claude-opus-4-8
> Signed-off-by: Siwei Zhang <oss@fourdim.xyz>
> ---
> include/net/bluetooth/l2cap.h | 10 +++--
> net/bluetooth/6lowpan.c | 31 +++++++------
> net/bluetooth/l2cap_core.c | 41 ++++++++++++-----
> net/bluetooth/l2cap_sock.c | 83 +++++++++++++++++++++--------------
> net/bluetooth/smp.c | 17 ++++---
> 5 files changed, 113 insertions(+), 69 deletions(-)
>
> diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
> index e0a1f2293679..7f5e4647f6e0 100644
> --- a/include/net/bluetooth/l2cap.h
> +++ b/include/net/bluetooth/l2cap.h
> @@ -620,7 +620,9 @@ struct l2cap_chan {
> struct l2cap_ops {
> char *name;
>
> - struct l2cap_chan *(*new_connection) (struct l2cap_chan *chan);
> + int (*new_connection)(struct l2cap_conn *conn,
> + struct l2cap_chan *chan,
> + struct l2cap_chan *new_chan);
> int (*recv) (struct l2cap_chan * chan,
> struct sk_buff *skb);
> void (*teardown) (struct l2cap_chan *chan, int err);
> @@ -884,9 +886,11 @@ static inline __u16 __next_seq(struct l2cap_chan *chan, __u16 seq)
> return (seq + 1) % (chan->tx_win_max + 1);
> }
>
> -static inline struct l2cap_chan *l2cap_chan_no_new_connection(struct l2cap_chan *chan)
> +static inline int l2cap_chan_no_new_connection(struct l2cap_conn *conn,
> + struct l2cap_chan *chan,
> + struct l2cap_chan *new_chan)
> {
> - return NULL;
> + return -EOPNOTSUPP;
> }
>
> static inline int l2cap_chan_no_recv(struct l2cap_chan *chan, struct sk_buff *skb)
> diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
> index cb1e329d66fd..94863af97a44 100644
> --- a/net/bluetooth/6lowpan.c
> +++ b/net/bluetooth/6lowpan.c
> @@ -624,6 +624,15 @@ static bool is_bt_6lowpan(struct hci_conn *hcon)
> return true;
> }
>
> +static void chan_init(struct l2cap_chan *chan)
> +{
> + l2cap_chan_set_defaults(chan);
> +
> + chan->chan_type = L2CAP_CHAN_CONN_ORIENTED;
> + chan->mode = L2CAP_MODE_LE_FLOWCTL;
> + chan->imtu = 1280;
> +}
> +
> static struct l2cap_chan *chan_create(void)
> {
> struct l2cap_chan *chan;
> @@ -632,11 +641,7 @@ static struct l2cap_chan *chan_create(void)
> if (!chan)
> return NULL;
>
> - l2cap_chan_set_defaults(chan);
> -
> - chan->chan_type = L2CAP_CHAN_CONN_ORIENTED;
> - chan->mode = L2CAP_MODE_LE_FLOWCTL;
> - chan->imtu = 1280;
> + chan_init(chan);
>
> return chan;
> }
> @@ -745,19 +750,19 @@ static inline void chan_ready_cb(struct l2cap_chan *chan)
> ifup(dev->netdev);
> }
>
> -static inline struct l2cap_chan *chan_new_conn_cb(struct l2cap_chan *pchan)
> +static inline int chan_new_conn_cb(struct l2cap_conn *conn,
> + struct l2cap_chan *pchan,
> + struct l2cap_chan *chan)
> {
> - struct l2cap_chan *chan;
> -
> - chan = chan_create();
> - if (!chan)
> - return NULL;
> -
> + chan_init(chan);
> chan->ops = pchan->ops;
>
> + /* Take the conn list reference; see l2cap_new_connection(). */
> + __l2cap_chan_add(conn, chan);
> +
> BT_DBG("chan %p pchan %p", chan, pchan);
>
> - return chan;
> + return 0;
> }
>
> static void unregister_dev(struct lowpan_btle_dev *dev)
> diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
> index c4ccfbda9d78..62acf90837fb 100644
> --- a/net/bluetooth/l2cap_core.c
> +++ b/net/bluetooth/l2cap_core.c
> @@ -4007,6 +4007,31 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn,
> return 0;
> }
>
> +/* Allocate and initialise a channel for an incoming connection.
> + *
> + * ->new_connection() initialises the channel and links it into @conn with
> + * __l2cap_chan_add(). The l2cap_chan_create() reference becomes the one owned
> + * by the parent subsystem (l2cap_pi(sk)->chan, conn->smp or peer->chan) and is
> + * released by its teardown callback; the conn list reference is released by
> + * l2cap_chan_del().
> + */
> +static struct l2cap_chan *l2cap_new_connection(struct l2cap_conn *conn,
> + struct l2cap_chan *pchan)
> +{
> + struct l2cap_chan *chan;
> +
> + chan = l2cap_chan_create();
> + if (!chan)
> + return NULL;
> +
> + if (pchan->ops->new_connection(conn, pchan, chan) < 0) {
> + l2cap_chan_put(chan);
> + return NULL;
> + }
I don't quite get why we can't just place __l2cap_chan_add here
instead of having it called by new_connection callbacks?
> +
> + return chan;
> +}
> +
> static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
> u8 *data, u8 rsp_code)
> {
> @@ -4053,7 +4078,7 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
> goto response;
> }
>
> - chan = pchan->ops->new_connection(pchan);
> + chan = l2cap_new_connection(conn, pchan);
> if (!chan)
> goto response;
>
> @@ -4071,8 +4096,6 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
> chan->psm = psm;
> chan->dcid = scid;
>
> - __l2cap_chan_add(conn, chan);
> -
> dcid = chan->scid;
>
> __set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
> @@ -4955,7 +4978,7 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
> goto response_unlock;
> }
>
> - chan = pchan->ops->new_connection(pchan);
> + chan = l2cap_new_connection(conn, pchan);
> if (!chan) {
> result = L2CAP_CR_LE_NO_MEM;
> goto response_unlock;
> @@ -4970,8 +4993,6 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
> chan->omtu = mtu;
> chan->remote_mps = mps;
>
> - __l2cap_chan_add(conn, chan);
> -
> l2cap_le_flowctl_init(chan, __le16_to_cpu(req->credits));
>
> dcid = chan->scid;
> @@ -5179,7 +5200,7 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn,
> continue;
> }
>
> - chan = pchan->ops->new_connection(pchan);
> + chan = l2cap_new_connection(conn, pchan);
> if (!chan) {
> result = L2CAP_CR_LE_NO_MEM;
> continue;
> @@ -5194,8 +5215,6 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn,
> chan->omtu = mtu;
> chan->remote_mps = mps;
>
> - __l2cap_chan_add(conn, chan);
> -
> l2cap_ecred_init(chan, __le16_to_cpu(req->credits));
>
> /* Init response */
> @@ -7470,14 +7489,12 @@ static void l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
> goto next;
>
> l2cap_chan_lock(pchan);
> - chan = pchan->ops->new_connection(pchan);
> + chan = l2cap_new_connection(conn, pchan);
> if (chan) {
> bacpy(&chan->src, &hcon->src);
> bacpy(&chan->dst, &hcon->dst);
> chan->src_type = bdaddr_src_type(hcon);
> chan->dst_type = dst_type;
> -
> - __l2cap_chan_add(conn, chan);
> }
>
> l2cap_chan_unlock(pchan);
> diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
> index 025329636353..87f4c0db5c0c 100644
> --- a/net/bluetooth/l2cap_sock.c
> +++ b/net/bluetooth/l2cap_sock.c
> @@ -46,7 +46,8 @@ static struct bt_sock_list l2cap_sk_list = {
> static const struct proto_ops l2cap_sock_ops;
> static void l2cap_sock_init(struct sock *sk, struct sock *parent);
> static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
> - int proto, gfp_t prio, int kern);
> + int proto, gfp_t prio, int kern,
> + struct l2cap_chan *chan);
> static void l2cap_sock_cleanup_listen(struct sock *parent);
>
> bool l2cap_is_socket(struct socket *sock)
> @@ -1287,6 +1288,23 @@ static int l2cap_sock_recvmsg(struct socket *sock, struct msghdr *msg,
> return err;
> }
>
> +/* Release the sock's ref on chan and clear the pointer so that the ref is
> + * dropped exactly once even if both l2cap_sock_kill() and
> + * l2cap_sock_destruct() run. Setting chan->data to NULL first stops any other
> + * task from dereferencing the now-dead sock pointer.
> + */
> +static void l2cap_sock_put_chan(struct sock *sk)
> +{
> + struct l2cap_chan *chan = l2cap_pi(sk)->chan;
> +
> + if (!chan)
> + return;
> +
> + chan->data = NULL;
> + l2cap_pi(sk)->chan = NULL;
> + l2cap_chan_put(chan);
> +}
> +
> /* Kill socket (only if zapped and orphan)
> * Must be called on unlocked socket, with l2cap channel lock.
> */
> @@ -1297,13 +1315,9 @@ static void l2cap_sock_kill(struct sock *sk)
>
> BT_DBG("sk %p state %s", sk, state_to_string(sk->sk_state));
>
> - /* Sock is dead, so set chan data to NULL, avoid other task use invalid
> - * sock pointer.
> - */
> - l2cap_pi(sk)->chan->data = NULL;
> - /* Kill poor orphan */
> + l2cap_sock_put_chan(sk);
>
> - l2cap_chan_put(l2cap_pi(sk)->chan);
> + /* Kill poor orphan */
> sock_set_flag(sk, SOCK_DEAD);
> sock_put(sk);
> }
> @@ -1546,12 +1560,14 @@ static void l2cap_sock_cleanup_listen(struct sock *parent)
> }
> }
>
> -static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan)
> +static int l2cap_sock_new_connection_cb(struct l2cap_conn *conn,
> + struct l2cap_chan *chan,
> + struct l2cap_chan *new_chan)
> {
> struct sock *sk, *parent = chan->data;
>
> if (!parent)
> - return NULL;
> + return -EINVAL;
>
> lock_sock(parent);
>
> @@ -1559,25 +1575,33 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan)
> if (sk_acceptq_is_full(parent)) {
> BT_DBG("backlog full %d", parent->sk_ack_backlog);
> release_sock(parent);
> - return NULL;
> + return -ENOBUFS;
> }
>
> sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP,
> - GFP_ATOMIC, 0);
> + GFP_ATOMIC, 0, new_chan);
> if (!sk) {
> release_sock(parent);
> - return NULL;
> - }
> + return -ENOMEM;
> + }
>
> bt_sock_reclassify_lock(sk, BTPROTO_L2CAP);
>
> l2cap_sock_init(sk, parent);
>
> + /* Link the channel into conn before exposing the new socket via the
> + * accept queue. Once release_sock() below drops the parent lock the
> + * socket may be freed by another task, dropping its reference on
> + * new_chan; the conn list reference taken here keeps new_chan alive so
> + * the caller can safely use it after ->new_connection() returns.
> + */
> + __l2cap_chan_add(conn, new_chan);
> +
> bt_accept_enqueue(parent, sk, false);
>
> release_sock(parent);
>
> - return l2cap_pi(sk)->chan;
> + return 0;
> }
>
> static int l2cap_sock_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
> @@ -1874,10 +1898,7 @@ static void l2cap_sock_destruct(struct sock *sk)
>
> BT_DBG("sk %p", sk);
>
> - if (l2cap_pi(sk)->chan) {
> - l2cap_pi(sk)->chan->data = NULL;
> - l2cap_chan_put(l2cap_pi(sk)->chan);
> - }
> + l2cap_sock_put_chan(sk);
>
> list_for_each_entry_safe(rx_busy, next, &l2cap_pi(sk)->rx_busy, list) {
> kfree_skb(rx_busy->skb);
> @@ -1978,10 +1999,10 @@ static struct proto l2cap_proto = {
> };
>
> static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
> - int proto, gfp_t prio, int kern)
> + int proto, gfp_t prio, int kern,
> + struct l2cap_chan *chan)
> {
> struct sock *sk;
> - struct l2cap_chan *chan;
>
> sk = bt_sock_alloc(net, sock, &l2cap_proto, proto, prio, kern);
> if (!sk)
> @@ -1992,16 +2013,7 @@ static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
>
> INIT_LIST_HEAD(&l2cap_pi(sk)->rx_busy);
>
> - chan = l2cap_chan_create();
> - if (!chan) {
> - sk_free(sk);
> - if (sock)
> - sock->sk = NULL;
> - return NULL;
> - }
> -
> - l2cap_chan_hold(chan);
> -
> + /* The sock takes ownership of the caller's reference on chan. */
> l2cap_pi(sk)->chan = chan;
>
> return sk;
> @@ -2011,6 +2023,7 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol,
> int kern)
> {
> struct sock *sk;
> + struct l2cap_chan *chan;
>
> BT_DBG("sock %p", sock);
>
> @@ -2025,10 +2038,16 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol,
>
> sock->ops = &l2cap_sock_ops;
>
> - sk = l2cap_sock_alloc(net, sock, protocol, GFP_ATOMIC, kern);
> - if (!sk)
> + chan = l2cap_chan_create();
> + if (!chan)
> return -ENOMEM;
>
> + sk = l2cap_sock_alloc(net, sock, protocol, GFP_ATOMIC, kern, chan);
> + if (!sk) {
> + l2cap_chan_put(chan);
> + return -ENOMEM;
> + }
> +
> l2cap_sock_init(sk, NULL);
> bt_sock_link(&l2cap_sk_list, sk);
> return 0;
> diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
> index 1739c1989dbd..2d31c3c7bbc0 100644
> --- a/net/bluetooth/smp.c
> +++ b/net/bluetooth/smp.c
> @@ -3204,16 +3204,12 @@ static const struct l2cap_ops smp_chan_ops = {
> .get_sndtimeo = l2cap_chan_no_get_sndtimeo,
> };
>
> -static inline struct l2cap_chan *smp_new_conn_cb(struct l2cap_chan *pchan)
> +static inline int smp_new_conn_cb(struct l2cap_conn *conn,
> + struct l2cap_chan *pchan,
> + struct l2cap_chan *chan)
> {
> - struct l2cap_chan *chan;
> -
> BT_DBG("pchan %p", pchan);
>
> - chan = l2cap_chan_create();
> - if (!chan)
> - return NULL;
> -
> chan->chan_type = pchan->chan_type;
> chan->ops = &smp_chan_ops;
> chan->scid = pchan->scid;
> @@ -3229,9 +3225,12 @@ static inline struct l2cap_chan *smp_new_conn_cb(struct l2cap_chan *pchan)
> */
> atomic_set(&chan->nesting, L2CAP_NESTING_SMP);
>
> - BT_DBG("created chan %p", chan);
> + /* Take the conn list reference; see l2cap_new_connection(). */
> + __l2cap_chan_add(conn, chan);
>
> - return chan;
> + BT_DBG("initialised chan %p", chan);
> +
> + return 0;
> }
>
> static const struct l2cap_ops smp_root_chan_ops = {
> --
> 2.54.0
>
--
Luiz Augusto von Dentz
^ permalink raw reply
* Re: [PATCH v3 RESEND 3/5] Bluetooth: btusb: fix wakeup source leak on probe failure
From: Luiz Augusto von Dentz @ 2026-06-03 18:06 UTC (permalink / raw)
To: Johan Hovold
Cc: Marcel Holtmann, linux-bluetooth, linux-kernel, stable,
Rajat Jain
In-Reply-To: <20260603143643.2514595-4-johan@kernel.org>
Hi Johan,
On Wed, Jun 3, 2026 at 10:37 AM Johan Hovold <johan@kernel.org> wrote:
>
> Make sure to disable wakeup on probe failure to avoid leaking the wakeup
> source.
>
> Fixes: fd913ef7ce61 ("Bluetooth: btusb: Add out-of-band wakeup support")
> Cc: stable@vger.kernel.org # 4.11
> Cc: Rajat Jain <rajatja@google.com>
> Signed-off-by: Johan Hovold <johan@kernel.org>
> ---
> drivers/bluetooth/btusb.c | 5 ++++-
> 1 file changed, 4 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
> index d0a83a1ffdf2..622df2fff497 100644
> --- a/drivers/bluetooth/btusb.c
> +++ b/drivers/bluetooth/btusb.c
> @@ -4181,7 +4181,7 @@ static int btusb_probe(struct usb_interface *intf,
> if (id->driver_info & BTUSB_MARVELL && data->oob_wake_irq) {
> err = marvell_config_oob_wake(hdev);
> if (err)
> - goto out_free_dev;
> + goto err_disable_wakeup;
> }
> #endif
> if (id->driver_info & BTUSB_CW6622)
> @@ -4427,6 +4427,9 @@ static int btusb_probe(struct usb_interface *intf,
> }
> err_kill_tx_urbs:
> usb_kill_anchored_urbs(&data->tx_anchor);
> +err_disable_wakeup:
> + if (data->oob_wake_irq)
> + device_init_wakeup(&data->udev->dev, false);
> out_free_dev:
> if (data->reset_gpio)
> gpiod_put(data->reset_gpio);
> --
> 2.53.0
This seem to trigger a compilation problem according to sashiko:
The goto statement targeting err_disable_wakeup is wrapped in an ifdef
CONFIG_PM block earlier in the function, but this label is defined
unconditionally here.
[]https://sashiko.dev/#/patchset/20260603143643.2514595-1-johan%40kernel.org
--
Luiz Augusto von Dentz
^ permalink raw reply
* Re: [PATCH] Bluetooth: btmtk: Disable remote wakeup for MT7922/MT7925
From: patchwork-bot+bluetooth @ 2026-06-03 17:50 UTC (permalink / raw)
To: Rong Zhang
Cc: marcel, luiz.dentz, matthias.bgg, angelogioacchino.delregno,
linux-bluetooth, linux-kernel, linux-arm-kernel, linux-mediatek
In-Reply-To: <20260603-btmtk-remote-wakeup-v1-1-5c1006442f36@rong.moe>
Hello:
This patch was applied to bluetooth/bluetooth-next.git (master)
by Luiz Augusto von Dentz <luiz.von.dentz@intel.com>:
On Wed, 03 Jun 2026 02:38:10 +0800 you wrote:
> These NICs are often reported to lose their Bluetooth interfaces, i.e,
> their USB interfaces suddenly become completely unresponsive, causing
> the USB core to reset them, only to find that they are no longer
> accessible. A power cycle is required to make the Bluetooth interfaces
> recover.
>
> After some investigations, I found that their USB autosuspend remote
> wakeup capabilities are so broken that they are precisely the culprit
> behind the issue:
>
> [...]
Here is the summary with links:
- Bluetooth: btmtk: Disable remote wakeup for MT7922/MT7925
https://git.kernel.org/bluetooth/bluetooth-next/c/247570151789
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH v4] Bluetooth: hci_core: Fix UAF in hci_unregister_dev()
From: patchwork-bot+bluetooth @ 2026-06-03 17:50 UTC (permalink / raw)
To: Jordan Walters; +Cc: linux-bluetooth, linux-kernel
In-Reply-To: <20260603085047.256779-1-jaggyaur@gmail.com>
Hello:
This patch was applied to bluetooth/bluetooth-next.git (master)
by Luiz Augusto von Dentz <luiz.von.dentz@intel.com>:
On Wed, 3 Jun 2026 04:50:47 -0400 you wrote:
> hci_unregister_dev() does not disable cmd_timer and ncmd_timer
> before the hci_dev structure is freed. If a timeout fires
> during device teardown, the callback dereferences freed memory
> (including the hdev->reset function pointer), leading to a
> use-after-free.
>
> Add disable_delayed_work_sync() calls alongside the existing
> disable_work_sync() calls to ensure both timers are fully
> quiesced before teardown proceeds.
>
> [...]
Here is the summary with links:
- [v4] Bluetooth: hci_core: Fix UAF in hci_unregister_dev()
https://git.kernel.org/bluetooth/bluetooth-next/c/eec3deaeaafe
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH v2] Bluetooth: eir: Fix stack OOB write when prepending the Flags AD
From: patchwork-bot+bluetooth @ 2026-06-03 17:50 UTC (permalink / raw)
To: Weiming Shi
Cc: marcel, luiz.dentz, armansito, linux-bluetooth, linux-kernel,
xmei5
In-Reply-To: <20260602170621.1454711-2-bestswngs@gmail.com>
Hello:
This patch was applied to bluetooth/bluetooth-next.git (master)
by Luiz Augusto von Dentz <luiz.von.dentz@intel.com>:
On Wed, 3 Jun 2026 01:06:21 +0800 you wrote:
> eir_create_adv_data() builds the advertising data into a fixed-size
> buffer ("size", 31 for the legacy path). It may prepend a 3-byte "Flags"
> AD structure (LE_AD_NO_BREDR on an LE-only controller) and then copies
> the per-instance data without checking that it still fits:
>
> memcpy(ptr, adv->adv_data, adv->adv_data_len);
>
> [...]
Here is the summary with links:
- [v2] Bluetooth: eir: Fix stack OOB write when prepending the Flags AD
https://git.kernel.org/bluetooth/bluetooth-next/c/83dc982fad52
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH v2] Bluetooth: hci_event: fix simultaneous discovery stuck in FINDING
From: patchwork-bot+bluetooth @ 2026-06-03 17:50 UTC (permalink / raw)
To: Jiajia Liu
Cc: marcel, luiz.dentz, brian.gix, linux-bluetooth, linux-kernel,
liujia6264
In-Reply-To: <20260602070032.51248-1-liujiajia@kylinos.cn>
Hello:
This patch was applied to bluetooth/bluetooth-next.git (master)
by Luiz Augusto von Dentz <luiz.von.dentz@intel.com>:
On Tue, 2 Jun 2026 15:00:32 +0800 you wrote:
> When hci_inquiry_complete_evt is called between le_scan_disable and
> le_set_scan_enable_complete and no remote name needs to be resolved,
> the interleaved discovery with SIMULTANEOUS quirk gets stuck in
> DISCOVERY_FINDING. le_set_scan_enable_complete does not check inquiry
> state. No one sets DISCOVERY_STOPPED in this process.
>
> Add state check in le_set_scan_enable_complete and change state if
> the state is DISCOVERY_FINDING. Tested with AX201 (8087:0026) in Dell
> Vostro 13. Discovering disabled MGMT Event below is reported when
> running into the above condition.
>
> [...]
Here is the summary with links:
- [v2] Bluetooth: hci_event: fix simultaneous discovery stuck in FINDING
https://git.kernel.org/bluetooth/bluetooth-next/c/ad85ec7a145b
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH v2] Bluetooth: btusb: Add support for TP-Link TL-UB250
From: patchwork-bot+bluetooth @ 2026-06-03 17:50 UTC (permalink / raw)
To: Cris; +Cc: linux-bluetooth, marcel, luiz.dentz, linux-kernel, pmenzel
In-Reply-To: <20260603035818.926654-1-cxs1494089474@gmail.com>
Hello:
This patch was applied to bluetooth/bluetooth-next.git (master)
by Luiz Augusto von Dentz <luiz.von.dentz@intel.com>:
On Wed, 3 Jun 2026 11:58:18 +0800 you wrote:
> Add USB ID 2357:0607 for TP-Link TL-UB250.
>
> This is a Realtek RTL8761BUV based Bluetooth adapter.
>
> Without this entry the device is picked up by the generic Bluetooth USB
> class match and exposes hci0, but the Realtek setup path is not used and
> rtl8761bu firmware/config are not loaded.
>
> [...]
Here is the summary with links:
- [v2] Bluetooth: btusb: Add support for TP-Link TL-UB250
https://git.kernel.org/bluetooth/bluetooth-next/c/7e7dff125429
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* RE: Bluetooth: L2CAP: Fix use-after-free in l2cap_sock_new_connection_cb()
From: bluez.test.bot @ 2026-06-03 17:40 UTC (permalink / raw)
To: linux-bluetooth, oss
In-Reply-To: <20260603150835.3539963-2-oss@fourdim.xyz>
[-- Attachment #1: Type: text/plain, Size: 1150 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=1105389
---Test result---
Test Summary:
CheckPatch PASS 1.55 seconds
VerifyFixes PASS 0.10 seconds
VerifySignedoff PASS 0.07 seconds
GitLint PASS 0.32 seconds
SubjectPrefix PASS 0.07 seconds
BuildKernel PASS 26.94 seconds
CheckAllWarning PASS 29.57 seconds
CheckSparse PASS 27.88 seconds
BuildKernel32 PASS 26.28 seconds
TestRunnerSetup PASS 580.30 seconds
TestRunner_l2cap-tester PASS 60.03 seconds
TestRunner_smp-tester PASS 23.45 seconds
TestRunner_6lowpan-tester PASS 22.67 seconds
IncrementalBuild PASS 25.76 seconds
https://github.com/bluez/bluetooth-next/pull/283
---
Regards,
Linux Bluetooth
^ permalink raw reply
* RE: [v2] Bluetooth: btintel_pcie: Add support for smart trigger dump
From: bluez.test.bot @ 2026-06-03 17:38 UTC (permalink / raw)
To: linux-bluetooth, kiran.k
In-Reply-To: <20260603155415.50855-1-kiran.k@intel.com>
[-- Attachment #1: Type: text/plain, Size: 988 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=1105419
---Test result---
Test Summary:
CheckPatch PASS 1.08 seconds
VerifyFixes PASS 0.08 seconds
VerifySignedoff PASS 0.07 seconds
GitLint PASS 0.70 seconds
SubjectPrefix PASS 0.07 seconds
BuildKernel PASS 26.31 seconds
CheckAllWarning PASS 29.14 seconds
CheckSparse PASS 27.48 seconds
BuildKernel32 PASS 25.47 seconds
TestRunnerSetup PASS 569.85 seconds
IncrementalBuild PASS 25.57 seconds
https://github.com/bluez/bluetooth-next/pull/284
---
Regards,
Linux Bluetooth
^ permalink raw reply
* RE: Bluetooth: L2CAP: Fix UAF in l2cap_chan_timeout
From: bluez.test.bot @ 2026-06-03 17:37 UTC (permalink / raw)
To: linux-bluetooth, elver
In-Reply-To: <20260603123111.2334409-1-elver@google.com>
[-- Attachment #1: Type: text/plain, Size: 3532 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=1105291
---Test result---
Test Summary:
CheckPatch FAIL 0.97 seconds
VerifyFixes PASS 0.13 seconds
VerifySignedoff PASS 0.13 seconds
GitLint FAIL 0.33 seconds
SubjectPrefix PASS 0.12 seconds
BuildKernel PASS 25.11 seconds
CheckAllWarning PASS 28.16 seconds
CheckSparse PASS 26.76 seconds
BuildKernel32 PASS 24.42 seconds
TestRunnerSetup PASS 524.47 seconds
TestRunner_l2cap-tester PASS 58.55 seconds
IncrementalBuild PASS 23.63 seconds
Details
##############################
Test: CheckPatch - FAIL
Desc: Run checkpatch.pl script
Output:
Bluetooth: L2CAP: Fix UAF in l2cap_chan_timeout
WARNING: Prefer a maximum 75 chars per line (possible unwrapped commit description?)
#98:
| BUG: KASAN: slab-use-after-free in instrument_atomic_read_write include/linux/instrumented.h:112 [inline]
ERROR: Unrecognized email address: 'https://sashiko.dev/#/patchset/20260521021249.3258069-1-oss%40fourdim.xyz'
#191:
Reported-by: https://sashiko.dev/#/patchset/20260521021249.3258069-1-oss%40fourdim.xyz
WARNING: Reported-by: should be immediately followed by Closes: with a URL to the report
#191:
Reported-by: https://sashiko.dev/#/patchset/20260521021249.3258069-1-oss%40fourdim.xyz
Signed-off-by: Marco Elver <elver@google.com>
total: 1 errors, 2 warnings, 0 checks, 89 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/14609364.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: L2CAP: Fix UAF in l2cap_chan_timeout
9: B1 Line exceeds max length (107>80): "| BUG: KASAN: slab-use-after-free in instrument_atomic_read_write include/linux/instrumented.h:112 [inline]"
10: B1 Line exceeds max length (125>80): "| BUG: KASAN: slab-use-after-free in atomic_long_try_cmpxchg_acquire include/linux/atomic/atomic-instrumented.h:4456 [inline]"
11: B1 Line exceeds max length (93>80): "| BUG: KASAN: slab-use-after-free in __mutex_trylock_fast kernel/locking/mutex.c:161 [inline]"
12: B1 Line exceeds max length (84>80): "| BUG: KASAN: slab-use-after-free in mutex_lock+0x4f/0xa0 kernel/locking/mutex.c:318"
15: B1 Line exceeds max length (100>80): "| CPU: 2 UID: 0 PID: 83 Comm: kworker/2:1 Not tainted 7.1.0-rc6-next-20260601-dirty #6 PREEMPT(full)"
16: B1 Line exceeds max length (95>80): "| Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.17.0-debian-1.17.0-1 04/01/2014"
21: B1 Line exceeds max length (91>80): "| atomic_long_try_cmpxchg_acquire include/linux/atomic/atomic-instrumented.h:4456 [inline]"
62: B1 Line exceeds max length (82>80): "| syscall_exit_to_user_mode_prepare include/linux/irq-entry-common.h:230 [inline]"
https://github.com/bluez/bluetooth-next/pull/281
---
Regards,
Linux Bluetooth
^ permalink raw reply
* RE: Bluetooth: btusb: fix wakeup irq devres lifetime
From: bluez.test.bot @ 2026-06-03 17:36 UTC (permalink / raw)
To: linux-bluetooth, johan
In-Reply-To: <20260603143643.2514595-2-johan@kernel.org>
[-- Attachment #1: Type: text/plain, Size: 2427 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=1105369
---Test result---
Test Summary:
CheckPatch FAIL 3.64 seconds
VerifyFixes PASS 0.14 seconds
VerifySignedoff PASS 0.14 seconds
GitLint FAIL 1.87 seconds
SubjectPrefix PASS 0.66 seconds
BuildKernel PASS 25.55 seconds
CheckAllWarning PASS 29.82 seconds
CheckSparse PASS 28.84 seconds
BuildKernel32 PASS 26.46 seconds
TestRunnerSetup PASS 529.51 seconds
IncrementalBuild PASS 34.26 seconds
Details
##############################
Test: CheckPatch - FAIL
Desc: Run checkpatch.pl script
Output:
[v3,RESEND,4/5] Bluetooth: btusb: fix wakeup irq devres lifetime
ERROR: Please use git commit description style 'commit <12+ chars of sha1> ("<title line>")' - ie: 'commit 699fb50d9903 ("drivers: base: Free devm resources when unregistering a device")'
#82:
[1] 699fb50d9903 ("drivers: base: Free devm resources when unregistering
total: 1 errors, 0 warnings, 51 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/14609619.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:
[v3,RESEND,1/5] Bluetooth: btusb: fix use-after-free on registration failure
10: B3 Line contains hard tab characters (\t): "Cc: stable@vger.kernel.org # 2.6.27"
[v3,RESEND,2/5] Bluetooth: btusb: fix use-after-free on marvell probe failure
10: B3 Line contains hard tab characters (\t): "Cc: stable@vger.kernel.org # 4.11"
[v3,RESEND,3/5] Bluetooth: btusb: fix wakeup source leak on probe failure
6: B3 Line contains hard tab characters (\t): "Cc: stable@vger.kernel.org # 4.11"
https://github.com/bluez/bluetooth-next/pull/282
---
Regards,
Linux Bluetooth
^ permalink raw reply
* Re: [PATCH] Bluetooth: L2CAP: Fix UAF in l2cap_chan_timeout
From: Luiz Augusto von Dentz @ 2026-06-03 17:31 UTC (permalink / raw)
To: Marco Elver
Cc: Marcel Holtmann, linux-bluetooth, linux-kernel, kasan-dev, stable,
Siwei Zhang, Luiz Augusto von Dentz
In-Reply-To: <CANpmjNPQCx8rynFhOUfqgagP-KBh0pJsXz6PQt6G3LomdzVJYw@mail.gmail.com>
Hi Marco,
On Wed, Jun 3, 2026 at 9:16 AM Marco Elver <elver@google.com> wrote:
>
> On Wed, 3 Jun 2026 at 14:31, Marco Elver <elver@google.com> wrote:
> >
> > l2cap_chan_timeout() accesses chan->conn without holding a reference to
> > the connection object. If l2cap_conn_del() races and tears down the
> > connection while the timer is waiting for locks, it can result in a
> > use-after-free when the timer wakes up and attempts to acquire
> > conn->lock:
> >
> > | BUG: KASAN: slab-use-after-free in instrument_atomic_read_write include/linux/instrumented.h:112 [inline]
> > | BUG: KASAN: slab-use-after-free in atomic_long_try_cmpxchg_acquire include/linux/atomic/atomic-instrumented.h:4456 [inline]
> > | BUG: KASAN: slab-use-after-free in __mutex_trylock_fast kernel/locking/mutex.c:161 [inline]
> > | BUG: KASAN: slab-use-after-free in mutex_lock+0x4f/0xa0 kernel/locking/mutex.c:318
> > | Write of size 8 at addr ffff8881298d9550 by task kworker/2:1/83
> > |
> > | CPU: 2 UID: 0 PID: 83 Comm: kworker/2:1 Not tainted 7.1.0-rc6-next-20260601-dirty #6 PREEMPT(full)
> > | Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.17.0-debian-1.17.0-1 04/01/2014
> > | Workqueue: events l2cap_chan_timeout
> > | Call Trace:
> > | <TASK>
> > | instrument_atomic_read_write include/linux/instrumented.h:112 [inline]
> > | atomic_long_try_cmpxchg_acquire include/linux/atomic/atomic-instrumented.h:4456 [inline]
> > | __mutex_trylock_fast kernel/locking/mutex.c:161 [inline]
> > | mutex_lock+0x4f/0xa0 kernel/locking/mutex.c:318
> > | l2cap_chan_timeout+0x5d/0x1b0 net/bluetooth/l2cap_core.c:422
> > | process_one_work kernel/workqueue.c:3326 [inline]
> > | process_scheduled_works+0x7c8/0xfb0 kernel/workqueue.c:3409
> > | worker_thread+0x8a9/0xcf0 kernel/workqueue.c:3490
> > | kthread+0x346/0x430 kernel/kthread.c:436
> > | ret_from_fork+0x1a3/0x470 arch/x86/kernel/process.c:158
> > | ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
> > | </TASK>
> > |
> > | Allocated by task 320:
> > | l2cap_conn_add+0xa7/0x820 net/bluetooth/l2cap_core.c:7075
> > | l2cap_connect_cfm+0xdb/0xd70 net/bluetooth/l2cap_core.c:7452
> > | hci_connect_cfm include/net/bluetooth/hci_core.h:2139 [inline]
> > | hci_remote_features_evt+0x52f/0x9f0 net/bluetooth/hci_event.c:3760
> > | hci_event_func net/bluetooth/hci_event.c:7796 [inline]
> > | hci_event_packet+0x561/0xa70 net/bluetooth/hci_event.c:7847
> > | hci_rx_work+0x370/0x890 net/bluetooth/hci_core.c:4040
> > | process_one_work kernel/workqueue.c:3326 [inline]
> > | process_scheduled_works+0x7c8/0xfb0 kernel/workqueue.c:3409
> > | worker_thread+0x8a9/0xcf0 kernel/workqueue.c:3490
> > | kthread+0x346/0x430 kernel/kthread.c:436
> > | ret_from_fork+0x1a3/0x470 arch/x86/kernel/process.c:158
> > | ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
> > |
> > | Freed by task 322:
> > | hci_disconn_cfm include/net/bluetooth/hci_core.h:2154 [inline]
> > | hci_conn_hash_flush+0x101/0x1f0 net/bluetooth/hci_conn.c:2736
> > | hci_dev_close_sync+0x889/0xde0 net/bluetooth/hci_sync.c:5405
> > | hci_dev_do_close net/bluetooth/hci_core.c:502 [inline]
> > | hci_unregister_dev+0x1f7/0x370 net/bluetooth/hci_core.c:2679
> > | vhci_release+0x12a/0x180 drivers/bluetooth/hci_vhci.c:690
> > | __fput+0x369/0x890 fs/file_table.c:510
> > | task_work_run+0x160/0x1d0 kernel/task_work.c:233
> > | get_signal+0xf5b/0x1120 kernel/signal.c:2810
> > | arch_do_signal_or_restart+0x4d/0x600 arch/x86/kernel/signal.c:337
> > | __exit_to_user_mode_loop kernel/entry/common.c:64 [inline]
> > | exit_to_user_mode_loop+0x85/0x510 kernel/entry/common.c:98
> > | __exit_to_user_mode_prepare include/linux/irq-entry-common.h:207 [inline]
> > | syscall_exit_to_user_mode_prepare include/linux/irq-entry-common.h:230 [inline]
> > | syscall_exit_to_user_mode include/linux/entry-common.h:318 [inline]
> > | do_syscall_64+0x263/0x3d0 arch/x86/entry/syscall_64.c:100
> > | entry_SYSCALL_64_after_hwframe+0x77/0x7f
> > |
> > | Last potentially related work creation:
> > | hci_connect_cfm include/net/bluetooth/hci_core.h:2139 [inline]
> > | hci_remote_features_evt+0x52f/0x9f0 net/bluetooth/hci_event.c:3760
> > | hci_event_func net/bluetooth/hci_event.c:7796 [inline]
> > | hci_event_packet+0x561/0xa70 net/bluetooth/hci_event.c:7847
> > | hci_rx_work+0x370/0x890 net/bluetooth/hci_core.c:4040
> > | process_one_work kernel/workqueue.c:3326 [inline]
> > | process_scheduled_works+0x7c8/0xfb0 kernel/workqueue.c:3409
> > | worker_thread+0x8a9/0xcf0 kernel/workqueue.c:3490
> > | kthread+0x346/0x430 kernel/kthread.c:436
> > | ret_from_fork+0x1a3/0x470 arch/x86/kernel/process.c:158
> > | ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
> > |
> > | The buggy address belongs to the object at ffff8881298d9400
> > | which belongs to the cache kmalloc-512 of size 512
> > | The buggy address is located 336 bytes inside of
> > | freed 512-byte region [ffff8881298d9400, ffff8881298d9600)
> >
> > Fix it by holding a reference to the connection when the channel timer
> > is scheduled, and releasing it when the timer is either canceled or
> > executes to completion.
> >
> > Since l2cap_chan_del() nullifies chan->conn to disassociate the channel
> > during teardown, the timer handler might read NULL from chan->conn even
> > if it held a reference. To address this, introduce a `timer_conn` field
> > to `struct l2cap_chan` to store the connection pointer associated with
> > the active timer. The timer handler uses this field to acquire locks and
> > release the connection reference, and skips channel closing operations
> > if chan->conn has already been nullified by teardown.
> >
> > Fixes: 75780ca4c6a8 ("Bluetooth: L2CAP: use chan timer to close channels in cleanup_listen()")
> > Cc: <stable@vger.kernel.org>
> > Cc: Siwei Zhang <oss@fourdim.xyz>
> > Cc: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
> > Assisted-by: Gemini:gemini-3.1-pro-preview
> > Reported-by: https://sashiko.dev/#/patchset/20260521021249.3258069-1-oss%40fourdim.xyz
> > Signed-off-by: Marco Elver <elver@google.com>
>
> Sigh, Sashiko points out more problems here:
> https://sashiko.dev/#/patchset/20260603123111.2334409-1-elver%40google.com
>
> > Can this lockless read of chan->timer_conn cause a use-after-free or double
> > free if another thread re-arms the timer concurrently?
>
> I haven't analyzed this further yet, so consider this patch a
> bug-report-only. If anyone finds a better fix sooner, please go ahead.
I was thinking or something like the following:
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index c4ccfbda9d78..dfe9318272f3 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -406,17 +406,39 @@ static void l2cap_chan_timeout(struct work_struct *work)
{
struct l2cap_chan *chan = container_of(work, struct l2cap_chan,
chan_timer.work);
- struct l2cap_conn *conn = chan->conn;
+ struct l2cap_conn *conn;
int reason;
BT_DBG("chan %p state %s", chan, state_to_string(chan->state));
+ /* Hold a reference to the connection while we are processing this
+ * timeout, to prevent it from being freed out from under us by
+ * l2cap_conn_del().
+ */
+ conn = l2cap_conn_hold_unless_zero(chan->conn);
if (!conn) {
l2cap_chan_put(chan);
return;
}
mutex_lock(&conn->lock);
+
+ /* If l2cap_chan_del() was called while waiting for conn->lock the
+ * channel shall be considered already closed and its last reference
+ * shall be released with l2cap_chan_put(chan) here.
+ *
+ * l2cap_conn_del() doesn't wait the channel's works and instead just
+ * leaves the timer reference behind which needs to be released here in
+ * order to free the channel and then l2cap_conn_put() to finally free
+ * the connection.
+ */
+ if (!chan->conn) {
+ mutex_unlock(&conn->lock);
+ l2cap_chan_put(chan);
+ l2cap_conn_put(conn);
+ return;
+ }
+
/* __set_chan_timer() calls l2cap_chan_hold(chan) while scheduling
* this work. No need to call l2cap_chan_hold(chan) here again.
*/
@@ -438,6 +460,8 @@ static void l2cap_chan_timeout(struct work_struct *work)
l2cap_chan_put(chan);
mutex_unlock(&conn->lock);
+
+ l2cap_conn_put(conn);
}
struct l2cap_chan *l2cap_chan_create(void)
> > ---
> > include/net/bluetooth/l2cap.h | 18 ++++++++++++++++--
> > net/bluetooth/l2cap_core.c | 26 +++++++++++++++-----------
> > 2 files changed, 31 insertions(+), 13 deletions(-)
> >
> > diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
> > index e0a1f2293679..83719777512e 100644
> > --- a/include/net/bluetooth/l2cap.h
> > +++ b/include/net/bluetooth/l2cap.h
> > @@ -514,6 +514,7 @@ struct l2cap_seq_list {
> >
> > struct l2cap_chan {
> > struct l2cap_conn *conn;
> > + struct l2cap_conn *timer_conn; /* for chan_timer */
> > struct kref kref;
> > atomic_t nesting;
> >
> > @@ -835,6 +836,9 @@ static inline void l2cap_chan_unlock(struct l2cap_chan *chan)
> > mutex_unlock(&chan->lock);
> > }
> >
> > +struct l2cap_conn *l2cap_conn_get(struct l2cap_conn *conn);
> > +void l2cap_conn_put(struct l2cap_conn *conn);
> > +
> > static inline void l2cap_set_timer(struct l2cap_chan *chan,
> > struct delayed_work *work, long timeout)
> > {
> > @@ -843,8 +847,13 @@ static inline void l2cap_set_timer(struct l2cap_chan *chan,
> >
> > /* If delayed work cancelled do not hold(chan)
> > since it is already done with previous set_timer */
> > - if (!cancel_delayed_work(work))
> > + if (!cancel_delayed_work(work)) {
> > l2cap_chan_hold(chan);
> > + if (work == &chan->chan_timer && chan->conn) {
> > + l2cap_conn_get(chan->conn);
> > + chan->timer_conn = chan->conn;
> > + }
> > + }
> >
> > schedule_delayed_work(work, timeout);
> > }
> > @@ -857,8 +866,13 @@ static inline bool l2cap_clear_timer(struct l2cap_chan *chan,
> > /* put(chan) if delayed work cancelled otherwise it
> > is done in delayed work function */
> > ret = cancel_delayed_work(work);
> > - if (ret)
> > + if (ret) {
> > + if (work == &chan->chan_timer && chan->timer_conn) {
> > + l2cap_conn_put(chan->timer_conn);
> > + chan->timer_conn = NULL;
> > + }
> > l2cap_chan_put(chan);
> > + }
> >
> > return ret;
> > }
> > diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
> > index c4ccfbda9d78..491b03bf6903 100644
> > --- a/net/bluetooth/l2cap_core.c
> > +++ b/net/bluetooth/l2cap_core.c
> > @@ -406,7 +406,7 @@ static void l2cap_chan_timeout(struct work_struct *work)
> > {
> > struct l2cap_chan *chan = container_of(work, struct l2cap_chan,
> > chan_timer.work);
> > - struct l2cap_conn *conn = chan->conn;
> > + struct l2cap_conn *conn = chan->timer_conn;
> > int reason;
> >
> > BT_DBG("chan %p state %s", chan, state_to_string(chan->state));
> > @@ -421,23 +421,27 @@ static void l2cap_chan_timeout(struct work_struct *work)
> > * this work. No need to call l2cap_chan_hold(chan) here again.
> > */
> > l2cap_chan_lock(chan);
> > + chan->timer_conn = NULL;
> > +
> > + if (chan->conn) {
> > + if (chan->state == BT_CONNECTED || chan->state == BT_CONFIG)
> > + reason = ECONNREFUSED;
> > + else if (chan->state == BT_CONNECT &&
> > + chan->sec_level != BT_SECURITY_SDP)
> > + reason = ECONNREFUSED;
> > + else
> > + reason = ETIMEDOUT;
> >
> > - if (chan->state == BT_CONNECTED || chan->state == BT_CONFIG)
> > - reason = ECONNREFUSED;
> > - else if (chan->state == BT_CONNECT &&
> > - chan->sec_level != BT_SECURITY_SDP)
> > - reason = ECONNREFUSED;
> > - else
> > - reason = ETIMEDOUT;
> > -
> > - l2cap_chan_close(chan, reason);
> > + l2cap_chan_close(chan, reason);
> >
> > - chan->ops->close(chan);
> > + chan->ops->close(chan);
> > + }
> >
> > l2cap_chan_unlock(chan);
> > l2cap_chan_put(chan);
> >
> > mutex_unlock(&conn->lock);
> > + l2cap_conn_put(conn);
> > }
> >
> > struct l2cap_chan *l2cap_chan_create(void)
> > --
> > 2.54.0.1013.g208068f2d8-goog
> >
--
Luiz Augusto von Dentz
^ permalink raw reply related
* [GIT PULL] bluetooth 2026-06-03
From: Luiz Augusto von Dentz @ 2026-06-03 16:27 UTC (permalink / raw)
To: davem, kuba; +Cc: linux-bluetooth, netdev
The following changes since commit cdf88b35e06f1b385f7f6228060ae541d44fbb72:
Bluetooth: hci_sync: Reset device counters in hci_dev_close_sync() (2026-05-28 08:52:21 -0400)
are available in the Git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth.git tags/for-net-2026-06-03
for you to fetch changes up to 149324fc762c2a7acef9c26790566f81f475e51f:
Bluetooth: MGMT: Fix backward compatibility with userspace (2026-06-03 11:24:12 -0400)
----------------------------------------------------------------
bluetooth pull request for net:
- hci_core: fix memory leak in error path of hci_alloc_dev()
- hci_sync: reject oversized Broadcast Announcement prepend
- MGMT: Fix backward compatibility with userspace
- MGMT: validate advertising TLV before type checks
- L2CAP: reject BR/EDR signaling packets over MTUsig
- RFCOMM: validate skb length in MCC handlers
- RFCOMM: hold listener socket in rfcomm_connect_ind()
- ISO: Fix not releasing hdev reference on iso_conn_big_sync
- ISO: Fix a use-after-free of the hci_conn pointer
- ISO: Fix data-race on iso_pi fields in hci_get_route calls
- SCO: Fix data-race on sco_pi fields in sco_connect
- BNEP: reject short frames before parsing
----------------------------------------------------------------
Bharath Reddy (1):
Bluetooth: fix memory leak in error path of hci_alloc_dev()
Luiz Augusto von Dentz (3):
Bluetooth: ISO: Fix not releasing hdev reference on iso_conn_big_sync
Bluetooth: ISO: Fix a use-after-free of the hci_conn pointer
Bluetooth: MGMT: Fix backward compatibility with userspace
Michael Bommarito (1):
Bluetooth: L2CAP: reject BR/EDR signaling packets over MTUsig
SeungJu Cheon (3):
Bluetooth: RFCOMM: validate skb length in MCC handlers
Bluetooth: ISO: Fix data-race on iso_pi fields in hci_get_route calls
Bluetooth: SCO: Fix data-race on sco_pi fields in sco_connect
Yuqi Xu (1):
Bluetooth: hci_sync: reject oversized Broadcast Announcement prepend
Zhang Cen (3):
Bluetooth: RFCOMM: hold listener socket in rfcomm_connect_ind()
Bluetooth: MGMT: validate advertising TLV before type checks
Bluetooth: bnep: reject short frames before parsing
include/net/bluetooth/l2cap.h | 1 +
net/bluetooth/bnep/core.c | 57 ++++++++++++++++++++++--------------
net/bluetooth/hci_sync.c | 5 ++++
net/bluetooth/hci_sysfs.c | 6 ++--
net/bluetooth/iso.c | 63 +++++++++++++++++++++++++++++-----------
net/bluetooth/l2cap_core.c | 46 +++++++++++++++++++++++++++++
net/bluetooth/mgmt.c | 17 +++++------
net/bluetooth/rfcomm/core.c | 67 +++++++++++++++++++++++++++++++------------
net/bluetooth/rfcomm/sock.c | 26 ++++++++++++++---
net/bluetooth/sco.c | 20 +++++++++----
10 files changed, 233 insertions(+), 75 deletions(-)
^ permalink raw reply
* [bluez/bluez]
From: BluezTestBot @ 2026-06-03 15:58 UTC (permalink / raw)
To: linux-bluetooth
Branch: refs/heads/1104846
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
* [bluez/bluez] 2a6968: advertising: Fix sending extra bytes with MGMT_OP_...
From: Luiz Augusto von Dentz @ 2026-06-03 15:57 UTC (permalink / raw)
To: linux-bluetooth
Branch: refs/heads/master
Home: https://github.com/bluez/bluez
Commit: 2a6968b40378dca5650e18e03ad0407738c47be5
https://github.com/bluez/bluez/commit/2a6968b40378dca5650e18e03ad0407738c47be5
Author: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Date: 2026-06-02 (Tue, 02 Jun 2026)
Changed paths:
M src/advertising.c
Log Message:
-----------
advertising: Fix sending extra bytes with MGMT_OP_ADD_EXT_ADV_DATA
MGMT_OP_ADD_EXT_ADV_DATA expects the command to be of size of
struct mgmt_cp_add_ext_adv_data not mgmt_cp_add_advertising.
To unsubscribe from these emails, change your notification settings at https://github.com/bluez/bluez/settings/notifications
^ permalink raw reply
* [PATCH v2] Bluetooth: btintel_pcie: Add support for smart trigger dump
From: Kiran K @ 2026-06-03 15:54 UTC (permalink / raw)
To: linux-bluetooth
Cc: ravishankar.srivatsa, chethan.tumkur.narayan,
chandrashekar.devegowda, Kiran K
Based on the debug configuration, firmware can raise MSI-X interrupt with
firmware trigger cause bit set on specific events like Disconnection,
Connection Timeout, Page Timeout etc.
Upon receiving an MSI-X interrupt with the firmware trigger cause bit
set, the driver performs the following actions:
1. Reads Device Memory: Retrieves data from the device memory,
constructs an HCI diagnostic event, and sends it to the monitor. This
event includes details about the trigger, such as connection timeout or
page timeout.
2. Dumps Device Coredump: Generates a coredump containing firmware
traces for further analysis.
The coredump can be retrieved using:
$ cat /sys/class/devcoredump/devcd*/data > /tmp/btintel_coredump.bin
HCI traces:
= Vendor Diagnostic (len 12)
a5 a5 a5 a5 01 03 00 23 00 01 00 00
Signed-off-by: Kiran K <kiran.k@intel.com>
---
Update in v2:
- commit message update to include HCI traces and coredump retrieval
- convert debug data into hci diagnostic event
drivers/bluetooth/btintel.h | 1 +
drivers/bluetooth/btintel_pcie.c | 164 +++++++++++++++++++++++++++++++
drivers/bluetooth/btintel_pcie.h | 10 +-
3 files changed, 174 insertions(+), 1 deletion(-)
diff --git a/drivers/bluetooth/btintel.h b/drivers/bluetooth/btintel.h
index 70d812ad36a2..7cce1bdebae0 100644
--- a/drivers/bluetooth/btintel.h
+++ b/drivers/bluetooth/btintel.h
@@ -53,6 +53,7 @@ struct intel_tlv {
} __packed;
#define BTINTEL_HCI_OP_RESET 0xfc01
+#define BTINTEL_HCI_OP_DEBUG 0xfcd9
#define BTINTEL_CNVI_BLAZARI 0x900 /* BlazarI - Lunar Lake */
#define BTINTEL_CNVI_BLAZARIW 0x901 /* BlazarIW - Wildcat Lake */
diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c
index eeaefffaaf6b..2baef130d101 100644
--- a/drivers/bluetooth/btintel_pcie.c
+++ b/drivers/bluetooth/btintel_pcie.c
@@ -145,6 +145,22 @@ struct btintel_pcie_dbgc_ctxt {
struct btintel_pcie_dbgc_ctxt_buf bufs[BTINTEL_PCIE_DBGC_BUFFER_COUNT];
};
+struct btintel_pcie_trigger_evt {
+ u8 type;
+ u8 len;
+ __le32 addr;
+ __le32 size;
+} __packed;
+
+struct btintel_pcie_fwtrigger_evt {
+ __le32 reserved;
+ u8 type; /* Debug Trigger event */
+ __le16 len;
+ u8 event_type;
+ __le16 event_id;
+ __le16 reserved2;
+} __packed;
+
static LIST_HEAD(btintel_pcie_recovery_list);
static DEFINE_SPINLOCK(btintel_pcie_recovery_lock);
@@ -696,6 +712,11 @@ static int btintel_pcie_read_dram_buffers(struct btintel_pcie_data *data)
sizeof(*tlv) + strlen(vendor) +
sizeof(*tlv) + strlen(driver);
+ if (data->dmp_hdr.event_type && data->dmp_hdr.event_id) {
+ data_len += sizeof(*tlv) + sizeof(data->dmp_hdr.event_type);
+ data_len += sizeof(*tlv) + sizeof(data->dmp_hdr.event_id);
+ }
+
/*
* sizeof(u32) - signature
* sizeof(data_len) - to store tlv data size
@@ -743,6 +764,17 @@ static int btintel_pcie_read_dram_buffers(struct btintel_pcie_data *data)
p = btintel_pcie_copy_tlv(p, BTINTEL_CNVI_TOP, &data->dmp_hdr.cnvi_top,
sizeof(data->dmp_hdr.cnvi_top));
+ if (data->dmp_hdr.event_type && data->dmp_hdr.event_id) {
+ p = btintel_pcie_copy_tlv(p, BTINTEL_EVENT_TYPE,
+ &data->dmp_hdr.event_type,
+ sizeof(data->dmp_hdr.event_type));
+ p = btintel_pcie_copy_tlv(p, BTINTEL_EVENT_ID,
+ &data->dmp_hdr.event_id,
+ sizeof(data->dmp_hdr.event_id));
+ data->dmp_hdr.event_type = 0;
+ data->dmp_hdr.event_id = 0;
+ }
+
memcpy(p, dbgc->bufs[0].data, dbgc->count * BTINTEL_PCIE_DBGC_BUFFER_SIZE);
dev_coredumpv(&hdev->dev, pdata, dump_size, GFP_KERNEL);
return 0;
@@ -1330,6 +1362,73 @@ static void btintel_pcie_read_hwexp(struct btintel_pcie_data *data)
kfree(buf);
}
+static int btintel_pcie_dump_fwtrigger_event(struct btintel_pcie_data *data)
+{
+ struct btintel_pcie_fwtrigger_evt *evt;
+ struct sk_buff *skb;
+ unsigned int len;
+ int err;
+ u8 *buf;
+
+ if (!data->debug_evt_size || !data->debug_evt_addr)
+ return -EINVAL;
+
+ len = data->debug_evt_size;
+
+ len = ALIGN_DOWN(len, 4);
+
+ if (len < sizeof(*evt) || len > HCI_MAX_EVENT_SIZE) {
+ bt_dev_err(data->hdev, "Invalid FW trigger data size (%u bytes)", len);
+ return -EINVAL;
+ }
+
+ buf = kzalloc(len, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ btintel_pcie_mac_init(data);
+
+ err = btintel_pcie_read_device_mem(data, buf, data->debug_evt_addr,
+ len);
+ if (err)
+ goto exit_on_error;
+
+ evt = (void *)buf;
+ data->dmp_hdr.event_type = evt->event_type;
+ data->dmp_hdr.event_id = le16_to_cpu(evt->event_id);
+
+ bt_dev_dbg(data->hdev, "event type: 0x%2.2x event id: 0x%4.4x len: %u",
+ data->dmp_hdr.event_type, data->dmp_hdr.event_id, len);
+
+ skb = bt_skb_alloc(len, GFP_KERNEL);
+ if (!skb) {
+ err = -ENOMEM;
+ goto exit_on_error;
+ }
+ skb_put_data(skb, buf, len);
+
+ hci_recv_diag(data->hdev, skb);
+ err = 0;
+
+exit_on_error:
+ kfree(buf);
+ return err;
+}
+
+static void btintel_pcie_msix_fw_trigger_handler(struct btintel_pcie_data *data)
+{
+ bt_dev_dbg(data->hdev, "Received firmware smart trigger cause");
+
+ if (test_and_set_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, &data->flags))
+ return;
+
+ /* Trigger device core dump when there is FW assert */
+ if (!test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags))
+ data->dmp_hdr.trigger_reason = BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT;
+
+ queue_work(data->workqueue, &data->rx_work);
+}
+
static void btintel_pcie_msix_hw_exp_handler(struct btintel_pcie_data *data)
{
bt_dev_err(data->hdev, "Received hw exception interrupt");
@@ -1352,6 +1451,14 @@ static void btintel_pcie_rx_work(struct work_struct *work)
struct btintel_pcie_data *data = container_of(work,
struct btintel_pcie_data, rx_work);
struct sk_buff *skb;
+ int err;
+
+ if (test_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, &data->flags)) {
+ err = btintel_pcie_dump_fwtrigger_event(data);
+ if (err)
+ bt_dev_warn(data->hdev, "failed to log fwtrigger event");
+ clear_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, &data->flags);
+ }
if (test_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags)) {
btintel_pcie_dump_traces(data->hdev);
@@ -1509,6 +1616,9 @@ static irqreturn_t btintel_pcie_irq_msix_handler(int irq, void *dev_id)
btintel_pcie_msix_tx_handle(data);
}
+ if (intr_hw & BTINTEL_PCIE_MSIX_HW_INT_CAUSES_FWTRIG)
+ btintel_pcie_msix_fw_trigger_handler(data);
+
/* This interrupt is triggered by the firmware after updating
* boot_stage register and image_response register
*/
@@ -1583,6 +1693,7 @@ static struct btintel_pcie_causes_list causes_list[] = {
{ BTINTEL_PCIE_MSIX_FH_INT_CAUSES_1, BTINTEL_PCIE_CSR_MSIX_FH_INT_MASK, 0x01 },
{ BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0, BTINTEL_PCIE_CSR_MSIX_HW_INT_MASK, 0x20 },
{ BTINTEL_PCIE_MSIX_HW_INT_CAUSES_HWEXP, BTINTEL_PCIE_CSR_MSIX_HW_INT_MASK, 0x23 },
+ { BTINTEL_PCIE_MSIX_HW_INT_CAUSES_FWTRIG, BTINTEL_PCIE_CSR_MSIX_HW_INT_MASK, 0x25 },
};
/* This function configures the interrupt masks for both HW_INT_CAUSES and
@@ -2069,6 +2180,55 @@ static void btintel_pcie_synchronize_irqs(struct btintel_pcie_data *data)
synchronize_irq(data->msix_entries[i].vector);
}
+static int btintel_pcie_get_debug_info_addr(struct hci_dev *hdev)
+{
+ struct btintel_pcie_data *data = hci_get_drvdata(hdev);
+ struct btintel_pcie_trigger_evt *evt;
+ u8 param[1] = {0x10};
+ struct sk_buff *skb;
+ int err = 0;
+
+ skb = __hci_cmd_sync(hdev, BTINTEL_HCI_OP_DEBUG, 1, param,
+ HCI_CMD_TIMEOUT);
+ if (IS_ERR(skb)) {
+ bt_dev_err(hdev, "Reading Intel read debug info address command failed (%ld)",
+ PTR_ERR(skb));
+ /* Not all Intel products supports this command */
+ if (PTR_ERR(skb) == -EOPNOTSUPP)
+ return 0;
+ return PTR_ERR(skb);
+ }
+
+ if (skb->len < (1 + sizeof(*evt))) {
+ bt_dev_err(hdev, "Debug info response too short (%u bytes)", skb->len);
+ err = -EIO;
+ goto exit_error;
+ }
+
+ /* Check the status */
+ if (skb->data[0]) {
+ bt_dev_err(hdev, "Reading Intel read debug info command failed (0x%2.2x)",
+ skb->data[0]);
+ err = -EIO;
+ goto exit_error;
+ }
+
+ /* Consume Command Complete Status field */
+ skb_pull(skb, 1);
+
+ evt = (void *)skb->data;
+
+ data->debug_evt_addr = le32_to_cpu(evt->addr);
+ data->debug_evt_size = le32_to_cpu(evt->size);
+
+ bt_dev_dbg(hdev, "config type: %u config len: %u debug event addr: 0x%8.8x size: 0x%8.8x",
+ evt->type, evt->len, data->debug_evt_addr,
+ data->debug_evt_size);
+exit_error:
+ kfree_skb(skb);
+ return err;
+}
+
static int btintel_pcie_setup_internal(struct hci_dev *hdev)
{
struct btintel_pcie_data *data = hci_get_drvdata(hdev);
@@ -2168,6 +2328,10 @@ static int btintel_pcie_setup_internal(struct hci_dev *hdev)
if (ver_tlv.img_type == 0x02 || ver_tlv.img_type == 0x03)
data->dmp_hdr.fw_git_sha1 = ver_tlv.git_sha1;
+ err = btintel_pcie_get_debug_info_addr(hdev);
+ if (err)
+ goto exit_error;
+
btintel_print_fseq_info(hdev);
exit_error:
kfree_skb(skb);
diff --git a/drivers/bluetooth/btintel_pcie.h b/drivers/bluetooth/btintel_pcie.h
index 7fc8c46ed689..cae84b00a700 100644
--- a/drivers/bluetooth/btintel_pcie.h
+++ b/drivers/bluetooth/btintel_pcie.h
@@ -98,6 +98,7 @@ enum msix_hw_int_causes {
BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0 = BIT(0), /* cause 32 */
BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP1 = BIT(1), /* cause 33 */
BTINTEL_PCIE_MSIX_HW_INT_CAUSES_HWEXP = BIT(3), /* cause 35 */
+ BTINTEL_PCIE_MSIX_HW_INT_CAUSES_FWTRIG = BIT(5), /* cause 37 */
};
/* PCIe device states
@@ -115,6 +116,7 @@ enum {
BTINTEL_PCIE_CORE_HALTED,
BTINTEL_PCIE_HWEXP_INPROGRESS,
BTINTEL_PCIE_COREDUMP_INPROGRESS,
+ BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS,
BTINTEL_PCIE_RECOVERY_IN_PROGRESS,
BTINTEL_PCIE_SETUP_DONE
};
@@ -130,7 +132,9 @@ enum btintel_pcie_tlv_type {
BTINTEL_DUMP_TIME,
BTINTEL_FW_BUILD,
BTINTEL_VENDOR,
- BTINTEL_DRIVER
+ BTINTEL_DRIVER,
+ BTINTEL_EVENT_TYPE,
+ BTINTEL_EVENT_ID
};
/* causes for the MBOX interrupts */
@@ -430,6 +434,8 @@ struct btintel_pcie_dump_header {
u32 wrap_ctr;
u16 trigger_reason;
int state;
+ u8 event_type;
+ u16 event_id;
};
/* struct btintel_pcie_data
@@ -518,6 +524,8 @@ struct btintel_pcie_data {
struct btintel_pcie_dbgc dbgc;
struct btintel_pcie_dump_header dmp_hdr;
u8 pm_sx_event;
+ u32 debug_evt_addr;
+ u32 debug_evt_size;
};
static inline u32 btintel_pcie_rd_reg32(struct btintel_pcie_data *data,
--
2.54.0
^ permalink raw reply related
* [PATCH v9 1/1] Bluetooth: L2CAP: Fix use-after-free in l2cap_sock_new_connection_cb()
From: Siwei Zhang @ 2026-06-03 15:06 UTC (permalink / raw)
To: Marcel Holtmann, Luiz Augusto von Dentz; +Cc: linux-bluetooth, Siwei Zhang
In-Reply-To: <20260603150835.3539963-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.
Allocate the channel outside the func to prevent this.
Fixes: 8ffb929098a5 ("Bluetooth: Remove parent socket usage from l2cap_core.c")
Cc: stable@kernel.org
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Siwei Zhang <oss@fourdim.xyz>
---
include/net/bluetooth/l2cap.h | 10 +++--
net/bluetooth/6lowpan.c | 31 +++++++------
net/bluetooth/l2cap_core.c | 41 ++++++++++++-----
net/bluetooth/l2cap_sock.c | 83 +++++++++++++++++++++--------------
net/bluetooth/smp.c | 17 ++++---
5 files changed, 113 insertions(+), 69 deletions(-)
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index e0a1f2293679..7f5e4647f6e0 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -620,7 +620,9 @@ struct l2cap_chan {
struct l2cap_ops {
char *name;
- struct l2cap_chan *(*new_connection) (struct l2cap_chan *chan);
+ int (*new_connection)(struct l2cap_conn *conn,
+ struct l2cap_chan *chan,
+ struct l2cap_chan *new_chan);
int (*recv) (struct l2cap_chan * chan,
struct sk_buff *skb);
void (*teardown) (struct l2cap_chan *chan, int err);
@@ -884,9 +886,11 @@ static inline __u16 __next_seq(struct l2cap_chan *chan, __u16 seq)
return (seq + 1) % (chan->tx_win_max + 1);
}
-static inline struct l2cap_chan *l2cap_chan_no_new_connection(struct l2cap_chan *chan)
+static inline int l2cap_chan_no_new_connection(struct l2cap_conn *conn,
+ struct l2cap_chan *chan,
+ struct l2cap_chan *new_chan)
{
- return NULL;
+ return -EOPNOTSUPP;
}
static inline int l2cap_chan_no_recv(struct l2cap_chan *chan, struct sk_buff *skb)
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
index cb1e329d66fd..94863af97a44 100644
--- a/net/bluetooth/6lowpan.c
+++ b/net/bluetooth/6lowpan.c
@@ -624,6 +624,15 @@ static bool is_bt_6lowpan(struct hci_conn *hcon)
return true;
}
+static void chan_init(struct l2cap_chan *chan)
+{
+ l2cap_chan_set_defaults(chan);
+
+ chan->chan_type = L2CAP_CHAN_CONN_ORIENTED;
+ chan->mode = L2CAP_MODE_LE_FLOWCTL;
+ chan->imtu = 1280;
+}
+
static struct l2cap_chan *chan_create(void)
{
struct l2cap_chan *chan;
@@ -632,11 +641,7 @@ static struct l2cap_chan *chan_create(void)
if (!chan)
return NULL;
- l2cap_chan_set_defaults(chan);
-
- chan->chan_type = L2CAP_CHAN_CONN_ORIENTED;
- chan->mode = L2CAP_MODE_LE_FLOWCTL;
- chan->imtu = 1280;
+ chan_init(chan);
return chan;
}
@@ -745,19 +750,19 @@ static inline void chan_ready_cb(struct l2cap_chan *chan)
ifup(dev->netdev);
}
-static inline struct l2cap_chan *chan_new_conn_cb(struct l2cap_chan *pchan)
+static inline int chan_new_conn_cb(struct l2cap_conn *conn,
+ struct l2cap_chan *pchan,
+ struct l2cap_chan *chan)
{
- struct l2cap_chan *chan;
-
- chan = chan_create();
- if (!chan)
- return NULL;
-
+ chan_init(chan);
chan->ops = pchan->ops;
+ /* Take the conn list reference; see l2cap_new_connection(). */
+ __l2cap_chan_add(conn, chan);
+
BT_DBG("chan %p pchan %p", chan, pchan);
- return chan;
+ return 0;
}
static void unregister_dev(struct lowpan_btle_dev *dev)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index c4ccfbda9d78..62acf90837fb 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -4007,6 +4007,31 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn,
return 0;
}
+/* Allocate and initialise a channel for an incoming connection.
+ *
+ * ->new_connection() initialises the channel and links it into @conn with
+ * __l2cap_chan_add(). The l2cap_chan_create() reference becomes the one owned
+ * by the parent subsystem (l2cap_pi(sk)->chan, conn->smp or peer->chan) and is
+ * released by its teardown callback; the conn list reference is released by
+ * l2cap_chan_del().
+ */
+static struct l2cap_chan *l2cap_new_connection(struct l2cap_conn *conn,
+ struct l2cap_chan *pchan)
+{
+ struct l2cap_chan *chan;
+
+ chan = l2cap_chan_create();
+ if (!chan)
+ return NULL;
+
+ if (pchan->ops->new_connection(conn, pchan, chan) < 0) {
+ l2cap_chan_put(chan);
+ return NULL;
+ }
+
+ return chan;
+}
+
static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
u8 *data, u8 rsp_code)
{
@@ -4053,7 +4078,7 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
goto response;
}
- chan = pchan->ops->new_connection(pchan);
+ chan = l2cap_new_connection(conn, pchan);
if (!chan)
goto response;
@@ -4071,8 +4096,6 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
chan->psm = psm;
chan->dcid = scid;
- __l2cap_chan_add(conn, chan);
-
dcid = chan->scid;
__set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
@@ -4955,7 +4978,7 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
goto response_unlock;
}
- chan = pchan->ops->new_connection(pchan);
+ chan = l2cap_new_connection(conn, pchan);
if (!chan) {
result = L2CAP_CR_LE_NO_MEM;
goto response_unlock;
@@ -4970,8 +4993,6 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
chan->omtu = mtu;
chan->remote_mps = mps;
- __l2cap_chan_add(conn, chan);
-
l2cap_le_flowctl_init(chan, __le16_to_cpu(req->credits));
dcid = chan->scid;
@@ -5179,7 +5200,7 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn,
continue;
}
- chan = pchan->ops->new_connection(pchan);
+ chan = l2cap_new_connection(conn, pchan);
if (!chan) {
result = L2CAP_CR_LE_NO_MEM;
continue;
@@ -5194,8 +5215,6 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn,
chan->omtu = mtu;
chan->remote_mps = mps;
- __l2cap_chan_add(conn, chan);
-
l2cap_ecred_init(chan, __le16_to_cpu(req->credits));
/* Init response */
@@ -7470,14 +7489,12 @@ static void l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
goto next;
l2cap_chan_lock(pchan);
- chan = pchan->ops->new_connection(pchan);
+ chan = l2cap_new_connection(conn, pchan);
if (chan) {
bacpy(&chan->src, &hcon->src);
bacpy(&chan->dst, &hcon->dst);
chan->src_type = bdaddr_src_type(hcon);
chan->dst_type = dst_type;
-
- __l2cap_chan_add(conn, chan);
}
l2cap_chan_unlock(pchan);
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 025329636353..87f4c0db5c0c 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -46,7 +46,8 @@ static struct bt_sock_list l2cap_sk_list = {
static const struct proto_ops l2cap_sock_ops;
static void l2cap_sock_init(struct sock *sk, struct sock *parent);
static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
- int proto, gfp_t prio, int kern);
+ int proto, gfp_t prio, int kern,
+ struct l2cap_chan *chan);
static void l2cap_sock_cleanup_listen(struct sock *parent);
bool l2cap_is_socket(struct socket *sock)
@@ -1287,6 +1288,23 @@ static int l2cap_sock_recvmsg(struct socket *sock, struct msghdr *msg,
return err;
}
+/* Release the sock's ref on chan and clear the pointer so that the ref is
+ * dropped exactly once even if both l2cap_sock_kill() and
+ * l2cap_sock_destruct() run. Setting chan->data to NULL first stops any other
+ * task from dereferencing the now-dead sock pointer.
+ */
+static void l2cap_sock_put_chan(struct sock *sk)
+{
+ struct l2cap_chan *chan = l2cap_pi(sk)->chan;
+
+ if (!chan)
+ return;
+
+ chan->data = NULL;
+ l2cap_pi(sk)->chan = NULL;
+ l2cap_chan_put(chan);
+}
+
/* Kill socket (only if zapped and orphan)
* Must be called on unlocked socket, with l2cap channel lock.
*/
@@ -1297,13 +1315,9 @@ static void l2cap_sock_kill(struct sock *sk)
BT_DBG("sk %p state %s", sk, state_to_string(sk->sk_state));
- /* Sock is dead, so set chan data to NULL, avoid other task use invalid
- * sock pointer.
- */
- l2cap_pi(sk)->chan->data = NULL;
- /* Kill poor orphan */
+ l2cap_sock_put_chan(sk);
- l2cap_chan_put(l2cap_pi(sk)->chan);
+ /* Kill poor orphan */
sock_set_flag(sk, SOCK_DEAD);
sock_put(sk);
}
@@ -1546,12 +1560,14 @@ static void l2cap_sock_cleanup_listen(struct sock *parent)
}
}
-static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan)
+static int l2cap_sock_new_connection_cb(struct l2cap_conn *conn,
+ struct l2cap_chan *chan,
+ struct l2cap_chan *new_chan)
{
struct sock *sk, *parent = chan->data;
if (!parent)
- return NULL;
+ return -EINVAL;
lock_sock(parent);
@@ -1559,25 +1575,33 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan)
if (sk_acceptq_is_full(parent)) {
BT_DBG("backlog full %d", parent->sk_ack_backlog);
release_sock(parent);
- return NULL;
+ return -ENOBUFS;
}
sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP,
- GFP_ATOMIC, 0);
+ GFP_ATOMIC, 0, new_chan);
if (!sk) {
release_sock(parent);
- return NULL;
- }
+ return -ENOMEM;
+ }
bt_sock_reclassify_lock(sk, BTPROTO_L2CAP);
l2cap_sock_init(sk, parent);
+ /* Link the channel into conn before exposing the new socket via the
+ * accept queue. Once release_sock() below drops the parent lock the
+ * socket may be freed by another task, dropping its reference on
+ * new_chan; the conn list reference taken here keeps new_chan alive so
+ * the caller can safely use it after ->new_connection() returns.
+ */
+ __l2cap_chan_add(conn, new_chan);
+
bt_accept_enqueue(parent, sk, false);
release_sock(parent);
- return l2cap_pi(sk)->chan;
+ return 0;
}
static int l2cap_sock_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
@@ -1874,10 +1898,7 @@ static void l2cap_sock_destruct(struct sock *sk)
BT_DBG("sk %p", sk);
- if (l2cap_pi(sk)->chan) {
- l2cap_pi(sk)->chan->data = NULL;
- l2cap_chan_put(l2cap_pi(sk)->chan);
- }
+ l2cap_sock_put_chan(sk);
list_for_each_entry_safe(rx_busy, next, &l2cap_pi(sk)->rx_busy, list) {
kfree_skb(rx_busy->skb);
@@ -1978,10 +1999,10 @@ static struct proto l2cap_proto = {
};
static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
- int proto, gfp_t prio, int kern)
+ int proto, gfp_t prio, int kern,
+ struct l2cap_chan *chan)
{
struct sock *sk;
- struct l2cap_chan *chan;
sk = bt_sock_alloc(net, sock, &l2cap_proto, proto, prio, kern);
if (!sk)
@@ -1992,16 +2013,7 @@ static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
INIT_LIST_HEAD(&l2cap_pi(sk)->rx_busy);
- chan = l2cap_chan_create();
- if (!chan) {
- sk_free(sk);
- if (sock)
- sock->sk = NULL;
- return NULL;
- }
-
- l2cap_chan_hold(chan);
-
+ /* The sock takes ownership of the caller's reference on chan. */
l2cap_pi(sk)->chan = chan;
return sk;
@@ -2011,6 +2023,7 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol,
int kern)
{
struct sock *sk;
+ struct l2cap_chan *chan;
BT_DBG("sock %p", sock);
@@ -2025,10 +2038,16 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol,
sock->ops = &l2cap_sock_ops;
- sk = l2cap_sock_alloc(net, sock, protocol, GFP_ATOMIC, kern);
- if (!sk)
+ chan = l2cap_chan_create();
+ if (!chan)
return -ENOMEM;
+ sk = l2cap_sock_alloc(net, sock, protocol, GFP_ATOMIC, kern, chan);
+ if (!sk) {
+ l2cap_chan_put(chan);
+ return -ENOMEM;
+ }
+
l2cap_sock_init(sk, NULL);
bt_sock_link(&l2cap_sk_list, sk);
return 0;
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index 1739c1989dbd..2d31c3c7bbc0 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -3204,16 +3204,12 @@ static const struct l2cap_ops smp_chan_ops = {
.get_sndtimeo = l2cap_chan_no_get_sndtimeo,
};
-static inline struct l2cap_chan *smp_new_conn_cb(struct l2cap_chan *pchan)
+static inline int smp_new_conn_cb(struct l2cap_conn *conn,
+ struct l2cap_chan *pchan,
+ struct l2cap_chan *chan)
{
- struct l2cap_chan *chan;
-
BT_DBG("pchan %p", pchan);
- chan = l2cap_chan_create();
- if (!chan)
- return NULL;
-
chan->chan_type = pchan->chan_type;
chan->ops = &smp_chan_ops;
chan->scid = pchan->scid;
@@ -3229,9 +3225,12 @@ static inline struct l2cap_chan *smp_new_conn_cb(struct l2cap_chan *pchan)
*/
atomic_set(&chan->nesting, L2CAP_NESTING_SMP);
- BT_DBG("created chan %p", chan);
+ /* Take the conn list reference; see l2cap_new_connection(). */
+ __l2cap_chan_add(conn, chan);
- return chan;
+ BT_DBG("initialised chan %p", chan);
+
+ return 0;
}
static const struct l2cap_ops smp_root_chan_ops = {
--
2.54.0
^ permalink raw reply related
* [PATCH v9 0/1] Bluetooth: L2CAP: Fix use-after-free in l2cap_sock_new_connection_cb()
From: Siwei Zhang @ 2026-06-03 15:06 UTC (permalink / raw)
To: Marcel Holtmann, Luiz Augusto von Dentz; +Cc: linux-bluetooth, Siwei Zhang
Compared to v2, addresses comments on https://sashiko.dev/#/patchset/20260415204842.2363950-1-oss%40fourdim.xyz .
Compared to v3, rebase against bluetooth-next.
Compared to v4, allocate the channel outside the function and pass it in as an argument to avoid the use-after-free.
Compared to v5, extract the channel init to a separate function.
Compared to v6, balance puts and holds on chans.
Compared to v7, rebase against bluetooth-next and refactor the chan refcounting.
Compared to v8, adopt the philosophy of one assignment one reference. Make refcounting easier to follow.
Siwei Zhang (1):
Bluetooth: L2CAP: Fix use-after-free in l2cap_sock_new_connection_cb()
include/net/bluetooth/l2cap.h | 10 +++--
net/bluetooth/6lowpan.c | 31 +++++++------
net/bluetooth/l2cap_core.c | 41 ++++++++++++-----
net/bluetooth/l2cap_sock.c | 83 +++++++++++++++++++++--------------
net/bluetooth/smp.c | 17 ++++---
5 files changed, 113 insertions(+), 69 deletions(-)
--
2.54.0
^ permalink raw reply
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