* [PATCH] Bluetooth: RFCOMM: serialize session teardown
@ 2026-08-21 17:45 Chengfeng Ye
2026-08-21 19:21 ` Ali Ahmet Memis
2026-08-22 8:22 ` Pauli Virtanen
0 siblings, 2 replies; 6+ messages in thread
From: Chengfeng Ye @ 2026-08-21 17:45 UTC (permalink / raw)
To: Marcel Holtmann, Luiz Augusto von Dentz, Kees Cook, Chengfeng Ye,
Jakub Kicinski, Pengpeng Hou, Jiale Yao, SeungJu Cheon,
Ali Ahmet Memis, Tim Bird
Cc: linux-bluetooth, linux-kernel, stable
rfcomm_kill_listener() walks session_list and deletes every session
without holding rfcomm_mutex. A connect task holds that mutex while
rfcomm_session_create() adds a session and while its error path deletes
the session, but the unlocked teardown can still observe the object
between those operations.
The race can proceed as follows:
connect task krfcommd
------------ --------
rfcomm_lock()
rfcomm_session_add()
fetch session from session_list
kernel_connect() fails
rfcomm_session_del()
remove and free session
rfcomm_session_del(session)
The final call then reads the freed session and may corrupt the list.
KASAN reported:
BUG: KASAN: slab-use-after-free in rfcomm_session_del+0x15f/0x170
Read of size 8 at addr ffff8881019e9b40 by task krfcommd/87
Call Trace:
rfcomm_session_del+0x15f/0x170
rfcomm_run+0x16d5/0x3de0
kthread+0x2c6/0x3b0
ret_from_fork+0x36e/0x5a0
Allocated by task 96:
rfcomm_session_add+0x9e/0x300
rfcomm_dlc_open+0x8b1/0xdf0
rfcomm_sock_connect+0x34c/0x530
Freed by task 96:
kfree+0x131/0x3c0
rfcomm_session_del+0x109/0x170
rfcomm_dlc_open+0x9eb/0xdf0
rfcomm_sock_connect+0x34c/0x530
Hold rfcomm_mutex across the teardown traversal, matching the locking
used by normal session processing and connect error cleanup.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Signed-off-by: Chengfeng Ye <nicoyip.dev@gmail.com>
---
net/bluetooth/rfcomm/core.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c
index 9cdfea666a2c..5fe2758e8c47 100644
--- a/net/bluetooth/rfcomm/core.c
+++ b/net/bluetooth/rfcomm/core.c
@@ -2178,8 +2178,10 @@ static void rfcomm_kill_listener(void)
BT_DBG("");
+ rfcomm_lock();
list_for_each_entry_safe(s, n, &session_list, list)
rfcomm_session_del(s);
+ rfcomm_unlock();
}
static int rfcomm_run(void *unused)
--
2.43.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH] Bluetooth: RFCOMM: serialize session teardown
2026-08-21 17:45 [PATCH] Bluetooth: RFCOMM: serialize session teardown Chengfeng Ye
@ 2026-08-21 19:21 ` Ali Ahmet Memis
2026-08-22 7:14 ` Chengfeng Ye
2026-08-22 8:22 ` Pauli Virtanen
1 sibling, 1 reply; 6+ messages in thread
From: Ali Ahmet Memis @ 2026-08-21 19:21 UTC (permalink / raw)
To: Chengfeng Ye
Cc: Marcel Holtmann, Luiz Augusto von Dentz, Kees Cook,
Jakub Kicinski, Pengpeng Hou, Jiale Yao, SeungJu Cheon, Tim Bird,
linux-bluetooth, linux-kernel, stable
Hi,
On Sat, Aug 22, 2026 at 01:45:14AM +0800, Chengfeng Ye wrote:
> rfcomm_kill_listener() walks session_list and deletes every session
> without holding rfcomm_mutex.
I agree with that observation but I don't think this race is reachable. I
also could not reproduce the reported splat.
I tested 7.2-rc5 with KASAN and RFCOMM as a module. An open RFCOMM socket
keeps rfcomm.ko pinned so the module cannot be unloaded while connect() is
running. Even with forced unloads and a widened add/drop window, I only
hit execution from freed module text. I never saw a UAF in
rfcomm_session_del().
The splat also looks like it came from a built-in RFCOMM build. The
missing module tag and the way rfcomm_exit() works in that case make the
reported path unlikely.
Could you send the full report and the reproducer? In particular the
"buggy address" block and both full stacks would help. faddr2line on your
vmlinux should also show which traversal the offset points to.
The locking itself is safe but I don't think the changelog currently
describes a reachable race. There may be a more relevant unlocked
session_list access in rfcomm_security_cfm().
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] Bluetooth: RFCOMM: serialize session teardown
2026-08-21 19:21 ` Ali Ahmet Memis
@ 2026-08-22 7:14 ` Chengfeng Ye
2026-08-22 8:30 ` Ali Ahmet Memis
0 siblings, 1 reply; 6+ messages in thread
From: Chengfeng Ye @ 2026-08-22 7:14 UTC (permalink / raw)
To: Ali Ahmet Memis
Cc: Marcel Holtmann, Luiz Augusto von Dentz, Kees Cook,
Jakub Kicinski, Pengpeng Hou, Jiale Yao, SeungJu Cheon, Tim Bird,
linux-bluetooth, linux-kernel, stable
Hi Ali,
Thanks for taking a close look.
On Sat, Aug 22, 2026 at 3:22 AM Ali Ahmet Memis <ali@iusegentoo.com> wrote:
>
> Hi,
>
> On Sat, Aug 22, 2026 at 01:45:14AM +0800, Chengfeng Ye wrote:
> > rfcomm_kill_listener() walks session_list and deletes every session
> > without holding rfcomm_mutex.
>
> I agree with that observation but I don't think this race is reachable. I
> also could not reproduce the reported splat.
>
> I tested 7.2-rc5 with KASAN and RFCOMM as a module. An open RFCOMM socket
> keeps rfcomm.ko pinned so the module cannot be unloaded while connect() is
> running. Even with forced unloads and a widened add/drop window, I only
> hit execution from freed module text. I never saw a UAF in
> rfcomm_session_del().
You are right that my changelog did not describe the reachability
constraints clearly enough. The reproducer I used relies on forced
module unload via: syscall(SYS_delete_module, "rfcomm", O_TRUNC) so
the path is only reachable by a privileged user with module-unload
capability. Furthermore, to make the race triggering stable, we add
the following kernel-delay log for race reproduction (which is
acceptable approach for demonstrating the bug[1]):
index 2e8c080b4d9e..fb9b4f6d1a86 100644
--- a/net/bluetooth/rfcomm/core.c
+++ b/net/bluetooth/rfcomm/core.c
@@ -25,6 +25,7 @@
#include <linux/module.h>
#include <linux/debugfs.h>
#include <linux/kthread.h>
+#include <linux/delay.h>
#include <linux/unaligned.h>
#include <net/bluetooth/bluetooth.h>
@@ -701,7 +702,12 @@ static struct rfcomm_session
*rfcomm_session_add(struct socket *sock, int state)
return NULL;
}
+ /*
+ * Widen the transient BT_BOUND lifetime after insertion so forced
+ * unload can overlap a live session on session_list.
+ */
list_add(&s->list, &session_list);
+ mdelay(30);
return s;
}
@@ -2160,8 +2166,14 @@ static void rfcomm_kill_listener(void)
BT_DBG("");
- list_for_each_entry_safe(s, n, &session_list, list)
+ list_for_each_entry_safe(s, n, &session_list, list) {
+ /*
+ * Widen the unlocked teardown window so forced module
unload can
+ * overlap a concurrent session teardown on the same list entry.
+ */
+ mdelay(50);
rfcomm_session_del(s);
+ }
}
>
>
> The splat also looks like it came from a built-in RFCOMM build. The
> missing module tag and the way rfcomm_exit() works in that case make the
> reported path unlikely.
>
> Could you send the full report and the reproducer? In particular the
> "buggy address" block and both full stacks would help. faddr2line on your
> vmlinux should also show which traversal the offset points to.
I will send the reproducer as a separate email as the maillist does
not accept an attachment. The full KASAN I reproduced on the current
master branch as following (where the address would be different from
that on the commit message cause that one was reproduced on a kernel
built roughly 2 months ago):
[ 29.867919] ==================================================================
[ 29.868407] BUG: KASAN: slab-use-after-free in
rfcomm_run+0x3802/0x3f00 [rfcomm]
[ 29.868954] Read of size 8 at addr ffff888111058d40 by task krfcommd/79
[ 29.869432]
[ 29.869544] CPU: 0 UID: 0 PID: 79 Comm: krfcommd Tainted: G R
7.2.0-05126-ga4ff2be345d0-dirty #20 PREEMPT(lazy)
[ 29.869550] Tainted: [R]=FORCED_RMMOD
[ 29.869551] Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX,
arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
[ 29.869554] Call Trace:
[ 29.869558] <TASK>
[ 29.869559] dump_stack_lvl+0x53/0x70
[ 29.869591] print_report+0xd0/0x630
[ 29.869611] ? __pfx__raw_spin_lock_irqsave+0x10/0x10
[ 29.869620] ? rfcomm_run+0x3802/0x3f00 [rfcomm]
[ 29.869623] kasan_report+0xce/0x100
[ 29.869625] ? rfcomm_run+0x3802/0x3f00 [rfcomm]
[ 29.869629] rfcomm_run+0x3802/0x3f00 [rfcomm]
[ 29.869632] ? __pfx___set_cpus_allowed_ptr+0x10/0x10
[ 29.869643] ? __pfx_rfcomm_run+0x10/0x10 [rfcomm]
[ 29.869647] ? mutex_unlock+0x7b/0xd0
[ 29.869649] ? __pfx_mutex_unlock+0x10/0x10
[ 29.869651] ? __pfx_woken_wake_function+0x10/0x10
[ 29.869655] ? __pfx__raw_spin_lock_irqsave+0x10/0x10
[ 29.869657] ? __pfx_kthread_affine_node+0x10/0x10
[ 29.869661] ? __kthread_parkme+0x88/0x170
[ 29.869666] ? __pfx_rfcomm_run+0x10/0x10 [rfcomm]
[ 29.869669] ? __pfx_rfcomm_run+0x10/0x10 [rfcomm]
[ 29.869672] kthread+0x2c8/0x3b0
[ 29.869674] ? recalc_sigpending+0x15c/0x1e0
[ 29.869678] ? __pfx_kthread+0x10/0x10
[ 29.869680] ret_from_fork+0x36e/0x5a0
[ 29.869690] ? __pfx_ret_from_fork+0x10/0x10
[ 29.869695] ? __switch_to+0x572/0xdd0
[ 29.869699] ? __pfx_kthread+0x10/0x10
[ 29.869701] ret_from_fork_asm+0x1a/0x30
[ 29.869705] </TASK>
[ 29.869706]
[ 29.879688] Allocated by task 86:
[ 29.879947] kasan_save_stack+0x33/0x60
[ 29.880247] kasan_save_track+0x14/0x30
[ 29.880550] __kasan_kmalloc+0x8f/0xa0
[ 29.880846] __kmalloc_cache_noprof+0x15a/0x370
[ 29.881198] rfcomm_session_add+0xa1/0x300 [rfcomm]
[ 29.881555] rfcomm_dlc_open+0x8b2/0xf30 [rfcomm]
[ 29.881908] rfcomm_sock_connect+0x34c/0x530 [rfcomm]
[ 29.882248] __sys_connect+0xfc/0x130
[ 29.882491] __x64_sys_connect+0x6d/0xb0
[ 29.882785] do_syscall_64+0xdd/0x4a0
[ 29.883050] entry_SYSCALL_64_after_hwframe+0x77/0x7f
[ 29.883425]
[ 29.883530] Freed by task 86:
[ 29.883758] kasan_save_stack+0x33/0x60
[ 29.884029] kasan_save_track+0x14/0x30
[ 29.884321] kasan_save_free_info+0x3b/0x60
[ 29.884642] __kasan_slab_free+0x43/0x70
[ 29.884920] kfree+0x121/0x3c0
[ 29.885145] rfcomm_dlc_open+0xab7/0xf30 [rfcomm]
[ 29.885520] rfcomm_sock_connect+0x34c/0x530 [rfcomm]
[ 29.885824] __sys_connect+0xfc/0x130
[ 29.886047] __x64_sys_connect+0x6d/0xb0
[ 29.886286] do_syscall_64+0xdd/0x4a0
[ 29.886508] entry_SYSCALL_64_after_hwframe+0x77/0x7f
[ 29.886808]
[ 29.886906] The buggy address belongs to the object at ffff888111058d00
[ 29.886906] which belongs to the cache kmalloc-part-02-128 of size 128
[ 29.887669] The buggy address is located 64 bytes inside of
[ 29.887669] freed 128-byte region [ffff888111058d00, ffff888111058d80)
[ 29.888371]
[ 29.888470] The buggy address belongs to the physical page:
[ 29.888797] page: refcount:0 mapcount:0 mapping:0000000000000000
index:0x0 pfn:0x111058
[ 29.889270] flags: 0x200000000000000(node=0|zone=2)
[ 29.889563] page_type: f5(slab)
[ 29.889756] raw: 0200000000000000 ffff888100043c80 dead000000000122
0000000000000000
[ 29.890207] raw: 0000000000000000 0000000000100010 00000000f5000000
0000000000000000
[ 29.890660] page dumped because: kasan: bad access detected
[ 29.890987]
[ 29.891086] Memory state around the buggy address:
[ 29.891373] ffff888111058c00: fc fc fc fc fc fc fc fc fc fc fc fc
fc fc fc fc
[ 29.891793] ffff888111058c80: fc fc fc fc fc fc fc fc fc fc fc fc
fc fc fc fc
[ 29.892212] >ffff888111058d00: fa fb fb fb fb fb fb fb fb fb fb fb
fb fb fb fb
[ 29.892636] ^
[ 29.892948] ffff888111058d80: fc fc fc fc fc fc fc fc fc fc fc fc
fc fc fc fc
[ 29.893373] ffff888111058e00: fa fb fb fb fb fb fb fb fb fb fb fb
fb fb fb fb
[ 29.893793] ==================================================================
[ 29.894249] Oops: general protection fault, probably for
non-canonical address 0xe0fd7c116000000a: 0000 [#1] SMP KASAN NOPTI
[ 29.894901] KASAN: maybe wild-memory-access in range
[0x07ec008b00000050-0x07ec008b00000057]
[ 29.895397] CPU: 0 UID: 0 PID: 79 Comm: krfcommd Tainted: G R B
7.2.0-05126-ga4ff2be345d0-dirty #20 PREEMPT(lazy)
[ 29.896068] Tainted: [R]=FORCED_RMMOD, [B]=BAD_PAGE
[ 29.896358] Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX,
arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
[ 29.897017] RIP: 0010:rfcomm_run+0x148f/0x3f00 [rfcomm]
[ 29.897331] Code: 08 23 00 00 49 8d 7c 24 08 49 8b 46 08 48 89 f9
48 c1 e9 03 80 3c 29 00 0f 85 da 22 00 00 48 89 c1 49 89 44 24 08 48
c1 e9 03 <80> 3c 29 00 0f 85 ad 22 00 00 4c 89 20 49 8d 7e 18 48 b8 22
01 00
[ 29.898405] RSP: 0018:ffff888111fa7cd0 EFLAGS: 00010207
[ 29.898715] RAX: 07ec008b00000056 RBX: 0000000000000003 RCX: 00fd80116000000a
[ 29.899131] RDX: 0000000000000001 RSI: 0000000000000008 RDI: ffff888111fa7c98
[ 29.899552] RBP: dffffc0000000000 R08: 0000000000000001 R09: fffffbfff4c5b440
[ 29.899970] R10: ffffffffa62da207 R11: 3d3d3d3d3d3d3d3d R12: ffff888111ef9a00
[ 29.900388] R13: ffff888100fd5300 R14: ffff888111058d00 R15: dead000000000100
[ 29.900804] FS: 0000000000000000(0000) GS:ffff88817557e000(0000)
knlGS:0000000000000000
[ 29.901278] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 29.901615] CR2: 00007f2f84a69b30 CR3: 0000000111c02002 CR4: 0000000000770ef0
[ 29.902032] PKRU: 55555554
[ 29.902197] Call Trace:
[ 29.902353] <TASK>
[ 29.902486] ? __pfx___set_cpus_allowed_ptr+0x10/0x10
[ 29.902787] ? __pfx_rfcomm_run+0x10/0x10 [rfcomm]
[ 29.903074] ? mutex_unlock+0x7b/0xd0
[ 29.903299] ? __pfx_mutex_unlock+0x10/0x10
[ 29.903549] ? __pfx_woken_wake_function+0x10/0x10
[ 29.903833] ? __pfx__raw_spin_lock_irqsave+0x10/0x10
[ 29.904134] ? __pfx_kthread_affine_node+0x10/0x10
[ 29.904423] ? __kthread_parkme+0x88/0x170
[ 29.904667] ? __pfx_rfcomm_run+0x10/0x10 [rfcomm]
[ 29.904952] ? __pfx_rfcomm_run+0x10/0x10 [rfcomm]
[ 29.905237] kthread+0x2c8/0x3b0
[ 29.905441] ? recalc_sigpending+0x15c/0x1e0
[ 29.905698] ? __pfx_kthread+0x10/0x10
[ 29.905924] ret_from_fork+0x36e/0x5a0
[ 29.906150] ? __pfx_ret_from_fork+0x10/0x10
[ 29.906408] ? __switch_to+0x572/0xdd0
[ 29.906636] ? __pfx_kthread+0x10/0x10
[ 29.906862] ret_from_fork_asm+0x1a/0x30
[ 29.907097] </TASK>
[ 29.907233] Modules linked in: rfcomm(-)
[ 29.907480] ---[ end trace 0000000000000000 ]---
[ 29.907757] RIP: 0010:rfcomm_run+0x148f/0x3f00 [rfcomm]
[ 29.908068] Code: 08 23 00 00 49 8d 7c 24 08 49 8b 46 08 48 89 f9
48 c1 e9 03 80 3c 29 00 0f 85 da 22 00 00 48 89 c1 49 89 44 24 08 48
c1 e9 03 <80> 3c 29 00 0f 85 ad 22 00 00 4c 89 20 49 8d 7e 18 48 b8 22
01 00
[ 29.909139] RSP: 0018:ffff888111fa7cd0 EFLAGS: 00010207
[ 29.909455] RAX: 07ec008b00000056 RBX: 0000000000000003 RCX: 00fd80116000000a
[ 29.909870] RDX: 0000000000000001 RSI: 0000000000000008 RDI: ffff888111fa7c98
[ 29.910295] RBP: dffffc0000000000 R08: 0000000000000001 R09: fffffbfff4c5b440
[ 29.910711] R10: ffffffffa62da207 R11: 3d3d3d3d3d3d3d3d R12: ffff888111ef9a00
[ 29.911128] R13: ffff888100fd5300 R14: ffff888111058d00 R15: dead000000000100
[ 29.911550] FS: 0000000000000000(0000) GS:ffff88817557e000(0000)
knlGS:0000000000000000
[ 29.912019] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 29.912364] CR2: 00007f2f84a69b30 CR3: 0000000111c02002 CR4: 0000000000770ef0
[ 29.912782] PKRU: 55555554
[ 29.912948] Kernel panic - not syncing: Fatal exception
[ 29.913315] Kernel Offset: 0x1f400000 from 0xffffffff81000000
(relocation range: 0xffffffff80000000-0xffffffffbfffffff)
The probably useful addr2line information is like:
### `rfcomm_run+0x3802/0x3f00 [rfcomm]`
```text
rfcomm_run+0x3802/0x3f00:
rfcomm_session_del at /home/cyeaa/linux/net/bluetooth/rfcomm/core.c:717
(inlined by) fcomm_kill_listener at
/home/cyeaa/linux/net/bluetooth/rfcomm/core.c:2175
(inlined by) fcomm_run at /home/cyeaa/linux/net/bluetooth/rfcomm/core.c:2198
```
### `rfcomm_run+0x148f/0x3f00 [rfcomm]`
```text
rfcomm_run+0x148f/0x3f00:
__list_del at /home/cyeaa/linux/./include/linux/list.h:227 (discriminator 2)
(inlined by) __list_del_entry at
/home/cyeaa/linux/./include/linux/list.h:249 (discriminator 2)
(inlined by) list_del at /home/cyeaa/linux/./include/linux/list.h:260
(discriminator 2)
(inlined by) fcomm_session_del at
/home/cyeaa/linux/net/bluetooth/rfcomm/core.c:721 (discriminator 2)
(inlined by) fcomm_kill_listener at
/home/cyeaa/linux/net/bluetooth/rfcomm/core.c:2175 (discriminator 2)
(inlined by) fcomm_run at
/home/cyeaa/linux/net/bluetooth/rfcomm/core.c:2198 (discriminator 2)
```
### `__pfx_rfcomm_run+0x10/0x10 [rfcomm]`
```text
__pfx_rfcomm_run+0x10/0x10:
rfcomm_run at /home/cyeaa/linux/net/bluetooth/rfcomm/core.c:2180
```
### `rfcomm_session_add+0xa1/0x300 [rfcomm]`
```text
rfcomm_session_add+0xa1/0x300:
_kmalloc_noprof at /home/cyeaa/linux/./include/linux/slab.h:988
(inlined by) _kzalloc_noprof at /home/cyeaa/linux/./include/linux/slab.h:1309
(inlined by) fcomm_session_add at
/home/cyeaa/linux/net/bluetooth/rfcomm/core.c:681
```
### `rfcomm_dlc_open+0x8b2/0xf30 [rfcomm]`
```text
rfcomm_dlc_open+0x8b2/0xf30:
rfcomm_session_create at /home/cyeaa/linux/net/bluetooth/rfcomm/core.c:801
(inlined by) __rfcomm_dlc_open at
/home/cyeaa/linux/net/bluetooth/rfcomm/core.c:386
(inlined by) fcomm_dlc_open at /home/cyeaa/linux/net/bluetooth/rfcomm/core.c:429
```
### `rfcomm_sock_connect+0x34c/0x530 [rfcomm]`
```text
rfcomm_sock_connect+0x34c/0x530:
lock_sock at /home/cyeaa/linux/./include/net/sock.h:1713
(inlined by) fcomm_sock_connect at
/home/cyeaa/linux/net/bluetooth/rfcomm/sock.c:422
```
### `rfcomm_dlc_open+0xab7/0xf30 [rfcomm]`
```text
rfcomm_dlc_open+0xab7/0xf30:
rfcomm_session_del at /home/cyeaa/linux/net/bluetooth/rfcomm/core.c:727
(inlined by) fcomm_session_create at
/home/cyeaa/linux/net/bluetooth/rfcomm/core.c:818
(inlined by) __rfcomm_dlc_open at
/home/cyeaa/linux/net/bluetooth/rfcomm/core.c:386
(inlined by) fcomm_dlc_open at /home/cyeaa/linux/net/bluetooth/rfcomm/core.c:429
> The locking itself is safe but I don't think the changelog currently
> describes a reachable race. There may be a more relevant unlocked
> session_list access in rfcomm_security_cfm().
Yes the commit message might be not clear enough, I am happy to make
v2 for it if you decide to fix the bug.
Best regards,
Chengfeng
[1] https://lore.kernel.org/netdev/20251128151919.576920-1-jhs@mojatatu.com/T/
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH] Bluetooth: RFCOMM: serialize session teardown
2026-08-21 17:45 [PATCH] Bluetooth: RFCOMM: serialize session teardown Chengfeng Ye
2026-08-21 19:21 ` Ali Ahmet Memis
@ 2026-08-22 8:22 ` Pauli Virtanen
1 sibling, 0 replies; 6+ messages in thread
From: Pauli Virtanen @ 2026-08-22 8:22 UTC (permalink / raw)
To: Chengfeng Ye, Marcel Holtmann, Luiz Augusto von Dentz,
Ali Ahmet Memis
Cc: linux-bluetooth, linux-kernel
Hi,
la, 2026-08-22 kello 01:45 +0800, Chengfeng Ye kirjoitti:
> rfcomm_kill_listener() walks session_list and deletes every session
> without holding rfcomm_mutex. A connect task holds that mutex while
> rfcomm_session_create() adds a session and while its error path deletes
> the session, but the unlocked teardown can still observe the object
> between those operations.
Reviewed-by: Pauli Virtanen <pav@iki.fi>
While looking at these locking bugs, please also consider using LLVM
context analysis to globally audit the locking of the structure, to
catch the other related data races:
https://docs.kernel.org/dev-tools/context-analysis.html
https://mirrors.edge.kernel.org/pub/tools/llvm/
I think adding the annotations probably should usually go in separate
patch to make backporting of the fix easier.
For example, adding here:
diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c
index 0e496b85e6ce..6b5c44f0d89a 100644
--- a/net/bluetooth/rfcomm/core.c
+++ b/net/bluetooth/rfcomm/core.c
@@ -47,7 +47,7 @@ static DEFINE_MUTEX(rfcomm_mutex);
#define rfcomm_unlock() mutex_unlock(&rfcomm_mutex)
-static LIST_HEAD(session_list);
+static __guarded_by(&rfcomm_mutex) LIST_HEAD(session_list);
static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len);
static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci);
@@ -676,6 +676,7 @@ int rfcomm_dlc_get_modem_status(struct rfcomm_dlc *d, u8 *v24_sig)
/* ---- RFCOMM sessions ---- */
static struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state)
+ __must_hold(&rfcomm_mutex)
{
struct rfcomm_session *s = kzalloc_obj(*s);
plus other __must_hold(&rfcomm_mutex) annotations to functions where
needed, compiler warnings show the session_list is accessed without
holding rfcomm_mutex also from
rfcomm_run() -> rfcomm_session_add()
rfcomm_security_cfm() -> rfcomm_session_get()
Are these reachable? Former is probably theoretical, the latter could
be reachable. Also if unreachable, in my view, taking the lock may be
better than relying on a possibly brittle and hard to understand
invariant to protect the access.
>
> The race can proceed as follows:
>
> connect task krfcommd
> ------------ --------
> rfcomm_lock()
> rfcomm_session_add()
> fetch session from session_list
> kernel_connect() fails
> rfcomm_session_del()
> remove and free session
> rfcomm_session_del(session)
>
> The final call then reads the freed session and may corrupt the list.
>
> KASAN reported:
>
> BUG: KASAN: slab-use-after-free in rfcomm_session_del+0x15f/0x170
> Read of size 8 at addr ffff8881019e9b40 by task krfcommd/87
> Call Trace:
> rfcomm_session_del+0x15f/0x170
> rfcomm_run+0x16d5/0x3de0
> kthread+0x2c6/0x3b0
> ret_from_fork+0x36e/0x5a0
> Allocated by task 96:
> rfcomm_session_add+0x9e/0x300
> rfcomm_dlc_open+0x8b1/0xdf0
> rfcomm_sock_connect+0x34c/0x530
> Freed by task 96:
> kfree+0x131/0x3c0
> rfcomm_session_del+0x109/0x170
> rfcomm_dlc_open+0x9eb/0xdf0
> rfcomm_sock_connect+0x34c/0x530
>
> Hold rfcomm_mutex across the teardown traversal, matching the locking
> used by normal session processing and connect error cleanup.
>
> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
> Cc: stable@vger.kernel.org
> Signed-off-by: Chengfeng Ye <nicoyip.dev@gmail.com>
> ---
> net/bluetooth/rfcomm/core.c | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c
> index 9cdfea666a2c..5fe2758e8c47 100644
> --- a/net/bluetooth/rfcomm/core.c
> +++ b/net/bluetooth/rfcomm/core.c
> @@ -2178,8 +2178,10 @@ static void rfcomm_kill_listener(void)
>
> BT_DBG("");
>
> + rfcomm_lock();
> list_for_each_entry_safe(s, n, &session_list, list)
> rfcomm_session_del(s);
> + rfcomm_unlock();
> }
>
> static int rfcomm_run(void *unused)
--
Pauli Virtanen
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH] Bluetooth: RFCOMM: serialize session teardown
2026-08-22 7:14 ` Chengfeng Ye
@ 2026-08-22 8:30 ` Ali Ahmet Memis
2026-08-22 15:16 ` Chengfeng Ye
0 siblings, 1 reply; 6+ messages in thread
From: Ali Ahmet Memis @ 2026-08-22 8:30 UTC (permalink / raw)
To: Chengfeng Ye
Cc: Pauli Virtanen, Marcel Holtmann, Luiz Augusto von Dentz,
Kees Cook, Jakub Kicinski, Pengpeng Hou, Jiale Yao, SeungJu Cheon,
Tim Bird, linux-bluetooth, linux-kernel, stable
I reproduced the splat here with the same recipe, forced unload plus the
two mdelay()s:
BUG: KASAN: slab-use-after-free in rfcomm_run+0x3dda/0x3f20 [rfcomm]
Read of size 8 at addr ffff8880037b3140 by task krfcommd/75
Tainted: [R]=FORCED_RMMOD
faddr2line agrees with your report:
rfcomm_session_del at net/bluetooth/rfcomm/core.c:713
(inlined by) rfcomm_kill_listener at core.c:2167
(inlined by) rfcomm_run at core.c:2190
With your patch applied and the same delays in place, five runs stayed
quiet. I also could not reproduce the other crash I mentioned. With a
delay in rfcomm_session_create(), forced unload would kill the box while
it was executing freed module text. Eight runs with the patch were clean.
So the change does fix the reported race.
The main thing left is the changelog. As written, it sounds like a plain
connect() can race with krfcommd. I don't think that's possible. Without
CAP_SYS_MODULE and delete_module(O_TRUNC), the module cannot be unloaded
while the socket is open. That means rfcomm_kill_listener() cannot run at
the same time as rfcomm_dlc_open(). The trigger here is a forced unload
and the delays need to be mentioned.
I'd also drop Cc: stable and the Fixes: tag. Forced unload requires root,
taints the kernel and is documented as unsafe: MODULE_FORCE_UNLOAD removes
the module "even if the kernel believes it is unsafe". This isn't
something that affects normal users, and Fixes: 1da177e4c3f4 invites
backports into every stable tree.
For consistency I think the change still makes sense. Every other
session_list traversal takes rfcomm_mutex, apart from
rfcomm_security_cfm(), which I mention below, and this one should too.
With a changelog along those lines and without the stable tag:
Reviewed-by: Ali Ahmet Memis <ali@iusegentoo.com>
Tested-by: Ali Ahmet Memis <ali@iusegentoo.com>
I'm not a Bluetooth maintainer and I don't have merge access.
get_maintainer.pl lists me here because of a recent commit to this file,
so this is only a review and test report from me. Whether the change
should be merged is up to the Bluetooth maintainers.
Pauli raised the same two spots while I was writing this. On rfcomm_run()
-> rfcomm_session_add(), I don't think it is reachable. kthread_run() runs
from rfcomm_init() before rfcomm_init_ttys() and rfcomm_init_sockets(), so
there is no socket or rfcomm dev yet and nothing else can touch
session_list.
rfcomm_security_cfm() -> rfcomm_session_get() is the one I'd worry about.
It runs from the hci_cb callback with only hci_cb_list_lock held, while
krfcommd can free the same session under rfcomm_mutex. I haven't tried to
reproduce that one.
I also agree with Pauli that taking the lock is better than relying on the
invariant. My objection is only to the stable tag.
One unrelated nit: the ### headings and fenced text blocks are a bit
unusual for LKML, but I don't see any problem with keeping them.
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] Bluetooth: RFCOMM: serialize session teardown
2026-08-22 8:30 ` Ali Ahmet Memis
@ 2026-08-22 15:16 ` Chengfeng Ye
0 siblings, 0 replies; 6+ messages in thread
From: Chengfeng Ye @ 2026-08-22 15:16 UTC (permalink / raw)
To: Ali Ahmet Memis
Cc: Pauli Virtanen, Marcel Holtmann, Luiz Augusto von Dentz,
Kees Cook, Jakub Kicinski, Pengpeng Hou, Jiale Yao, SeungJu Cheon,
Tim Bird, linux-bluetooth, linux-kernel, stable
Hi all,
Thanks for your review and feedback, the v2 is just sent:
https://lore.kernel.org/linux-bluetooth/20260822150619.3684599-1-nicoyip.dev@gmail.com/T/#u
On Sat, Aug 22, 2026 at 4:31 PM Ali Ahmet Memis <ali@iusegentoo.com> wrote:
> rfcomm_security_cfm() -> rfcomm_session_get() is the one I'd worry about.
> It runs from the hci_cb callback with only hci_cb_list_lock held, while
> krfcommd can free the same session under rfcomm_mutex. I haven't tried to
> reproduce that one.
You are right on that, actually I have a reproducer for that UAF and
just have not yet been able to send a patch for it as I am afraid
sending too many patches in a short period will introduce a high
review workload. That one is a real security issue as it can be
reachable from unprivileged users, as it is now raised in the public
maillist, I will send a patch for it soon.
Best regards,
Chengfeng
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-08-22 15:16 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-21 17:45 [PATCH] Bluetooth: RFCOMM: serialize session teardown Chengfeng Ye
2026-08-21 19:21 ` Ali Ahmet Memis
2026-08-22 7:14 ` Chengfeng Ye
2026-08-22 8:30 ` Ali Ahmet Memis
2026-08-22 15:16 ` Chengfeng Ye
2026-08-22 8:22 ` Pauli Virtanen
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox