* Re: [PATCH v2] Bluetooth: btintel_pcie: Separate coredump work from RX work
From: patchwork-bot+bluetooth @ 2026-06-11 17:58 UTC (permalink / raw)
To: Kiran K
Cc: linux-bluetooth, ravishankar.srivatsa, chethan.tumkur.narayan,
chandrashekar.devegowda, ravindra
In-Reply-To: <20260610162544.240444-1-kiran.k@intel.com>
Hello:
This patch was applied to bluetooth/bluetooth-next.git (master)
by Luiz Augusto von Dentz <luiz.von.dentz@intel.com>:
On Wed, 10 Jun 2026 21:55:44 +0530 you wrote:
> From: Ravindra <ravindra@intel.com>
>
> Sharing a single workqueue between coredump processing and RX
> delays evacuation of RX events while a coredump is in progress.
> The firmware's RX buffers can overflow during that window, leading
> to dropped events. The issue was observed in HID use cases where
> HID reports arrive in bursts and quickly fill the RX path while a
> coredump is being collected.
>
> [...]
Here is the summary with links:
- [v2] Bluetooth: btintel_pcie: Separate coredump work from RX work
https://git.kernel.org/bluetooth/bluetooth-next/c/27e1ec48aeb4
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH] Bluetooth: btmtksdio: fix infinite loop in btmtksdio_txrx_work()
From: patchwork-bot+bluetooth @ 2026-06-11 17:58 UTC (permalink / raw)
To: Sergey Senozhatsky
Cc: marcel, luiz.dentz, mark-yw.chen, sean.wang, tfiga,
linux-bluetooth, linux-kernel, linux-arm-kernel, linux-mediatek,
stable
In-Reply-To: <20260609121329.1262170-1-senozhatsky@chromium.org>
Hello:
This patch was applied to bluetooth/bluetooth-next.git (master)
by Luiz Augusto von Dentz <luiz.von.dentz@intel.com>:
On Tue, 9 Jun 2026 21:10:06 +0900 you wrote:
> Every once in a while we see a hung btmtksdio_flush() task:
>
> INFO: task kworker/u17:0:189 blocked for more than 122 seconds.
> __cancel_work_timer+0x3f4/0x460
> cancel_work_sync+0x1c/0x2c
> btmtksdio_flush+0x2c/0x40
> hci_dev_open_sync+0x10c4/0x2190
> [..]
>
> [...]
Here is the summary with links:
- Bluetooth: btmtksdio: fix infinite loop in btmtksdio_txrx_work()
https://git.kernel.org/bluetooth/bluetooth-next/c/a4263306d01d
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [GIT PULL] bluetooth 2026-06-03
From: patchwork-bot+bluetooth @ 2026-06-11 17:58 UTC (permalink / raw)
To: Luiz Augusto von Dentz; +Cc: davem, kuba, linux-bluetooth, netdev
In-Reply-To: <20260603162714.342496-1-luiz.dentz@gmail.com>
Hello:
This pull request was applied to bluetooth/bluetooth-next.git (master)
by Jakub Kicinski <kuba@kernel.org>:
On Wed, 3 Jun 2026 12:27:14 -0400 you wrote:
> The following changes since commit cdf88b35e06f1b385f7f6228060ae541d44fbb72:
>
> Bluetooth: hci_sync: Reset device counters in hci_dev_close_sync() (2026-05-28 08:52:21 -0400)
>
> are available in the Git repository at:
>
> git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth.git tags/for-net-2026-06-03
>
> [...]
Here is the summary with links:
- [GIT,PULL] bluetooth 2026-06-03
https://git.kernel.org/bluetooth/bluetooth-next/c/d20da9130830
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [GIT PULL] bluetooth 2026-05-20
From: patchwork-bot+bluetooth @ 2026-06-11 17:58 UTC (permalink / raw)
To: Luiz Augusto von Dentz; +Cc: davem, kuba, linux-bluetooth, netdev
In-Reply-To: <20260520204959.2902497-1-luiz.dentz@gmail.com>
Hello:
This pull request was applied to bluetooth/bluetooth-next.git (master)
by Jakub Kicinski <kuba@kernel.org>:
On Wed, 20 May 2026 16:49:58 -0400 you wrote:
> The following changes since commit 375ba7484132662a4a8c7547d088fb6275c00282:
>
> Bluetooth: hci_qca: Convert timeout from jiffies to ms (2026-05-14 09:58:08 -0400)
>
> are available in the Git repository at:
>
> git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth.git tags/for-net-2026-05-20
>
> [...]
Here is the summary with links:
- [GIT,PULL] bluetooth 2026-05-20
https://git.kernel.org/bluetooth/bluetooth-next/c/5027c886e26c
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH net] 6lowpan: fix off-by-one in multicast context address compression
From: patchwork-bot+bluetooth @ 2026-06-11 17:58 UTC (permalink / raw)
To: Yizhou Zhao
Cc: netdev, alex.aring, davem, edumazet, kuba, pabeni, horms,
linux-bluetooth, linux-wpan, linux-kernel, yangyx22, wangao,
fengxw06, qli01, xuke
In-Reply-To: <20260527081806.42747-1-zhaoyz24@mails.tsinghua.edu.cn>
Hello:
This patch was applied to bluetooth/bluetooth-next.git (master)
by Jakub Kicinski <kuba@kernel.org>:
On Wed, 27 May 2026 16:18:01 +0800 you wrote:
> The second memcpy in lowpan_iphc_mcast_ctx_addr_compress() uses
> &data[1] as destination and &ipaddr->s6_addr[11] as source, but
> both should be offset by one: &data[2] and &ipaddr->s6_addr[12]
> respectively.
>
> This off-by-one has two consequences:
> 1. data[1] is overwritten with s6_addr[11], corrupting the RIID
> field in the compressed multicast address
> 2. data[5] is never written, so uninitialized kernel stack memory
> is transmitted over the network via lowpan_push_hc_data(),
> leaking kernel stack contents
>
> [...]
Here is the summary with links:
- [net] 6lowpan: fix off-by-one in multicast context address compression
https://git.kernel.org/bluetooth/bluetooth-next/c/2a58899d1100
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [GIT PULL] bluetooth 2026-05-14
From: patchwork-bot+bluetooth @ 2026-06-11 17:58 UTC (permalink / raw)
To: Luiz Augusto von Dentz; +Cc: davem, kuba, linux-bluetooth, netdev
In-Reply-To: <20260514172340.1515042-1-luiz.dentz@gmail.com>
Hello:
This pull request was applied to bluetooth/bluetooth-next.git (master)
by Jakub Kicinski <kuba@kernel.org>:
On Thu, 14 May 2026 13:23:40 -0400 you wrote:
> The following changes since commit c78bdba7b9666020c0832150a4fc4c0aebc7c6ac:
>
> net: phy: DP83TC811: add reading of abilities (2026-05-14 15:17:12 +0200)
>
> are available in the Git repository at:
>
> git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth.git tags/for-net-2026-05-14
>
> [...]
Here is the summary with links:
- [GIT,PULL] bluetooth 2026-05-14
https://git.kernel.org/bluetooth/bluetooth-next/c/23f3b04f157c
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* [bluez/bluez] b5b40a: profiles/audio/bass: Use BASS_Modify_Source when a...
From: Luiz Augusto von Dentz @ 2026-06-11 17:53 UTC (permalink / raw)
To: linux-bluetooth
Branch: refs/heads/1110215
Home: https://github.com/bluez/bluez
Commit: b5b40a4bd3dd23a5569c616ebaf14126881b7ed1
https://github.com/bluez/bluez/commit/b5b40a4bd3dd23a5569c616ebaf14126881b7ed1
Author: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Date: 2026-06-11 (Thu, 11 Jun 2026)
Changed paths:
M profiles/audio/bass.c
Log Message:
-----------
profiles/audio/bass: Use BASS_Modify_Source when assistant is active
When a MediaAssistant is already active (streaming), a subsequent Push
should send Modify Source with PA_SYNC_NO_SYNC and bis_sync=0 to
remove the broadcast source, rather than adding a duplicate source.
Split push() into push_add_src() and push_mod_src() helpers. The push
dispatcher checks the assistant state: ACTIVE non-local assistants and
LOCAL assistants with a tracked source entry both route to
push_mod_src, while all others proceed with push_add_src.
Add per-device source tracking for LOCAL assistants via a bass_src
struct (keyed by bt_bass pointer) stored in a per-assistant srcs
queue. This is needed because LOCAL assistants never change state, so
we cannot rely on the state machine alone to detect active sources.
Also fix the bass_src_changed BID filter: the previous check rejected
non-local assistants when BID was zero, but this incorrectly excluded
LOCAL assistants in REQUESTING state whose BID happens to be non-zero.
Change to reject assistants whose own BID is non-zero when the
notification BID is zero, which correctly matches local streams.
Commit: e7d235efae7ed33f0ae0df91205277668181e0f4
https://github.com/bluez/bluez/commit/e7d235efae7ed33f0ae0df91205277668181e0f4
Author: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Date: 2026-06-11 (Thu, 11 Jun 2026)
Changed paths:
M profiles/audio/bass.c
M src/shared/bass.c
Log Message:
-----------
profiles/audio/bass: Handle PA_SYNC_NO_SYNC in handle_mod_src_req
When the delegator receives a Modify Source operation with pa_sync set
to PA_SYNC_NO_SYNC while already synchronized to PA, release all
setups instead of updating BIS sync which would not apply the
requested termination.
Also reset the encryption state to no encryption when the last BIS
index is cleared from a subgroup, so subsequent source additions
start with a clean encryption state.
Commit: ef38d6371550e7596888e8198d328c802520bd18
https://github.com/bluez/bluez/commit/ef38d6371550e7596888e8198d328c802520bd18
Author: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Date: 2026-06-11 (Thu, 11 Jun 2026)
Changed paths:
M profiles/audio/bass.c
Log Message:
-----------
profiles/audio/bass: Fix delegator reprobing after disconnect
delegator_disconnect left the old delegator in the delegators queue
after calling device_remove_profile. When a new delegator was created
for the same device (e.g. on a subsequent Add Source after Modify
Source removal), delegator_attach would find the stale entry and
return early, leaving the new delegator without a service. This
caused the "Unable to probe service" error and prevented the BASS
bcode exchange from working on reconnection.
Fix by removing the delegator from the queue, clearing service user
data, and calling delegator_free before disconnecting the service
and removing the profile. This also fixes a potential use-after-free
where delegator_disconnect accessed dg fields after device_remove_profile
could trigger delegator_detach -> delegator_free through the
bap_detached callback chain.
Also add validation in bass_req_bcode to detect and discard invalid
(all-zeros) broadcast codes that may have been stored from a previous
connection.
Commit: ca320ea1e889ef325c64aaf9d41e32acdc7285d0
https://github.com/bluez/bluez/commit/ca320ea1e889ef325c64aaf9d41e32acdc7285d0
Author: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Date: 2026-06-11 (Thu, 11 Jun 2026)
Changed paths:
M doc/org.bluez.MediaAssistant.rst
Log Message:
-----------
doc: Document Push behavior when MediaAssistant is active
Document that calling Push on an assistant already in active state uses
BASS_Modify_Source to update the existing source rather than adding a
new one.
Compare: https://github.com/bluez/bluez/compare/b5b40a4bd3dd%5E...ca320ea1e889
To unsubscribe from these emails, change your notification settings at https://github.com/bluez/bluez/settings/notifications
^ permalink raw reply
* [PATCH BlueZ v1 4/4] doc: Document Push behavior when MediaAssistant is active
From: Luiz Augusto von Dentz @ 2026-06-11 17:22 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <20260611172235.92930-1-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Document that calling Push on an assistant already in active state uses
BASS_Modify_Source to update the existing source rather than adding a
new one.
---
doc/org.bluez.MediaAssistant.rst | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/doc/org.bluez.MediaAssistant.rst b/doc/org.bluez.MediaAssistant.rst
index 45143cee8dbf..2f29a7b5c253 100644
--- a/doc/org.bluez.MediaAssistant.rst
+++ b/doc/org.bluez.MediaAssistant.rst
@@ -27,6 +27,10 @@ void Push(dict properties)
Send stream information to the remote device.
+If the assistant is in the "active" state, this method uses
+BASS_Modify_Source to update the existing source on the remote device
+instead of adding a new one with BASS_Add_Source.
+
:dict properties:
Indicate stream properties that will be sent to the peer.
--
2.54.0
^ permalink raw reply related
* [PATCH BlueZ v1 3/4] profiles/audio/bass: Fix delegator reprobing after disconnect
From: Luiz Augusto von Dentz @ 2026-06-11 17:22 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <20260611172235.92930-1-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
delegator_disconnect left the old delegator in the delegators queue
after calling device_remove_profile. When a new delegator was created
for the same device (e.g. on a subsequent Add Source after Modify
Source removal), delegator_attach would find the stale entry and
return early, leaving the new delegator without a service. This
caused the "Unable to probe service" error and prevented the BASS
bcode exchange from working on reconnection.
Fix by removing the delegator from the queue, clearing service user
data, and calling delegator_free before disconnecting the service
and removing the profile. This also fixes a potential use-after-free
where delegator_disconnect accessed dg fields after device_remove_profile
could trigger delegator_detach -> delegator_free through the
bap_detached callback chain.
Also add validation in bass_req_bcode to detect and discard invalid
(all-zeros) broadcast codes that may have been stored from a previous
connection.
---
profiles/audio/bass.c | 35 ++++++++++++++++++++++++++++++-----
1 file changed, 30 insertions(+), 5 deletions(-)
diff --git a/profiles/audio/bass.c b/profiles/audio/bass.c
index 5eb7bff076bb..a5ef80fbc835 100644
--- a/profiles/audio/bass.c
+++ b/profiles/audio/bass.c
@@ -257,9 +257,17 @@ static void bass_req_bcode(struct bt_bap_stream *stream,
}
if (dg->bcode) {
- /* Broadcast Code has already been received before. */
- setup_set_bcode(dg->bcode, setup, reply, reply_data);
- return;
+ uint8_t empty_bcode[BT_BASS_BCAST_CODE_SIZE] = {0};
+
+ if (memcmp(dg->bcode, empty_bcode, BT_BASS_BCAST_CODE_SIZE)) {
+ /* Broadcast Code has already been received before. */
+ setup_set_bcode(dg->bcode, setup, reply, reply_data);
+ return;
+ }
+
+ error("dg %p has invalid bcode", dg);
+ free(dg->bcode);
+ dg->bcode = NULL;
}
/* Create a request for the Broadcast Code. The request
@@ -376,20 +384,37 @@ static void setup_free(void *data)
free(setup);
}
+static void delegator_free(struct bass_delegator *dg);
+
static void delegator_disconnect(struct bass_delegator *dg)
{
struct btd_device *device = dg->device;
struct btd_service *service = dg->service;
+ struct btd_profile *profile = btd_service_get_profile(service);
DBG("%p", dg);
+ /* Remove delegator from the queue and free it before removing the
+ * service profile. This prevents delegator_attach from finding a
+ * stale entry when a new delegator is created for the same device,
+ * and also avoids use-after-free since device_remove_profile may
+ * trigger bap_detached -> delegator_detach which would otherwise
+ * try to free the delegator again.
+ */
+ queue_remove(delegators, dg);
+ btd_service_set_user_data(service, NULL);
+ delegator_free(dg);
+
/* Disconnect service so BAP driver is cleanup properly and bt_bap is
* detached from the device.
*/
btd_service_disconnect(service);
- /* Remove service since delegator shold have been freed at this point */
- device_remove_profile(device, btd_service_get_profile(service));
+ /* Remove service since delegator has been freed at this point.
+ * delegator_detach (triggered by bap_detached) will be a no-op
+ * since the delegator has already been removed from the queue.
+ */
+ device_remove_profile(device, profile);
/* If the device is no longer consider connected it means no other
* service was connected so it has no longer any use and can be safely
--
2.54.0
^ permalink raw reply related
* [PATCH BlueZ v1 1/4] profiles/audio/bass: Use BASS_Modify_Source when assistant is active
From: Luiz Augusto von Dentz @ 2026-06-11 17:22 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
When a MediaAssistant is already active (streaming), a subsequent Push
should send Modify Source with PA_SYNC_NO_SYNC and bis_sync=0 to
remove the broadcast source, rather than adding a duplicate source.
Split push() into push_add_src() and push_mod_src() helpers. The push
dispatcher checks the assistant state: ACTIVE non-local assistants and
LOCAL assistants with a tracked source entry both route to
push_mod_src, while all others proceed with push_add_src.
Add per-device source tracking for LOCAL assistants via a bass_src
struct (keyed by bt_bass pointer) stored in a per-assistant srcs
queue. This is needed because LOCAL assistants never change state, so
we cannot rely on the state machine alone to detect active sources.
Also fix the bass_src_changed BID filter: the previous check rejected
non-local assistants when BID was zero, but this incorrectly excluded
LOCAL assistants in REQUESTING state whose BID happens to be non-zero.
Change to reject assistants whose own BID is non-zero when the
notification BID is zero, which correctly matches local streams.
---
profiles/audio/bass.c | 176 +++++++++++++++++++++++++++++++++++-------
1 file changed, 150 insertions(+), 26 deletions(-)
diff --git a/profiles/audio/bass.c b/profiles/audio/bass.c
index 1fd7704a77a2..f018f8b3ad7f 100644
--- a/profiles/audio/bass.c
+++ b/profiles/audio/bass.c
@@ -108,9 +108,16 @@ struct bass_assistant {
struct iovec *meta;
struct iovec *caps;
enum assistant_state state;
+ uint8_t src_id;
+ struct queue *srcs; /* Per-device source tracking */
char *path;
};
+struct bass_src {
+ struct bt_bass *bass;
+ uint8_t src_id;
+};
+
struct bass_delegator {
struct btd_device *device; /* Broadcast source device */
struct btd_service *service;
@@ -158,6 +165,17 @@ static void bass_data_remove(struct bass_data *data);
static void bis_probe(uint8_t sid, uint8_t bis, uint8_t sgrp,
struct iovec *caps, struct iovec *meta,
struct bt_bap_qos *qos, void *user_data);
+
+static bool match_src_data(const void *data, const void *match_data)
+{
+ const struct bass_src *src = data;
+ const struct bt_bass *bass = match_data;
+
+ return src->bass == bass;
+}
+
+static struct bass_src *assistant_add_src(struct bass_assistant *assistant,
+ struct bt_bass *bass, uint8_t id);
static void bis_remove(struct bt_bap *bap, void *user_data);
@@ -1010,35 +1028,62 @@ static void assistant_past(struct bass_assistant *assistant)
free(addr);
}
-static DBusMessage *push(DBusConnection *conn, DBusMessage *msg,
- void *user_data)
+static DBusMessage *push_mod_src(struct bass_assistant *assistant,
+ DBusMessage *msg)
+{
+ struct bt_bass_bcast_audio_scan_cp_hdr hdr;
+ struct bt_bass_mod_src_params params = {0};
+ struct iovec iov = {0};
+ uint32_t bis_sync = 0;
+ uint8_t meta_len = 0;
+ int err;
+
+ hdr.op = BT_BASS_MOD_SRC;
+
+ params.id = assistant->src_id;
+ params.pa_sync = PA_SYNC_NO_SYNC;
+ params.pa_interval = PA_INTERVAL_UNKNOWN;
+ params.num_subgroups = assistant->sgrp + 1;
+
+ util_iov_append(&iov, ¶ms, sizeof(params));
+
+ for (uint8_t sgrp = 0; sgrp < params.num_subgroups; sgrp++) {
+ util_iov_append(&iov, &bis_sync, sizeof(bis_sync));
+ util_iov_append(&iov, &meta_len, sizeof(meta_len));
+ }
+
+ err = bt_bass_send(assistant->data->bass, &hdr, &iov);
+ if (err) {
+ DBG("Unable to send BASS Write Command");
+ return btd_error_failed(msg, strerror(-err));
+ }
+
+ free(iov.iov_base);
+
+ if (assistant->device) {
+ assistant_set_state(assistant, ASSISTANT_STATE_IDLE);
+ } else {
+ struct bass_src *src;
+
+ src = queue_remove_if(assistant->srcs, match_src_data,
+ assistant->data->bass);
+ free(src);
+ }
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *push_add_src(struct bass_assistant *assistant,
+ DBusMessage *msg)
{
- struct bass_assistant *assistant = user_data;
struct bt_bass_bcast_audio_scan_cp_hdr hdr;
struct bt_bass_add_src_params params = {0};
struct iovec iov = {0};
uint32_t bis_sync = 0;
uint8_t meta_len = 0;
int err;
- DBusMessageIter props, dict;
struct io *io;
- DBG("");
-
- dbus_message_iter_init(msg, &props);
-
- if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_ARRAY) {
- DBG("Unable to parse properties");
- return btd_error_invalid_args(msg);
- }
-
- dbus_message_iter_recurse(&props, &dict);
-
- if (assistant_parse_props(assistant, &dict)) {
- DBG("Unable to parse properties");
- return btd_error_invalid_args(msg);
- }
-
hdr.op = BT_BASS_ADD_SRC;
if (assistant->device) {
@@ -1130,11 +1175,52 @@ static DBusMessage *push(DBusConnection *conn, DBusMessage *msg,
free(iov.iov_base);
- assistant_set_state(assistant, ASSISTANT_STATE_PENDING);
+ if (assistant->state == ASSISTANT_STATE_LOCAL)
+ assistant_add_src(assistant, assistant->data->bass, 0);
+ else
+ assistant_set_state(assistant, ASSISTANT_STATE_PENDING);
return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
}
+static DBusMessage *push(DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ struct bass_assistant *assistant = user_data;
+ DBusMessageIter props, dict;
+
+ DBG("");
+
+ dbus_message_iter_init(msg, &props);
+
+ if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_ARRAY) {
+ DBG("Unable to parse properties");
+ return btd_error_invalid_args(msg);
+ }
+
+ dbus_message_iter_recurse(&props, &dict);
+
+ if (assistant_parse_props(assistant, &dict)) {
+ DBG("Unable to parse properties");
+ return btd_error_invalid_args(msg);
+ }
+
+ if (assistant->state == ASSISTANT_STATE_ACTIVE) {
+ return push_mod_src(assistant, msg);
+ } else if (assistant->state == ASSISTANT_STATE_LOCAL) {
+ struct bass_src *src;
+
+ src = queue_find(assistant->srcs, match_src_data,
+ assistant->data->bass);
+ if (src) {
+ assistant->src_id = src->src_id;
+ return push_mod_src(assistant, msg);
+ }
+ }
+
+ return push_add_src(assistant, msg);
+}
+
static const GDBusMethodTable assistant_methods[] = {
{GDBUS_EXPERIMENTAL_ASYNC_METHOD("Push",
GDBUS_ARGS({ "Props", "a{sv}" }),
@@ -1235,6 +1321,7 @@ static void assistant_free(void *data)
g_free(assistant->path);
util_iov_free(assistant->meta, 1);
util_iov_free(assistant->caps, 1);
+ queue_destroy(assistant->srcs, free);
free(assistant);
}
@@ -2015,9 +2102,33 @@ static void bass_handle_bcode_req(struct bass_assistant *assistant, int id)
free(iov.iov_base);
}
+static struct bass_src *assistant_add_src(struct bass_assistant *assistant,
+ struct bt_bass *bass, uint8_t id)
+{
+ struct bass_src *src;
+
+ if (!assistant->srcs)
+ assistant->srcs = queue_new();
+
+ src = queue_find(assistant->srcs, match_src_data, bass);
+ if (src) {
+ src->src_id = id;
+ return src;
+ }
+
+ src = new0(struct bass_src, 1);
+ src->bass = bass;
+ src->src_id = id;
+
+ queue_push_tail(assistant->srcs, src);
+
+ return src;
+}
+
static void bass_src_changed(uint8_t id, uint32_t bid, uint8_t state,
uint8_t enc, uint32_t bis_sync, void *user_data)
{
+ struct bass_data *data = user_data;
const struct queue_entry *entry;
for (entry = queue_get_entries(assistants); entry;
@@ -2032,9 +2143,9 @@ static void bass_src_changed(uint8_t id, uint32_t bid, uint8_t state,
continue;
/* If BID is not set it may happen to be local stream so ignore
- * non-local assistants.
+ * non-local assistants unless their BID is also not set.
*/
- if (!bid && assistant->state != ASSISTANT_STATE_LOCAL)
+ if (!bid && assistant->bid)
continue;
if (state == BT_BASS_SYNC_INFO_RE) {
@@ -2066,21 +2177,34 @@ static void bass_src_changed(uint8_t id, uint32_t bid, uint8_t state,
break;
/* Match BIS index */
- if (bis & bis_sync)
+ if (bis & bis_sync) {
+ assistant->src_id = id;
assistant_set_state(assistant,
ASSISTANT_STATE_ACTIVE);
+ }
break;
case BT_BASS_BIG_ENC_STATE_DEC:
/* Only handle assistant objects that
* have requested a Broadcast Code
*/
- if (assistant->state != ASSISTANT_STATE_REQUESTING)
+ if (assistant->state != ASSISTANT_STATE_REQUESTING &&
+ assistant->state != ASSISTANT_STATE_LOCAL)
break;
/* Match BIS index */
- if (bis & bis_sync)
+ if (bis & bis_sync) {
+ assistant->src_id = id;
+
+ if (assistant->state ==
+ ASSISTANT_STATE_LOCAL) {
+ assistant_add_src(assistant, data->bass,
+ id);
+ break;
+ }
+
assistant_set_state(assistant,
ASSISTANT_STATE_ACTIVE);
+ }
break;
default:
continue;
--
2.54.0
^ permalink raw reply related
* [PATCH BlueZ v1 2/4] profiles/audio/bass: Handle PA_SYNC_NO_SYNC in handle_mod_src_req
From: Luiz Augusto von Dentz @ 2026-06-11 17:22 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <20260611172235.92930-1-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
When the delegator receives a Modify Source operation with pa_sync set
to PA_SYNC_NO_SYNC while already synchronized to PA, release all
setups instead of updating BIS sync which would not apply the
requested termination.
Also reset the encryption state to no encryption when the last BIS
index is cleared from a subgroup, so subsequent source additions
start with a clean encryption state.
---
profiles/audio/bass.c | 72 ++++++++++++++++++++++++++++++++++++++++---
src/shared/bass.c | 6 ++++
2 files changed, 73 insertions(+), 5 deletions(-)
diff --git a/profiles/audio/bass.c b/profiles/audio/bass.c
index f018f8b3ad7f..5eb7bff076bb 100644
--- a/profiles/audio/bass.c
+++ b/profiles/audio/bass.c
@@ -420,7 +420,13 @@ static void setup_clear(struct bass_setup *setup, int bis)
bt_bass_clear_bis_sync(dg->src, bis);
setup->stream = NULL;
- queue_remove(setup->dg->setups, setup);
+
+ if (!queue_remove(setup->dg->setups, setup))
+ /* Setup has already been removed from the queue (e.g. during
+ * handle_mod_src_req PA_SYNC_NO_SYNC processing). Skip
+ * disconnect check and free since the caller handles cleanup.
+ */
+ return;
/* Remove any pending bcode request associated with setup */
req = queue_remove_if(dg->bcode_reqs, match_bcode_setup, setup);
@@ -596,6 +602,8 @@ static void bass_remove_bis(struct bass_setup *setup)
{
struct queue *links = bt_bap_stream_io_get_links(setup->stream);
+ DBG("%p", setup);
+
queue_foreach(links, stream_unlink, setup->stream);
bt_bap_stream_release(setup->stream, NULL, NULL);
}
@@ -618,6 +626,8 @@ static void setup_disable_streaming(void *data, void *user_data)
static void bass_add_bis(struct bass_setup *setup)
{
+ DBG("%p", setup);
+
queue_foreach(setup->dg->setups, setup_disable_streaming, NULL);
setup_configure_stream(setup);
}
@@ -1941,8 +1951,40 @@ static int handle_set_bcode_req(struct bt_bcast_src *bcast_src,
return 0;
}
+static bool check_bis_sync(struct bt_bass_mod_src_params *params, uint8_t bis)
+{
+ uint32_t bitmask = 1 << (bis - 1);
+ struct iovec iov = {
+ .iov_base = params->subgroup_data,
+ .iov_len = 0,
+ };
+
+ /* Calculate subgroup data length based on each subgroup's
+ * bis_sync (4 bytes) + meta_len (1 byte) + meta fields.
+ */
+ for (uint8_t i = 0; i < params->num_subgroups; i++) {
+ uint32_t bis_sync;
+ uint8_t meta_len;
+
+ iov.iov_len += sizeof(bis_sync) + sizeof(meta_len);
+
+ memcpy(&bis_sync, params->subgroup_data + iov.iov_len -
+ sizeof(bis_sync) - sizeof(meta_len),
+ sizeof(bis_sync));
+ memcpy(&meta_len, params->subgroup_data + iov.iov_len -
+ sizeof(meta_len), sizeof(meta_len));
+
+ if (le32_to_cpu(bis_sync) & bitmask)
+ return true;
+
+ iov.iov_len += meta_len;
+ }
+
+ return false;
+}
+
static void bass_update_bis_sync(struct bass_delegator *dg,
- struct bt_bcast_src *bcast_src)
+ struct bt_bass_mod_src_params *params)
{
const struct queue_entry *entry;
@@ -1954,11 +1996,14 @@ static void bass_update_bis_sync(struct bass_delegator *dg,
state = bt_bap_stream_get_state(setup->stream);
- if (!setup->stream && bt_bass_check_bis(bcast_src, setup->bis))
+ DBG("stream %p: BIS %d state %s(%u)", setup->stream, setup->bis,
+ bt_bap_stream_statestr(state), state);
+
+ if (!setup->stream && check_bis_sync(params, setup->bis))
bass_add_bis(setup);
else if (setup->stream &&
state == BT_BAP_STREAM_STATE_STREAMING &&
- !bt_bass_check_bis(bcast_src, setup->bis))
+ !check_bis_sync(params, setup->bis))
bass_remove_bis(setup);
}
}
@@ -1981,9 +2026,26 @@ static int handle_mod_src_req(struct bt_bcast_src *bcast_src,
if (err)
return err;
+ DBG("PA sync state %d", sync_state);
+
switch (sync_state) {
case BT_BASS_SYNCHRONIZED_TO_PA:
- bass_update_bis_sync(dg, bcast_src);
+ if (params->pa_sync == PA_SYNC_NO_SYNC) {
+ /* Release all setups. Note: bass_remove_bis may
+ * trigger synchronous state transitions that call
+ * setup_clear which will return early since the
+ * setup has already been removed from the queue.
+ */
+ struct bass_setup *setup;
+
+ while ((setup = queue_pop_head(dg->setups))) {
+ bass_remove_bis(setup);
+ setup_free(setup);
+ }
+
+ delegator_disconnect(dg);
+ } else
+ bass_update_bis_sync(dg, params);
break;
case BT_BASS_NOT_SYNCHRONIZED_TO_PA:
if (params->pa_sync == PA_SYNC_NO_PAST) {
diff --git a/src/shared/bass.c b/src/shared/bass.c
index 19cc9531d617..65ee4a8cbb18 100644
--- a/src/shared/bass.c
+++ b/src/shared/bass.c
@@ -1930,6 +1930,12 @@ int bt_bass_clear_bis_sync(struct bt_bcast_src *bcast_src, uint8_t bis)
if (sgrp->bis_sync & bitmask) {
sgrp->bis_sync &= ~bitmask;
+ /* If there are no BIS index left then reset encryption
+ * state to no encryption.
+ */
+ if (!sgrp->bis_sync)
+ bcast_src->enc = BT_BASS_BIG_ENC_STATE_NO_ENC;
+
iov = bass_parse_bcast_src(bcast_src);
if (!iov)
return -ENOMEM;
--
2.54.0
^ permalink raw reply related
* [Bug 221637] Regression for Intel Corporation Device a876 (rev 10)
From: bugzilla-daemon @ 2026-06-11 17:18 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <bug-221637-62941@https.bugzilla.kernel.org/>
https://bugzilla.kernel.org/show_bug.cgi?id=221637
Paul Menzel (pmenzel+bugzilla.kernel.org@molgen.mpg.de) changed:
What |Removed |Added
----------------------------------------------------------------------------
CC| |pmenzel+bugzilla.kernel.org
| |@molgen.mpg.de
--- Comment #4 from Paul Menzel (pmenzel+bugzilla.kernel.org@molgen.mpg.de) ---
I also commented on the [Linux firmware merge
request](https://gitlab.com/kernel-firmware/linux-firmware/-/merge_requests/955).
--
You may reply to this email to add a comment.
You are receiving this mail because:
You are the assignee for the bug.
^ permalink raw reply
* Re: [PATCH v3] Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn()
From: Luiz Augusto von Dentz @ 2026-06-11 16:07 UTC (permalink / raw)
To: Siwei Zhang; +Cc: linux-bluetooth
In-Reply-To: <c1de98e4-e599-42e2-987e-d1a4a371eac2@app.fastmail.com>
Hi Siwei,
On Thu, Jun 11, 2026 at 11:51 AM Siwei Zhang <oss@fourdim.xyz> wrote:
>
> Hi Luiz,
>
> On Thu, Jun 11, 2026, at 11:36 AM, Luiz Augusto von Dentz wrote:
> > Hi Siwei,
> >
> > On Thu, Jun 11, 2026 at 11:20 AM Siwei Zhang <oss@fourdim.xyz> wrote:
> >>
> >> hci_abort_conn() read hci_skb_event(hdev->sent_cmd) when a connection
> >> was pending, but hdev->sent_cmd can be NULL while req_status is still
> >> HCI_REQ_PEND, leading to a NULL pointer dereference and a general
> >> protection fault from the hci_rx_work() receive path.
> >>
> >> Instead of inspecting hdev->sent_cmd, track the in-flight create
> >> connection command with a new per-connection HCI_CONN_CREATE flag and
> >> route all cancellation through hci_cancel_connect_sync(), which dequeues
> >> the command if still queued, or cancels the pending request if this
> >> connection owns the in-flight create command. CIS uses the same path
> >> via the existing HCI_CONN_CREATE_CIS flag.
> >>
> >> Fixes: a13f316e90fd ("Bluetooth: hci_conn: Consolidate code for aborting connections")
> >> Cc: stable@vger.kernel.org
> >> Assisted-by: Claude:claude-opus-4-8
> >> Signed-off-by: Siwei Zhang <oss@fourdim.xyz>
> >> ---
> >> include/net/bluetooth/hci_core.h | 1 +
> >> net/bluetooth/hci_conn.c | 21 ++--------
> >> net/bluetooth/hci_sync.c | 66 ++++++++++++++++++++++++++------
> >> 3 files changed, 58 insertions(+), 30 deletions(-)
> >>
> >> diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
> >> index aa600fbf9a53..aa554c34f9ec 100644
> >> --- a/include/net/bluetooth/hci_core.h
> >> +++ b/include/net/bluetooth/hci_core.h
> >> @@ -988,6 +988,7 @@ enum {
> >> HCI_CONN_AUTH_FAILURE,
> >> HCI_CONN_PER_ADV,
> >> HCI_CONN_BIG_CREATED,
> >> + HCI_CONN_CREATE,
> >> HCI_CONN_CREATE_CIS,
> >> HCI_CONN_CREATE_BIG_SYNC,
> >> HCI_CONN_BIG_SYNC,
> >> diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
> >> index 54eabaa46960..eba4a548bef5 100644
> >> --- a/net/bluetooth/hci_conn.c
> >> +++ b/net/bluetooth/hci_conn.c
> >> @@ -3181,26 +3181,11 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason)
> >>
> >> conn->abort_reason = reason;
> >>
> >> - /* If the connection is pending check the command opcode since that
> >> - * might be blocking on hci_cmd_sync_work while waiting its respective
> >> - * event so we need to hci_cmd_sync_cancel to cancel it.
> >> - *
> >> - * hci_connect_le serializes the connection attempts so only one
> >> - * connection can be in BT_CONNECT at time.
> >> + /* Cancel the connect attempt. A return of 0 means the create command
> >> + * was still queued and got dequeued, so there is nothing to disconnect.
> >> */
> >> - if (conn->state == BT_CONNECT && READ_ONCE(hdev->req_status) == HCI_REQ_PEND) {
> >> - switch (hci_skb_event(hdev->sent_cmd)) {
> >> - case HCI_EV_CONN_COMPLETE:
> >> - case HCI_EV_LE_CONN_COMPLETE:
> >> - case HCI_EV_LE_ENHANCED_CONN_COMPLETE:
> >> - case HCI_EVT_LE_CIS_ESTABLISHED:
> >> - hci_cmd_sync_cancel(hdev, ECANCELED);
> >> - break;
> >> - }
> >> - /* Cancel connect attempt if still queued/pending */
> >> - } else if (!hci_cancel_connect_sync(hdev, conn)) {
> >> + if (!hci_cancel_connect_sync(hdev, conn))
> >> return 0;
> >> - }
> >>
> >> /* Run immediately if on cmd_sync_work since this may be called
> >> * as a result to MGMT_OP_DISCONNECT/MGMT_OP_UNPAIR which does
> >> diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
> >> index df23245d6ccd..1fe19ddbbc2c 100644
> >> --- a/net/bluetooth/hci_sync.c
> >> +++ b/net/bluetooth/hci_sync.c
> >> @@ -6668,6 +6668,12 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
> >> &own_addr_type);
> >> if (err)
> >> goto done;
> >> +
> >> + /* Mark create connection in flight so hci_cancel_connect_sync() can
> >> + * cancel it while blocking on the connection complete event.
> >> + */
> >> + set_bit(HCI_CONN_CREATE, &conn->flags);
> >> +
> >> /* Send command LE Extended Create Connection if supported */
> >> if (use_ext_conn(hdev)) {
> >> err = hci_le_ext_create_conn_sync(hdev, conn, own_addr_type);
> >> @@ -6703,6 +6709,8 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
> >> conn->conn_timeout, NULL);
> >>
> >> done:
> >> + clear_bit(HCI_CONN_CREATE, &conn->flags);
> >> +
> >> if (err == -ETIMEDOUT)
> >> hci_le_connect_cancel_sync(hdev, conn, 0x00);
> >>
> >> @@ -6982,10 +6990,19 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data)
> >> else
> >> cp.role_switch = 0x00;
> >>
> >> - return __hci_cmd_sync_status_sk(hdev, HCI_OP_CREATE_CONN,
> >> - sizeof(cp), &cp,
> >> - HCI_EV_CONN_COMPLETE,
> >> - conn->conn_timeout, NULL);
> >> + /* Mark create connection in flight so hci_cancel_connect_sync() can
> >> + * cancel it while blocking on the connection complete event.
> >> + */
> >> + set_bit(HCI_CONN_CREATE, &conn->flags);
> >> +
> >> + err = __hci_cmd_sync_status_sk(hdev, HCI_OP_CREATE_CONN,
> >> + sizeof(cp), &cp,
> >> + HCI_EV_CONN_COMPLETE,
> >> + conn->conn_timeout, NULL);
> >> +
> >> + clear_bit(HCI_CONN_CREATE, &conn->flags);
> >> +
> >> + return err;
> >> }
> >>
> >> int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn *conn)
> >> @@ -7039,20 +7056,45 @@ int hci_connect_le_sync(struct hci_dev *hdev, struct hci_conn *conn)
> >>
> >> int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn *conn)
> >> {
> >> - if (conn->state != BT_OPEN)
> >> - return -EINVAL;
> >> + hci_cmd_sync_work_func_t func = NULL;
> >> + hci_cmd_sync_work_destroy_t destroy = NULL;
> >> + int create_flag = -1;
> >>
> >> switch (conn->type) {
> >> case ACL_LINK:
> >> - return !hci_cmd_sync_dequeue_once(hdev,
> >> - hci_acl_create_conn_sync,
> >> - conn, NULL);
> >> + func = hci_acl_create_conn_sync;
> >> + create_flag = HCI_CONN_CREATE;
> >> + break;
> >> case LE_LINK:
> >> - return !hci_cmd_sync_dequeue_once(hdev, hci_le_create_conn_sync,
> >> - conn, create_le_conn_complete);
> >> + func = hci_le_create_conn_sync;
> >> + destroy = create_le_conn_complete;
> >> + create_flag = HCI_CONN_CREATE;
> >> + break;
> >> + case CIS_LINK:
> >> + /* LE Create CIS is shared by the whole CIG and cannot be
> >> + * dequeued per-connection; only cancel it in-flight below.
> >> + */
> >> + create_flag = HCI_CONN_CREATE_CIS;
> >> + break;
> >> + default:
> >> + return -ENOENT;
> >> }
> >>
> >> - return -ENOENT;
> >> + /* The create command is in exactly one of two states: still queued, or
> >> + * in flight. The create flag is only set once the worker has dequeued
> >> + * the entry and started running it, so the two states are mutually
> >> + * exclusive. Try to dequeue first: if it succeeds the command had not
> >> + * started yet and we are done. Otherwise the worker already pulled it
> >> + * off the queue (the dequeue is a no-op here),
> >
> > This is where I disagree, `dequeue` is not a no-op. It must traverse
> > the list of entries in cmd_sync_work_list, so it is cheaper to check
> > the flag first and then attempt to dequeue.
> >
>
> If we test the flag first and read 0 while the entry is still queued, the worker
> can advance it to in-flight before our dequeue runs.
> In that case we will return without cancelling.
Maybe we need to hold cmd_sync_work_lock to make sure nothing is
changed in the meantime before we dequeue (well dequeue will hold it
but not for the window immeditely after checking the flag).
> I can update the comment saying that it has no effect instead of no-op.
>
> >> + * owns the in-flight create command, cancel the pending request.
> >> + */
> >> + if (func && hci_cmd_sync_dequeue_once(hdev, func, conn, destroy))
> >> + return 0;
> >> +
> >> + if (create_flag >= 0 && test_bit(create_flag, &conn->flags))
> >> + hci_cmd_sync_cancel(hdev, ECANCELED);
> >> +
> >> + return -EBUSY;
> >> }
> >>
> >> int hci_le_conn_update_sync(struct hci_dev *hdev, struct hci_conn *conn,
> >> --
> >> 2.54.0
> >>
> >
> >
> > --
> > Luiz Augusto von Dentz
>
> Best,
> Siwei
--
Luiz Augusto von Dentz
^ permalink raw reply
* Re: [PATCH v3] Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn()
From: Pauli Virtanen @ 2026-06-11 16:05 UTC (permalink / raw)
To: Siwei Zhang, Luiz Augusto von Dentz; +Cc: linux-bluetooth
In-Reply-To: <20260611152039.2176565-1-oss@fourdim.xyz>
Hi,
to, 2026-06-11 kello 11:19 -0400, Siwei Zhang kirjoitti:
> hci_abort_conn() read hci_skb_event(hdev->sent_cmd) when a connection
> was pending, but hdev->sent_cmd can be NULL while req_status is still
> HCI_REQ_PEND, leading to a NULL pointer dereference and a general
> protection fault from the hci_rx_work() receive path.
>
> Instead of inspecting hdev->sent_cmd, track the in-flight create
> connection command with a new per-connection HCI_CONN_CREATE flag and
> route all cancellation through hci_cancel_connect_sync(), which
> dequeues
> the command if still queued, or cancels the pending request if this
> connection owns the in-flight create command. CIS uses the same path
> via the existing HCI_CONN_CREATE_CIS flag.
>
> Fixes: a13f316e90fd ("Bluetooth: hci_conn: Consolidate code for
> aborting connections")
> Cc: stable@vger.kernel.org
> Assisted-by: Claude:claude-opus-4-8
> Signed-off-by: Siwei Zhang <oss@fourdim.xyz>
> ---
> include/net/bluetooth/hci_core.h | 1 +
> net/bluetooth/hci_conn.c | 21 ++--------
> net/bluetooth/hci_sync.c | 66 ++++++++++++++++++++++++++----
> --
> 3 files changed, 58 insertions(+), 30 deletions(-)
>
> diff --git a/include/net/bluetooth/hci_core.h
> b/include/net/bluetooth/hci_core.h
> index aa600fbf9a53..aa554c34f9ec 100644
> --- a/include/net/bluetooth/hci_core.h
> +++ b/include/net/bluetooth/hci_core.h
> @@ -988,6 +988,7 @@ enum {
> HCI_CONN_AUTH_FAILURE,
> HCI_CONN_PER_ADV,
> HCI_CONN_BIG_CREATED,
> + HCI_CONN_CREATE,
> HCI_CONN_CREATE_CIS,
> HCI_CONN_CREATE_BIG_SYNC,
> HCI_CONN_BIG_SYNC,
> diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
> index 54eabaa46960..eba4a548bef5 100644
> --- a/net/bluetooth/hci_conn.c
> +++ b/net/bluetooth/hci_conn.c
> @@ -3181,26 +3181,11 @@ int hci_abort_conn(struct hci_conn *conn, u8
> reason)
>
> conn->abort_reason = reason;
>
> - /* If the connection is pending check the command opcode
> since that
> - * might be blocking on hci_cmd_sync_work while waiting its
> respective
> - * event so we need to hci_cmd_sync_cancel to cancel it.
> - *
> - * hci_connect_le serializes the connection attempts so only
> one
> - * connection can be in BT_CONNECT at time.
> + /* Cancel the connect attempt. A return of 0 means the
> create command
> + * was still queued and got dequeued, so there is nothing to
> disconnect.
> */
> - if (conn->state == BT_CONNECT && READ_ONCE(hdev->req_status)
> == HCI_REQ_PEND) {
> - switch (hci_skb_event(hdev->sent_cmd)) {
> - case HCI_EV_CONN_COMPLETE:
> - case HCI_EV_LE_CONN_COMPLETE:
> - case HCI_EV_LE_ENHANCED_CONN_COMPLETE:
> - case HCI_EVT_LE_CIS_ESTABLISHED:
> - hci_cmd_sync_cancel(hdev, ECANCELED);
> - break;
> - }
> - /* Cancel connect attempt if still queued/pending */
> - } else if (!hci_cancel_connect_sync(hdev, conn)) {
> + if (!hci_cancel_connect_sync(hdev, conn))
> return 0;
> - }
>
> /* Run immediately if on cmd_sync_work since this may be
> called
> * as a result to MGMT_OP_DISCONNECT/MGMT_OP_UNPAIR which
> does
> diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
> index df23245d6ccd..1fe19ddbbc2c 100644
> --- a/net/bluetooth/hci_sync.c
> +++ b/net/bluetooth/hci_sync.c
> @@ -6668,6 +6668,12 @@ static int hci_le_create_conn_sync(struct
> hci_dev *hdev, void *data)
> &own_addr_type);
> if (err)
> goto done;
> +
> + /* Mark create connection in flight so
> hci_cancel_connect_sync() can
> + * cancel it while blocking on the connection complete
> event.
> + */
> + set_bit(HCI_CONN_CREATE, &conn->flags);
> +
> /* Send command LE Extended Create Connection if supported
> */
> if (use_ext_conn(hdev)) {
> err = hci_le_ext_create_conn_sync(hdev, conn,
> own_addr_type);
> @@ -6703,6 +6709,8 @@ static int hci_le_create_conn_sync(struct
> hci_dev *hdev, void *data)
> conn->conn_timeout, NULL);
>
> done:
> + clear_bit(HCI_CONN_CREATE, &conn->flags);
> +
> if (err == -ETIMEDOUT)
> hci_le_connect_cancel_sync(hdev, conn, 0x00);
>
> @@ -6982,10 +6990,19 @@ static int hci_acl_create_conn_sync(struct
> hci_dev *hdev, void *data)
> else
> cp.role_switch = 0x00;
>
> - return __hci_cmd_sync_status_sk(hdev, HCI_OP_CREATE_CONN,
> - sizeof(cp), &cp,
> - HCI_EV_CONN_COMPLETE,
> - conn->conn_timeout, NULL);
> + /* Mark create connection in flight so
> hci_cancel_connect_sync() can
> + * cancel it while blocking on the connection complete
> event.
> + */
> + set_bit(HCI_CONN_CREATE, &conn->flags);
> +
> + err = __hci_cmd_sync_status_sk(hdev, HCI_OP_CREATE_CONN,
> + sizeof(cp), &cp,
> + HCI_EV_CONN_COMPLETE,
> + conn->conn_timeout, NULL);
> +
> + clear_bit(HCI_CONN_CREATE, &conn->flags);
> +
> + return err;
> }
>
> int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn
> *conn)
> @@ -7039,20 +7056,45 @@ int hci_connect_le_sync(struct hci_dev *hdev,
> struct hci_conn *conn)
>
> int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn
> *conn)
> {
> - if (conn->state != BT_OPEN)
> - return -EINVAL;
> + hci_cmd_sync_work_func_t func = NULL;
> + hci_cmd_sync_work_destroy_t destroy = NULL;
> + int create_flag = -1;
>
> switch (conn->type) {
> case ACL_LINK:
> - return !hci_cmd_sync_dequeue_once(hdev,
> -
> hci_acl_create_conn_sync,
> - conn, NULL);
> + func = hci_acl_create_conn_sync;
> + create_flag = HCI_CONN_CREATE;
> + break;
> case LE_LINK:
> - return !hci_cmd_sync_dequeue_once(hdev,
> hci_le_create_conn_sync,
> - conn,
> create_le_conn_complete);
> + func = hci_le_create_conn_sync;
> + destroy = create_le_conn_complete;
> + create_flag = HCI_CONN_CREATE;
> + break;
> + case CIS_LINK:
> + /* LE Create CIS is shared by the whole CIG and
> cannot be
> + * dequeued per-connection; only cancel it in-flight
> below.
> + */
> + create_flag = HCI_CONN_CREATE_CIS;
> + break;
> + default:
> + return -ENOENT;
> }
>
> - return -ENOENT;
> + /* The create command is in exactly one of two states: still
> queued, or
> + * in flight. The create flag is only set once the worker
> has dequeued
> + * the entry and started running it, so the two states are
> mutually
> + * exclusive. Try to dequeue first: if it succeeds the
> command had not
> + * started yet and we are done. Otherwise the worker already
> pulled it
> + * off the queue (the dequeue is a no-op here), so if this
> connection
> + * owns the in-flight create command, cancel the pending
> request.
This may run from context with no synchronization (kernel default event
workqueue), the tests below don't run in a critical section, so not
clear the reasoning is strictly valid.
Eg. hci_cmd_sync_cancel() could cancel unintended command if hci_sync
finished just after we test_bit here.
> + */
> + if (func && hci_cmd_sync_dequeue_once(hdev, func, conn,
> destroy))
> + return 0;
> +
> + if (create_flag >= 0 && test_bit(create_flag, &conn->flags))
> + hci_cmd_sync_cancel(hdev, ECANCELED);
> +
> + return -EBUSY;
> }
>
> int hci_le_conn_update_sync(struct hci_dev *hdev, struct hci_conn
> *conn,
--
Pauli Virtanen
^ permalink raw reply
* Re: [PATCH v3] Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn()
From: Siwei Zhang @ 2026-06-11 15:51 UTC (permalink / raw)
To: Luiz Augusto von Dentz; +Cc: linux-bluetooth
In-Reply-To: <CABBYNZJf5fVxkLVWKwXWOdhyrxANBLfF9Rc_1oka-RgW-mx+Ow@mail.gmail.com>
Hi Luiz,
On Thu, Jun 11, 2026, at 11:36 AM, Luiz Augusto von Dentz wrote:
> Hi Siwei,
>
> On Thu, Jun 11, 2026 at 11:20 AM Siwei Zhang <oss@fourdim.xyz> wrote:
>>
>> hci_abort_conn() read hci_skb_event(hdev->sent_cmd) when a connection
>> was pending, but hdev->sent_cmd can be NULL while req_status is still
>> HCI_REQ_PEND, leading to a NULL pointer dereference and a general
>> protection fault from the hci_rx_work() receive path.
>>
>> Instead of inspecting hdev->sent_cmd, track the in-flight create
>> connection command with a new per-connection HCI_CONN_CREATE flag and
>> route all cancellation through hci_cancel_connect_sync(), which dequeues
>> the command if still queued, or cancels the pending request if this
>> connection owns the in-flight create command. CIS uses the same path
>> via the existing HCI_CONN_CREATE_CIS flag.
>>
>> Fixes: a13f316e90fd ("Bluetooth: hci_conn: Consolidate code for aborting connections")
>> Cc: stable@vger.kernel.org
>> Assisted-by: Claude:claude-opus-4-8
>> Signed-off-by: Siwei Zhang <oss@fourdim.xyz>
>> ---
>> include/net/bluetooth/hci_core.h | 1 +
>> net/bluetooth/hci_conn.c | 21 ++--------
>> net/bluetooth/hci_sync.c | 66 ++++++++++++++++++++++++++------
>> 3 files changed, 58 insertions(+), 30 deletions(-)
>>
>> diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
>> index aa600fbf9a53..aa554c34f9ec 100644
>> --- a/include/net/bluetooth/hci_core.h
>> +++ b/include/net/bluetooth/hci_core.h
>> @@ -988,6 +988,7 @@ enum {
>> HCI_CONN_AUTH_FAILURE,
>> HCI_CONN_PER_ADV,
>> HCI_CONN_BIG_CREATED,
>> + HCI_CONN_CREATE,
>> HCI_CONN_CREATE_CIS,
>> HCI_CONN_CREATE_BIG_SYNC,
>> HCI_CONN_BIG_SYNC,
>> diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
>> index 54eabaa46960..eba4a548bef5 100644
>> --- a/net/bluetooth/hci_conn.c
>> +++ b/net/bluetooth/hci_conn.c
>> @@ -3181,26 +3181,11 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason)
>>
>> conn->abort_reason = reason;
>>
>> - /* If the connection is pending check the command opcode since that
>> - * might be blocking on hci_cmd_sync_work while waiting its respective
>> - * event so we need to hci_cmd_sync_cancel to cancel it.
>> - *
>> - * hci_connect_le serializes the connection attempts so only one
>> - * connection can be in BT_CONNECT at time.
>> + /* Cancel the connect attempt. A return of 0 means the create command
>> + * was still queued and got dequeued, so there is nothing to disconnect.
>> */
>> - if (conn->state == BT_CONNECT && READ_ONCE(hdev->req_status) == HCI_REQ_PEND) {
>> - switch (hci_skb_event(hdev->sent_cmd)) {
>> - case HCI_EV_CONN_COMPLETE:
>> - case HCI_EV_LE_CONN_COMPLETE:
>> - case HCI_EV_LE_ENHANCED_CONN_COMPLETE:
>> - case HCI_EVT_LE_CIS_ESTABLISHED:
>> - hci_cmd_sync_cancel(hdev, ECANCELED);
>> - break;
>> - }
>> - /* Cancel connect attempt if still queued/pending */
>> - } else if (!hci_cancel_connect_sync(hdev, conn)) {
>> + if (!hci_cancel_connect_sync(hdev, conn))
>> return 0;
>> - }
>>
>> /* Run immediately if on cmd_sync_work since this may be called
>> * as a result to MGMT_OP_DISCONNECT/MGMT_OP_UNPAIR which does
>> diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
>> index df23245d6ccd..1fe19ddbbc2c 100644
>> --- a/net/bluetooth/hci_sync.c
>> +++ b/net/bluetooth/hci_sync.c
>> @@ -6668,6 +6668,12 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
>> &own_addr_type);
>> if (err)
>> goto done;
>> +
>> + /* Mark create connection in flight so hci_cancel_connect_sync() can
>> + * cancel it while blocking on the connection complete event.
>> + */
>> + set_bit(HCI_CONN_CREATE, &conn->flags);
>> +
>> /* Send command LE Extended Create Connection if supported */
>> if (use_ext_conn(hdev)) {
>> err = hci_le_ext_create_conn_sync(hdev, conn, own_addr_type);
>> @@ -6703,6 +6709,8 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
>> conn->conn_timeout, NULL);
>>
>> done:
>> + clear_bit(HCI_CONN_CREATE, &conn->flags);
>> +
>> if (err == -ETIMEDOUT)
>> hci_le_connect_cancel_sync(hdev, conn, 0x00);
>>
>> @@ -6982,10 +6990,19 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data)
>> else
>> cp.role_switch = 0x00;
>>
>> - return __hci_cmd_sync_status_sk(hdev, HCI_OP_CREATE_CONN,
>> - sizeof(cp), &cp,
>> - HCI_EV_CONN_COMPLETE,
>> - conn->conn_timeout, NULL);
>> + /* Mark create connection in flight so hci_cancel_connect_sync() can
>> + * cancel it while blocking on the connection complete event.
>> + */
>> + set_bit(HCI_CONN_CREATE, &conn->flags);
>> +
>> + err = __hci_cmd_sync_status_sk(hdev, HCI_OP_CREATE_CONN,
>> + sizeof(cp), &cp,
>> + HCI_EV_CONN_COMPLETE,
>> + conn->conn_timeout, NULL);
>> +
>> + clear_bit(HCI_CONN_CREATE, &conn->flags);
>> +
>> + return err;
>> }
>>
>> int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn *conn)
>> @@ -7039,20 +7056,45 @@ int hci_connect_le_sync(struct hci_dev *hdev, struct hci_conn *conn)
>>
>> int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn *conn)
>> {
>> - if (conn->state != BT_OPEN)
>> - return -EINVAL;
>> + hci_cmd_sync_work_func_t func = NULL;
>> + hci_cmd_sync_work_destroy_t destroy = NULL;
>> + int create_flag = -1;
>>
>> switch (conn->type) {
>> case ACL_LINK:
>> - return !hci_cmd_sync_dequeue_once(hdev,
>> - hci_acl_create_conn_sync,
>> - conn, NULL);
>> + func = hci_acl_create_conn_sync;
>> + create_flag = HCI_CONN_CREATE;
>> + break;
>> case LE_LINK:
>> - return !hci_cmd_sync_dequeue_once(hdev, hci_le_create_conn_sync,
>> - conn, create_le_conn_complete);
>> + func = hci_le_create_conn_sync;
>> + destroy = create_le_conn_complete;
>> + create_flag = HCI_CONN_CREATE;
>> + break;
>> + case CIS_LINK:
>> + /* LE Create CIS is shared by the whole CIG and cannot be
>> + * dequeued per-connection; only cancel it in-flight below.
>> + */
>> + create_flag = HCI_CONN_CREATE_CIS;
>> + break;
>> + default:
>> + return -ENOENT;
>> }
>>
>> - return -ENOENT;
>> + /* The create command is in exactly one of two states: still queued, or
>> + * in flight. The create flag is only set once the worker has dequeued
>> + * the entry and started running it, so the two states are mutually
>> + * exclusive. Try to dequeue first: if it succeeds the command had not
>> + * started yet and we are done. Otherwise the worker already pulled it
>> + * off the queue (the dequeue is a no-op here),
>
> This is where I disagree, `dequeue` is not a no-op. It must traverse
> the list of entries in cmd_sync_work_list, so it is cheaper to check
> the flag first and then attempt to dequeue.
>
If we test the flag first and read 0 while the entry is still queued, the worker
can advance it to in-flight before our dequeue runs.
In that case we will return without cancelling.
I can update the comment saying that it has no effect instead of no-op.
>> + * owns the in-flight create command, cancel the pending request.
>> + */
>> + if (func && hci_cmd_sync_dequeue_once(hdev, func, conn, destroy))
>> + return 0;
>> +
>> + if (create_flag >= 0 && test_bit(create_flag, &conn->flags))
>> + hci_cmd_sync_cancel(hdev, ECANCELED);
>> +
>> + return -EBUSY;
>> }
>>
>> int hci_le_conn_update_sync(struct hci_dev *hdev, struct hci_conn *conn,
>> --
>> 2.54.0
>>
>
>
> --
> Luiz Augusto von Dentz
Best,
Siwei
^ permalink raw reply
* RE: [v2] Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn()
From: bluez.test.bot @ 2026-06-11 15:43 UTC (permalink / raw)
To: linux-bluetooth, oss
In-Reply-To: <20260611150628.2164737-1-oss@fourdim.xyz>
[-- Attachment #1: Type: text/plain, Size: 2204 bytes --]
This is automated email and please do not reply to this email!
Dear submitter,
Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=1110130
---Test result---
Test Summary:
CheckPatch PASS 1.18 seconds
VerifyFixes PASS 0.13 seconds
VerifySignedoff PASS 0.13 seconds
GitLint PASS 0.33 seconds
SubjectPrefix PASS 0.12 seconds
BuildKernel PASS 25.41 seconds
CheckAllWarning PASS 28.07 seconds
CheckSparse PASS 26.83 seconds
BuildKernel32 PASS 25.17 seconds
TestRunnerSetup PASS 524.61 seconds
TestRunner_l2cap-tester PASS 59.42 seconds
TestRunner_iso-tester PASS 85.93 seconds
TestRunner_bnep-tester PASS 19.21 seconds
TestRunner_mgmt-tester FAIL 212.48 seconds
TestRunner_rfcomm-tester PASS 25.50 seconds
TestRunner_sco-tester PASS 32.14 seconds
TestRunner_ioctl-tester PASS 25.91 seconds
TestRunner_mesh-tester FAIL 25.97 seconds
TestRunner_smp-tester PASS 23.16 seconds
TestRunner_userchan-tester PASS 20.34 seconds
TestRunner_6lowpan-tester PASS 22.86 seconds
IncrementalBuild PASS 24.46 seconds
Details
##############################
Test: TestRunner_mgmt-tester - FAIL
Desc: Run mgmt-tester with test-runner
Output:
Total: 494, Passed: 489 (99.0%), Failed: 1, Not Run: 4
Failed Test Cases
Read Exp Feature - Success Failed 0.241 seconds
##############################
Test: TestRunner_mesh-tester - FAIL
Desc: Run mesh-tester with test-runner
Output:
Total: 10, Passed: 8 (80.0%), Failed: 2, Not Run: 0
Failed Test Cases
Mesh - Send cancel - 1 Timed out 2.387 seconds
Mesh - Send cancel - 2 Timed out 1.990 seconds
https://github.com/bluez/bluetooth-next/pull/303
---
Regards,
Linux Bluetooth
^ permalink raw reply
* [bluetooth-next:master] BUILD SUCCESS 559cab24b04e1daf8c3ca274005ba6d05908ce3a
From: kernel test robot @ 2026-06-11 15:37 UTC (permalink / raw)
To: Luiz Augusto von Dentz; +Cc: linux-bluetooth
tree/branch: https://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git master
branch HEAD: 559cab24b04e1daf8c3ca274005ba6d05908ce3a Bluetooth: qca: Add BT FW build version to kernel log
elapsed time: 832m
configs tested: 220
configs skipped: 2
The following configs have been built successfully.
More configs may be tested in the coming days.
tested configs:
alpha allnoconfig gcc-16.1.0
alpha allyesconfig gcc-16.1.0
alpha defconfig gcc-16.1.0
arc allmodconfig clang-23
arc allnoconfig gcc-16.1.0
arc allyesconfig clang-23
arc defconfig gcc-16.1.0
arc randconfig-001 gcc-14.3.0
arc randconfig-001-20260611 gcc-14.3.0
arc randconfig-002 gcc-14.3.0
arc randconfig-002-20260611 gcc-14.3.0
arm allnoconfig gcc-16.1.0
arm allyesconfig clang-23
arm defconfig gcc-16.1.0
arm omap1_defconfig gcc-16.1.0
arm randconfig-001 gcc-14.3.0
arm randconfig-001-20260611 gcc-14.3.0
arm randconfig-002 gcc-14.3.0
arm randconfig-002-20260611 gcc-14.3.0
arm randconfig-003 gcc-14.3.0
arm randconfig-003-20260611 gcc-14.3.0
arm randconfig-004 gcc-14.3.0
arm randconfig-004-20260611 gcc-14.3.0
arm64 allmodconfig clang-23
arm64 allnoconfig gcc-16.1.0
arm64 defconfig gcc-16.1.0
arm64 randconfig-001-20260611 gcc-14.3.0
arm64 randconfig-002-20260611 gcc-14.3.0
arm64 randconfig-003-20260611 gcc-14.3.0
arm64 randconfig-004-20260611 gcc-14.3.0
csky allmodconfig gcc-16.1.0
csky allnoconfig gcc-16.1.0
csky defconfig gcc-16.1.0
csky randconfig-001-20260611 gcc-14.3.0
csky randconfig-002-20260611 gcc-14.3.0
hexagon allmodconfig gcc-16.1.0
hexagon allnoconfig gcc-16.1.0
hexagon defconfig gcc-16.1.0
hexagon randconfig-001-20260611 clang-17
hexagon randconfig-002-20260611 clang-17
i386 allmodconfig clang-22
i386 allmodconfig gcc-14
i386 allnoconfig gcc-16.1.0
i386 allyesconfig clang-22
i386 allyesconfig gcc-14
i386 buildonly-randconfig-001-20260611 clang-22
i386 buildonly-randconfig-002-20260611 clang-22
i386 buildonly-randconfig-003-20260611 clang-22
i386 buildonly-randconfig-004-20260611 clang-22
i386 buildonly-randconfig-005-20260611 clang-22
i386 buildonly-randconfig-006-20260611 clang-22
i386 defconfig gcc-16.1.0
i386 randconfig-001 gcc-14
i386 randconfig-001-20260611 gcc-14
i386 randconfig-002 gcc-14
i386 randconfig-002-20260611 gcc-14
i386 randconfig-003 gcc-14
i386 randconfig-003-20260611 gcc-14
i386 randconfig-004 gcc-14
i386 randconfig-004-20260611 gcc-14
i386 randconfig-005 gcc-14
i386 randconfig-005-20260611 gcc-14
i386 randconfig-006 gcc-14
i386 randconfig-006-20260611 gcc-14
i386 randconfig-007 gcc-14
i386 randconfig-007-20260611 gcc-14
i386 randconfig-011-20260611 gcc-14
i386 randconfig-012-20260611 gcc-14
i386 randconfig-013-20260611 gcc-14
i386 randconfig-014-20260611 gcc-14
i386 randconfig-015-20260611 gcc-14
i386 randconfig-016-20260611 gcc-14
i386 randconfig-017-20260611 gcc-14
loongarch allmodconfig clang-23
loongarch allnoconfig gcc-16.1.0
loongarch defconfig clang-23
loongarch randconfig-001-20260611 clang-17
loongarch randconfig-002-20260611 clang-17
m68k allmodconfig gcc-16.1.0
m68k allnoconfig gcc-16.1.0
m68k allyesconfig clang-23
m68k defconfig clang-23
microblaze allnoconfig gcc-16.1.0
microblaze allyesconfig gcc-16.1.0
microblaze defconfig clang-23
mips allmodconfig gcc-16.1.0
mips allnoconfig gcc-16.1.0
mips allyesconfig gcc-16.1.0
nios2 allmodconfig clang-20
nios2 allnoconfig clang-23
nios2 defconfig clang-23
nios2 randconfig-001-20260611 clang-17
nios2 randconfig-002-20260611 clang-17
openrisc allmodconfig clang-20
openrisc allnoconfig clang-23
openrisc defconfig gcc-16.1.0
parisc allmodconfig gcc-16.1.0
parisc allnoconfig clang-23
parisc allyesconfig clang-17
parisc defconfig gcc-16.1.0
parisc randconfig-001-20260611 gcc-13.4.0
parisc randconfig-002-20260611 gcc-13.4.0
parisc64 defconfig clang-23
powerpc akebono_defconfig clang-23
powerpc allmodconfig gcc-16.1.0
powerpc allnoconfig clang-23
powerpc randconfig-001-20260611 gcc-13.4.0
powerpc randconfig-002-20260611 gcc-13.4.0
powerpc64 randconfig-001-20260611 gcc-13.4.0
powerpc64 randconfig-002-20260611 gcc-13.4.0
riscv allmodconfig clang-23
riscv allnoconfig clang-23
riscv allyesconfig clang-23
riscv defconfig gcc-16.1.0
riscv randconfig-001 gcc-12.5.0
riscv randconfig-001-20260611 gcc-12.5.0
riscv randconfig-002 gcc-12.5.0
riscv randconfig-002-20260611 gcc-12.5.0
s390 allmodconfig clang-17
s390 allnoconfig clang-23
s390 allyesconfig gcc-16.1.0
s390 defconfig gcc-16.1.0
s390 randconfig-001 gcc-12.5.0
s390 randconfig-001-20260611 gcc-12.5.0
s390 randconfig-002 gcc-12.5.0
s390 randconfig-002-20260611 gcc-12.5.0
sh allmodconfig gcc-16.1.0
sh allnoconfig clang-23
sh allyesconfig clang-17
sh defconfig gcc-14
sh randconfig-001 gcc-12.5.0
sh randconfig-001-20260611 gcc-12.5.0
sh randconfig-002 gcc-12.5.0
sh randconfig-002-20260611 gcc-12.5.0
sparc allnoconfig clang-23
sparc defconfig gcc-16.1.0
sparc randconfig-001-20260611 gcc-15.2.0
sparc randconfig-002-20260611 gcc-15.2.0
sparc64 allmodconfig clang-20
sparc64 defconfig gcc-14
sparc64 randconfig-001-20260611 gcc-15.2.0
sparc64 randconfig-002-20260611 gcc-15.2.0
um allmodconfig clang-17
um allnoconfig clang-23
um allyesconfig gcc-16.1.0
um defconfig gcc-14
um i386_defconfig gcc-14
um randconfig-001-20260611 gcc-15.2.0
um randconfig-002-20260611 gcc-15.2.0
um x86_64_defconfig gcc-14
x86_64 allmodconfig clang-22
x86_64 allnoconfig clang-23
x86_64 allyesconfig clang-22
x86_64 buildonly-randconfig-001 gcc-14
x86_64 buildonly-randconfig-001-20260611 gcc-14
x86_64 buildonly-randconfig-002 gcc-14
x86_64 buildonly-randconfig-002-20260611 gcc-14
x86_64 buildonly-randconfig-003 gcc-14
x86_64 buildonly-randconfig-003-20260611 gcc-14
x86_64 buildonly-randconfig-004 gcc-14
x86_64 buildonly-randconfig-004-20260611 gcc-14
x86_64 buildonly-randconfig-005 gcc-14
x86_64 buildonly-randconfig-005-20260611 gcc-14
x86_64 buildonly-randconfig-006 gcc-14
x86_64 buildonly-randconfig-006-20260611 gcc-14
x86_64 defconfig gcc-14
x86_64 kexec clang-22
x86_64 randconfig-001 clang-22
x86_64 randconfig-001-20260611 clang-22
x86_64 randconfig-001-20260611 gcc-14
x86_64 randconfig-002 clang-22
x86_64 randconfig-002-20260611 clang-22
x86_64 randconfig-002-20260611 gcc-14
x86_64 randconfig-003 clang-22
x86_64 randconfig-003-20260611 clang-22
x86_64 randconfig-003-20260611 gcc-14
x86_64 randconfig-004 clang-22
x86_64 randconfig-004-20260611 clang-22
x86_64 randconfig-004-20260611 gcc-14
x86_64 randconfig-005 clang-22
x86_64 randconfig-005-20260611 clang-22
x86_64 randconfig-005-20260611 gcc-14
x86_64 randconfig-006 clang-22
x86_64 randconfig-006-20260611 clang-22
x86_64 randconfig-006-20260611 gcc-14
x86_64 randconfig-011 clang-22
x86_64 randconfig-011-20260611 clang-22
x86_64 randconfig-011-20260611 gcc-14
x86_64 randconfig-012 clang-22
x86_64 randconfig-012-20260611 clang-22
x86_64 randconfig-012-20260611 gcc-14
x86_64 randconfig-013 clang-22
x86_64 randconfig-013-20260611 clang-22
x86_64 randconfig-013-20260611 gcc-14
x86_64 randconfig-014 clang-22
x86_64 randconfig-014-20260611 clang-22
x86_64 randconfig-014-20260611 gcc-14
x86_64 randconfig-015 clang-22
x86_64 randconfig-015-20260611 clang-22
x86_64 randconfig-015-20260611 gcc-14
x86_64 randconfig-016 clang-22
x86_64 randconfig-016-20260611 clang-22
x86_64 randconfig-016-20260611 gcc-14
x86_64 randconfig-071-20260611 clang-22
x86_64 randconfig-072-20260611 clang-22
x86_64 randconfig-073-20260611 clang-22
x86_64 randconfig-074-20260611 clang-22
x86_64 randconfig-075-20260611 clang-22
x86_64 randconfig-076-20260611 clang-22
x86_64 rhel-9.4 clang-22
x86_64 rhel-9.4-bpf gcc-14
x86_64 rhel-9.4-func clang-22
x86_64 rhel-9.4-kselftests clang-22
x86_64 rhel-9.4-kunit gcc-14
x86_64 rhel-9.4-ltp gcc-14
x86_64 rhel-9.4-rust clang-22
xtensa allnoconfig clang-23
xtensa allyesconfig clang-20
xtensa randconfig-001-20260611 gcc-15.2.0
xtensa randconfig-002-20260611 gcc-15.2.0
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* Re: [PATCH v3] Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn()
From: Luiz Augusto von Dentz @ 2026-06-11 15:36 UTC (permalink / raw)
To: Siwei Zhang; +Cc: linux-bluetooth
In-Reply-To: <20260611152039.2176565-1-oss@fourdim.xyz>
Hi Siwei,
On Thu, Jun 11, 2026 at 11:20 AM Siwei Zhang <oss@fourdim.xyz> wrote:
>
> hci_abort_conn() read hci_skb_event(hdev->sent_cmd) when a connection
> was pending, but hdev->sent_cmd can be NULL while req_status is still
> HCI_REQ_PEND, leading to a NULL pointer dereference and a general
> protection fault from the hci_rx_work() receive path.
>
> Instead of inspecting hdev->sent_cmd, track the in-flight create
> connection command with a new per-connection HCI_CONN_CREATE flag and
> route all cancellation through hci_cancel_connect_sync(), which dequeues
> the command if still queued, or cancels the pending request if this
> connection owns the in-flight create command. CIS uses the same path
> via the existing HCI_CONN_CREATE_CIS flag.
>
> Fixes: a13f316e90fd ("Bluetooth: hci_conn: Consolidate code for aborting connections")
> Cc: stable@vger.kernel.org
> Assisted-by: Claude:claude-opus-4-8
> Signed-off-by: Siwei Zhang <oss@fourdim.xyz>
> ---
> include/net/bluetooth/hci_core.h | 1 +
> net/bluetooth/hci_conn.c | 21 ++--------
> net/bluetooth/hci_sync.c | 66 ++++++++++++++++++++++++++------
> 3 files changed, 58 insertions(+), 30 deletions(-)
>
> diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
> index aa600fbf9a53..aa554c34f9ec 100644
> --- a/include/net/bluetooth/hci_core.h
> +++ b/include/net/bluetooth/hci_core.h
> @@ -988,6 +988,7 @@ enum {
> HCI_CONN_AUTH_FAILURE,
> HCI_CONN_PER_ADV,
> HCI_CONN_BIG_CREATED,
> + HCI_CONN_CREATE,
> HCI_CONN_CREATE_CIS,
> HCI_CONN_CREATE_BIG_SYNC,
> HCI_CONN_BIG_SYNC,
> diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
> index 54eabaa46960..eba4a548bef5 100644
> --- a/net/bluetooth/hci_conn.c
> +++ b/net/bluetooth/hci_conn.c
> @@ -3181,26 +3181,11 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason)
>
> conn->abort_reason = reason;
>
> - /* If the connection is pending check the command opcode since that
> - * might be blocking on hci_cmd_sync_work while waiting its respective
> - * event so we need to hci_cmd_sync_cancel to cancel it.
> - *
> - * hci_connect_le serializes the connection attempts so only one
> - * connection can be in BT_CONNECT at time.
> + /* Cancel the connect attempt. A return of 0 means the create command
> + * was still queued and got dequeued, so there is nothing to disconnect.
> */
> - if (conn->state == BT_CONNECT && READ_ONCE(hdev->req_status) == HCI_REQ_PEND) {
> - switch (hci_skb_event(hdev->sent_cmd)) {
> - case HCI_EV_CONN_COMPLETE:
> - case HCI_EV_LE_CONN_COMPLETE:
> - case HCI_EV_LE_ENHANCED_CONN_COMPLETE:
> - case HCI_EVT_LE_CIS_ESTABLISHED:
> - hci_cmd_sync_cancel(hdev, ECANCELED);
> - break;
> - }
> - /* Cancel connect attempt if still queued/pending */
> - } else if (!hci_cancel_connect_sync(hdev, conn)) {
> + if (!hci_cancel_connect_sync(hdev, conn))
> return 0;
> - }
>
> /* Run immediately if on cmd_sync_work since this may be called
> * as a result to MGMT_OP_DISCONNECT/MGMT_OP_UNPAIR which does
> diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
> index df23245d6ccd..1fe19ddbbc2c 100644
> --- a/net/bluetooth/hci_sync.c
> +++ b/net/bluetooth/hci_sync.c
> @@ -6668,6 +6668,12 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
> &own_addr_type);
> if (err)
> goto done;
> +
> + /* Mark create connection in flight so hci_cancel_connect_sync() can
> + * cancel it while blocking on the connection complete event.
> + */
> + set_bit(HCI_CONN_CREATE, &conn->flags);
> +
> /* Send command LE Extended Create Connection if supported */
> if (use_ext_conn(hdev)) {
> err = hci_le_ext_create_conn_sync(hdev, conn, own_addr_type);
> @@ -6703,6 +6709,8 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
> conn->conn_timeout, NULL);
>
> done:
> + clear_bit(HCI_CONN_CREATE, &conn->flags);
> +
> if (err == -ETIMEDOUT)
> hci_le_connect_cancel_sync(hdev, conn, 0x00);
>
> @@ -6982,10 +6990,19 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data)
> else
> cp.role_switch = 0x00;
>
> - return __hci_cmd_sync_status_sk(hdev, HCI_OP_CREATE_CONN,
> - sizeof(cp), &cp,
> - HCI_EV_CONN_COMPLETE,
> - conn->conn_timeout, NULL);
> + /* Mark create connection in flight so hci_cancel_connect_sync() can
> + * cancel it while blocking on the connection complete event.
> + */
> + set_bit(HCI_CONN_CREATE, &conn->flags);
> +
> + err = __hci_cmd_sync_status_sk(hdev, HCI_OP_CREATE_CONN,
> + sizeof(cp), &cp,
> + HCI_EV_CONN_COMPLETE,
> + conn->conn_timeout, NULL);
> +
> + clear_bit(HCI_CONN_CREATE, &conn->flags);
> +
> + return err;
> }
>
> int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn *conn)
> @@ -7039,20 +7056,45 @@ int hci_connect_le_sync(struct hci_dev *hdev, struct hci_conn *conn)
>
> int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn *conn)
> {
> - if (conn->state != BT_OPEN)
> - return -EINVAL;
> + hci_cmd_sync_work_func_t func = NULL;
> + hci_cmd_sync_work_destroy_t destroy = NULL;
> + int create_flag = -1;
>
> switch (conn->type) {
> case ACL_LINK:
> - return !hci_cmd_sync_dequeue_once(hdev,
> - hci_acl_create_conn_sync,
> - conn, NULL);
> + func = hci_acl_create_conn_sync;
> + create_flag = HCI_CONN_CREATE;
> + break;
> case LE_LINK:
> - return !hci_cmd_sync_dequeue_once(hdev, hci_le_create_conn_sync,
> - conn, create_le_conn_complete);
> + func = hci_le_create_conn_sync;
> + destroy = create_le_conn_complete;
> + create_flag = HCI_CONN_CREATE;
> + break;
> + case CIS_LINK:
> + /* LE Create CIS is shared by the whole CIG and cannot be
> + * dequeued per-connection; only cancel it in-flight below.
> + */
> + create_flag = HCI_CONN_CREATE_CIS;
> + break;
> + default:
> + return -ENOENT;
> }
>
> - return -ENOENT;
> + /* The create command is in exactly one of two states: still queued, or
> + * in flight. The create flag is only set once the worker has dequeued
> + * the entry and started running it, so the two states are mutually
> + * exclusive. Try to dequeue first: if it succeeds the command had not
> + * started yet and we are done. Otherwise the worker already pulled it
> + * off the queue (the dequeue is a no-op here),
This is where I disagree, `dequeue` is not a no-op. It must traverse
the list of entries in cmd_sync_work_list, so it is cheaper to check
the flag first and then attempt to dequeue.
> + * owns the in-flight create command, cancel the pending request.
> + */
> + if (func && hci_cmd_sync_dequeue_once(hdev, func, conn, destroy))
> + return 0;
> +
> + if (create_flag >= 0 && test_bit(create_flag, &conn->flags))
> + hci_cmd_sync_cancel(hdev, ECANCELED);
> +
> + return -EBUSY;
> }
>
> int hci_le_conn_update_sync(struct hci_dev *hdev, struct hci_conn *conn,
> --
> 2.54.0
>
--
Luiz Augusto von Dentz
^ permalink raw reply
* RE: Initial Channel Sounding Support for
From: bluez.test.bot @ 2026-06-11 15:21 UTC (permalink / raw)
To: linux-bluetooth, naga.akella
In-Reply-To: <20260611120044.1641009-2-naga.akella@oss.qualcomm.com>
[-- Attachment #1: Type: text/plain, Size: 825 bytes --]
This is automated email and please do not reply to this email!
Dear submitter,
Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=1109997
---Test result---
Test Summary:
CheckPatch PASS 1.88 seconds
GitLint PASS 0.95 seconds
BuildEll PASS 22.94 seconds
BluezMake PASS 611.29 seconds
CheckSmatch PASS 327.26 seconds
bluezmakeextell PASS 164.50 seconds
IncrementalBuild PASS 656.58 seconds
ScanBuild PASS 942.40 seconds
https://github.com/bluez/bluez/pull/2217
---
Regards,
Linux Bluetooth
^ permalink raw reply
* [PATCH v3] Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn()
From: Siwei Zhang @ 2026-06-11 15:19 UTC (permalink / raw)
To: Luiz Augusto von Dentz; +Cc: linux-bluetooth, Siwei Zhang
hci_abort_conn() read hci_skb_event(hdev->sent_cmd) when a connection
was pending, but hdev->sent_cmd can be NULL while req_status is still
HCI_REQ_PEND, leading to a NULL pointer dereference and a general
protection fault from the hci_rx_work() receive path.
Instead of inspecting hdev->sent_cmd, track the in-flight create
connection command with a new per-connection HCI_CONN_CREATE flag and
route all cancellation through hci_cancel_connect_sync(), which dequeues
the command if still queued, or cancels the pending request if this
connection owns the in-flight create command. CIS uses the same path
via the existing HCI_CONN_CREATE_CIS flag.
Fixes: a13f316e90fd ("Bluetooth: hci_conn: Consolidate code for aborting connections")
Cc: stable@vger.kernel.org
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Siwei Zhang <oss@fourdim.xyz>
---
include/net/bluetooth/hci_core.h | 1 +
net/bluetooth/hci_conn.c | 21 ++--------
net/bluetooth/hci_sync.c | 66 ++++++++++++++++++++++++++------
3 files changed, 58 insertions(+), 30 deletions(-)
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index aa600fbf9a53..aa554c34f9ec 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -988,6 +988,7 @@ enum {
HCI_CONN_AUTH_FAILURE,
HCI_CONN_PER_ADV,
HCI_CONN_BIG_CREATED,
+ HCI_CONN_CREATE,
HCI_CONN_CREATE_CIS,
HCI_CONN_CREATE_BIG_SYNC,
HCI_CONN_BIG_SYNC,
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 54eabaa46960..eba4a548bef5 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -3181,26 +3181,11 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason)
conn->abort_reason = reason;
- /* If the connection is pending check the command opcode since that
- * might be blocking on hci_cmd_sync_work while waiting its respective
- * event so we need to hci_cmd_sync_cancel to cancel it.
- *
- * hci_connect_le serializes the connection attempts so only one
- * connection can be in BT_CONNECT at time.
+ /* Cancel the connect attempt. A return of 0 means the create command
+ * was still queued and got dequeued, so there is nothing to disconnect.
*/
- if (conn->state == BT_CONNECT && READ_ONCE(hdev->req_status) == HCI_REQ_PEND) {
- switch (hci_skb_event(hdev->sent_cmd)) {
- case HCI_EV_CONN_COMPLETE:
- case HCI_EV_LE_CONN_COMPLETE:
- case HCI_EV_LE_ENHANCED_CONN_COMPLETE:
- case HCI_EVT_LE_CIS_ESTABLISHED:
- hci_cmd_sync_cancel(hdev, ECANCELED);
- break;
- }
- /* Cancel connect attempt if still queued/pending */
- } else if (!hci_cancel_connect_sync(hdev, conn)) {
+ if (!hci_cancel_connect_sync(hdev, conn))
return 0;
- }
/* Run immediately if on cmd_sync_work since this may be called
* as a result to MGMT_OP_DISCONNECT/MGMT_OP_UNPAIR which does
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index df23245d6ccd..1fe19ddbbc2c 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -6668,6 +6668,12 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
&own_addr_type);
if (err)
goto done;
+
+ /* Mark create connection in flight so hci_cancel_connect_sync() can
+ * cancel it while blocking on the connection complete event.
+ */
+ set_bit(HCI_CONN_CREATE, &conn->flags);
+
/* Send command LE Extended Create Connection if supported */
if (use_ext_conn(hdev)) {
err = hci_le_ext_create_conn_sync(hdev, conn, own_addr_type);
@@ -6703,6 +6709,8 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
conn->conn_timeout, NULL);
done:
+ clear_bit(HCI_CONN_CREATE, &conn->flags);
+
if (err == -ETIMEDOUT)
hci_le_connect_cancel_sync(hdev, conn, 0x00);
@@ -6982,10 +6990,19 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data)
else
cp.role_switch = 0x00;
- return __hci_cmd_sync_status_sk(hdev, HCI_OP_CREATE_CONN,
- sizeof(cp), &cp,
- HCI_EV_CONN_COMPLETE,
- conn->conn_timeout, NULL);
+ /* Mark create connection in flight so hci_cancel_connect_sync() can
+ * cancel it while blocking on the connection complete event.
+ */
+ set_bit(HCI_CONN_CREATE, &conn->flags);
+
+ err = __hci_cmd_sync_status_sk(hdev, HCI_OP_CREATE_CONN,
+ sizeof(cp), &cp,
+ HCI_EV_CONN_COMPLETE,
+ conn->conn_timeout, NULL);
+
+ clear_bit(HCI_CONN_CREATE, &conn->flags);
+
+ return err;
}
int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn *conn)
@@ -7039,20 +7056,45 @@ int hci_connect_le_sync(struct hci_dev *hdev, struct hci_conn *conn)
int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn *conn)
{
- if (conn->state != BT_OPEN)
- return -EINVAL;
+ hci_cmd_sync_work_func_t func = NULL;
+ hci_cmd_sync_work_destroy_t destroy = NULL;
+ int create_flag = -1;
switch (conn->type) {
case ACL_LINK:
- return !hci_cmd_sync_dequeue_once(hdev,
- hci_acl_create_conn_sync,
- conn, NULL);
+ func = hci_acl_create_conn_sync;
+ create_flag = HCI_CONN_CREATE;
+ break;
case LE_LINK:
- return !hci_cmd_sync_dequeue_once(hdev, hci_le_create_conn_sync,
- conn, create_le_conn_complete);
+ func = hci_le_create_conn_sync;
+ destroy = create_le_conn_complete;
+ create_flag = HCI_CONN_CREATE;
+ break;
+ case CIS_LINK:
+ /* LE Create CIS is shared by the whole CIG and cannot be
+ * dequeued per-connection; only cancel it in-flight below.
+ */
+ create_flag = HCI_CONN_CREATE_CIS;
+ break;
+ default:
+ return -ENOENT;
}
- return -ENOENT;
+ /* The create command is in exactly one of two states: still queued, or
+ * in flight. The create flag is only set once the worker has dequeued
+ * the entry and started running it, so the two states are mutually
+ * exclusive. Try to dequeue first: if it succeeds the command had not
+ * started yet and we are done. Otherwise the worker already pulled it
+ * off the queue (the dequeue is a no-op here), so if this connection
+ * owns the in-flight create command, cancel the pending request.
+ */
+ if (func && hci_cmd_sync_dequeue_once(hdev, func, conn, destroy))
+ return 0;
+
+ if (create_flag >= 0 && test_bit(create_flag, &conn->flags))
+ hci_cmd_sync_cancel(hdev, ECANCELED);
+
+ return -EBUSY;
}
int hci_le_conn_update_sync(struct hci_dev *hdev, struct hci_conn *conn,
--
2.54.0
^ permalink raw reply related
* [PATCH v10 1/1] Bluetooth: L2CAP: Fix use-after-free in l2cap_sock_new_connection_cb()
From: Siwei Zhang @ 2026-06-11 15:08 UTC (permalink / raw)
To: Luiz Augusto von Dentz; +Cc: linux-bluetooth, Siwei Zhang
In-Reply-To: <20260611150822.2166160-1-oss@fourdim.xyz>
l2cap_sock_new_connection_cb() returned l2cap_pi(sk)->chan after
release_sock(parent). Once the parent lock is dropped the newly
enqueued child socket sk is reachable via the accept queue, so another
task can accept and free it before the callback dereferences sk,
resulting in a use-after-free.
Rework the ->new_connection() op so the core, rather than the callback,
owns the child channel's lifetime. The op now receives a pre-allocated
new_chan and returns an errno instead of allocating and returning a
channel. l2cap_new_connection() allocates the child channel and links
it into the conn list via __l2cap_chan_add() before invoking the
callback, so the conn-list reference keeps the channel alive once
release_sock(parent) exposes the socket to other tasks.
Channel configuration that was duplicated in l2cap_sock_init() and the
various new_connection callbacks is consolidated into
l2cap_chan_set_defaults(), which now inherits from the parent channel
when one is supplied.
Fixes: 8ffb929098a5 ("Bluetooth: Remove parent socket usage from l2cap_core.c")
Cc: stable@kernel.org
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Siwei Zhang <oss@fourdim.xyz>
---
include/net/bluetooth/l2cap.h | 10 ++--
net/bluetooth/6lowpan.c | 18 +-----
net/bluetooth/l2cap_core.c | 78 ++++++++++++++++++++-----
net/bluetooth/l2cap_sock.c | 103 ++++++++++++++++------------------
net/bluetooth/smp.c | 27 ++-------
5 files changed, 127 insertions(+), 109 deletions(-)
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index e0a1f2293679..27acfedf5c79 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -620,7 +620,8 @@ struct l2cap_chan {
struct l2cap_ops {
char *name;
- struct l2cap_chan *(*new_connection) (struct l2cap_chan *chan);
+ int (*new_connection)(struct l2cap_chan *chan,
+ struct l2cap_chan *new_chan);
int (*recv) (struct l2cap_chan * chan,
struct sk_buff *skb);
void (*teardown) (struct l2cap_chan *chan, int err);
@@ -884,9 +885,10 @@ static inline __u16 __next_seq(struct l2cap_chan *chan, __u16 seq)
return (seq + 1) % (chan->tx_win_max + 1);
}
-static inline struct l2cap_chan *l2cap_chan_no_new_connection(struct l2cap_chan *chan)
+static inline int l2cap_chan_no_new_connection(struct l2cap_chan *chan,
+ struct l2cap_chan *new_chan)
{
- return NULL;
+ return -EOPNOTSUPP;
}
static inline int l2cap_chan_no_recv(struct l2cap_chan *chan, struct sk_buff *skb)
@@ -963,7 +965,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
void l2cap_chan_busy(struct l2cap_chan *chan, int busy);
void l2cap_chan_rx_avail(struct l2cap_chan *chan, ssize_t rx_avail);
int l2cap_chan_check_security(struct l2cap_chan *chan, bool initiator);
-void l2cap_chan_set_defaults(struct l2cap_chan *chan);
+void l2cap_chan_set_defaults(struct l2cap_chan *chan, struct l2cap_chan *pchan);
int l2cap_ertm_init(struct l2cap_chan *chan);
void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan);
void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan);
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
index cb1e329d66fd..6e57b4b95c94 100644
--- a/net/bluetooth/6lowpan.c
+++ b/net/bluetooth/6lowpan.c
@@ -632,7 +632,7 @@ static struct l2cap_chan *chan_create(void)
if (!chan)
return NULL;
- l2cap_chan_set_defaults(chan);
+ l2cap_chan_set_defaults(chan, NULL);
chan->chan_type = L2CAP_CHAN_CONN_ORIENTED;
chan->mode = L2CAP_MODE_LE_FLOWCTL;
@@ -745,21 +745,6 @@ static inline void chan_ready_cb(struct l2cap_chan *chan)
ifup(dev->netdev);
}
-static inline struct l2cap_chan *chan_new_conn_cb(struct l2cap_chan *pchan)
-{
- struct l2cap_chan *chan;
-
- chan = chan_create();
- if (!chan)
- return NULL;
-
- chan->ops = pchan->ops;
-
- BT_DBG("chan %p pchan %p", chan, pchan);
-
- return chan;
-}
-
static void unregister_dev(struct lowpan_btle_dev *dev)
{
struct hci_dev *hdev = READ_ONCE(dev->hdev);
@@ -901,7 +886,6 @@ static long chan_get_sndtimeo_cb(struct l2cap_chan *chan)
static const struct l2cap_ops bt_6lowpan_chan_ops = {
.name = "L2CAP 6LoWPAN channel",
- .new_connection = chan_new_conn_cb,
.recv = chan_recv_cb,
.close = chan_close_cb,
.state_change = chan_state_change_cb,
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index c4ccfbda9d78..78ea7cdcc906 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -519,7 +519,10 @@ void l2cap_chan_put(struct l2cap_chan *c)
}
EXPORT_SYMBOL_GPL(l2cap_chan_put);
-void l2cap_chan_set_defaults(struct l2cap_chan *chan)
+/* Initialise @chan with default values, inheriting from the parent channel
+ * @pchan when it is given.
+ */
+void l2cap_chan_set_defaults(struct l2cap_chan *chan, struct l2cap_chan *pchan)
{
chan->fcs = L2CAP_FCS_CRC16;
chan->max_tx = L2CAP_DEFAULT_MAX_TX;
@@ -533,6 +536,31 @@ void l2cap_chan_set_defaults(struct l2cap_chan *chan)
chan->retrans_timeout = L2CAP_DEFAULT_RETRANS_TO;
chan->monitor_timeout = L2CAP_DEFAULT_MONITOR_TO;
+ if (pchan) {
+ BT_DBG("chan %p pchan %p", chan, pchan);
+
+ chan->chan_type = pchan->chan_type;
+ chan->imtu = pchan->imtu;
+ chan->omtu = pchan->omtu;
+ chan->mode = pchan->mode;
+ chan->fcs = pchan->fcs;
+ chan->max_tx = pchan->max_tx;
+ chan->tx_win = pchan->tx_win;
+ chan->tx_win_max = pchan->tx_win_max;
+ chan->sec_level = pchan->sec_level;
+ chan->conf_state = pchan->conf_state;
+ chan->flags = pchan->flags;
+ chan->tx_credits = pchan->tx_credits;
+ chan->rx_credits = pchan->rx_credits;
+
+ if (chan->chan_type == L2CAP_CHAN_FIXED) {
+ chan->scid = pchan->scid;
+ chan->dcid = pchan->scid;
+ }
+
+ return;
+ }
+
chan->conf_state = 0;
set_bit(CONF_NOT_COMPLETE, &chan->conf_state);
@@ -4007,6 +4035,38 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn,
return 0;
}
+/* Allocate and initialise a channel for an incoming connection.
+ *
+ * The channel inherits its configuration from @pchan and is linked into @conn
+ * before ->new_connection() runs, so the conn list reference keeps it alive if
+ * the callback exposes it (e.g. via the socket accept queue) before this
+ * returns. The l2cap_chan_create() reference is taken over by the subsystem on
+ * success and dropped here on failure.
+ */
+static struct l2cap_chan *l2cap_new_connection(struct l2cap_conn *conn,
+ struct l2cap_chan *pchan)
+{
+ struct l2cap_chan *chan;
+
+ chan = l2cap_chan_create();
+ if (!chan)
+ return NULL;
+
+ l2cap_chan_set_defaults(chan, pchan);
+ chan->ops = pchan->ops;
+
+ __l2cap_chan_add(conn, chan);
+
+ if (pchan->ops->new_connection &&
+ pchan->ops->new_connection(pchan, chan) < 0) {
+ l2cap_chan_del(chan, 0);
+ l2cap_chan_put(chan);
+ return NULL;
+ }
+
+ return chan;
+}
+
static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
u8 *data, u8 rsp_code)
{
@@ -4053,7 +4113,7 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
goto response;
}
- chan = pchan->ops->new_connection(pchan);
+ chan = l2cap_new_connection(conn, pchan);
if (!chan)
goto response;
@@ -4071,8 +4131,6 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
chan->psm = psm;
chan->dcid = scid;
- __l2cap_chan_add(conn, chan);
-
dcid = chan->scid;
__set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
@@ -4955,7 +5013,7 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
goto response_unlock;
}
- chan = pchan->ops->new_connection(pchan);
+ chan = l2cap_new_connection(conn, pchan);
if (!chan) {
result = L2CAP_CR_LE_NO_MEM;
goto response_unlock;
@@ -4970,8 +5028,6 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
chan->omtu = mtu;
chan->remote_mps = mps;
- __l2cap_chan_add(conn, chan);
-
l2cap_le_flowctl_init(chan, __le16_to_cpu(req->credits));
dcid = chan->scid;
@@ -5179,7 +5235,7 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn,
continue;
}
- chan = pchan->ops->new_connection(pchan);
+ chan = l2cap_new_connection(conn, pchan);
if (!chan) {
result = L2CAP_CR_LE_NO_MEM;
continue;
@@ -5194,8 +5250,6 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn,
chan->omtu = mtu;
chan->remote_mps = mps;
- __l2cap_chan_add(conn, chan);
-
l2cap_ecred_init(chan, __le16_to_cpu(req->credits));
/* Init response */
@@ -7470,14 +7524,12 @@ static void l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
goto next;
l2cap_chan_lock(pchan);
- chan = pchan->ops->new_connection(pchan);
+ chan = l2cap_new_connection(conn, pchan);
if (chan) {
bacpy(&chan->src, &hcon->src);
bacpy(&chan->dst, &hcon->dst);
chan->src_type = bdaddr_src_type(hcon);
chan->dst_type = dst_type;
-
- __l2cap_chan_add(conn, chan);
}
l2cap_chan_unlock(pchan);
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 025329636353..7370f2b4d28e 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -46,7 +46,8 @@ static struct bt_sock_list l2cap_sk_list = {
static const struct proto_ops l2cap_sock_ops;
static void l2cap_sock_init(struct sock *sk, struct sock *parent);
static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
- int proto, gfp_t prio, int kern);
+ int proto, gfp_t prio, int kern,
+ struct l2cap_chan *chan);
static void l2cap_sock_cleanup_listen(struct sock *parent);
bool l2cap_is_socket(struct socket *sock)
@@ -1287,6 +1288,23 @@ static int l2cap_sock_recvmsg(struct socket *sock, struct msghdr *msg,
return err;
}
+/* Release the sock's ref on chan and clear the pointer so that the ref is
+ * dropped exactly once even if both l2cap_sock_kill() and
+ * l2cap_sock_destruct() run. Setting chan->data to NULL first stops any other
+ * task from dereferencing the now-dead sock pointer.
+ */
+static void l2cap_sock_put_chan(struct sock *sk)
+{
+ struct l2cap_chan *chan = l2cap_pi(sk)->chan;
+
+ if (!chan)
+ return;
+
+ chan->data = NULL;
+ l2cap_pi(sk)->chan = NULL;
+ l2cap_chan_put(chan);
+}
+
/* Kill socket (only if zapped and orphan)
* Must be called on unlocked socket, with l2cap channel lock.
*/
@@ -1297,13 +1315,9 @@ static void l2cap_sock_kill(struct sock *sk)
BT_DBG("sk %p state %s", sk, state_to_string(sk->sk_state));
- /* Sock is dead, so set chan data to NULL, avoid other task use invalid
- * sock pointer.
- */
- l2cap_pi(sk)->chan->data = NULL;
- /* Kill poor orphan */
+ l2cap_sock_put_chan(sk);
- l2cap_chan_put(l2cap_pi(sk)->chan);
+ /* Kill poor orphan */
sock_set_flag(sk, SOCK_DEAD);
sock_put(sk);
}
@@ -1546,12 +1560,13 @@ static void l2cap_sock_cleanup_listen(struct sock *parent)
}
}
-static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan)
+static int l2cap_sock_new_connection_cb(struct l2cap_chan *chan,
+ struct l2cap_chan *new_chan)
{
struct sock *sk, *parent = chan->data;
if (!parent)
- return NULL;
+ return -EINVAL;
lock_sock(parent);
@@ -1559,25 +1574,28 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan)
if (sk_acceptq_is_full(parent)) {
BT_DBG("backlog full %d", parent->sk_ack_backlog);
release_sock(parent);
- return NULL;
+ return -ENOBUFS;
}
sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP,
- GFP_ATOMIC, 0);
+ GFP_ATOMIC, 0, new_chan);
if (!sk) {
release_sock(parent);
- return NULL;
- }
+ return -ENOMEM;
+ }
bt_sock_reclassify_lock(sk, BTPROTO_L2CAP);
l2cap_sock_init(sk, parent);
+ /* The conn list reference taken by l2cap_new_connection() keeps new_chan
+ * alive once release_sock() lets another task free this socket.
+ */
bt_accept_enqueue(parent, sk, false);
release_sock(parent);
- return l2cap_pi(sk)->chan;
+ return 0;
}
static int l2cap_sock_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
@@ -1874,10 +1892,7 @@ static void l2cap_sock_destruct(struct sock *sk)
BT_DBG("sk %p", sk);
- if (l2cap_pi(sk)->chan) {
- l2cap_pi(sk)->chan->data = NULL;
- l2cap_chan_put(l2cap_pi(sk)->chan);
- }
+ l2cap_sock_put_chan(sk);
list_for_each_entry_safe(rx_busy, next, &l2cap_pi(sk)->rx_busy, list) {
kfree_skb(rx_busy->skb);
@@ -1910,30 +1925,12 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
BT_DBG("sk %p", sk);
if (parent) {
- struct l2cap_chan *pchan = l2cap_pi(parent)->chan;
-
sk->sk_type = parent->sk_type;
bt_sk(sk)->flags = bt_sk(parent)->flags;
- chan->chan_type = pchan->chan_type;
- chan->imtu = pchan->imtu;
- chan->omtu = pchan->omtu;
- chan->conf_state = pchan->conf_state;
- chan->mode = pchan->mode;
- chan->fcs = pchan->fcs;
- chan->max_tx = pchan->max_tx;
- chan->tx_win = pchan->tx_win;
- chan->tx_win_max = pchan->tx_win_max;
- chan->sec_level = pchan->sec_level;
- chan->flags = pchan->flags;
- chan->tx_credits = pchan->tx_credits;
- chan->rx_credits = pchan->rx_credits;
-
- if (chan->chan_type == L2CAP_CHAN_FIXED) {
- chan->scid = pchan->scid;
- chan->dcid = pchan->scid;
- }
-
+ /* Channel configuration is inherited from the parent by
+ * l2cap_new_connection().
+ */
security_sk_clone(parent, sk);
} else {
switch (sk->sk_type) {
@@ -1959,7 +1956,7 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
chan->mode = L2CAP_MODE_BASIC;
}
- l2cap_chan_set_defaults(chan);
+ l2cap_chan_set_defaults(chan, NULL);
}
/* Default config options */
@@ -1978,10 +1975,10 @@ static struct proto l2cap_proto = {
};
static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
- int proto, gfp_t prio, int kern)
+ int proto, gfp_t prio, int kern,
+ struct l2cap_chan *chan)
{
struct sock *sk;
- struct l2cap_chan *chan;
sk = bt_sock_alloc(net, sock, &l2cap_proto, proto, prio, kern);
if (!sk)
@@ -1992,16 +1989,7 @@ static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
INIT_LIST_HEAD(&l2cap_pi(sk)->rx_busy);
- chan = l2cap_chan_create();
- if (!chan) {
- sk_free(sk);
- if (sock)
- sock->sk = NULL;
- return NULL;
- }
-
- l2cap_chan_hold(chan);
-
+ /* The sock takes ownership of the caller's reference on chan. */
l2cap_pi(sk)->chan = chan;
return sk;
@@ -2011,6 +1999,7 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol,
int kern)
{
struct sock *sk;
+ struct l2cap_chan *chan;
BT_DBG("sock %p", sock);
@@ -2025,10 +2014,16 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol,
sock->ops = &l2cap_sock_ops;
- sk = l2cap_sock_alloc(net, sock, protocol, GFP_ATOMIC, kern);
- if (!sk)
+ chan = l2cap_chan_create();
+ if (!chan)
return -ENOMEM;
+ sk = l2cap_sock_alloc(net, sock, protocol, GFP_ATOMIC, kern, chan);
+ if (!sk) {
+ l2cap_chan_put(chan);
+ return -ENOMEM;
+ }
+
l2cap_sock_init(sk, NULL);
bt_sock_link(&l2cap_sk_list, sk);
return 0;
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index 1739c1989dbd..d3f9207a026d 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -3204,34 +3204,19 @@ static const struct l2cap_ops smp_chan_ops = {
.get_sndtimeo = l2cap_chan_no_get_sndtimeo,
};
-static inline struct l2cap_chan *smp_new_conn_cb(struct l2cap_chan *pchan)
+static inline int smp_new_conn_cb(struct l2cap_chan *chan,
+ struct l2cap_chan *new_chan)
{
- struct l2cap_chan *chan;
-
- BT_DBG("pchan %p", pchan);
-
- chan = l2cap_chan_create();
- if (!chan)
- return NULL;
-
- chan->chan_type = pchan->chan_type;
- chan->ops = &smp_chan_ops;
- chan->scid = pchan->scid;
- chan->dcid = chan->scid;
- chan->imtu = pchan->imtu;
- chan->omtu = pchan->omtu;
- chan->mode = pchan->mode;
+ new_chan->ops = &smp_chan_ops;
/* Other L2CAP channels may request SMP routines in order to
* change the security level. This means that the SMP channel
* lock must be considered in its own category to avoid lockdep
* warnings.
*/
- atomic_set(&chan->nesting, L2CAP_NESTING_SMP);
-
- BT_DBG("created chan %p", chan);
+ atomic_set(&new_chan->nesting, L2CAP_NESTING_SMP);
- return chan;
+ return 0;
}
static const struct l2cap_ops smp_root_chan_ops = {
@@ -3291,7 +3276,7 @@ static struct l2cap_chan *smp_add_cid(struct hci_dev *hdev, u16 cid)
l2cap_add_scid(chan, cid);
- l2cap_chan_set_defaults(chan);
+ l2cap_chan_set_defaults(chan, NULL);
if (cid == L2CAP_CID_SMP) {
u8 bdaddr_type;
--
2.54.0
^ permalink raw reply related
* [PATCH v10 0/1] Bluetooth: L2CAP: Fix use-after-free in l2cap_sock_new_connection_cb()
From: Siwei Zhang @ 2026-06-11 15:08 UTC (permalink / raw)
To: Luiz Augusto von Dentz; +Cc: linux-bluetooth, Siwei Zhang
Compared to v2, addresses comments on https://sashiko.dev/#/patchset/20260415204842.2363950-1-oss%40fourdim.xyz .
Compared to v3, rebase against bluetooth-next.
Compared to v4, allocate the channel outside the function and pass it in as an argument to avoid the use-after-free.
Compared to v5, extract the channel init to a separate function.
Compared to v6, balance puts and holds on chans.
Compared to v7, rebase against bluetooth-next and refactor the chan refcounting.
Compared to v8, adopt the philosophy of one assignment one reference. Make refcounting easier to follow.
Compared to v9, rework on l2cap_chan_set_defaults so that new chan's default comes from pchan and
can be used by __l2cap_chan_add.
Siwei Zhang (1):
Bluetooth: L2CAP: Fix use-after-free in l2cap_sock_new_connection_cb()
include/net/bluetooth/l2cap.h | 10 ++--
net/bluetooth/6lowpan.c | 18 +-----
net/bluetooth/l2cap_core.c | 78 ++++++++++++++++++++-----
net/bluetooth/l2cap_sock.c | 103 ++++++++++++++++------------------
net/bluetooth/smp.c | 27 ++-------
5 files changed, 127 insertions(+), 109 deletions(-)
--
2.54.0
^ permalink raw reply
* Re: [PATCH v2] Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn()
From: Siwei Zhang @ 2026-06-11 15:07 UTC (permalink / raw)
To: Luiz Augusto von Dentz; +Cc: linux-bluetooth
In-Reply-To: <20260611150628.2164737-1-oss@fourdim.xyz>
On Thu, Jun 11, 2026, at 11:06 AM, Siwei Zhang wrote:
> hci_abort_conn() read hci_skb_event(hdev->sent_cmd) when a connection
> was pending, but hdev->sent_cmd can be NULL while req_status is still
> HCI_REQ_PEND, leading to a NULL pointer dereference and a general
> protection fault from the hci_rx_work() receive path.
>
> Instead of inspecting hdev->sent_cmd, track the in-flight create
> connection command with a new per-connection HCI_CONN_CREATE_CONN flag
> and route all cancellation through hci_cancel_connect_sync(), which
> dequeues the command if still queued, or cancels the pending request if
> this connection owns the in-flight create command. CIS uses the same
> path via the existing HCI_CONN_CREATE_CIS flag.
>
> Fixes: a13f316e90fd ("Bluetooth: hci_conn: Consolidate code for
> aborting connections")
> Cc: stable@vger.kernel.org
> Assisted-by: Claude:claude-opus-4-8
> Signed-off-by: Siwei Zhang <oss@fourdim.xyz>
> ---
> include/net/bluetooth/hci_core.h | 1 +
> net/bluetooth/hci_conn.c | 21 ++----------
> net/bluetooth/hci_sync.c | 58 +++++++++++++++++++++++++-------
> 3 files changed, 50 insertions(+), 30 deletions(-)
>
> diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
> index aa600fbf9a53..c90ca6173680 100644
> --- a/include/net/bluetooth/hci_core.h
> +++ b/include/net/bluetooth/hci_core.h
> @@ -988,6 +988,7 @@ enum {
> HCI_CONN_AUTH_FAILURE,
> HCI_CONN_PER_ADV,
> HCI_CONN_BIG_CREATED,
> + HCI_CONN_CREATE_CONN,
> HCI_CONN_CREATE_CIS,
> HCI_CONN_CREATE_BIG_SYNC,
> HCI_CONN_BIG_SYNC,
> diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
> index 54eabaa46960..eba4a548bef5 100644
> --- a/net/bluetooth/hci_conn.c
> +++ b/net/bluetooth/hci_conn.c
> @@ -3181,26 +3181,11 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason)
>
> conn->abort_reason = reason;
>
> - /* If the connection is pending check the command opcode since that
> - * might be blocking on hci_cmd_sync_work while waiting its respective
> - * event so we need to hci_cmd_sync_cancel to cancel it.
> - *
> - * hci_connect_le serializes the connection attempts so only one
> - * connection can be in BT_CONNECT at time.
> + /* Cancel the connect attempt. A return of 0 means the create command
> + * was still queued and got dequeued, so there is nothing to
> disconnect.
> */
> - if (conn->state == BT_CONNECT && READ_ONCE(hdev->req_status) ==
> HCI_REQ_PEND) {
> - switch (hci_skb_event(hdev->sent_cmd)) {
> - case HCI_EV_CONN_COMPLETE:
> - case HCI_EV_LE_CONN_COMPLETE:
> - case HCI_EV_LE_ENHANCED_CONN_COMPLETE:
> - case HCI_EVT_LE_CIS_ESTABLISHED:
> - hci_cmd_sync_cancel(hdev, ECANCELED);
> - break;
> - }
> - /* Cancel connect attempt if still queued/pending */
> - } else if (!hci_cancel_connect_sync(hdev, conn)) {
> + if (!hci_cancel_connect_sync(hdev, conn))
> return 0;
> - }
>
> /* Run immediately if on cmd_sync_work since this may be called
> * as a result to MGMT_OP_DISCONNECT/MGMT_OP_UNPAIR which does
> diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
> index df23245d6ccd..08917b8167de 100644
> --- a/net/bluetooth/hci_sync.c
> +++ b/net/bluetooth/hci_sync.c
> @@ -6668,6 +6668,12 @@ static int hci_le_create_conn_sync(struct
> hci_dev *hdev, void *data)
> &own_addr_type);
> if (err)
> goto done;
> +
> + /* Mark create connection in flight so hci_cancel_connect_sync() can
> + * cancel it while blocking on the connection complete event.
> + */
> + set_bit(HCI_CONN_CREATE_CONN, &conn->flags);
> +
> /* Send command LE Extended Create Connection if supported */
> if (use_ext_conn(hdev)) {
> err = hci_le_ext_create_conn_sync(hdev, conn, own_addr_type);
> @@ -6703,6 +6709,8 @@ static int hci_le_create_conn_sync(struct hci_dev
> *hdev, void *data)
> conn->conn_timeout, NULL);
>
> done:
> + clear_bit(HCI_CONN_CREATE_CONN, &conn->flags);
> +
> if (err == -ETIMEDOUT)
> hci_le_connect_cancel_sync(hdev, conn, 0x00);
>
> @@ -6982,10 +6990,19 @@ static int hci_acl_create_conn_sync(struct
> hci_dev *hdev, void *data)
> else
> cp.role_switch = 0x00;
>
> - return __hci_cmd_sync_status_sk(hdev, HCI_OP_CREATE_CONN,
> - sizeof(cp), &cp,
> - HCI_EV_CONN_COMPLETE,
> - conn->conn_timeout, NULL);
> + /* Mark create connection in flight so hci_cancel_connect_sync() can
> + * cancel it while blocking on the connection complete event.
> + */
> + set_bit(HCI_CONN_CREATE_CONN, &conn->flags);
> +
> + err = __hci_cmd_sync_status_sk(hdev, HCI_OP_CREATE_CONN,
> + sizeof(cp), &cp,
> + HCI_EV_CONN_COMPLETE,
> + conn->conn_timeout, NULL);
> +
> + clear_bit(HCI_CONN_CREATE_CONN, &conn->flags);
> +
> + return err;
> }
>
> int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn *conn)
> @@ -7039,20 +7056,37 @@ int hci_connect_le_sync(struct hci_dev *hdev,
> struct hci_conn *conn)
>
> int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn *conn)
> {
> - if (conn->state != BT_OPEN)
> - return -EINVAL;
> + hci_cmd_sync_work_func_t func = NULL;
> + hci_cmd_sync_work_destroy_t destroy = NULL;
> + int create_flag = -1;
>
> switch (conn->type) {
> case ACL_LINK:
> - return !hci_cmd_sync_dequeue_once(hdev,
> - hci_acl_create_conn_sync,
> - conn, NULL);
> + func = hci_acl_create_conn_sync;
> + create_flag = HCI_CONN_CREATE_CONN;
> + break;
> case LE_LINK:
> - return !hci_cmd_sync_dequeue_once(hdev, hci_le_create_conn_sync,
> - conn, create_le_conn_complete);
> + func = hci_le_create_conn_sync;
> + destroy = create_le_conn_complete;
> + create_flag = HCI_CONN_CREATE_CONN;
> + break;
> + case CIS_LINK:
> + /* LE Create CIS is shared by the whole CIG and cannot be
> + * dequeued per-connection; only cancel it in-flight below.
> + */
> + create_flag = HCI_CONN_CREATE_CIS;
> + break;
> + default:
> + return -ENOENT;
> }
>
> - return -ENOENT;
> + if (func && hci_cmd_sync_dequeue_once(hdev, func, conn, destroy))
> + return 0;
> +
> + if (create_flag >= 0 && test_bit(create_flag, &conn->flags))
> + hci_cmd_sync_cancel(hdev, ECANCELED);
> +
> + return -EBUSY;
> }
>
> int hci_le_conn_update_sync(struct hci_dev *hdev, struct hci_conn *conn,
> --
> 2.54.0
Sorry, please ignore this. Wrong patch.
^ permalink raw reply
* [PATCH v2] Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn()
From: Siwei Zhang @ 2026-06-11 15:06 UTC (permalink / raw)
To: Luiz Augusto von Dentz; +Cc: linux-bluetooth, Siwei Zhang
hci_abort_conn() read hci_skb_event(hdev->sent_cmd) when a connection
was pending, but hdev->sent_cmd can be NULL while req_status is still
HCI_REQ_PEND, leading to a NULL pointer dereference and a general
protection fault from the hci_rx_work() receive path.
Instead of inspecting hdev->sent_cmd, track the in-flight create
connection command with a new per-connection HCI_CONN_CREATE_CONN flag
and route all cancellation through hci_cancel_connect_sync(), which
dequeues the command if still queued, or cancels the pending request if
this connection owns the in-flight create command. CIS uses the same
path via the existing HCI_CONN_CREATE_CIS flag.
Fixes: a13f316e90fd ("Bluetooth: hci_conn: Consolidate code for aborting connections")
Cc: stable@vger.kernel.org
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Siwei Zhang <oss@fourdim.xyz>
---
include/net/bluetooth/hci_core.h | 1 +
net/bluetooth/hci_conn.c | 21 ++----------
net/bluetooth/hci_sync.c | 58 +++++++++++++++++++++++++-------
3 files changed, 50 insertions(+), 30 deletions(-)
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index aa600fbf9a53..c90ca6173680 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -988,6 +988,7 @@ enum {
HCI_CONN_AUTH_FAILURE,
HCI_CONN_PER_ADV,
HCI_CONN_BIG_CREATED,
+ HCI_CONN_CREATE_CONN,
HCI_CONN_CREATE_CIS,
HCI_CONN_CREATE_BIG_SYNC,
HCI_CONN_BIG_SYNC,
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 54eabaa46960..eba4a548bef5 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -3181,26 +3181,11 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason)
conn->abort_reason = reason;
- /* If the connection is pending check the command opcode since that
- * might be blocking on hci_cmd_sync_work while waiting its respective
- * event so we need to hci_cmd_sync_cancel to cancel it.
- *
- * hci_connect_le serializes the connection attempts so only one
- * connection can be in BT_CONNECT at time.
+ /* Cancel the connect attempt. A return of 0 means the create command
+ * was still queued and got dequeued, so there is nothing to disconnect.
*/
- if (conn->state == BT_CONNECT && READ_ONCE(hdev->req_status) == HCI_REQ_PEND) {
- switch (hci_skb_event(hdev->sent_cmd)) {
- case HCI_EV_CONN_COMPLETE:
- case HCI_EV_LE_CONN_COMPLETE:
- case HCI_EV_LE_ENHANCED_CONN_COMPLETE:
- case HCI_EVT_LE_CIS_ESTABLISHED:
- hci_cmd_sync_cancel(hdev, ECANCELED);
- break;
- }
- /* Cancel connect attempt if still queued/pending */
- } else if (!hci_cancel_connect_sync(hdev, conn)) {
+ if (!hci_cancel_connect_sync(hdev, conn))
return 0;
- }
/* Run immediately if on cmd_sync_work since this may be called
* as a result to MGMT_OP_DISCONNECT/MGMT_OP_UNPAIR which does
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index df23245d6ccd..08917b8167de 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -6668,6 +6668,12 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
&own_addr_type);
if (err)
goto done;
+
+ /* Mark create connection in flight so hci_cancel_connect_sync() can
+ * cancel it while blocking on the connection complete event.
+ */
+ set_bit(HCI_CONN_CREATE_CONN, &conn->flags);
+
/* Send command LE Extended Create Connection if supported */
if (use_ext_conn(hdev)) {
err = hci_le_ext_create_conn_sync(hdev, conn, own_addr_type);
@@ -6703,6 +6709,8 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
conn->conn_timeout, NULL);
done:
+ clear_bit(HCI_CONN_CREATE_CONN, &conn->flags);
+
if (err == -ETIMEDOUT)
hci_le_connect_cancel_sync(hdev, conn, 0x00);
@@ -6982,10 +6990,19 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data)
else
cp.role_switch = 0x00;
- return __hci_cmd_sync_status_sk(hdev, HCI_OP_CREATE_CONN,
- sizeof(cp), &cp,
- HCI_EV_CONN_COMPLETE,
- conn->conn_timeout, NULL);
+ /* Mark create connection in flight so hci_cancel_connect_sync() can
+ * cancel it while blocking on the connection complete event.
+ */
+ set_bit(HCI_CONN_CREATE_CONN, &conn->flags);
+
+ err = __hci_cmd_sync_status_sk(hdev, HCI_OP_CREATE_CONN,
+ sizeof(cp), &cp,
+ HCI_EV_CONN_COMPLETE,
+ conn->conn_timeout, NULL);
+
+ clear_bit(HCI_CONN_CREATE_CONN, &conn->flags);
+
+ return err;
}
int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn *conn)
@@ -7039,20 +7056,37 @@ int hci_connect_le_sync(struct hci_dev *hdev, struct hci_conn *conn)
int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn *conn)
{
- if (conn->state != BT_OPEN)
- return -EINVAL;
+ hci_cmd_sync_work_func_t func = NULL;
+ hci_cmd_sync_work_destroy_t destroy = NULL;
+ int create_flag = -1;
switch (conn->type) {
case ACL_LINK:
- return !hci_cmd_sync_dequeue_once(hdev,
- hci_acl_create_conn_sync,
- conn, NULL);
+ func = hci_acl_create_conn_sync;
+ create_flag = HCI_CONN_CREATE_CONN;
+ break;
case LE_LINK:
- return !hci_cmd_sync_dequeue_once(hdev, hci_le_create_conn_sync,
- conn, create_le_conn_complete);
+ func = hci_le_create_conn_sync;
+ destroy = create_le_conn_complete;
+ create_flag = HCI_CONN_CREATE_CONN;
+ break;
+ case CIS_LINK:
+ /* LE Create CIS is shared by the whole CIG and cannot be
+ * dequeued per-connection; only cancel it in-flight below.
+ */
+ create_flag = HCI_CONN_CREATE_CIS;
+ break;
+ default:
+ return -ENOENT;
}
- return -ENOENT;
+ if (func && hci_cmd_sync_dequeue_once(hdev, func, conn, destroy))
+ return 0;
+
+ if (create_flag >= 0 && test_bit(create_flag, &conn->flags))
+ hci_cmd_sync_cancel(hdev, ECANCELED);
+
+ return -EBUSY;
}
int hci_le_conn_update_sync(struct hci_dev *hdev, struct hci_conn *conn,
--
2.54.0
^ permalink raw reply related
* Re: [PATCH BlueZ v2 1/4] btio: Handle EOPNOTSUPP from accept() to prevent busy loop
From: Bastien Nocera @ 2026-06-11 14:58 UTC (permalink / raw)
To: Luiz Augusto von Dentz, linux-bluetooth
In-Reply-To: <20260609185313.155105-1-luiz.dentz@gmail.com>
On Tue, 2026-06-09 at 14:53 -0400, Luiz Augusto von Dentz wrote:
> From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
>
> When accept() returns EOPNOTSUPP on an L2CAP SEQPACKET server socket
> (e.g. AVCTP browsing channel, PSM 0x1b), the error is permanent and
> retrying will never succeed. Previously, only EBADFD was treated as
> fatal, causing server_cb to return TRUE for EOPNOTSUPP. Since the fd
> remains readable, this creates an infinite busy loop that hangs
> bluetoothd.
>
> Treat EOPNOTSUPP the same as EBADFD by returning FALSE to remove the
> GLib IO watch and stop the loop.
This was the infinite loop that was happening on startup, right?
Were there specific circumstances for it to hang like this? I was
wondering whether this was something I could reproduce in the
integration tests I put together for the meson port.
> ---
> btio/btio.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/btio/btio.c b/btio/btio.c
> index 39d4411f790b..4c69d60350f8 100644
> --- a/btio/btio.c
> +++ b/btio/btio.c
> @@ -274,7 +274,7 @@ static gboolean server_cb(GIOChannel *io,
> GIOCondition cond,
>
> cli_sock = accept(srv_sock, NULL, NULL);
> if (cli_sock < 0) {
> - if (errno == EBADFD)
> + if (errno == EBADFD || errno == EOPNOTSUPP)
> return FALSE;
> return TRUE;
> }
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox