Re: [PATCH] Bluetooth: RFCOMM: serialize session teardown
From: Pauli Virtanen
Date: Sat Aug 22 2026 - 04:22:59 EST
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@xxxxxx>
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@xxxxxxxxxxxxxxx
> Signed-off-by: Chengfeng Ye <nicoyip.dev@xxxxxxxxx>
> ---
> 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