CVE-2026-46111
Description
In the Linux kernel, the following vulnerability has been resolved:
Bluetooth: hci_conn: fix potential UAF in create_big_sync
Add hci_conn_valid() check in create_big_sync() to detect stale connections before proceeding with BIG creation. Handle the resulting -ECANCELED in create_big_complete() and re-validate the connection under hci_dev_lock() before dereferencing, matching the pattern used by create_le_conn_complete() and create_pa_complete().
Keep the hci_conn object alive across the async boundary by taking a reference via hci_conn_get() when queueing create_big_sync(), and dropping it in the completion callback. The refcount and the lock are complementary: the refcount keeps the object allocated, while hci_dev_lock() serializes hci_conn_hash_del()'s list_del_rcu() on hdev->conn_hash, as required by hci_conn_del().
hci_conn_put() is called outside hci_dev_unlock() so the final put (which resolves to kfree() via bt_link_release) does not run under hdev->lock, though the release path would be safe either way.
Without this, create_big_complete() would unconditionally dereference the conn pointer on error, causing a use-after-free via hci_connect_cfm() and hci_conn_del().
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
A use-after-free in Linux kernel's Bluetooth hci_conn could be triggered by creating a BIG sync with a stale connection, leading to potential code execution.
Vulnerability
In the Linux kernel's Bluetooth subsystem, the function create_big_sync() in hci_conn.c lacked a check for stale connections before proceeding with BIG (BIS) creation. This could lead to a use-after-free (UAF) when create_big_complete() dereferences the conn pointer on error, as hci_connect_cfm() and hci_conn_del() could free the connection object. The issue affects kernel versions prior to the fix commit [1].
Exploitation
An attacker with local access and the ability to trigger Bluetooth operations could exploit this by initiating a BIG creation with a connection that becomes stale (e.g., due to disconnection or timeout). The race window between the async operation and connection deletion allows the UAF. No special privileges beyond Bluetooth stack access are required.
Impact
Successful exploitation could result in a use-after-free, potentially leading to arbitrary code execution in kernel context, denial of service, or information disclosure. The attacker gains the ability to corrupt kernel memory, escalating privileges or crashing the system.
Mitigation
The fix was committed to the Linux kernel stable tree as commit 6823f730bf195fc296d9edd09e2ca94bc1ff5584 [1]. Users should update to a kernel version containing this fix. No workaround is available; the vulnerability is resolved by applying the patch.
AI Insight generated on May 28, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
1Patches
106823f730bf19Bluetooth: hci_conn: fix potential UAF in create_big_sync
1 file changed · +18 −2
net/bluetooth/hci_conn.c+18 −2 modifieddiff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 97e48c1f69aff7..f51c530a3c4583 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -2014,6 +2014,9 @@ static int create_big_sync(struct hci_dev *hdev, void *data) u32 flags = 0; int err; + if (!hci_conn_valid(hdev, conn)) + return -ECANCELED; + if (qos->bcast.out.phy == 0x02) flags |= MGMT_ADV_FLAG_SEC_2M; @@ -2125,11 +2128,24 @@ static void create_big_complete(struct hci_dev *hdev, void *data, int err) bt_dev_dbg(hdev, "conn %p", conn); + if (err == -ECANCELED) + goto done; + + hci_dev_lock(hdev); + + if (!hci_conn_valid(hdev, conn)) + goto unlock; + if (err) { bt_dev_err(hdev, "Unable to create BIG: %d", err); hci_connect_cfm(conn, err); hci_conn_del(conn); } + +unlock: + hci_dev_unlock(hdev); +done: + hci_conn_put(conn); } struct hci_conn *hci_bind_bis(struct hci_dev *hdev, bdaddr_t *dst, @@ -2230,10 +2246,11 @@ struct hci_conn *hci_connect_bis(struct hci_dev *hdev, bdaddr_t *dst, BT_BOUND, &data); /* Queue start periodic advertising and create BIG */ - err = hci_cmd_sync_queue(hdev, create_big_sync, conn, + err = hci_cmd_sync_queue(hdev, create_big_sync, hci_conn_get(conn), create_big_complete); if (err < 0) { hci_conn_drop(conn); + hci_conn_put(conn); return ERR_PTR(err); } -- cgit 1.3-korg
1750a2df0eabBluetooth: hci_conn: fix potential UAF in create_big_sync
1 file changed · +18 −2
net/bluetooth/hci_conn.c+18 −2 modifieddiff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index bf1c39be05211c..f89af453cb3b18 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -2051,6 +2051,9 @@ static int create_big_sync(struct hci_dev *hdev, void *data) u32 flags = 0; int err; + if (!hci_conn_valid(hdev, conn)) + return -ECANCELED; + if (qos->bcast.out.phy == 0x02) flags |= MGMT_ADV_FLAG_SEC_2M; @@ -2125,11 +2128,24 @@ static void create_big_complete(struct hci_dev *hdev, void *data, int err) bt_dev_dbg(hdev, "conn %p", conn); + if (err == -ECANCELED) + goto done; + + hci_dev_lock(hdev); + + if (!hci_conn_valid(hdev, conn)) + goto unlock; + if (err) { bt_dev_err(hdev, "Unable to create BIG: %d", err); hci_connect_cfm(conn, err); hci_conn_del(conn); } + +unlock: + hci_dev_unlock(hdev); +done: + hci_conn_put(conn); } struct hci_conn *hci_bind_bis(struct hci_dev *hdev, bdaddr_t *dst, @@ -2230,10 +2246,11 @@ struct hci_conn *hci_connect_bis(struct hci_dev *hdev, bdaddr_t *dst, BT_BOUND, &data); /* Queue start periodic advertising and create BIG */ - err = hci_cmd_sync_queue(hdev, create_big_sync, conn, + err = hci_cmd_sync_queue(hdev, create_big_sync, hci_conn_get(conn), create_big_complete); if (err < 0) { hci_conn_drop(conn); + hci_conn_put(conn); return ERR_PTR(err); } -- cgit 1.3-korg
dc34f8d8240fBluetooth: hci_conn: fix potential UAF in create_big_sync
1 file changed · +18 −2
net/bluetooth/hci_conn.c+18 −2 modifieddiff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 71a24be2a6d67d..1b63bc2753a24e 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -2113,6 +2113,9 @@ static int create_big_sync(struct hci_dev *hdev, void *data) u32 flags = 0; int err; + if (!hci_conn_valid(hdev, conn)) + return -ECANCELED; + if (qos->bcast.out.phy == 0x02) flags |= MGMT_ADV_FLAG_SEC_2M; @@ -2188,11 +2191,24 @@ static void create_big_complete(struct hci_dev *hdev, void *data, int err) bt_dev_dbg(hdev, "conn %p", conn); + if (err == -ECANCELED) + goto done; + + hci_dev_lock(hdev); + + if (!hci_conn_valid(hdev, conn)) + goto unlock; + if (err) { bt_dev_err(hdev, "Unable to create BIG: %d", err); hci_connect_cfm(conn, err); hci_conn_del(conn); } + +unlock: + hci_dev_unlock(hdev); +done: + hci_conn_put(conn); } struct hci_conn *hci_bind_bis(struct hci_dev *hdev, bdaddr_t *dst, __u8 sid, @@ -2309,10 +2325,11 @@ struct hci_conn *hci_connect_bis(struct hci_dev *hdev, bdaddr_t *dst, BT_BOUND, &data); /* Queue start periodic advertising and create BIG */ - err = hci_cmd_sync_queue(hdev, create_big_sync, conn, + err = hci_cmd_sync_queue(hdev, create_big_sync, hci_conn_get(conn), create_big_complete); if (err < 0) { hci_conn_drop(conn); + hci_conn_put(conn); return ERR_PTR(err); } -- cgit 1.3-korg
f8eaf92c57adBluetooth: hci_conn: fix potential UAF in create_big_sync
1 file changed · +18 −2
net/bluetooth/hci_conn.c+18 −2 modifieddiff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 11d3ad8d255145..9fa6901aae9fc1 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -2130,6 +2130,9 @@ static int create_big_sync(struct hci_dev *hdev, void *data) u32 flags = 0; int err; + if (!hci_conn_valid(hdev, conn)) + return -ECANCELED; + if (qos->bcast.out.phys == BIT(1)) flags |= MGMT_ADV_FLAG_SEC_2M; @@ -2204,11 +2207,24 @@ static void create_big_complete(struct hci_dev *hdev, void *data, int err) bt_dev_dbg(hdev, "conn %p", conn); + if (err == -ECANCELED) + goto done; + + hci_dev_lock(hdev); + + if (!hci_conn_valid(hdev, conn)) + goto unlock; + if (err) { bt_dev_err(hdev, "Unable to create BIG: %d", err); hci_connect_cfm(conn, err); hci_conn_del(conn); } + +unlock: + hci_dev_unlock(hdev); +done: + hci_conn_put(conn); } struct hci_conn *hci_bind_bis(struct hci_dev *hdev, bdaddr_t *dst, __u8 sid, @@ -2336,10 +2352,11 @@ struct hci_conn *hci_connect_bis(struct hci_dev *hdev, bdaddr_t *dst, BT_BOUND, &data); /* Queue start periodic advertising and create BIG */ - err = hci_cmd_sync_queue(hdev, create_big_sync, conn, + err = hci_cmd_sync_queue(hdev, create_big_sync, hci_conn_get(conn), create_big_complete); if (err < 0) { hci_conn_drop(conn); + hci_conn_put(conn); return ERR_PTR(err); } -- cgit 1.3-korg
0beddb0c380bBluetooth: hci_conn: fix potential UAF in create_big_sync
1 file changed · +18 −2
net/bluetooth/hci_conn.c+18 −2 modifieddiff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 3a059259908615..96e345fcf30362 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -2130,6 +2130,9 @@ static int create_big_sync(struct hci_dev *hdev, void *data) u32 flags = 0; int err; + if (!hci_conn_valid(hdev, conn)) + return -ECANCELED; + if (qos->bcast.out.phys == BIT(1)) flags |= MGMT_ADV_FLAG_SEC_2M; @@ -2204,11 +2207,24 @@ static void create_big_complete(struct hci_dev *hdev, void *data, int err) bt_dev_dbg(hdev, "conn %p", conn); + if (err == -ECANCELED) + goto done; + + hci_dev_lock(hdev); + + if (!hci_conn_valid(hdev, conn)) + goto unlock; + if (err) { bt_dev_err(hdev, "Unable to create BIG: %d", err); hci_connect_cfm(conn, err); hci_conn_del(conn); } + +unlock: + hci_dev_unlock(hdev); +done: + hci_conn_put(conn); } struct hci_conn *hci_bind_bis(struct hci_dev *hdev, bdaddr_t *dst, __u8 sid, @@ -2336,10 +2352,11 @@ struct hci_conn *hci_connect_bis(struct hci_dev *hdev, bdaddr_t *dst, BT_BOUND, &data); /* Queue start periodic advertising and create BIG */ - err = hci_cmd_sync_queue(hdev, create_big_sync, conn, + err = hci_cmd_sync_queue(hdev, create_big_sync, hci_conn_get(conn), create_big_complete); if (err < 0) { hci_conn_drop(conn); + hci_conn_put(conn); return ERR_PTR(err); } -- cgit 1.3-korg
6823f730bf19Bluetooth: hci_conn: fix potential UAF in create_big_sync
1 file changed · +18 −2
net/bluetooth/hci_conn.c+18 −2 modifieddiff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 97e48c1f69aff7..f51c530a3c4583 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -2014,6 +2014,9 @@ static int create_big_sync(struct hci_dev *hdev, void *data) u32 flags = 0; int err; + if (!hci_conn_valid(hdev, conn)) + return -ECANCELED; + if (qos->bcast.out.phy == 0x02) flags |= MGMT_ADV_FLAG_SEC_2M; @@ -2125,11 +2128,24 @@ static void create_big_complete(struct hci_dev *hdev, void *data, int err) bt_dev_dbg(hdev, "conn %p", conn); + if (err == -ECANCELED) + goto done; + + hci_dev_lock(hdev); + + if (!hci_conn_valid(hdev, conn)) + goto unlock; + if (err) { bt_dev_err(hdev, "Unable to create BIG: %d", err); hci_connect_cfm(conn, err); hci_conn_del(conn); } + +unlock: + hci_dev_unlock(hdev); +done: + hci_conn_put(conn); } struct hci_conn *hci_bind_bis(struct hci_dev *hdev, bdaddr_t *dst, @@ -2230,10 +2246,11 @@ struct hci_conn *hci_connect_bis(struct hci_dev *hdev, bdaddr_t *dst, BT_BOUND, &data); /* Queue start periodic advertising and create BIG */ - err = hci_cmd_sync_queue(hdev, create_big_sync, conn, + err = hci_cmd_sync_queue(hdev, create_big_sync, hci_conn_get(conn), create_big_complete); if (err < 0) { hci_conn_drop(conn); + hci_conn_put(conn); return ERR_PTR(err); } -- cgit 1.3-korg
1750a2df0eabBluetooth: hci_conn: fix potential UAF in create_big_sync
1 file changed · +18 −2
net/bluetooth/hci_conn.c+18 −2 modifieddiff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index bf1c39be05211c..f89af453cb3b18 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -2051,6 +2051,9 @@ static int create_big_sync(struct hci_dev *hdev, void *data) u32 flags = 0; int err; + if (!hci_conn_valid(hdev, conn)) + return -ECANCELED; + if (qos->bcast.out.phy == 0x02) flags |= MGMT_ADV_FLAG_SEC_2M; @@ -2125,11 +2128,24 @@ static void create_big_complete(struct hci_dev *hdev, void *data, int err) bt_dev_dbg(hdev, "conn %p", conn); + if (err == -ECANCELED) + goto done; + + hci_dev_lock(hdev); + + if (!hci_conn_valid(hdev, conn)) + goto unlock; + if (err) { bt_dev_err(hdev, "Unable to create BIG: %d", err); hci_connect_cfm(conn, err); hci_conn_del(conn); } + +unlock: + hci_dev_unlock(hdev); +done: + hci_conn_put(conn); } struct hci_conn *hci_bind_bis(struct hci_dev *hdev, bdaddr_t *dst, @@ -2230,10 +2246,11 @@ struct hci_conn *hci_connect_bis(struct hci_dev *hdev, bdaddr_t *dst, BT_BOUND, &data); /* Queue start periodic advertising and create BIG */ - err = hci_cmd_sync_queue(hdev, create_big_sync, conn, + err = hci_cmd_sync_queue(hdev, create_big_sync, hci_conn_get(conn), create_big_complete); if (err < 0) { hci_conn_drop(conn); + hci_conn_put(conn); return ERR_PTR(err); } -- cgit 1.3-korg
dc34f8d8240fBluetooth: hci_conn: fix potential UAF in create_big_sync
1 file changed · +18 −2
net/bluetooth/hci_conn.c+18 −2 modifieddiff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 71a24be2a6d67d..1b63bc2753a24e 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -2113,6 +2113,9 @@ static int create_big_sync(struct hci_dev *hdev, void *data) u32 flags = 0; int err; + if (!hci_conn_valid(hdev, conn)) + return -ECANCELED; + if (qos->bcast.out.phy == 0x02) flags |= MGMT_ADV_FLAG_SEC_2M; @@ -2188,11 +2191,24 @@ static void create_big_complete(struct hci_dev *hdev, void *data, int err) bt_dev_dbg(hdev, "conn %p", conn); + if (err == -ECANCELED) + goto done; + + hci_dev_lock(hdev); + + if (!hci_conn_valid(hdev, conn)) + goto unlock; + if (err) { bt_dev_err(hdev, "Unable to create BIG: %d", err); hci_connect_cfm(conn, err); hci_conn_del(conn); } + +unlock: + hci_dev_unlock(hdev); +done: + hci_conn_put(conn); } struct hci_conn *hci_bind_bis(struct hci_dev *hdev, bdaddr_t *dst, __u8 sid, @@ -2309,10 +2325,11 @@ struct hci_conn *hci_connect_bis(struct hci_dev *hdev, bdaddr_t *dst, BT_BOUND, &data); /* Queue start periodic advertising and create BIG */ - err = hci_cmd_sync_queue(hdev, create_big_sync, conn, + err = hci_cmd_sync_queue(hdev, create_big_sync, hci_conn_get(conn), create_big_complete); if (err < 0) { hci_conn_drop(conn); + hci_conn_put(conn); return ERR_PTR(err); } -- cgit 1.3-korg
f8eaf92c57adBluetooth: hci_conn: fix potential UAF in create_big_sync
1 file changed · +18 −2
net/bluetooth/hci_conn.c+18 −2 modifieddiff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 11d3ad8d255145..9fa6901aae9fc1 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -2130,6 +2130,9 @@ static int create_big_sync(struct hci_dev *hdev, void *data) u32 flags = 0; int err; + if (!hci_conn_valid(hdev, conn)) + return -ECANCELED; + if (qos->bcast.out.phys == BIT(1)) flags |= MGMT_ADV_FLAG_SEC_2M; @@ -2204,11 +2207,24 @@ static void create_big_complete(struct hci_dev *hdev, void *data, int err) bt_dev_dbg(hdev, "conn %p", conn); + if (err == -ECANCELED) + goto done; + + hci_dev_lock(hdev); + + if (!hci_conn_valid(hdev, conn)) + goto unlock; + if (err) { bt_dev_err(hdev, "Unable to create BIG: %d", err); hci_connect_cfm(conn, err); hci_conn_del(conn); } + +unlock: + hci_dev_unlock(hdev); +done: + hci_conn_put(conn); } struct hci_conn *hci_bind_bis(struct hci_dev *hdev, bdaddr_t *dst, __u8 sid, @@ -2336,10 +2352,11 @@ struct hci_conn *hci_connect_bis(struct hci_dev *hdev, bdaddr_t *dst, BT_BOUND, &data); /* Queue start periodic advertising and create BIG */ - err = hci_cmd_sync_queue(hdev, create_big_sync, conn, + err = hci_cmd_sync_queue(hdev, create_big_sync, hci_conn_get(conn), create_big_complete); if (err < 0) { hci_conn_drop(conn); + hci_conn_put(conn); return ERR_PTR(err); } -- cgit 1.3-korg
0beddb0c380bBluetooth: hci_conn: fix potential UAF in create_big_sync
1 file changed · +18 −2
net/bluetooth/hci_conn.c+18 −2 modifieddiff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 3a059259908615..96e345fcf30362 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -2130,6 +2130,9 @@ static int create_big_sync(struct hci_dev *hdev, void *data) u32 flags = 0; int err; + if (!hci_conn_valid(hdev, conn)) + return -ECANCELED; + if (qos->bcast.out.phys == BIT(1)) flags |= MGMT_ADV_FLAG_SEC_2M; @@ -2204,11 +2207,24 @@ static void create_big_complete(struct hci_dev *hdev, void *data, int err) bt_dev_dbg(hdev, "conn %p", conn); + if (err == -ECANCELED) + goto done; + + hci_dev_lock(hdev); + + if (!hci_conn_valid(hdev, conn)) + goto unlock; + if (err) { bt_dev_err(hdev, "Unable to create BIG: %d", err); hci_connect_cfm(conn, err); hci_conn_del(conn); } + +unlock: + hci_dev_unlock(hdev); +done: + hci_conn_put(conn); } struct hci_conn *hci_bind_bis(struct hci_dev *hdev, bdaddr_t *dst, __u8 sid, @@ -2336,10 +2352,11 @@ struct hci_conn *hci_connect_bis(struct hci_dev *hdev, bdaddr_t *dst, BT_BOUND, &data); /* Queue start periodic advertising and create BIG */ - err = hci_cmd_sync_queue(hdev, create_big_sync, conn, + err = hci_cmd_sync_queue(hdev, create_big_sync, hci_conn_get(conn), create_big_complete); if (err < 0) { hci_conn_drop(conn); + hci_conn_put(conn); return ERR_PTR(err); } -- cgit 1.3-korg
Vulnerability mechanics
Root cause
"Missing stale-connection validation and missing reference count in the asynchronous BIG creation path allows use-after-free of the hci_conn object."
Attack vector
An attacker with the ability to trigger Bluetooth connection teardown (e.g., by initiating a disconnect or forcing a link loss) while a BIG (BIS) creation is in progress can cause the `hci_conn` object to be freed before the asynchronous `create_big_complete()` callback runs. The callback would then unconditionally dereference the stale `conn` pointer, leading to a use-after-free via `hci_connect_cfm()` and `hci_conn_del()` [patch_id=2898625]. The bug is reachable through the Bluetooth HCI command path with no special privileges beyond local Bluetooth access.
Affected code
The vulnerability resides in `net/bluetooth/hci_conn.c` in the functions `create_big_sync()` and `create_big_complete()`. The `create_big_sync()` function is queued as an asynchronous work item via `hci_cmd_sync_queue()`, and `create_big_complete()` is the corresponding completion callback. No reference write-ups beyond the patch are provided.
What the fix does
The patch adds an `hci_conn_valid()` check at the start of `create_big_sync()` to return `-ECANCELED` if the connection has been removed from the hash table. In `create_big_complete()`, the error value `-ECANCELED` is handled by skipping all dereferences and jumping to the cleanup path. The callback now acquires `hci_dev_lock()` and re-validates the connection before calling `hci_connect_cfm()` or `hci_conn_del()`. Additionally, `hci_conn_get()` is called when queueing the work item, and `hci_conn_put()` is called in the completion callback, ensuring the object stays alive across the asynchronous boundary [patch_id=2898625].
Preconditions
- inputThe attacker must be able to trigger Bluetooth connection teardown (e.g., via a disconnect or link loss) while a BIG creation is queued.
- configThe target system must have Bluetooth enabled and a BIS (BIG) connection in progress.
Generated on May 28, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- git.kernel.org/stable/c/0beddb0c380bed5f5b8e61ddbe14635bb73d0b41nvd
- git.kernel.org/stable/c/1750a2df0eab61dc421a7afae74abdd239a44b85nvd
- git.kernel.org/stable/c/6823f730bf195fc296d9edd09e2ca94bc1ff5584nvd
- git.kernel.org/stable/c/dc34f8d8240f25dd137dc2758ebbcc75e3779142nvd
- git.kernel.org/stable/c/f8eaf92c57ad99358dd372580d5ff87623343a72nvd
News mentions
0No linked articles in our index yet.