From nobody Thu Sep 24 18:42:13 2026 Received: from mail-pz2-f12.google.com (mail-pz2-f12.google.com [74.125.228.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 73E594C33CE for ; Mon, 21 Sep 2026 15:39:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.228.12 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790005157; cv=none; b=EZTSvP237P0FnJdD/u64HRQpsPtSvG4QsTQqbfDaBxS+VeUrjR9OitgUA00pHRRVft4gWUhM7uqjXqz5FpYy1vpeaDIqKnLpUUMFp5ETldsHAKquTWixr7TuCznO6n0zQ3lQ+H1G8fAnMmpvf3Up5wGCwDlAfZyTqD+ZG015vfY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790005157; c=relaxed/simple; bh=GJDLDKF1u5CPMp4Uv16UsWMnehm7Urz6TVe/2jIm/eQ=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=tGdWP6eF+UQJZX8X4dR1vj6dKUY5x5zy4AZwPOMR8JPDCQqL1K9uySYfemX2XDAvuQQW44rC8kzPUxTRsl0Zid4NJLvXNTpBF93tSR6+fr5AyrNb+3etjsisKgV2uAw/YAYLwP/r4bJjGPRQ0S6SoBdM9K0n32TVKYii3EBRrVc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=HeaK/cHY; arc=none smtp.client-ip=74.125.228.12 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="HeaK/cHY" Received: by mail-pz2-f12.google.com with SMTP id d2e1a72fcca58-85469e211a3so2759640b3a.2 for ; Mon, 21 Sep 2026 08:39:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1790005156; x=1790609956; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=1X79HRIHH4he9wBEHQfrVBFJwF+UUIpDljtJ3hHJkuQ=; b=HeaK/cHYu3aIk8htG6QpSieWavPUbbG3LfBmZ/sda9d0iGkVoWqO+9IPaxIMNjxBxh jbySz9Oa/ojvkLe68+m7a187lBdctzW+fSdDdwbwZNAYuB1IzCjMGXOx1CRuPUsvJY8H SXDRQFyZGKI7IuMPCEygnzcj2LGBAyDpHN/EbjSE0aEFQwGepifzNTso4An44RCmgSGY hbcPj8WLsDU3oBR9mp4KVsBW7hHdvWokjX+6kC5ZPiKlekABTecICf9RftwHx1dCr9Zx ge3VohAMbl7+0+JsoBcfgkEqdxOvSbeb6BoAaZ3jQ92NqXwVEfJYVGxnuq3sI7OIcX8u gqYg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790005156; x=1790609956; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=1X79HRIHH4he9wBEHQfrVBFJwF+UUIpDljtJ3hHJkuQ=; b=vFTj9dGuFhgPttDrX92DZKckOmnLYauAqvLL32LvVm5fLXc+ptjME2Vvnnoi6Vphtv tjYc1Gw54GNCh2weYxeOiYQZAJeBTTgk58+JEz/vWyWWIXWG67zVIgBDZqlJPBvYtcl8 eH/XCKigMiQNH9rBsC2Z1gpyEwk1Cv+34DZDmqcAVnABv+giI6jpGfXMsgHNIfz1u4Ie UDEnQiCkqGyvOBLlswehR2wE/Td4pihHGmeM5YiujRIfHxQX/T5EB54Ffg94qEx4VMas XR1hyrp+ZIEAXK9TWUh4hr8nbix84gG/pa3/mokG/U7ujd/7b+1lB92lLdlqRB4udjXf Jq4w== X-Gm-Message-State: AFuF++nuaXyxe5DwlGcoVUxn1f5etKGN+yFhVCRLlopZ/xj742q36J+a Xm9ryXK74m6nY9A1EkRFy/+2FkkGvtjXArg83o33yzdGUjPNRwJU8hzq X-Gm-Gg: AYBFou3mGPHW+W2lLGh6lZZIMvQ043gOA9h+tMIMlIvrrplst2fotDlP+zhnVXtrNm4 MPBwNpTjRAiSYcwbCAwaESE7sRg3tvQkzzXdLMy4gHp6zcVt+kdzqP4Zs6dAQHpNA5014eao0NO +B+aus7PX5uFYgbp4CKfQhMcEPoqSFMTnBnsQ68goXZf6Uq1vrPb+6ut3ByfR+BOx4Etm/o3qxb xfXfvTeKhYtTZJGle8rj9udedr7yhFgTUDplzNOmCjE2I8/e5+KwY9IVPhanNyoazy43i1fs2PW UUcuU250HoHmCQ6YNFhFhFapceEE9PJQqENB6LFzI+Q4XxjjrIJ7Xed+Y3TWxjAWnSAk49diA5U K1Fpi2gwzqPO4/37EiR2WZSPKax/iKRx1vX2GRmhZc716YkM+ynXQYtMMb3FEaoD8lDX9iE/fs5 d0DmqWPbcHdrPQluS68zUQEbqp8WY/hZF0wGSmLw+fc1A+8EWpOg4lTUKfTpTcNclnbQ== X-Received: by 2002:a05:6a00:2d26:b0:857:72f8:dc98 with SMTP id d2e1a72fcca58-874dddfe1ffmr15944015b3a.25.1790005155685; Mon, 21 Sep 2026 08:39:15 -0700 (PDT) Received: from localhost ([111.228.63.84]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-877a6ae7d71sm3448186b3a.2.2026.09.21.08.39.11 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 21 Sep 2026 08:39:15 -0700 (PDT) From: Cen Zhang To: Tejun Heo , Lai Jiangshan , Marcel Holtmann , Luiz Augusto von Dentz , Marco Elver , Jukka Rissanen Cc: linux-kernel@vger.kernel.org, linux-bluetooth@vger.kernel.org, baijiaju1990@gmail.com, jjzuming@gmail.com, zzzccc427@gmail.com Subject: [PATCH 1/5] Bluetooth: L2CAP: ignore close requests for deleted channels Date: Mon, 21 Sep 2026 23:38:58 +0800 Message-Id: X-Mailer: git-send-email 2.34.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" l2cap_chan_del() keeps chan->conn alive until channel destruction and marks removal with FLAG_DEL, but does not necessarily change the state. A caller retaining a temporary reference can therefore enter l2cap_chan_close_unlocked() after deletion and follow a stale connected state, rearming the channel timer and sending another disconnection request for a channel no longer on the connection list. Check FLAG_DEL after taking the channel and connection locks so that concurrent deletion is serialized with the decision to close. Return without further timer or signaling work when the channel is deleted. Fixes: b66774b48dd9 ("Bluetooth: L2CAP: Fix UAF in channel timeout by holdi= ng conn ref") Assisted-by: LLM Signed-off-by: Cen Zhang --- net/bluetooth/l2cap_core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 49a998804908..65e957fdc7ae 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -939,8 +939,10 @@ void l2cap_chan_close_unlocked(struct l2cap_chan *chan= , int reason) =20 have_conn =3D l2cap_chan_lock_conn(chan); =20 - /* Context analysis: consider chan->conn->lock held also if conn NULL */ - context_unsafe(__l2cap_chan_close(chan, reason)); + if (!test_bit(FLAG_DEL, &chan->flags)) { + /* Consider chan->conn->lock held also if conn NULL */ + context_unsafe(__l2cap_chan_close(chan, reason)); + } =20 l2cap_chan_unlock_conn(chan, have_conn); } base-commit: 019debf20bfd648b40ba10377ee0168db5eb241e --=20 2.43.0 From nobody Thu Sep 24 18:42:13 2026 Received: from mail-pf1-f170.google.com (mail-pf1-f170.google.com [209.85.210.170]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D1CE44C6515 for ; Mon, 21 Sep 2026 15:39:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790005163; cv=none; b=mbOE+xZ0yXa6ToLo/WQ0Ax4Dw/Sszmb5HAwqciE8EfUs9yiwR9hR1TcfFku+2PxXPYsZ04cIPpS8uV/iwlLreY1mZ/QxwjJKv8eK+j9ij6nC597RxepIdhD+kGSPftgFr7h3ZJROdFt9SEnzTV/ATB6V/mLVnw9tBPzpRUOrN9o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790005163; c=relaxed/simple; bh=lJmFUvv1y720XfvJovLrsTCuIMar34mvrHnhguM16E8=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=ff1G26/6W7Kuf+ExQmovng5+6Xxcz7zGzGTC4CJ+/tg0ljuIq6qZ80jaZ/LDqKQTN64IOZHpFj0zAjPQG/ygDWWq5R4BrUlUJHdHxbAd6j1uqg0HR+I3PCskdpjyEEJerqRa5TxXNc0Ic3Z0vf4v80nzXR2+b4hkyv5mSN1PPyY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=o/29qR3i; arc=none smtp.client-ip=209.85.210.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="o/29qR3i" Received: by mail-pf1-f170.google.com with SMTP id d2e1a72fcca58-853e2610bb4so10696b3a.0 for ; Mon, 21 Sep 2026 08:39:21 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1790005161; x=1790609961; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=O3XTZ9CTE6oB6PdoqCRQ1HTKuKJNIEpv7/7Iapup7c0=; b=o/29qR3iKcg5Ry9P8efXUMukhuo0aUcZ/mKz63l4RNnhFmDFTk3en/x0KD3ixGrOGR OwkgI3Ab+/hoBnRbtb2Gb8HkUw+taY+1ENWWDpX5CWZ0X8rGtm4Q/HZFrZLyTPfSivV1 o3AZRrcuJ9x3DsjxJo43e+RDqSGk5FoMOQP4FpP/0v8qwnNP7LH66wvrpRdP/2CDhi+g 3uAIfNu5ONpff2gz6JCZjbRJhL57cq6wDw7pXg3nwU4ef/VXBwkkA2mmjszzx3H246Mx Yk7tAvqx7YuvEHgMt5y1fDvhdN0LIqHzRCX1C4AVvcimF5sKY1ys/dpM5tKUxx3sy2bx deuQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790005161; x=1790609961; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=O3XTZ9CTE6oB6PdoqCRQ1HTKuKJNIEpv7/7Iapup7c0=; b=cQrO3S+2FUiLxDgohJ7XVA1J3rzV8Zb6Lv/1QtN0nfWwEyOo+xdUkoP92dw2Y9FsD5 2KvQmGV2TzJEaHu/cdD6bDJBJawd1UBkY/JqW5rDVzSEi/0aZlQZOIEE5FTe7KVMB+4b 8sGgCpSUulftJHioZoZAlkCAadlX3yauzDeP1aWAOz7j9O1GK3wo+GiX2JroPfemnBN9 +6c+YetNu3RKYNH4PkacXdnnsqPsKK27PEe/8iaANGQ/reKiuMaSr1W0VzU/9WLVns10 CnJLwiIfnMor0adAl6zBY0Qfvnt2bLE7xFlxvY2Se7vSdyJXwvNW589BKto0SaOmXzyF w96Q== X-Gm-Message-State: AFuF++mXaD4StGKNXrOapNr1LTvoS5mEbDSYbXpnnPwKuxkav0KSntZz 702BQbItyeGcLI5+pGC0iEBD++od/3b+P+gQTbnBBGvMqpQR1uwB5gDa X-Gm-Gg: AYBFou2W/KrE6Ycta/Ks5oXm86RdbpRS6EK+amGNhXR95QhBeKmnNDo9DvPq+KLmNem 5rShqyQ7reHa5ETOJMefAuY5M6q5NOy7I36jTPX+jSdbIyRb/qPps5vFS9KruboAvGyxq00qCMk 2aCVnJggWESayul5bd8zxfem0Ha24E6mGL8i4Vlhyg5+KLGj+18FPZ/RnQg5guPzTkUcpHZnww8 ZROOT4hxa9t1SD3oEAO5seR6pHyOUNti/9CGcG/QMCj1dWzSR93KoA/hc9RHeYqwy7uHYl30eZo kZggMZzVrP8oUZ/LClq0Yv4Pm0Nj1NfIwUQAJHbWbk5P1sxku5XpfQmJla8wxEWkq7vjSO5RlTz oO4B5dcgZfvhV21rAk/uim8NQyrSpqX+Bxn6Ei3lVPKx8csZH/IGhooMqEOrTypcTB7ZpoNCPWQ 3g9tENIqfkrZx3HGgNkpv0dDqS5XprwvG0vM0BpMBDtqNxO0M4+zpnMiUHV3eFqsva1JM= X-Received: by 2002:a05:6a00:158c:b0:878:3705:5726 with SMTP id d2e1a72fcca58-87837055eb1mr4496461b3a.50.1790005161045; Mon, 21 Sep 2026 08:39:21 -0700 (PDT) Received: from localhost ([111.228.63.84]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-877a6ae7d71sm3448186b3a.2.2026.09.21.08.39.16 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 21 Sep 2026 08:39:20 -0700 (PDT) From: Cen Zhang To: Tejun Heo , Lai Jiangshan , Marcel Holtmann , Luiz Augusto von Dentz , Marco Elver , Jukka Rissanen Cc: linux-kernel@vger.kernel.org, linux-bluetooth@vger.kernel.org, baijiaju1990@gmail.com, jjzuming@gmail.com, zzzccc427@gmail.com Subject: [PATCH 2/5] workqueue: add support for module-owned work Date: Mon, 21 Sep 2026 23:38:59 +0800 Message-Id: X-Mailer: git-send-email 2.34.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Queueing a callback on a system workqueue does not take a reference to the module containing that callback. A caller which releases its last module reference after queueing work can therefore leave a callback in unloaded text. Releasing the reference from the callback itself also leaves its return path unprotected. Add module_work and schedule_module_work() to hold the callback's owner from queueing until the callback returns. Run the dispatch and final module_put() in workqueue core, which remains present when the callback's module is unloaded. Cache the function and owner before invoking the callback so that it can free the containing work item. 6LoWPAN needs this for deferred network-device deletion after removing the last peer. Assisted-by: LLM Signed-off-by: Cen Zhang --- include/linux/workqueue.h | 15 ++++++++++++++ kernel/workqueue.c | 43 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index c8a36423cb34..9920796c8822 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -128,6 +128,14 @@ struct rcu_work { struct workqueue_struct *wq; }; =20 +struct module; + +struct module_work { + struct work_struct work; + struct module *owner; + work_func_t func; +}; + enum wq_affn_scope { WQ_AFFN_DFL, /* use system default */ WQ_AFFN_CPU, /* one pod per CPU */ @@ -220,6 +228,11 @@ static inline struct rcu_work *to_rcu_work(struct work= _struct *work) return container_of(work, struct rcu_work, work); } =20 +static inline struct module_work *to_module_work(struct work_struct *work) +{ + return container_of(work, struct module_work, work); +} + struct execute_work { struct work_struct work; }; @@ -634,6 +647,8 @@ extern void __flush_workqueue(struct workqueue_struct *= wq); extern void drain_workqueue(struct workqueue_struct *wq); =20 extern int schedule_on_each_cpu(work_func_t func); +bool schedule_module_work(struct module_work *mwork, work_func_t func, + struct module *owner); =20 int execute_in_process_context(work_func_t fn, struct execute_work *); =20 diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 1ae3732a2c51..1a16bc5dfb68 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -48,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -4808,6 +4809,48 @@ int execute_in_process_context(work_func_t fn, struc= t execute_work *ew) } EXPORT_SYMBOL_GPL(execute_in_process_context); =20 +static void module_work_func(struct work_struct *work) +{ + struct module_work *mwork =3D to_module_work(work); + struct module *owner =3D mwork->owner; + work_func_t func =3D mwork->func; + + func(work); + module_put(owner); +} + +/** + * schedule_module_work - schedule work owned by a module + * @mwork: module work to schedule + * @func: work function to schedule + * @owner: module owning @func + * + * Take a reference to @owner before scheduling @func. The reference is + * released by workqueue core after the callback returns. The callback may + * free @mwork. @mwork must not be pending. + * + * Return: %false if the module is being removed or the work could not be + * queued, %true otherwise. + */ +bool schedule_module_work(struct module_work *mwork, work_func_t func, + struct module *owner) +{ + if (!try_module_get(owner)) + return false; + + INIT_WORK(&mwork->work, module_work_func); + mwork->owner =3D owner; + mwork->func =3D func; + + if (!schedule_work(&mwork->work)) { + module_put(owner); + return false; + } + + return true; +} +EXPORT_SYMBOL_GPL(schedule_module_work); + /** * free_workqueue_attrs - free a workqueue_attrs * @attrs: workqueue_attrs to free --=20 2.43.0 From nobody Thu Sep 24 18:42:13 2026 Received: from mail-pz2-f41.google.com (mail-pz2-f41.google.com [74.125.228.41]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EE9234C9E13 for ; Mon, 21 Sep 2026 15:39:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.228.41 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790005169; cv=none; b=glDspN0IU9f1AQnS+Gex5WNwbwf41YLpibhD7u+LjAkjfJ6KSXN1U8LDJl/6B6foaUWAb3lvbDzDI8fCoKw4ffgb2y1rOOTI+fdJfagu4yJsIB86bBhkEyIHkwRpSh7zS8oYr7TJkfdl9iXQK0KF8ZjgbbBxmzlztBwJQ/78h1U= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790005169; c=relaxed/simple; bh=tK/DOiKTS9+8p6418bETPstSP0bDfPyum5QKnVTwCQk=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=SFzpo9lPN6udE2tECeawie/CwDUJD1YOyey+OzCvT5fL+c7JhB0LejFGMpj0xbdknIj6MzK7wpd9Z3T2B5ABbVE8Vx5/Xe8YEV3j/7DSYhsG98pSsU9ldQAjn2SZL+1BLeVYWse0CzhypX2/rEFmkUcIZ11kxYqzo/CznStJeYA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=IFZWYHfe; arc=none smtp.client-ip=74.125.228.41 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="IFZWYHfe" Received: by mail-pz2-f41.google.com with SMTP id d2e1a72fcca58-85469e25187so1776110b3a.2 for ; Mon, 21 Sep 2026 08:39:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1790005166; x=1790609966; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=xLGIKtekCp4Iv+F6FSHNltwIt7TcrRN5peUWusm9SWk=; b=IFZWYHfeYxiuJDvpIp2lKYUueQ4Uv/GyEM8X/JXmrNWIHmDnVZIvvxofGK20KLcNM3 df/zA8GC4UDgifIrhFH0AYIzxTCVWEs5fxHz+7E/mruz3AfHpAr4hzmSdoK2uvM/WeL7 q0WahKIuTTncQiH4Sy1wWpIF2426W13LYrnnn/QR4i/W5AiFi1518DPkgIpdAYKIgLrA V+J7vfczRyX21R6kY/ImaSTXrG6ORUw3DVAi40FHOBv9JypnnBQlSj6otSCIi2AdQHb0 zgN0y7IrUavW89zb2XIkOyyXyeYC7FchQNtU6tqxC1vUBBG56KAcIOHE/BFYaWIW2cei BidQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790005166; x=1790609966; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=xLGIKtekCp4Iv+F6FSHNltwIt7TcrRN5peUWusm9SWk=; b=Hfy5ptRK0iqN0j6RzozKVevhY7rLKtHd34qiR1gYwfpGCvbeXdFFkSAIw5ld9kVBqh uQpa48dzoGxos91FNmPb8+nrOVLPww4M6V1n13HirTBscEZi5mRFogXi2ZEPGgydgn62 C9dF1Upel8CITQz1SlNDL7UxiTAzg9ZABBmk3xxpBTx4ZvRYkuf9Co8n2eE9N7ik3lkX CJD2VH+2vMrA4H3yZ+cZietVm8gvGIsfU/Rt0LAvKygI7mJ9juY2/s3usJ4PzY0F6GEI ZJ2m3oyYFjKRpenU3AUgCbl0ZHn7OAUTy84jdbDB1p8WAyyl62R+8DO8iBdZ3j8sNF3b 9g9g== X-Gm-Message-State: AFuF++l4Ue/ifnXJGEmNSiOx8q1uKbWCZ/qE/VsBsqURLe+sEM2Tc8wX ilWlnjVjl4cZKDb38QcqIDO6JXI5ClmDGUmfBeyWsLtAnDYwYWmqqMNK X-Gm-Gg: AYBFou1AJpHhCHX16SeZTPNT/wck8Xwzz0+UEYQajhMNzwKsSjwLMVyZKM+hwvR+fPG 4MBYJdrxn+/7BYtWdNfutwuX2PHntpavwhXIg+fpXiZOyVEDl9y94mMyUM/D2ghLYCFz+xbEnJT iB8xg5U1wjRzQjHjXElF7J8bvy6U9Ei4abyNKlMMtH7uv5UL3XHEKyK/h11ea41/Lj+8s9z5+fa /EpceckeuDq47xa8s396IeS7Bu0MdwAxLlBpklIMrC03R1Ow16zZXgXUnJn76Lbxv1STD+w5PjW TkxTrT1r9oC0pjJ/u07CXQqLGBTMLGg1AUurXvFG/L2srqXzT4pBD5AouB35herWTSUBNbGRXRa ypbrsYDtarNfFYYLYJ42Vf7BSKzr2dir8j8rF+PpsTiwPtpVhn6dCaHv8Cnzq9ADgQAIBUangwo SqsSlEwZVVcjQu6WuIFWTj4FPrCjyIEGfuI/nAm7IprpS4JPDOpx8nIDpM/PwvdKfBqw== X-Received: by 2002:a05:6a00:288a:b0:87b:784b:455c with SMTP id d2e1a72fcca58-87b784b5205mr1078518b3a.56.1790005165964; Mon, 21 Sep 2026 08:39:25 -0700 (PDT) Received: from localhost ([111.228.63.84]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-877a6ae7d71sm3448186b3a.2.2026.09.21.08.39.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 21 Sep 2026 08:39:25 -0700 (PDT) From: Cen Zhang To: Tejun Heo , Lai Jiangshan , Marcel Holtmann , Luiz Augusto von Dentz , Marco Elver , Jukka Rissanen Cc: linux-kernel@vger.kernel.org, linux-bluetooth@vger.kernel.org, baijiaju1990@gmail.com, jjzuming@gmail.com, zzzccc427@gmail.com Subject: [PATCH 3/5] Bluetooth: L2CAP: drain channel timers on connection teardown Date: Mon, 21 Sep 2026 23:39:00 +0800 Message-Id: X-Mailer: git-send-email 2.34.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" L2CAP channel timers run on system_percpu_wq. Connection deletion cancels them without waiting because callbacks may need conn->lock, but HCI unregister does not drain that workqueue after releasing the lock. A running callback can therefore outlive HCI-driver unregister. If it drops the last channel reference and releases a protocol module, it still has to return through Bluetooth code after those dependencies are gone. Individual channel deletion has another cancellation race. It cancels timers before calling the socket teardown callback, which can wait for sk lock. A concurrent recvmsg holding that lock can clear local busy and rearm the monitor timer. Deletion then unlinks the channel, so connection teardown can no longer find that delayed work through the channel list. Give each connection an ordered timer workqueue. Serialize timer queueing with channel and connection stop flags, and stop each channel before canceling its timers or calling teardown. At connection deletion, stop queueing for the connection, cancel all four timer types, then drop conn->lock and drain running callbacks before releasing the connection. This lets callbacks acquire their locks, observe FLAG_DEL and return before HCI unregister completes. The channel stop flag also ensures that no unlinked channel can leave delayed work behind on the new queue. The queue remains allocated until the drained connection is freed. This adds one workqueue per connection and serializes that connection's channel timers. Assisted-by: LLM Signed-off-by: Cen Zhang --- include/net/bluetooth/l2cap.h | 23 ++++++++++++++++++++++- net/bluetooth/l2cap_core.c | 34 ++++++++++++++++++++++++++++++---- 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index efb9b7f422d1..b4af087a0a81 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -611,6 +611,7 @@ struct l2cap_chan { =20 void *data; const struct l2cap_ops *ops; + bool timers_stopped; /* protected by conn->timer_lock */ struct mutex lock; }; =20 @@ -636,6 +637,10 @@ struct l2cap_conn { =20 struct sk_buff_head pending_rx; struct work_struct pending_rx_work; + struct workqueue_struct *timer_workqueue; + spinlock_t timer_lock; /* protects timer scheduling */ + + bool timers_stopped __guarded_by(&timer_lock); =20 struct delayed_work id_addr_timer; =20 @@ -856,13 +861,29 @@ static inline void l2cap_chan_unlock(struct l2cap_cha= n *chan) static inline void l2cap_set_timer(struct l2cap_chan *chan, struct delayed_work *work, long timeout) { + struct l2cap_conn *conn =3D chan->conn; + unsigned long flags; + bool pending; + BT_DBG("chan %p state %s timeout %ld", chan, state_to_string(chan->state), timeout); =20 + if (WARN_ON_ONCE(!conn)) + return; + + spin_lock_irqsave(&conn->timer_lock, flags); + if (conn->timers_stopped || chan->timers_stopped) { + spin_unlock_irqrestore(&conn->timer_lock, flags); + return; + } + l2cap_chan_hold(chan); =20 /* put(chan) if timer was already queued so it already has a ref */ - if (mod_delayed_work(system_percpu_wq, work, timeout)) + pending =3D mod_delayed_work(conn->timer_workqueue, work, timeout); + spin_unlock_irqrestore(&conn->timer_lock, flags); + + if (pending) l2cap_chan_put(chan); } =20 diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 65e957fdc7ae..0df7bda54473 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -686,9 +686,21 @@ void l2cap_chan_add(struct l2cap_conn *conn, struct l2= cap_chan *chan) =20 void l2cap_chan_del(struct l2cap_chan *chan, int err) { + struct l2cap_conn *conn =3D chan->conn; + unsigned long flags; + lockdep_assert(!chan->conn || lockdep_is_held(&chan->conn->lock)); =20 + if (conn) { + spin_lock_irqsave(&conn->timer_lock, flags); + chan->timers_stopped =3D true; + spin_unlock_irqrestore(&conn->timer_lock, flags); + } + __clear_chan_timer(chan); + __clear_retrans_timer(chan); + __clear_monitor_timer(chan); + __clear_ack_timer(chan); =20 BT_DBG("chan %p, err %d, state %s", chan, err, state_to_string(chan->state)); @@ -723,10 +735,6 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err) break; =20 case L2CAP_MODE_ERTM: - __clear_retrans_timer(chan); - __clear_monitor_timer(chan); - __clear_ack_timer(chan); - skb_queue_purge(&chan->srej_q); =20 l2cap_seq_list_free(&chan->srej_list); @@ -1879,6 +1887,7 @@ static void l2cap_conn_del(struct hci_conn *hcon, int= err) { struct l2cap_conn *conn =3D hcon->l2cap_data; struct l2cap_chan *chan, *l; + unsigned long flags; =20 if (!conn) return; @@ -1891,6 +1900,9 @@ static void l2cap_conn_del(struct hci_conn *hcon, int= err) cancel_work_sync(&conn->pending_rx_work); =20 mutex_lock(&conn->lock); + spin_lock_irqsave(&conn->timer_lock, flags); + conn->timers_stopped =3D true; + spin_unlock_irqrestore(&conn->timer_lock, flags); =20 kfree_skb(conn->rx_skb); =20 @@ -1925,6 +1937,11 @@ static void l2cap_conn_del(struct hci_conn *hcon, in= t err) spin_unlock(&hcon->proto_lock); =20 mutex_unlock(&conn->lock); + + /* Channel deletion canceled pending timers. Drop conn->lock before + * waiting for running callbacks so they can acquire it and return. + */ + drain_workqueue(conn->timer_workqueue); l2cap_conn_put(conn); } =20 @@ -1932,6 +1949,7 @@ static void l2cap_conn_free(struct kref *ref) { struct l2cap_conn *conn =3D container_of(ref, struct l2cap_conn, ref); =20 + destroy_workqueue(conn->timer_workqueue); hci_conn_put(conn->hcon); kfree(conn); } @@ -7416,6 +7434,14 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_= conn *hcon) return NULL; } =20 + conn->timer_workqueue =3D alloc_ordered_workqueue("l2cap", WQ_MEM_RECLAIM= ); + if (!conn->timer_workqueue) { + kfree(conn); + hci_chan_del(hchan); + return NULL; + } + spin_lock_init(&conn->timer_lock); + kref_init(&conn->ref); conn->hchan =3D hchan; =20 --=20 2.43.0 From nobody Thu Sep 24 18:42:13 2026 Received: from mail-pz2-f12.google.com (mail-pz2-f12.google.com [74.125.228.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 684344C8FE5 for ; Mon, 21 Sep 2026 15:39:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.228.12 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790005175; cv=none; b=rjYO5icScJjR9TXcwhazzW1B/XvF/d244iBO0IEtD4knJEv+SFL7b+narDSYoZiPOd2kHEmYzA30ccsMySfnYL+xnonFp/lN8K0SywfAUuAdYdZexZGdBw7U7T2KxqVNosynJNaTzSro1VDR2UsEmoT67dWZdhA71/iB4/J/VzY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790005175; c=relaxed/simple; bh=RGsXweNLYSt5iBVvkA+azD+iGKH56TkTGcXPIwSmtcc=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=t3KTGXW3aup0bhHdPZCUjV4IWAslrbBsdnN/Mq9eDUnrO218GRjTw+auYHKfjDM/lScpOMQWv5rjo7s8z+IqijC+2MFEiVXzpsueGpOWOttoZD4cen3QyKBAJygxsSBEKmVxNlYlKb/bxTlqBHcXq4PBWgyO2kHImV3L3Z6rAI4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=W57xejGz; arc=none smtp.client-ip=74.125.228.12 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="W57xejGz" Received: by mail-pz2-f12.google.com with SMTP id d2e1a72fcca58-86868f7707dso1668932b3a.2 for ; Mon, 21 Sep 2026 08:39:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1790005170; x=1790609970; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=BfUEoOdPgaQ8YyR2hDF8OszYSiHTNY3qANom129NxFg=; b=W57xejGz504xZL8xRUK3wn6NQzLn9USfMgk92O/PNGfnN/eMmS+vyHkXt5yl7GS5AH I92Jn7Th+2QnRS7B15WePTA3YxNYRUQOHaGX6BIEjDbtdz1qJkHreDreiVu2wnuCJ37L 0ni2EPr/Jj1aYPhDcMz7d9pBfVeqsvbswik0F/wlQOYntlgENdXPHfQawMNk0Oi+cs3z NkAaZa3vrYG7yVtjSesmu1L07iHyxNFrstq3aCBrLlwdLtuCbfgPTQOTr/c58tMonEgI GCb3UHoKu9uDNgBp/XKdDKhRL3HEVAE3J4Ais1nd0q+s/HIDBz4asFwCzfhZxsP5q/0z fvGA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790005170; x=1790609970; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=BfUEoOdPgaQ8YyR2hDF8OszYSiHTNY3qANom129NxFg=; b=eN7i8+9ezt985H+GcHf/8Y5Jm5gchG7fP8n6XDFE1n4eZe/sOi7DgpAFXbOF5FHgnz 2eI5WXrv/OTA+9bECFO2c4JDsvSnV/Ifpl+D74JiSqn/UL8w7v7pMzFE1seLeaGqXKqc XHkDs9dTEmmnwoiRV4WQeIAkp5tiBCu/jBDDGh48/i+8rbSAhUTerIIxo/kvvSbhpLS2 UyOIk78KOvlltddQ8gYm47ZbXyHrCSw1cB6COObdPDKoNwf3ikEDSdZPSYJGC3XLCpov Kbox92wdrxeLp6DBlRqrAZMQXZHvfjV1IcMv95uUmQe6wYCY2HTh1Zu/1XUrBOMnyDTF YZNQ== X-Gm-Message-State: AFuF++n7KBy9NDJ4N4oZOFDpeCKtKZzE50nF1iCqpDiIvg1Tjk2BhKf/ v2NtXDiMZUvT4P2aL+xGv+7OkeTQ9tJEtOwG03WnffoFvptRgPA1a0a9 X-Gm-Gg: AYBFou1wMHQDUrfwnOU5atkVJI4I9302YR0VAfr4SmXNTVRNbh0s+DDzVrwf3pBq2dW KOq4ooRo1x3h9CpAL5Tk4bvDUmzkZwzecgUcm2FEcTfId8ijvRgN88CK9TQpFKRYja2jxQhPotG Xklha1G+ov55uaN1ndBlWAc0Eh12Z/lzvf5TurBJlBQ9/ECfJqDJsSiMe/HqPPfZJP8TOjYao+k 03Ze/Nu0eTlyKKyeT9rFBewS5br69+G3MDbdSuHPgRgYhskVxDU7DqjJiiEhkpxLQ+K1vhlCxKX S7XbB+IrtVBJ1eP94tJX2bBzOAAfPVnjC4kXEQuiaCD3BZojSumURckOkAGoAe2bHfyewOR8kKJ wcAh2s71c0ey3jqt9+lt5aVHWmUemyKkczzlqk0/UFXkpXR/D8pH34+V50+MqDHtBx5ZFz1XVy1 DJIZRXO0dstviL1Zc4wipAph0du98oIHHO37UXKrXVFjUir15AjdkbqC/loadaEI5NoA== X-Received: by 2002:a05:6a00:b49:b0:871:b1b0:e495 with SMTP id d2e1a72fcca58-874dc4f31aemr15536113b3a.15.1790005170033; Mon, 21 Sep 2026 08:39:30 -0700 (PDT) Received: from localhost ([111.228.63.84]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-877a6ae7d71sm3448186b3a.2.2026.09.21.08.39.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 21 Sep 2026 08:39:29 -0700 (PDT) From: Cen Zhang To: Tejun Heo , Lai Jiangshan , Marcel Holtmann , Luiz Augusto von Dentz , Marco Elver , Jukka Rissanen Cc: linux-kernel@vger.kernel.org, linux-bluetooth@vger.kernel.org, baijiaju1990@gmail.com, jjzuming@gmail.com, zzzccc427@gmail.com Subject: [PATCH 4/5] Bluetooth: 6lowpan: handle channel setup failure and callback lifetime Date: Mon, 21 Sep 2026 23:39:01 +0800 Message-Id: X-Mailer: git-send-email 2.34.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" L2CAP marks a channel connected before invoking its ready callback. 6LoWPAN can then fail to allocate or publish a peer, leaving an unusable connected channel or a network device without a peer. Deletion before peer publication also leaks the channel's initial reference: close only releases it after finding a peer. The peer's module reference ends too early as well. Removing the last peer releases it while the close callback is still executing, allowing bluetooth_6lowpan to be unloaded before that callback returns. Deferred network-device deletion also needs protection through callback return. Let ready return an error and handle it in the LE CoC and ECRED request and response paths. Allocate the peer before setting up a network device, and publish it before bringing the device up. Track ownership of the initial reference explicitly and release it once from teardown, even when no peer was published. Hold separate request-handler references through rollback, unlock and response construction. Hold the operations owner's module reference for accepted and outgoing channels until channel destruction. Keep the listener itself unpinned so module exit can close it; check BT_LISTEN under the parent lock and take a temporary owner reference before invoking new_connection. Use module-owned work for deferred network-device deletion. A close/unload race produced this instruction-fetch fault: [ 101.222759] BUG: unable to handle page fault for address: ffffffffc0= 40199f [ 101.224102] #PF: supervisor instruction fetch in kernel mode [ 101.225119] #PF: error_code(0x0010) - not-present page [ 101.226980] Oops: Oops: 0010 [#1] SMP KASAN NOPTI [... omitted ...] [ 101.232306] Workqueue: hci1 hci_rx_work [ 101.233015] RIP: 0010:0xffffffffc040199f [ 101.233780] Code: Unable to access opcode bytes at 0xffffffffc040197= 5. [... omitted ...] [ 101.275265] Modules linked in: [last unloaded: bluetooth_6lowpan(O)] [ 101.276117] CR2: ffffffffc040199f [... omitted ...] [ 101.287138] Kernel panic - not syncing: Fatal exception Fixes: 6b8d4a6a0314 ("Bluetooth: 6LoWPAN: Use connected oriented channel in= stead of fixed one") Assisted-by: LLM Signed-off-by: Cen Zhang --- include/net/bluetooth/l2cap.h | 10 +++- net/bluetooth/6lowpan.c | 86 ++++++++++++++++++++------------ net/bluetooth/l2cap_core.c | 94 +++++++++++++++++++++++++++++------ net/bluetooth/l2cap_sock.c | 6 ++- net/bluetooth/smp.c | 4 +- 5 files changed, 148 insertions(+), 52 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index b4af087a0a81..7194dc570ee8 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -611,6 +611,8 @@ struct l2cap_chan { =20 void *data; const struct l2cap_ops *ops; + struct module *ops_owner; + bool ops_owner_pinned; bool timers_stopped; /* protected by conn->timer_lock */ struct mutex lock; }; @@ -669,7 +671,7 @@ struct l2cap_ops { __must_hold(&chan->lock); void (*state_change) (struct l2cap_chan *chan, int state, int err); - void (*ready) (struct l2cap_chan *chan) + int (*ready)(struct l2cap_chan *chan) __must_hold(&chan->lock) __must_hold(&chan->conn->lock); void (*defer) (struct l2cap_chan *chan); @@ -764,6 +766,7 @@ enum { FLAG_ECRED_CONN_REQ_SENT, FLAG_PENDING_SECURITY, FLAG_HOLD_HCI_CONN, + FLAG_RELEASE_CREATOR, FLAG_DEL, }; =20 @@ -948,8 +951,9 @@ static inline void l2cap_chan_no_close(struct l2cap_cha= n *chan) { } =20 -static inline void l2cap_chan_no_ready(struct l2cap_chan *chan) +static inline int l2cap_chan_no_ready(struct l2cap_chan *chan) { + return 0; } =20 static inline void l2cap_chan_no_state_change(struct l2cap_chan *chan, @@ -994,6 +998,8 @@ int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *sr= c, __le16 psm); int l2cap_add_scid(struct l2cap_chan *chan, __u16 scid); =20 struct l2cap_chan *l2cap_chan_create(void); +bool l2cap_chan_set_ops(struct l2cap_chan *chan, + const struct l2cap_ops *ops, struct module *owner); void l2cap_chan_close_unlocked(struct l2cap_chan *chan, int reason) __must_not_hold(&chan->lock); int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index 836add41f5d1..5c49dc146086 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -52,6 +52,7 @@ static bool enable_6lowpan; */ static struct l2cap_chan *listen_chan; static DEFINE_MUTEX(set_lock); +static const struct l2cap_ops bt_6lowpan_chan_ops; =20 enum { LOWPAN_PEER_CLOSING, @@ -78,7 +79,7 @@ struct lowpan_btle_dev { struct list_head peers; atomic_t peer_count; /* number of items in peers list */ =20 - struct work_struct delete_netdev; + struct module_work delete_netdev; struct delayed_work notify_peers; }; =20 @@ -101,8 +102,6 @@ static inline bool peer_del(struct lowpan_btle_dev *dev, list_del_rcu(&peer->list); kfree_rcu(peer, rcu); =20 - module_put(THIS_MODULE); - if (atomic_dec_and_test(&dev->peer_count)) { BT_DBG("last peer"); return true; @@ -641,16 +640,10 @@ static struct l2cap_chan *chan_create(void) return chan; } =20 -static struct l2cap_chan *add_peer_chan(struct l2cap_chan *chan, - struct lowpan_btle_dev *dev, - bool new_netdev) +static void add_peer_chan(struct l2cap_chan *chan, + struct lowpan_btle_dev *dev, + struct lowpan_peer *peer, bool new_netdev) { - struct lowpan_peer *peer; - - peer =3D kzalloc_obj(*peer, GFP_ATOMIC); - if (!peer) - return NULL; - peer->chan =3D chan; =20 baswap((void *)peer->lladdr, &chan->dst); @@ -666,8 +659,6 @@ static struct l2cap_chan *add_peer_chan(struct l2cap_ch= an *chan, if (new_netdev) INIT_DELAYED_WORK(&dev->notify_peers, do_notify_peers); schedule_delayed_work(&dev->notify_peers, msecs_to_jiffies(100)); - - return peer->chan; } =20 static int setup_netdev(struct l2cap_chan *chan, struct lowpan_btle_dev **= dev) @@ -721,30 +712,37 @@ static int setup_netdev(struct l2cap_chan *chan, stru= ct lowpan_btle_dev **dev) return err; } =20 -static inline void chan_ready_cb(struct l2cap_chan *chan) +static inline int chan_ready_cb(struct l2cap_chan *chan) __must_hold(&chan->lock) __must_hold(&chan->conn->lock) { struct lowpan_btle_dev *dev; + struct lowpan_peer *peer; bool new_netdev =3D false; + int err; + + peer =3D kzalloc_obj(*peer, GFP_ATOMIC); + if (!peer) + return -ENOMEM; =20 dev =3D lookup_dev(chan->conn); =20 BT_DBG("chan %p conn %p dev %p", chan, chan->conn, dev); =20 if (!dev) { - if (setup_netdev(chan, &dev) < 0) { - l2cap_chan_del(chan, -ENOENT); - return; - } + err =3D setup_netdev(chan, &dev); + if (err < 0) + goto free_peer; new_netdev =3D true; } =20 - if (!try_module_get(THIS_MODULE)) - return; - - add_peer_chan(chan, dev, new_netdev); + add_peer_chan(chan, dev, peer, new_netdev); ifup(dev->netdev); + return 0; + +free_peer: + kfree(peer); + return err; } =20 static void unregister_dev(struct lowpan_btle_dev *dev) @@ -771,7 +769,7 @@ static void delete_netdev(struct work_struct *work) { struct lowpan_btle_dev *entry =3D container_of(work, struct lowpan_btle_dev, - delete_netdev); + delete_netdev.work); =20 unregister_dev(entry); =20 @@ -785,6 +783,7 @@ static void chan_close_cb(struct l2cap_chan *chan) struct lowpan_peer *peer; int err =3D -ENOENT; bool last =3D false; + bool queued; =20 BT_DBG("chan %p conn %p", chan, chan->conn); =20 @@ -799,10 +798,6 @@ static void chan_close_cb(struct l2cap_chan *chan) =20 BT_DBG("dev %p removing %speer %p", dev, last ? "last " : "1 ", peer); - BT_DBG("chan %p orig refcnt %u", chan, - kref_read(&chan->kref)); - - l2cap_chan_put(chan); break; } } @@ -814,8 +809,9 @@ static void chan_close_cb(struct l2cap_chan *chan) =20 ifdown(dev->netdev); =20 - INIT_WORK(&entry->delete_netdev, delete_netdev); - schedule_work(&entry->delete_netdev); + queued =3D schedule_module_work(&entry->delete_netdev, + delete_netdev, THIS_MODULE); + WARN_ON_ONCE(!queued); } else { spin_unlock(&devices_lock); } @@ -874,8 +870,27 @@ static long chan_get_sndtimeo_cb(struct l2cap_chan *ch= an) return L2CAP_CONN_TIMEOUT; } =20 +static int chan_new_connection_cb(struct l2cap_chan *chan, + struct l2cap_chan *new_chan) +{ + if (!l2cap_chan_set_ops(new_chan, &bt_6lowpan_chan_ops, THIS_MODULE)) + return -ENODEV; + + set_bit(FLAG_RELEASE_CREATOR, &new_chan->flags); + return 0; +} + +static void chan_teardown_cb(struct l2cap_chan *chan, int err) +{ + chan->state =3D BT_CLOSED; + + if (test_and_clear_bit(FLAG_RELEASE_CREATOR, &chan->flags)) + l2cap_chan_put(chan); +} + static const struct l2cap_ops bt_6lowpan_chan_ops =3D { .name =3D "L2CAP 6LoWPAN channel", + .new_connection =3D chan_new_connection_cb, .recv =3D chan_recv_cb, .close =3D chan_close_cb, .state_change =3D chan_state_change_cb, @@ -885,7 +900,7 @@ static const struct l2cap_ops bt_6lowpan_chan_ops =3D { .get_sndtimeo =3D chan_get_sndtimeo_cb, .alloc_skb =3D chan_alloc_skb_cb, =20 - .teardown =3D l2cap_chan_no_teardown, + .teardown =3D chan_teardown_cb, .defer =3D l2cap_chan_no_defer, .set_shutdown =3D l2cap_chan_no_set_shutdown, }; @@ -899,7 +914,12 @@ static int bt_6lowpan_connect(bdaddr_t *addr, u8 dst_t= ype) if (!chan) return -EINVAL; =20 - chan->ops =3D &bt_6lowpan_chan_ops; + if (!l2cap_chan_set_ops(chan, &bt_6lowpan_chan_ops, THIS_MODULE)) { + l2cap_chan_put(chan); + return -ENODEV; + } + + set_bit(FLAG_RELEASE_CREATOR, &chan->flags); =20 err =3D l2cap_chan_connect(chan, cpu_to_le16(L2CAP_PSM_IPSP), 0, addr, dst_type, L2CAP_CONN_TIMEOUT); @@ -952,7 +972,9 @@ static struct l2cap_chan *bt_6lowpan_listen(void) if (!chan) return NULL; =20 + /* The listener is closed by module_exit(), so it must not self-pin. */ chan->ops =3D &bt_6lowpan_chan_ops; + chan->ops_owner =3D THIS_MODULE; chan->state =3D BT_LISTEN; chan->src_type =3D BDADDR_LE_PUBLIC; =20 diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 0df7bda54473..f6a87a44d8a6 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -481,9 +481,27 @@ struct l2cap_chan *l2cap_chan_create(void) } EXPORT_SYMBOL_GPL(l2cap_chan_create); =20 +bool l2cap_chan_set_ops(struct l2cap_chan *chan, + const struct l2cap_ops *ops, struct module *owner) +{ + if (WARN_ON_ONCE(chan->ops_owner)) + return false; + + if (!try_module_get(owner)) + return false; + + chan->ops =3D ops; + chan->ops_owner =3D owner; + chan->ops_owner_pinned =3D true; + return true; +} +EXPORT_SYMBOL_GPL(l2cap_chan_set_ops); + static void l2cap_chan_destroy(struct kref *kref) { struct l2cap_chan *chan =3D container_of(kref, struct l2cap_chan, kref); + struct module *ops_owner =3D chan->ops_owner; + bool ops_owner_pinned =3D chan->ops_owner_pinned; =20 BT_DBG("chan %p", chan); =20 @@ -495,6 +513,8 @@ static void l2cap_chan_destroy(struct kref *kref) l2cap_conn_put(chan->conn); =20 kfree(chan); + if (ops_owner_pinned) + module_put(ops_owner); } =20 void l2cap_chan_hold(struct l2cap_chan *c) @@ -1352,7 +1372,7 @@ void l2cap_send_conn_req(struct l2cap_chan *chan) l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ, sizeof(req), &req); } =20 -static void l2cap_chan_ready(struct l2cap_chan *chan) +static int l2cap_chan_ready(struct l2cap_chan *chan) __must_hold(&chan->lock) __must_hold(&chan->conn->lock) { @@ -1361,7 +1381,7 @@ static void l2cap_chan_ready(struct l2cap_chan *chan) * procedure is complete. */ if (chan->state =3D=3D BT_CONNECTED) - return; + return 0; =20 /* This clears all conf flags, including CONF_NOT_COMPLETE */ chan->conf_state =3D 0; @@ -1377,7 +1397,7 @@ static void l2cap_chan_ready(struct l2cap_chan *chan) =20 chan->state =3D BT_CONNECTED; =20 - chan->ops->ready(chan); + return chan->ops->ready(chan); } =20 static void l2cap_le_connect(struct l2cap_chan *chan) @@ -4241,10 +4261,20 @@ static struct l2cap_chan *l2cap_new_connection(stru= ct l2cap_conn *conn, __must_hold(&pchan->lock) { struct l2cap_chan *chan; + struct module *owner; + + if (pchan->state !=3D BT_LISTEN) + return NULL; + + owner =3D pchan->ops_owner; + if (!try_module_get(owner)) + return NULL; =20 chan =3D l2cap_chan_create(); - if (!chan) + if (!chan) { + module_put(owner); return NULL; + } =20 l2cap_chan_lock(chan); =20 @@ -4260,10 +4290,12 @@ static struct l2cap_chan *l2cap_new_connection(stru= ct l2cap_conn *conn, l2cap_chan_del(chan, 0); l2cap_chan_unlock(chan); l2cap_chan_put(chan); + module_put(owner); return NULL; } =20 l2cap_chan_unlock(chan); + module_put(owner); =20 return chan; } @@ -5011,7 +5043,7 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *co= nn, struct hci_conn *hcon =3D conn->hcon; u16 dcid, mtu, mps, credits, result; struct l2cap_chan *chan; - int err, sec_level; + int err, ready_err, sec_level; =20 if (cmd_len < sizeof(*rsp)) return -EPROTO; @@ -5056,7 +5088,11 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *c= onn, chan->omtu =3D mtu; chan->remote_mps =3D mps; chan->tx_credits =3D credits; - l2cap_chan_ready(chan); + ready_err =3D l2cap_chan_ready(chan); + if (ready_err < 0) { + l2cap_send_disconn_req(chan, -ready_err); + l2cap_chan_del(chan, -ready_err); + } break; =20 case L2CAP_CR_LE_AUTHENTICATION: @@ -5180,9 +5216,10 @@ static int l2cap_le_connect_req(struct l2cap_conn *c= onn, { struct l2cap_le_conn_req *req =3D (struct l2cap_le_conn_req *) data; struct l2cap_le_conn_rsp rsp; - struct l2cap_chan *chan, *pchan; + struct l2cap_chan *chan, *chan_ref =3D NULL, *pchan; u16 dcid, scid, credits, mtu, mps; __le16 psm; + int err; u8 result; =20 if (cmd_len !=3D sizeof(*req)) @@ -5260,6 +5297,9 @@ static int l2cap_le_connect_req(struct l2cap_conn *co= nn, goto response_unlock; } =20 + /* ->ready() may delete the channel. */ + l2cap_chan_hold(chan); + chan_ref =3D chan; l2cap_chan_lock(chan); =20 lockdep_assert_held(&chan->conn->lock); @@ -5293,18 +5333,26 @@ static int l2cap_le_connect_req(struct l2cap_conn *= conn, result =3D L2CAP_CR_PEND; chan->ops->defer(chan); } else { - l2cap_chan_ready(chan); - result =3D L2CAP_CR_LE_SUCCESS; + err =3D l2cap_chan_ready(chan); + if (err < 0) { + l2cap_chan_del(chan, -err); + chan =3D NULL; + dcid =3D 0; + credits =3D 0; + result =3D L2CAP_CR_LE_NO_MEM; + } else { + result =3D L2CAP_CR_LE_SUCCESS; + } } =20 - l2cap_chan_unlock(chan); + l2cap_chan_unlock(chan_ref); =20 response_unlock: l2cap_chan_unlock(pchan); l2cap_chan_put(pchan); =20 if (result =3D=3D L2CAP_CR_PEND) - return 0; + goto done; =20 response: if (chan) { @@ -5321,6 +5369,10 @@ static int l2cap_le_connect_req(struct l2cap_conn *c= onn, =20 l2cap_send_cmd(conn, cmd->ident, L2CAP_LE_CONN_RSP, sizeof(rsp), &rsp); =20 +done: + if (chan_ref) + l2cap_chan_put(chan_ref); + return 0; } =20 @@ -5385,7 +5437,7 @@ static inline int l2cap_ecred_conn_req(struct l2cap_c= onn *conn, u16 mtu, mps; __le16 psm; u8 result, rsp_len =3D 0; - int i, num_scid =3D 0; + int err, i, num_scid =3D 0; bool defer =3D false; =20 if (!enable_ecred) @@ -5493,6 +5545,8 @@ static inline int l2cap_ecred_conn_req(struct l2cap_c= onn *conn, continue; } =20 + /* ->ready() may delete the channel. */ + l2cap_chan_hold(chan); l2cap_chan_lock(chan); =20 lockdep_assert_held(&chan->conn->lock); @@ -5527,10 +5581,16 @@ static inline int l2cap_ecred_conn_req(struct l2cap= _conn *conn, defer =3D true; chan->ops->defer(chan); } else { - l2cap_chan_ready(chan); + err =3D l2cap_chan_ready(chan); + if (err < 0) { + l2cap_chan_del(chan, -err); + pdu->dcid[i] =3D 0; + result =3D L2CAP_CR_LE_NO_MEM; + } } =20 l2cap_chan_unlock(chan); + l2cap_chan_put(chan); } =20 unlock: @@ -5558,7 +5618,7 @@ static inline int l2cap_ecred_conn_rsp(struct l2cap_c= onn *conn, struct hci_conn *hcon =3D conn->hcon; u16 mtu, mps, credits, result; struct l2cap_chan *chan, *tmp; - int err =3D 0, sec_level; + int err =3D 0, ready_err, sec_level; int i =3D 0; =20 if (cmd_len < sizeof(*rsp)) @@ -5673,7 +5733,11 @@ static inline int l2cap_ecred_conn_rsp(struct l2cap_= conn *conn, chan->omtu =3D mtu; chan->remote_mps =3D mps; chan->tx_credits =3D credits; - l2cap_chan_ready(chan); + ready_err =3D l2cap_chan_ready(chan); + if (ready_err < 0) { + l2cap_send_disconn_req(chan, -ready_err); + l2cap_chan_del(chan, -ready_err); + } break; } =20 diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 278adb05c4c9..7a631581950f 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -1825,13 +1825,13 @@ static struct sk_buff *l2cap_sock_alloc_skb_cb(stru= ct l2cap_chan *chan, return skb; } =20 -static void l2cap_sock_ready_cb(struct l2cap_chan *chan) +static int l2cap_sock_ready_cb(struct l2cap_chan *chan) { struct sock *sk =3D chan->data; struct sock *parent; =20 if (!sk) - return; + return 0; =20 lock_sock(sk); =20 @@ -1846,6 +1846,8 @@ static void l2cap_sock_ready_cb(struct l2cap_chan *ch= an) parent->sk_data_ready(parent); =20 release_sock(sk); + + return 0; } =20 static void l2cap_sock_defer_cb(struct l2cap_chan *chan) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 0badb93a5725..28e32e8cf3b6 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -3155,7 +3155,7 @@ static void smp_resume_cb(struct l2cap_chan *chan) smp_distribute_keys(smp); } =20 -static void smp_ready_cb(struct l2cap_chan *chan) +static int smp_ready_cb(struct l2cap_chan *chan) { struct l2cap_conn *conn =3D chan->conn; struct hci_conn *hcon =3D conn->hcon; @@ -3172,6 +3172,8 @@ static void smp_ready_cb(struct l2cap_chan *chan) =20 if (hcon->type =3D=3D ACL_LINK && test_bit(HCI_CONN_ENCRYPT, &hcon->flags= )) bredr_pairing(chan); + + return 0; } =20 static int smp_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb) --=20 2.43.0 From nobody Thu Sep 24 18:42:13 2026 Received: from mail-pz2-f43.google.com (mail-pz2-f43.google.com [74.125.228.43]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 765A54C7546 for ; Mon, 21 Sep 2026 15:39:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.228.43 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790005178; cv=none; b=PkfKDdAt/Ig8rivKvlxxE+9qpM1BZUd8oO2BvjxCC5aSsbIJaRGS9jeOUodcTDd3XG7OZYkqTOAcxHHTgDIFum//Xo37vjH/XWChZDesWuOj8SDLkq9h1VAF9Wd5MDIZYkCmApMiZ4ddgCC3WfrgqEOjdiMGyiWgY81keZTC78c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790005178; c=relaxed/simple; bh=8gYOgwL82gLctBDUo5hY0Mf4FC393MJ0QflOqBAlaI8=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=bffaN5P5CM7qGjPiF86A6jxo9RSflroGpzpLKENYXoSdD3D9J4KUfbYpxzn+pGgzyVF4Skxbwegcd3DTk/LZseDUYVMBxt/+5hYbDUkIB4BuV63asvmChZD4RPvJiTA2jCDNOV565C5Z0QUcNKomsPgrIbQyKnvw1LZs8XDoWdg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=HYLmtnvG; arc=none smtp.client-ip=74.125.228.43 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="HYLmtnvG" Received: by mail-pz2-f43.google.com with SMTP id d2e1a72fcca58-8625b35df89so252147b3a.0 for ; Mon, 21 Sep 2026 08:39:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1790005175; x=1790609975; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=aQQ4QUI52Sa5O7i3Cjz9FO5zM2Do0eGarq+pPmIyhnc=; b=HYLmtnvGDt3N+3cD67nR65l6U43wDXTEls/J77w1RchPea2tivAOmkXBY75Abb+sxN keb0QTVZgrzT9fm1II3gK6HLS8y5LwIb8ADpHtM1O59APPt5EQC+KW8jlOzwaXRdp0/l icQLd2VEOwNf336swQ95wNEKHw9+tCpk5zHmm1j8cqep/j22eJ7J+7qrAnmefqs3Z2eu WxiSKpw5GVeRW0v3lz8qTm7EheNFWKqkcPGRNKj8zn8Gl2/hLFSkDUIcYWpdQUTgu1uw yhDI1BLDL1sqkABd58AGmiVeqkS8iYMvCHE8XvEyDlkrTjjQkmO/HxLcPOVUsYZyX34R earA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790005175; x=1790609975; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=aQQ4QUI52Sa5O7i3Cjz9FO5zM2Do0eGarq+pPmIyhnc=; b=KLhdvtw1p+wDU/zpkyhJrH7Tt5fIztnTrfWcwVjgoAw+CvpWmvC80aiBEs5u4P2er8 tohertjT07amo9Mya+coALvjpwn1BruxY91/xavraBoGE/1XyqvdgZL/43YrQ+0c1Mr1 dHRpyj6Duj9h4MZ2xpJWRScj0YVfk0sDprQUyAGwiN9pwQ9h+kkV2wta2Ce6UxUbGVuy 7k4rIpMSZ6NC6Pis/HqPXB11ADPhMP7lhMH7ugno1U9PZUj8CNVVb1kkzBtUZ2137HH8 epzBUa8GeZvUS6nnYgkrZU3PeJhUh1cso8xZyycerE5a+wgloXhGD+H6towqsYde/x7a tzRw== X-Gm-Message-State: AFuF++mXQQUmL0Wo1Yhx5ce+h0MXEutzRG0uLWJ+tJRXxT3xL5lSr4AY XWoLIZ/PZF3Vr8VhqwwFz6dM/mip6oFye1Rfejj7cAvTlXMftT74zNDI X-Gm-Gg: AYBFou1QCj4UMC/6KDnoyrBJv9ROrRv9Eo4oDwIqg1bsqmax4J8cyjxcgTft6jRd283 9Pe5X+pRlEbgpdJbougYjjtBXfvB7W4weZX+pLI+s6WBckPiVL+Z+45AqogLFKDDClGqS1JQkuE yc+o2qn+cdgd3d6cq8j/yy8nIjTzivsX2s5tLsFrGS9Oh2dpMRXzd00FLKT6niVZGWSMq5kMBxZ qOwTSBqF+wu6uuNkmEzbIo7hRtBli4ixmuHc/2COfppoVxxndAPJ6Ty8sn+cE3Obo47gphujS8o 5fA+uYsld3qalHYfbgO3C7hmZjX46/rLkRf2aT548TaTU0c48usFjMeuMTIAtnVRC04w2tViwzh b2nd6Pv6mQq8zRVQAiDmkYfxyORMetuoxOGJd+1tYDcrgmJ4fVuQNSGEBT83wf1Uo1DPd3grSgl AUM6fnocLJ5UV/wxmbcWuWg+MpLOThQGbEnPJct9ZUXa99+YDE34eV2FKTPlq66oMUog== X-Received: by 2002:a05:6a00:148f:b0:869:86ae:e94f with SMTP id d2e1a72fcca58-87bbef82b42mr18320b3a.4.1790005174987; Mon, 21 Sep 2026 08:39:34 -0700 (PDT) Received: from localhost ([111.228.63.84]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-877a6ae7d71sm3448186b3a.2.2026.09.21.08.39.30 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 21 Sep 2026 08:39:34 -0700 (PDT) From: Cen Zhang To: Tejun Heo , Lai Jiangshan , Marcel Holtmann , Luiz Augusto von Dentz , Marco Elver , Jukka Rissanen Cc: linux-kernel@vger.kernel.org, linux-bluetooth@vger.kernel.org, baijiaju1990@gmail.com, jjzuming@gmail.com, zzzccc427@gmail.com Subject: [PATCH 5/5] Bluetooth: 6lowpan: quiesce peers before channel deletion Date: Mon, 21 Sep 2026 23:39:02 +0800 Message-Id: X-Mailer: git-send-email 2.34.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 6LoWPAN removes a peer and drops its channel reference from the close callback. A transmitter which already observed the RCU-published peer can still use the freed channel. Keeping the allocation alive alone is insufficient: a transmitter which passed the deletion check can enqueue a packet after L2CAP has purged the transmit queue. Move peer cleanup to teardown, before FLAG_DEL and the transmit purge. Mark the channel closed, unlink the peer under devices_lock, then drop the lock and wait for network RCU readers with synchronize_net(). Free the peer and release the initial channel reference only after those transmitters have returned. Queue last-peer network-device deletion before releasing the channel's ownership reference. Disabling 6LoWPAN must also close the listener before sweeping existing peers. Otherwise a request holding the old listener can publish a child after the sweep. Serialize the transition with set_lock: requests which complete admission before listener teardown are included in the sweep, and requests which reach the closed listener are rejected. The transmit/removal race produced this report: [ 59.413897] BUG: KASAN: slab-use-after-free in send_pkt+0x3b1/0x3e0 [ 59.415014] Write of size 8 at addr ffff88810cf0e4a0 by task python3= /583 [ ... report excerpt omitted ... ] [ 59.490444] Freed by task 504: [ ... report excerpt omitted ... ] [ 59.493046] kfree+0x307/0x580 [ 59.493497] l2cap_chan_put+0x273/0x3a0 [ 59.494020] l2cap_disconnect_req+0x613/0x890 [ ... report excerpt omitted ... ] Fixes: 6b8d4a6a0314 ("Bluetooth: 6LoWPAN: Use connected oriented channel in= stead of fixed one") Assisted-by: LLM Signed-off-by: Cen Zhang --- net/bluetooth/6lowpan.c | 64 +++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index 5c49dc146086..60d2c6d1be99 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -61,7 +61,6 @@ enum { =20 struct lowpan_peer { struct list_head list; - struct rcu_head rcu; struct l2cap_chan *chan; =20 /* peer addresses in various formats */ @@ -100,7 +99,6 @@ static inline bool peer_del(struct lowpan_btle_dev *dev, struct lowpan_peer *peer) { list_del_rcu(&peer->list); - kfree_rcu(peer, rcu); =20 if (atomic_dec_and_test(&dev->peer_count)) { BT_DBG("last peer"); @@ -776,16 +774,15 @@ static void delete_netdev(struct work_struct *work) /* The entry pointer is deleted by the netdev destructor. */ } =20 -static void chan_close_cb(struct l2cap_chan *chan) +static void chan_teardown_cb(struct l2cap_chan *chan, int err) { struct lowpan_btle_dev *entry; struct lowpan_btle_dev *dev =3D NULL; - struct lowpan_peer *peer; - int err =3D -ENOENT; + struct lowpan_peer *peer =3D NULL; bool last =3D false; - bool queued; =20 BT_DBG("chan %p conn %p", chan, chan->conn); + chan->state =3D BT_CLOSED; =20 spin_lock(&devices_lock); =20 @@ -794,7 +791,6 @@ static void chan_close_cb(struct l2cap_chan *chan) peer =3D __peer_lookup_chan(dev, chan); if (peer) { last =3D peer_del(dev, peer); - err =3D 0; =20 BT_DBG("dev %p removing %speer %p", dev, last ? "last " : "1 ", peer); @@ -802,19 +798,28 @@ static void chan_close_cb(struct l2cap_chan *chan) } } =20 - if (!err && last && dev && !atomic_read(&dev->peer_count)) { - spin_unlock(&devices_lock); + spin_unlock(&devices_lock); =20 - cancel_delayed_work_sync(&dev->notify_peers); + if (peer) { + /* ndo_start_xmit() holds network RCU while using peer->chan. */ + synchronize_net(); + kfree(peer); =20 - ifdown(dev->netdev); + if (last && dev) { + bool queued; =20 - queued =3D schedule_module_work(&entry->delete_netdev, - delete_netdev, THIS_MODULE); - WARN_ON_ONCE(!queued); - } else { - spin_unlock(&devices_lock); + cancel_delayed_work_sync(&dev->notify_peers); + + ifdown(dev->netdev); + + queued =3D schedule_module_work(&entry->delete_netdev, + delete_netdev, THIS_MODULE); + WARN_ON_ONCE(!queued); + } } + + if (test_and_clear_bit(FLAG_RELEASE_CREATOR, &chan->flags)) + l2cap_chan_put(chan); } =20 static void chan_state_change_cb(struct l2cap_chan *chan, int state, int e= rr) @@ -873,6 +878,9 @@ static long chan_get_sndtimeo_cb(struct l2cap_chan *cha= n) static int chan_new_connection_cb(struct l2cap_chan *chan, struct l2cap_chan *new_chan) { + if (chan->state !=3D BT_LISTEN) + return -EINVAL; + if (!l2cap_chan_set_ops(new_chan, &bt_6lowpan_chan_ops, THIS_MODULE)) return -ENODEV; =20 @@ -880,19 +888,11 @@ static int chan_new_connection_cb(struct l2cap_chan *= chan, return 0; } =20 -static void chan_teardown_cb(struct l2cap_chan *chan, int err) -{ - chan->state =3D BT_CLOSED; - - if (test_and_clear_bit(FLAG_RELEASE_CREATOR, &chan->flags)) - l2cap_chan_put(chan); -} - static const struct l2cap_ops bt_6lowpan_chan_ops =3D { .name =3D "L2CAP 6LoWPAN channel", .new_connection =3D chan_new_connection_cb, .recv =3D chan_recv_cb, - .close =3D chan_close_cb, + .close =3D l2cap_chan_no_close, .state_change =3D chan_state_change_cb, .ready =3D chan_ready_cb, .resume =3D chan_resume_cb, @@ -1103,20 +1103,22 @@ static void disconnect_all_peers(void) =20 static void do_enable_set(bool flag) { - if (!flag || enable_6lowpan !=3D flag) - /* Disconnect existing connections if 6lowpan is - * disabled - */ - disconnect_all_peers(); + bool disconnect; + + mutex_lock(&set_lock); =20 + disconnect =3D !flag || enable_6lowpan !=3D flag; enable_6lowpan =3D flag; =20 - mutex_lock(&set_lock); if (listen_chan) { l2cap_chan_close_unlocked(listen_chan, 0); l2cap_chan_put(listen_chan); + listen_chan =3D NULL; } =20 + if (disconnect) + disconnect_all_peers(); + listen_chan =3D bt_6lowpan_listen(); mutex_unlock(&set_lock); } --=20 2.43.0