Re: [PATCH] Bluetooth: RFCOMM: serialize session teardown

Chengfeng Ye <[email protected]>
Newsgroups org.kernel.vger.linux-bluetooth,org.kernel.vger.linux-kernel,org.kernel.vger.stable
Message-ID <CAAo+4rUJomK9jJ6nHC6LU0AHiR0LhO_Wwp4-Z8iJOo=Za1JR2g@mail.gmail.com>
Hi Ali,

Thanks for taking a close look.

On Sat, Aug 22, 2026 at 3:22 AM Ali Ahmet Memis <[email protected]> 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/[email protected]/T/
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.