public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
* [rpms/bluez] f44: Rebase to latest upstream HEAD (Closes: #2528181, #2525293)
@ 2026-09-09 12:44 Bastien Nocera
0 siblings, 0 replies; only message in thread
From: Bastien Nocera @ 2026-09-09 12:44 UTC (permalink / raw)
To: git-commits
A new commit has been pushed.
Repo : rpms/bluez
Branch : f44
Commit : 1545d4ab7b767332bd490133c41926f48c05d0ab
Author : Bastien Nocera <hadess@hadess.net>
Date : 2026-09-09T14:43:51+02:00
Stats : +14/-5057 in 6 file(s)
URL : https://src.fedoraproject.org/rpms/bluez/c/1545d4ab7b767332bd490133c41926f48c05d0ab?branch=f44
Log:
Rebase to latest upstream HEAD (Closes: #2528181, #2525293)
---
diff --git a/5.87-bug-fixes-1.patch b/5.87-bug-fixes-1.patch
deleted file mode 100644
index 3915783..0000000
--- a/5.87-bug-fixes-1.patch
+++ /dev/null
@@ -1,2377 +0,0 @@
-From 5bc6aa79e53700d56fc1f9f9364573ba4c78da65 Mon Sep 17 00:00:00 2001
-From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
-Date: Thu, 2 Jul 2026 11:43:52 -0400
-Subject: [PATCH 01/19] adapter: Fix crash on dev_disconnected
-
-Commit 5d836f1c697c ("adapter: Fix failed bonding attempt after LE
-link disconnection") introduces a regression since it attempts to
-call device_is_connected after adapter_remove_connection which may
-free the device causing the following backtrace:
-
- #0 0x5cd6e384262f in btd_device_bearer_is_connected src/device.c:3754
- #1 0x5cd6e384266f in btd_device_is_connected src/device.c:3745
- #2 0x5cd6e37f8614 in dev_disconnected src/adapter.c:8626
-
-Fixes: https://github.com/bluez/bluez/issues/2221
----
- src/adapter.c | 25 +++++++++++++++++++++----
- 1 file changed, 21 insertions(+), 4 deletions(-)
-
-diff --git a/src/adapter.c b/src/adapter.c
-index 538f63e0a153..210225243f00 100644
---- a/src/adapter.c
-+++ b/src/adapter.c
-@@ -7612,12 +7612,16 @@ struct agent *adapter_get_agent(struct btd_adapter *adapter)
- static void adapter_remove_connection(struct btd_adapter *adapter,
- struct btd_device *device,
- uint8_t bdaddr_type,
-- uint8_t reason)
-+ uint8_t reason,
-+ bool *removed)
- {
- bool remove_device = false;
-
- DBG("");
-
-+ if (removed)
-+ *removed = false;
-+
- if (!g_slist_find(adapter->connections, device)) {
- btd_error(adapter->dev_id, "No matching connection for device");
- return;
-@@ -7638,6 +7642,9 @@ static void adapter_remove_connection(struct btd_adapter *adapter,
-
- DBG("Removing temporary device %s", path);
- btd_adapter_remove_device(adapter, device);
-+
-+ if (removed)
-+ *removed = true;
- }
- }
-
-@@ -7665,10 +7672,10 @@ static void adapter_stop(struct btd_adapter *adapter)
- uint8_t addr_type = btd_device_get_bdaddr_type(device);
-
- adapter_remove_connection(adapter, device, BDADDR_BREDR,
-- MGMT_DEV_DISCONN_UNKNOWN);
-+ MGMT_DEV_DISCONN_UNKNOWN, NULL);
- if (addr_type != BDADDR_BREDR)
- adapter_remove_connection(adapter, device, addr_type,
-- MGMT_DEV_DISCONN_UNKNOWN);
-+ MGMT_DEV_DISCONN_UNKNOWN, NULL);
- }
-
- g_dbus_emit_property_changed(dbus_conn, adapter->path,
-@@ -8618,7 +8625,17 @@ static void dev_disconnected(struct btd_adapter *adapter,
-
- device = btd_adapter_find_device(adapter, &addr->bdaddr, addr->type);
- if (device) {
-- adapter_remove_connection(adapter, device, addr->type, reason);
-+ bool removed;
-+
-+ adapter_remove_connection(adapter, device, addr->type, reason,
-+ &removed);
-+ /* No need to continue if device was removed from the adapter,
-+ * as it will be freed and the disconnect notify will be called
-+ * in the device free callback.
-+ */
-+ if (removed)
-+ return;
-+
- disconnect_notify(device, reason);
- }
-
---
-2.54.0
-
-
-From b84aa894921037e1b1de00d282db2ee27a4ce63f Mon Sep 17 00:00:00 2001
-From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
-Date: Mon, 6 Jul 2026 10:59:28 -0400
-Subject: [PATCH 02/19] l2test: Fix calling getsockopt(BT_PHY)
-
-getsockopt(BT_PHY) expectes a 32 bits argument not a
-sizeof(struct sockaddr_l2).
----
- tools/l2test.c | 5 ++++-
- 1 file changed, 4 insertions(+), 1 deletion(-)
-
-diff --git a/tools/l2test.c b/tools/l2test.c
-index aa05f5247c5e..08d2269936b9 100644
---- a/tools/l2test.c
-+++ b/tools/l2test.c
-@@ -358,7 +358,8 @@ static int print_info(int sk, struct l2cap_options *opts)
- struct sockaddr_l2 addr;
- socklen_t optlen;
- struct l2cap_conninfo conn;
-- int prio, phy;
-+ int prio;
-+ uint32_t phy;
- char ba[18];
-
- /* Get connection information */
-@@ -414,6 +415,8 @@ static int print_info(int sk, struct l2cap_options *opts)
- conn.dev_class[0], prio, rcvbuf);
-
-
-+ optlen = sizeof(phy);
-+
- if (!getsockopt(sk, SOL_BLUETOOTH, BT_PHY, &phy, &optlen)) {
- syslog(LOG_INFO, "Supported PHY: 0x%08x", phy);
- print_bitfield(2, phy, phy_table);
---
-2.54.0
-
-
-From 3f283c8e0ab3bad17ee8c13fa0dcf251a16e4e25 Mon Sep 17 00:00:00 2001
-From: raghavendra <raghavendra.rao@collabora.com>
-Date: Thu, 2 Jul 2026 18:39:04 +0530
-Subject: [PATCH 03/19] shared/bap: Validate codec configuration parameters
-
-This is required for PTS test ASCS/SR/SPE/BI-07-C
----
- src/shared/bap.c | 22 ++++++++++++++++++++++
- 1 file changed, 22 insertions(+)
-
-diff --git a/src/shared/bap.c b/src/shared/bap.c
-index 1f61227f82a7..6bc044a63758 100644
---- a/src/shared/bap.c
-+++ b/src/shared/bap.c
-@@ -29,6 +29,7 @@
- #include "src/shared/gatt-client.h"
- #include "src/shared/bap.h"
- #include "src/shared/ascs.h"
-+#include "src/shared/lc3.h"
- #include "src/shared/bap-debug.h"
-
- /* Maximum number of ASE(s) */
-@@ -3157,6 +3158,7 @@ static uint8_t ep_config(struct bt_bap_endpoint *ep, struct bt_bap *bap,
- struct iovec cc;
- const struct queue_entry *e;
- struct bt_bap_codec codec;
-+ uint8_t *ltv;
-
- DBG(bap, "ep %p id 0x%02x dir 0x%02x", ep, ep->id, ep->dir);
-
-@@ -3190,6 +3192,19 @@ static uint8_t ep_config(struct bt_bap_endpoint *ep, struct bt_bap *bap,
- return 0;
- }
-
-+ ltv = cc.iov_base;
-+
-+ if (req->codec.id == LC3_ID && cc.iov_len == 3 &&
-+ ltv[0] == 0x02 &&
-+ ltv[1] == LC3_CONFIG_DURATION &&
-+ ltv[2] != LC3_CONFIG_DURATION_7_5 &&
-+ ltv[2] != LC3_CONFIG_DURATION_10) {
-+ ascs_ase_rsp_add(rsp, req->ase,
-+ BT_ASCS_RSP_CONF_INVALID,
-+ BT_ASCS_REASON_CODEC_DATA);
-+ return 0;
-+ }
-+
- switch (ep->dir) {
- case BT_BAP_SINK:
- e = queue_get_entries(bap->ldb->sinks);
-@@ -3207,6 +3222,13 @@ static uint8_t ep_config(struct bt_bap_endpoint *ep, struct bt_bap *bap,
- codec.cid = le16_to_cpu(req->codec.cid);
- codec.vid = le16_to_cpu(req->codec.vid);
-
-+ if (codec.id != 0xff && (codec.cid || codec.vid)) {
-+ ascs_ase_rsp_add(rsp, req->ase,
-+ BT_ASCS_RSP_CONF_INVALID,
-+ BT_ASCS_REASON_CODEC);
-+ return 0;
-+ }
-+
- for (; e; e = e->next) {
- struct bt_bap_pac *pac = e->data;
-
---
-2.54.0
-
-
-From 5c1c679ec304b8aadaa287cca59b38589d339e91 Mon Sep 17 00:00:00 2001
-From: raghavendra <raghavendra.rao@collabora.com>
-Date: Thu, 2 Jul 2026 18:39:05 +0530
-Subject: [PATCH 04/19] shared/bap: Validate unicast QoS configuration
-
-This is required for PTS tests ASCS/SR/SPE/BI-08-C and ASCS/SR/SPE/BI-10-C.
----
- src/shared/bap.c | 26 ++++++++++++++++++++++++++
- 1 file changed, 26 insertions(+)
-
-diff --git a/src/shared/bap.c b/src/shared/bap.c
-index 6bc044a63758..f2be7c07ba5d 100644
---- a/src/shared/bap.c
-+++ b/src/shared/bap.c
-@@ -3316,6 +3316,32 @@ static uint8_t ep_qos(struct bt_bap_endpoint *ep, struct bt_bap *bap,
- return 0;
- }
-
-+ if (ep->stream->lpac->codec.id == LC3_ID &&
-+ qos->ucast.io_qos.interval < 0x0000ff) {
-+ ascs_ase_rsp_add(rsp, ep->id,
-+ BT_ASCS_RSP_CONF_INVALID,
-+ BT_ASCS_REASON_INTERVAL);
-+ return 0;
-+ }
-+
-+ if (qos->ucast.framing != BT_ASCS_QOS_FRAMING_UNFRAMED &&
-+ qos->ucast.framing != BT_ASCS_QOS_FRAMING_FRAMED) {
-+ ascs_ase_rsp_add(rsp, ep->id,
-+ BT_ASCS_RSP_CONF_INVALID,
-+ BT_ASCS_REASON_FRAMING);
-+ return 0;
-+ }
-+
-+ if (!qos->ucast.io_qos.phys ||
-+ (qos->ucast.io_qos.phys & ~(BT_BAP_CONFIG_PHY_1M |
-+ BT_BAP_CONFIG_PHY_2M |
-+ BT_BAP_CONFIG_PHY_CODEC))) {
-+ ascs_ase_rsp_add(rsp, ep->id,
-+ BT_ASCS_RSP_CONF_INVALID,
-+ BT_ASCS_REASON_PHY);
-+ return 0;
-+ }
-+
- return stream_qos(ep->stream, qos, rsp);
- }
-
---
-2.54.0
-
-
-From 13b14db95089382701f54a46e6ef0120b69d4a62 Mon Sep 17 00:00:00 2001
-From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
-Date: Tue, 7 Jul 2026 14:34:30 -0400
-Subject: [PATCH 05/19] shared/bap: Use util_ltv_foreach to process metadata
-
-This makes use of util_ltv_foreach to process the metadata entries
-instead of attempting to iterate over the entries manually which
-probably only works with the exact same order as used by PTS.
----
- src/shared/bap.c | 84 ++++++++++++++++++++++++++++++++++--------------
- 1 file changed, 59 insertions(+), 25 deletions(-)
-
-diff --git a/src/shared/bap.c b/src/shared/bap.c
-index f2be7c07ba5d..db6f4f204787 100644
---- a/src/shared/bap.c
-+++ b/src/shared/bap.c
-@@ -3404,35 +3404,69 @@ static uint8_t stream_enable(struct bt_bap_stream *stream, struct iovec *meta,
- return 0;
- }
-
--static bool ascs_metadata_rsp(struct bt_bap_endpoint *ep, struct iovec *meta,
-- struct iovec *rsp)
-+struct bap_metadata_process {
-+ struct bt_bap_endpoint *ep;
-+ uint16_t context;
-+ struct iovec *rsp;
-+ uint8_t err;
-+};
-+
-+static void bap_metadata_process(size_t i, uint8_t l, uint8_t t, uint8_t *v,
-+ void *user_data)
- {
-- struct bt_ltv *ltv;
-- uint16_t supported_context = 0;
-+ struct bap_metadata_process *data = user_data;
- uint16_t context;
-
-+ switch (t) {
-+ case BAP_METADATA_PREF_CONTEXT_LTV_TYPE:
-+ break;
-+ case BAP_METADATA_CONTEXT_LTV_TYPE:
-+ if (l != sizeof(context)) {
-+ ascs_ase_rsp_add(data->rsp, data->ep->id,
-+ BT_ASCS_RSP_METADATA_INVALID, t);
-+ data->err = BT_ASCS_RSP_METADATA_INVALID;
-+ break;
-+ }
-+
-+ context = get_le16(v);
-+ if (!context || (context & ~data->context)) {
-+ ascs_ase_rsp_add(data->rsp, data->ep->id,
-+ BT_ASCS_RSP_METADATA_INVALID, t);
-+ data->err = BT_ASCS_RSP_METADATA_INVALID;
-+ }
-+
-+ break;
-+ case BAP_METADATA_PROGRAM_INFO_LTV_TYPE:
-+ case BAP_METADATA_LANGUAGE_LTV_TYPE:
-+ break;
-+ default:
-+ ascs_ase_rsp_add(data->rsp, data->ep->id,
-+ BT_ASCS_RSP_METADATA_UNSUPPORTED,
-+ t);
-+ data->err = BT_ASCS_RSP_METADATA_UNSUPPORTED;
-+ break;
-+ };
-+}
-+
-+static bool ascs_metadata_rsp(struct bt_bap_endpoint *ep, struct bt_bap *bap,
-+ struct iovec *meta, struct iovec *rsp)
-+{
-+ struct bap_metadata_process data = {
-+ .ep = ep,
-+ .rsp = rsp,
-+ .err = 0,
-+ };
-+
- if (ep->stream && ep->stream->lpac)
-- supported_context = ep->stream->lpac->qos.supported_context;
-+ data.context = ep->stream->lpac->qos.supported_context;
-
-- ltv = meta->iov_base;
-- if (meta->iov_len >= sizeof(*ltv) &&
-- (ltv->type < BAP_METADATA_PREF_CONTEXT_LTV_TYPE ||
-- ltv->type > BAP_METADATA_LANGUAGE_LTV_TYPE)) {
-- ascs_ase_rsp_add(rsp, ep->id,
-- BT_ASCS_RSP_METADATA_UNSUPPORTED, ltv->type);
-- return true;
-- }
-+ util_ltv_foreach(meta->iov_base, meta->iov_len, NULL,
-+ bap_metadata_process, &data);
-
-- if (meta->iov_len >= sizeof(*ltv) + sizeof(context) &&
-- ltv->type == BAP_METADATA_CONTEXT_LTV_TYPE &&
-- ltv->len == sizeof(context) + 1) {
-- context = get_le16(ltv->value);
-- if (!context || (context & ~supported_context)) {
-- ascs_ase_rsp_add(rsp, ep->id,
-- BT_ASCS_RSP_METADATA_INVALID,
-- ltv->type);
-- return true;
-- }
-+ if (data.err) {
-+ DBG(bap, "ep %p id 0x%02x metadata error 0x%02x", ep, ep->id,
-+ data.err);
-+ return true;
- }
-
- return false;
-@@ -3469,7 +3503,7 @@ static uint8_t ep_enable(struct bt_bap_endpoint *ep, struct bt_bap *bap,
- return 0;
- }
-
-- if (ascs_metadata_rsp(ep, &meta, rsp))
-+ if (ascs_metadata_rsp(ep, bap, &meta, rsp))
- return 0;
-
- if (!ep->stream) {
-@@ -3705,7 +3739,7 @@ static uint8_t ep_metadata(struct bt_bap_endpoint *ep,
- meta.iov_base = util_iov_pull_mem(iov, req->len);
- meta.iov_len = req->len;
-
-- if (ascs_metadata_rsp(ep, &meta, rsp))
-+ if (ascs_metadata_rsp(ep, stream->bap, &meta, rsp))
- return 0;
-
- return stream_metadata(ep->stream, &meta, rsp);
---
-2.54.0
-
-
-From 9e9f0a370c591c126c39225f5f50b88915d11095 Mon Sep 17 00:00:00 2001
-From: Rahul Samana <rahul.samana@oss.qualcomm.com>
-Date: Tue, 7 Jul 2026 12:05:43 +0530
-Subject: [PATCH 06/19] gobex: Fix use-after-free when cancelling SRM PUT
- transfer
-
-In SRM mode put_get_data() queues the next packet during encoding of
-the current one, so transfer->req_id and pending_req->id can diverge.
-When g_obex_cancel_transfer() cancels the queued packet from tx_queue,
-cancel_complete() frees the transfer while pending_req still holds a
-callback pointing to it. Its G_OBEX_DEFAULT_TIMEOUT (10s) then fires
-on freed memory causing a SIGSEGV.
-
-Fix by clearing pending_req->rsp_func if it belongs to the same
-transfer being cancelled.
----
- gobex/gobex.c | 8 ++++++++
- 1 file changed, 8 insertions(+)
-
-diff --git a/gobex/gobex.c b/gobex/gobex.c
-index df80d79f31bb..10a438391eae 100644
---- a/gobex/gobex.c
-+++ b/gobex/gobex.c
-@@ -861,6 +861,14 @@ gboolean g_obex_cancel_req(GObex *obex, guint req_id, gboolean remove_callback)
-
- g_queue_delete_link(obex->tx_queue, match);
-
-+ /* In SRM mode pending_req may belong to the same transfer but carry
-+ * a different req_id. Clear its callback to avoid use-after-free
-+ * when its timeout fires after transfer_free().
-+ */
-+ if (remove_callback && obex->pending_req &&
-+ obex->pending_req->rsp_data == p->rsp_data)
-+ obex->pending_req->rsp_func = NULL;
-+
- immediate_completion:
- p->cancelled = TRUE;
- p->obex = g_obex_ref(obex);
---
-2.54.0
-
-
-From 82af2beafc39510e2c4a439bf44faea711d6503f Mon Sep 17 00:00:00 2001
-From: Tom Catshoek <tomcatshoek@zeelandnet.nl>
-Date: Thu, 9 Jul 2026 14:27:04 +0200
-Subject: [PATCH 07/19] adapter: Fix crash on UUID discovery filter match
-
-is_filter_match() looks up each discovery-filter UUID in the queue of
-services parsed from a device advertisement. When that services list was
-migrated from a GSList to a struct queue, the queue_find() call kept
-GLib's g_slist_find_custom() argument order, passing the UUID string
-where queue_find() expects a match function and the comparison function
-where it expects the match data.
-
-As a result queue_find() calls the UUID string as if it were a function,
-jumping into non-executable heap and crashing bluetoothd with SIGSEGV as
-soon as an advertisement matches a UUID filter configured via
-SetDiscoveryFilter.
-
-Add a queue_match_func_t helper and pass the arguments in the correct
-order.
-
-Fixes: https://github.com/bluez/bluez/issues/2282
----
- src/adapter.c | 11 +++++++++--
- 1 file changed, 9 insertions(+), 2 deletions(-)
-
-diff --git a/src/adapter.c b/src/adapter.c
-index 210225243f00..4ffa32a52c13 100644
---- a/src/adapter.c
-+++ b/src/adapter.c
-@@ -7184,6 +7184,14 @@ static void adapter_msd_notify(struct btd_adapter *adapter,
- }
- }
-
-+static bool match_uuid(const void *data, const void *match_data)
-+{
-+ const char *uuid = data;
-+ const char *match = match_data;
-+
-+ return strcmp(uuid, match) == 0;
-+}
-+
- static bool is_filter_match(GSList *discovery_filter, struct eir_data *eir_data,
- int8_t rssi)
- {
-@@ -7216,8 +7224,7 @@ static bool is_filter_match(GSList *discovery_filter, struct eir_data *eir_data,
- * uuid.
- */
- if (queue_find(eir_data->services,
-- m->data,
-- g_strcmp) != NULL)
-+ match_uuid, m->data))
- got_match = true;
- }
- }
---
-2.54.0
-
-
-From b7d71e5067856b0daf2f1f73b3fc95483236610e Mon Sep 17 00:00:00 2001
-From: Mikhail Gavrilov <mikhail.v.gavrilov@gmail.com>
-Date: Fri, 10 Jul 2026 12:28:17 +0500
-Subject: [PATCH 08/19] a2dp: Fix loading of remote SEP from cache
-
-Commit 912f5efb0dd9 ("a2dp: Fix handling of codec capability storage")
-added an 'i >= 2' condition to the loop that parses the stored codec
-capabilities. Since i starts at 0 the loop body never runs, i stays 0,
-and the subsequent 'if (i != size)' check discards every endpoint found
-in the cache:
-
- load_remote_sep() Unable to load LastUsed: rseid 6 not found
-
-With no remote SEP loaded, avdtp_discover() can no longer take the
-cached path and issues AVDTP Discover/Get All Capabilities on every
-reconnect, and a2dp_setup_remote_path() returns NULL so the transport
-ends up at /org/bluez/hciX/dev_YY/fd0 instead of .../sepN/fd0.
-
-The condition was presumably meant to bound the writes into data[128],
-which the original commit did not actually fix: caps is read with
-'%512s' so size / 2 can be up to 256. Validate size up front instead.
-
-Fixes: https://github.com/bluez/bluez/issues/2285
----
- profiles/audio/a2dp.c | 13 ++++++++++---
- 1 file changed, 10 insertions(+), 3 deletions(-)
-
-diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
-index c8adc3122563..a4ba1dacf943 100644
---- a/profiles/audio/a2dp.c
-+++ b/profiles/audio/a2dp.c
-@@ -2398,7 +2398,16 @@ static void load_remote_sep(struct a2dp_channel *chan, GKeyFile *key_file,
- delay_reporting = false;
- }
-
-- for (i = 0, size = strlen(caps); i < size && i >= 2; i += 2) {
-+ size = strlen(caps);
-+
-+ g_free(value);
-+
-+ if (size % 2 || size / 2 > (int) sizeof(data)) {
-+ warn("Unable to load Endpoint: seid %u", rseid);
-+ continue;
-+ }
-+
-+ for (i = 0; i < size; i += 2) {
- uint8_t *tmp = data + i / 2;
-
- if (sscanf(caps + i, "%02hhx", tmp) != 1) {
-@@ -2407,8 +2416,6 @@ static void load_remote_sep(struct a2dp_channel *chan, GKeyFile *key_file,
- }
- }
-
-- g_free(value);
--
- if (i != size)
- continue;
-
---
-2.54.0
-
-
-From 2a3d3b0e6f83516736c3c8191fb9440475171600 Mon Sep 17 00:00:00 2001
-From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
-Date: Fri, 10 Jul 2026 14:47:14 -0400
-Subject: [PATCH 09/19] monitor: Fix printing subpages bits after all subpages
-
-This fixes printing the decoded subpage bitfields after all subpages
-rather then after the subpage itself:
-
-before:
-
- Features[1/0][8]:
- 00 03 00 00 00 00 00 00 ........
- Features[1/1][8]:
- 00 00 00 00 00 00 00 00 ........
- Features[1/2][8]:
- 00 00 00 00 00 00 00 00 ........
- Shorter Connection Intervals
- Shorter Connection Intervals (Host Support)
-
-after:
-
- Features[1/0][8]:
- 00 03 00 00 00 00 00 00 ........
- Shorter Connection Intervals
- Shorter Connection Intervals (Host Support)
----
- monitor/packet.c | 33 +++++++++++++++++----------------
- 1 file changed, 17 insertions(+), 16 deletions(-)
-
-diff --git a/monitor/packet.c b/monitor/packet.c
-index e62ad4cfb337..ba6ff8e8e551 100644
---- a/monitor/packet.c
-+++ b/monitor/packet.c
-@@ -2925,17 +2925,29 @@ static const struct bitfield_data features_msft[] = {
-
- static void print_features_subpage(uint8_t page, uint8_t subpages,
- const uint8_t *features_array,
-- uint64_t *features)
-+ uint64_t *features,
-+ const struct bitfield_data *table)
- {
- int i, j;
- char str[18];
-
- for (i = 0; i < subpages; i++) {
-+ uint64_t mask;
-+
- for (j = 0; j < 8; j++)
- features[i] |= ((uint64_t) features_array[i * 8 + j])
- << (j * 8);
- sprintf(str, "Features[%u/%u]", page, i);
- print_hex_field(str, &features_array[i * 8], 8);
-+
-+ if (!table)
-+ continue;
-+
-+ mask = print_bitfield(2, features[i], table);
-+ if (mask)
-+ print_text(COLOR_UNKNOWN_FEATURE_BIT,
-+ " Unknown features (0x%16.16" PRIx64 ")",
-+ mask);
- }
- }
-
-@@ -2943,16 +2955,13 @@ static void print_features(uint8_t page, const uint8_t *features_array,
- uint8_t type)
- {
- const struct bitfield_data *features_table = NULL;
-- uint64_t mask, features[3] = {};
-+ uint64_t features[3] = {};
- uint8_t subpages = 1;
-- int i;
-
- /* LE pages 1-10 are 192 bits (24 octets) each */
- if (type == 0x01 && page)
- subpages = 3;
-
-- print_features_subpage(page, subpages, features_array, features);
--
- switch (type) {
- case 0x00:
- switch (page) {
-@@ -2985,16 +2994,8 @@ static void print_features(uint8_t page, const uint8_t *features_array,
- break;
- }
-
-- if (!features_table)
-- return;
--
-- for (i = 0; i < subpages; i++) {
-- mask = print_bitfield(2, features[i], features_table);
-- if (mask)
-- print_text(COLOR_UNKNOWN_FEATURE_BIT,
-- " Unknown features (0x%16.16" PRIx64 ")",
-- mask);
-- }
-+ print_features_subpage(page, subpages, features_array, features,
-+ features_table);
- }
-
- void packet_print_features_lmp(const uint8_t *features, uint8_t page)
-@@ -11065,7 +11066,7 @@ static const struct opcode_data opcode_table[] = {
- "LE Read Minimum Supported Connection Interval",
- null_cmd, 0, true, le_read_conn_interval_rsp,
- sizeof(struct bt_hci_rsp_le_read_conn_interval),
-- true },
-+ false },
- { }
- };
-
---
-2.54.0
-
-
-From 0f2f53fd04d4eb73801de1f8ca16a40e3b12c034 Mon Sep 17 00:00:00 2001
-From: Pauli Virtanen <pav@iki.fi>
-Date: Mon, 13 Jul 2026 16:50:23 +0300
-Subject: [PATCH 10/19] tools/sco-tester: fix missing clear of io_id in
- callbacks
-
-Fixes issues
-
-GLib-CRITICAL **: Source ID 126 was not found when attempting to remove it
----
- tools/sco-tester.c | 2 ++
- 1 file changed, 2 insertions(+)
-
-diff --git a/tools/sco-tester.c b/tools/sco-tester.c
-index a2185b8ef198..fca99a91c20f 100644
---- a/tools/sco-tester.c
-+++ b/tools/sco-tester.c
-@@ -1415,6 +1415,8 @@ static gboolean sco_accept_cb(GIOChannel *io, GIOCondition cond,
- gboolean ret;
- GIOChannel *new_io;
-
-+ data->io_id = 0;
-+
- tester_debug("New connection");
-
- sk = g_io_channel_unix_get_fd(io);
---
-2.54.0
-
-
-From 9353c0ea9f6c1bacff00d9afef0d963796d86bd3 Mon Sep 17 00:00:00 2001
-From: Pauli Virtanen <pav@iki.fi>
-Date: Mon, 13 Jul 2026 16:50:24 +0300
-Subject: [PATCH 11/19] tools/6lowpan-tester: fix race in Client Connect -
- Disable
-
-It's possible client_l2cap_disconnect_cb() is called after 6lowpan
-disable on connect. Prevent failing test in finish_step() in this case.
----
- tools/6lowpan-tester.c | 5 ++++-
- 1 file changed, 4 insertions(+), 1 deletion(-)
-
-diff --git a/tools/6lowpan-tester.c b/tools/6lowpan-tester.c
-index 95fdf206fc0c..738714d68dd1 100644
---- a/tools/6lowpan-tester.c
-+++ b/tools/6lowpan-tester.c
-@@ -648,8 +648,11 @@ static void client_l2cap_connect_cb(uint16_t handle, uint16_t cid,
- if (cdata->disable_on_connect) {
- if (write_6lowpan("6lowpan_enable", "0"))
- tester_test_failed();
-- else
-+ else {
-+ data->handle = 0;
-+ data->dcid = 0;
- tester_test_passed();
-+ }
- }
- }
-
---
-2.54.0
-
-
-From ba08e0f6be0f2ac17de3c0609c64e1d1acaa1563 Mon Sep 17 00:00:00 2001
-From: Pauli Virtanen <pav@iki.fi>
-Date: Mon, 13 Jul 2026 16:50:25 +0300
-Subject: [PATCH 12/19] tools/6lowpan-tester: add test for VHCI teardown
- without disable
-
-Check 6lowpan teardown when controller disappers with active devices.
-Add test
-
-Client Connect - No Disable
----
- tools/6lowpan-tester.c | 28 +++++++++++++++++++++++-----
- 1 file changed, 23 insertions(+), 5 deletions(-)
-
-diff --git a/tools/6lowpan-tester.c b/tools/6lowpan-tester.c
-index 738714d68dd1..a8faf6eb5976 100644
---- a/tools/6lowpan-tester.c
-+++ b/tools/6lowpan-tester.c
-@@ -76,6 +76,9 @@ struct client_data {
-
- /* Interface listener socket type, SOCK_RAW / DGRAM */
- int sk_type;
-+
-+ /* Don't disable 6lowpan before adapter teardown */
-+ bool no_teardown_disable;
- };
-
- static void print_debug(const char *str, void *user_data)
-@@ -264,6 +267,7 @@ static void test_pre_setup(const void *test_data)
- static void test_post_teardown(const void *test_data)
- {
- struct test_data *data = tester_get_data();
-+ const struct client_data *cdata = data->test_data;
- int ret;
-
- if (data->io_id > 0) {
-@@ -276,11 +280,13 @@ static void test_post_teardown(const void *test_data)
- data->packet_fd = -1;
- }
-
-- ret = write_6lowpan("6lowpan_enable", "0");
-- if (ret < 0) {
-- tester_warn("Failed to disable 6lowpan");
-- tester_post_teardown_failed();
-- return;
-+ if (!cdata || !cdata->no_teardown_disable) {
-+ ret = write_6lowpan("6lowpan_enable", "0");
-+ if (ret < 0) {
-+ tester_warn("Failed to disable 6lowpan");
-+ tester_post_teardown_failed();
-+ return;
-+ }
- }
-
- hciemu_unref(data->hciemu);
-@@ -317,6 +323,10 @@ static const struct client_data client_connect_disable = {
- .disable_on_connect = true,
- };
-
-+static const struct client_data client_connect_no_disable = {
-+ .no_teardown_disable = true,
-+};
-+
- static const struct client_data client_connect_disconnect = {
- .disconnect = true,
- };
-@@ -653,6 +663,10 @@ static void client_l2cap_connect_cb(uint16_t handle, uint16_t cid,
- data->dcid = 0;
- tester_test_passed();
- }
-+ } else if (cdata->no_teardown_disable) {
-+ data->handle = 0;
-+ data->dcid = 0;
-+ tester_test_passed();
- }
- }
-
-@@ -708,6 +722,10 @@ int main(int argc, char *argv[])
- setup_powered_client,
- test_connect);
-
-+ test_6lowpan("Client Connect - No Disable", &client_connect_no_disable,
-+ setup_powered_client,
-+ test_connect);
-+
- test_6lowpan("Client Connect - Disconnect", &client_connect_disconnect,
- setup_powered_client,
- test_connect);
---
-2.54.0
-
-
-From 1cfe2d8b2cbb8c645439464a217053d3cdc41e5f Mon Sep 17 00:00:00 2001
-From: Pauli Virtanen <pav@iki.fi>
-Date: Mon, 13 Jul 2026 16:50:26 +0300
-Subject: [PATCH 13/19] tools/l2cap-tester: fix Set PHY test spurious failures
-
-Setting BT_PHY does not take effect instantaneously as kernel waits for
-controller, so checking getsockopt() sometimes fails here. In PHY tests,
-use retry with timeout to check again later.
----
- tools/l2cap-tester.c | 18 +++++++++++-------
- 1 file changed, 11 insertions(+), 7 deletions(-)
-
-diff --git a/tools/l2cap-tester.c b/tools/l2cap-tester.c
-index a8cac3902b32..2771aee65814 100644
---- a/tools/l2cap-tester.c
-+++ b/tools/l2cap-tester.c
-@@ -277,7 +277,7 @@ static void test_data_free(void *test_data)
- #define test_l2cap(name, type, data, setup, func) \
- do { \
- struct test_data *user; \
-- user = malloc(sizeof(struct test_data)); \
-+ user = calloc(1, sizeof(struct test_data)); \
- if (!user) \
- break; \
- user->hciemu_type = type; \
-@@ -1657,8 +1657,10 @@ static gboolean socket_closed_cb(GIOChannel *io, GIOCondition cond,
- tester_print("err %d != %d expected_err", -err,
- l2data->expect_err);
- tester_test_failed();
-- } else
-+ } else if (!data->step)
- tester_test_passed();
-+ else
-+ tester_test_failed();
-
- return FALSE;
- }
-@@ -1729,12 +1731,13 @@ static gboolean check_phy(gpointer args)
- }
-
- if (l2data->phy && l2data->phy != data->phys) {
-- tester_warn("phy 0x%08x != 0x%08x", l2data->phy, data->phys);
-- tester_test_failed();
-- goto done;
-+ tester_print("phy 0x%08x != 0x%08x", l2data->phy, data->phys);
-+
-+ /* Retry */
-+ return TRUE;
- }
-
-- tester_test_passed();
-+ data->step--;
-
- done:
- shutdown(sk, SHUT_WR);
-@@ -1770,7 +1773,8 @@ static int check_phys(struct test_data *data, int sk)
- }
-
- /* Wait for the PHY to change */
-- g_idle_add(check_phy, INT_TO_PTR(sk));
-+ data->step++;
-+ g_timeout_add(50, check_phy, INT_TO_PTR(sk));
-
- return -EINPROGRESS;
- }
---
-2.54.0
-
-
-From 7f826d003ee7bc07698ddecf804697d55e7b9c86 Mon Sep 17 00:00:00 2001
-From: Ferose <ferose2@gmail.com>
-Date: Sat, 11 Jul 2026 00:02:30 -0700
-Subject: [PATCH 14/19] shared/bap: Fix rejecting re-attach of the same fd
-
-When the remote's STREAMING ASE notification arrives before
-iso_connect_cb() fires, the race handler in bap_state()
-(profiles/audio/bap.c) already attaches the io to the stream; the
-comment there notes "Order of STREAMING and iso_connect_cb() is
-nondeterministic". When iso_connect_cb() then calls
-bt_bap_stream_set_io() with the same fd, the guard in
-bap_ucast_set_io() sees an attached, non-connecting io and returns
-false. iso_connect_cb() treats that as fatal:
-
- profiles/audio/bap.c:iso_connect_cb() Unable to set IO
- profiles/audio/transport.c:bap_state_changed() Unable to get stream IO
-
-and tears down a CIS that btmon shows was established successfully
-(CIS Established status 0, both ISO data paths set up). With a device
-whose STREAMING notifications consistently win the race, streaming is
-never possible.
-
-Treat re-attaching the fd that is already attached as a no-op success,
-without re-running the per-stream state actions (which would send a
-duplicate Receiver Start Ready to a linked source ASE). Attaching a
-different fd to a non-connecting io remains an error.
-
-Fixes: https://github.com/bluez/bluez/issues/2223
----
- src/shared/bap.c | 11 ++++++++++-
- 1 file changed, 10 insertions(+), 1 deletion(-)
-
-diff --git a/src/shared/bap.c b/src/shared/bap.c
-index db6f4f204787..1660b8b2c1cf 100644
---- a/src/shared/bap.c
-+++ b/src/shared/bap.c
-@@ -2589,9 +2589,18 @@ static unsigned int bap_bcast_release(struct bt_bap_stream *stream,
-
- static bool bap_ucast_set_io(struct bt_bap_stream *stream, int fd)
- {
-- if (!stream || (fd >= 0 && stream->io && !stream->io->connecting))
-+ if (!stream)
- return false;
-
-+ /*
-+ * The STREAMING state handler may already have attached this io
-+ * if the remote's ASE notification raced ahead of iso_connect_cb
-+ * (see bap_state() in profiles/audio/bap.c). Re-attaching the
-+ * same fd is a no-op success; only a different fd is an error.
-+ */
-+ if (fd >= 0 && stream->io && !stream->io->connecting)
-+ return stream_io_get_fd(stream->io) == fd;
-+
- bap_stream_set_io(stream, INT_TO_PTR(fd));
-
- queue_foreach(stream->links, bap_stream_set_io, INT_TO_PTR(fd));
---
-2.54.0
-
-
-From bc49d63210f23e0724e07f3e5838aff05fbaa4aa Mon Sep 17 00:00:00 2001
-From: Pauli Virtanen <pav@iki.fi>
-Date: Sat, 11 Jul 2026 12:15:25 +0300
-Subject: [PATCH 15/19] lib: add iso_data_* macros to hci.h
-
-Add macros for parsing ISO Data Packet header items.
----
- lib/bluetooth/hci.h | 6 ++++++
- 1 file changed, 6 insertions(+)
-
-diff --git a/lib/bluetooth/hci.h b/lib/bluetooth/hci.h
-index 732477ec4b55..2e4aa394e13e 100644
---- a/lib/bluetooth/hci.h
-+++ b/lib/bluetooth/hci.h
-@@ -2335,6 +2335,12 @@ typedef struct {
- #define iso_flags_ts(f) ((f >> 2) & 0x0001)
- #define iso_flags_pack(pb, ts) ((pb & 0x03) | ((ts & 0x01) << 2))
-
-+/* ISO data length and flags pack/unpack */
-+#define iso_data_len_pack(h, f) ((__u16) (((h) & 0x0fff) | \
-+ (((f) & 0x3) << 14)))
-+#define iso_data_len(h) ((h) & 0x0fff)
-+#define iso_data_flags(h) ((h) >> 14)
-+
- #endif /* _NO_HCI_DEFS */
-
- /* HCI Socket options */
---
-2.54.0
-
-
-From 1309d5f9305d132bc09705ffa4afa113c6491c2b Mon Sep 17 00:00:00 2001
-From: Pauli Virtanen <pav@iki.fi>
-Date: Sat, 11 Jul 2026 12:15:26 +0300
-Subject: [PATCH 16/19] monitor: fix parsing of ISO packet data header and slen
- check
-
-Check PB flag to see whether timestamp and data headers are present.
-
-Check bounds before accessing data. Bump handle_str length to fit
-maximum.
-
-Byteswap length values, and mask Packet_Status_Flag correctly. Remove
-SDU length check, as we are not tracking fragmentation.
-
-Fixes: https://github.com/bluez/bluez/issues/2292
-Assisted-by: GLM-5.2
----
- monitor/packet.c | 69 ++++++++++++++++++++++++++++++++----------------
- 1 file changed, 46 insertions(+), 23 deletions(-)
-
-diff --git a/monitor/packet.c b/monitor/packet.c
-index ba6ff8e8e551..fb053e2b413f 100644
---- a/monitor/packet.c
-+++ b/monitor/packet.c
-@@ -14541,20 +14541,23 @@ void packet_hci_isodata(struct timeval *tv, struct ucred *cred, uint16_t index,
- bool in, const void *data, uint16_t size)
- {
- const struct bt_hci_iso_hdr *hdr = data;
-- const struct bt_hci_iso_data_start *start;
-- uint16_t handle = le16_to_cpu(hdr->handle);
-- uint8_t flags = acl_flags(handle);
-+ uint16_t handle, dlen;
-+ uint8_t flags, pb_flag;
- char label[8];
-- char handle_str[56], extra_str[50], ts_str[16] = { 0 };
-- struct index_buf_pool *pool = &index_list[index].iso;
-+ char handle_str[100], extra_str[70], ts_str[16] = { 0 };
-+ char sn_str[16] = { 0 }, slen_str[32] = { 0 };
-+ struct index_buf_pool *pool;
- struct packet_conn_data *conn;
- size_t ts_size = 0;
-+ bool have_hdr;
-
- if (index >= MAX_INDEX) {
- print_field("Invalid index (%d).", index);
- return;
- }
-
-+ pool = &index_list[index].iso;
-+
- index_list[index].frame++;
-
- if (size < sizeof(*hdr))
-@@ -14563,11 +14566,18 @@ void packet_hci_isodata(struct timeval *tv, struct ucred *cred, uint16_t index,
- data += sizeof(*hdr);
- size -= sizeof(*hdr);
-
-+ handle = le16_to_cpu(hdr->handle);
-+ flags = acl_flags(handle);
-+ pb_flag = iso_flags_pb(flags);
-+ dlen = le16_to_cpu(hdr->dlen);
-+
-+ have_hdr = (pb_flag == 0x00 || pb_flag == 0x02);
-+
- /* Detect if timestamp field is preset */
- if (iso_flags_ts(flags)) {
- ts_size = sizeof(uint32_t);
-
-- if (size < ts_size)
-+ if (size < ts_size || !have_hdr)
- goto malformed;
-
- snprintf(ts_str, sizeof(ts_str), " ts %u", get_le32(data));
-@@ -14576,20 +14586,40 @@ void packet_hci_isodata(struct timeval *tv, struct ucred *cred, uint16_t index,
- size -= ts_size;
- }
-
-- start = data;
-+ if (have_hdr) {
-+ const struct bt_hci_iso_data_start *start = data;
-+ uint8_t sflags;
-+ uint16_t slen;
-+
-+ if (size < sizeof(*start))
-+ goto malformed;
-+
-+ sflags = iso_data_flags(le16_to_cpu(start->slen));
-+ slen = iso_data_len(le16_to_cpu(start->slen));
-+
-+ if (slen)
-+ snprintf(slen_str, sizeof(slen_str), " slen %u", slen);
-+ if (sflags)
-+ snprintf(slen_str + strlen(slen_str),
-+ sizeof(slen_str) - strlen(slen_str),
-+ " sflags %u", sflags);
-+
-+ snprintf(sn_str, sizeof(sn_str), " SN %u",
-+ le16_to_cpu(start->sn));
-+ }
-+
- conn = packet_get_conn_data(handle);
-
- if (!in && pool->total)
-- sprintf(handle_str, "Handle %d [%u/%u] SN %u",
-- acl_handle(handle), ++pool->tx, pool->total, start->sn);
-+ sprintf(handle_str, "Handle %d [%u/%u]%s",
-+ acl_handle(handle), ++pool->tx, pool->total, sn_str);
- else
-- sprintf(handle_str, "Handle %u SN %u", acl_handle(handle),
-- start->sn);
-+ sprintf(handle_str, "Handle %u%s", acl_handle(handle), sn_str);
-
- handle_str_append_addr(handle_str, conn);
-
-- sprintf(extra_str, "flags 0x%2.2x dlen %u slen %u%s", flags, hdr->dlen,
-- start->slen, ts_str);
-+ sprintf(extra_str, "flags 0x%2.2x dlen %u%s%s", flags, dlen, slen_str,
-+ ts_str);
-
- if (conn)
- sprintf(label, "%s", conn_type_str(conn->type));
-@@ -14601,18 +14631,11 @@ void packet_hci_isodata(struct timeval *tv, struct ucred *cred, uint16_t index,
-
- if (!in)
- packet_enqueue_tx(tv, acl_handle(handle),
-- index_list[index].frame, hdr->dlen);
-+ index_list[index].frame, dlen);
-
-- if (size + ts_size != hdr->dlen) {
-+ if (size + ts_size != dlen) {
- print_text(COLOR_ERROR, "invalid packet size (%d != %d)",
-- size + (int)ts_size, hdr->dlen);
-- packet_hexdump(data, size);
-- return;
-- }
--
-- if (size != start->slen + 4) {
-- print_text(COLOR_ERROR, "invalid packet slen (%d+4 != %d)",
-- start->slen, size);
-+ size + (int)ts_size, dlen);
- packet_hexdump(data, size);
- return;
- }
---
-2.54.0
-
-
-From 7482aceece7044e688f20f113263b50c21d69fb6 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Danis?= <frederic.danis@collabora.com>
-Date: Wed, 15 Jul 2026 17:51:44 +0200
-Subject: [PATCH 17/19] profiles/audio: fix UAF on external media service
- teardown
-
-Keep media_app endpoint/player queues in sync with object lifetime to
-avoid stale pointers during proxy removal.
-
-When admin allowlist reapply removes audio services, endpoint/player
-objects may be destroyed through non-proxy paths first.
-Later proxy_removed_cb calls queue_remove_if() and matching by path
-can dereference freed endpoint/player memory.
-
-Fix by:
-- adding media_app back-references in media_endpoint/local_player
-- unlinking from app queues inside media_endpoint_remove or
- local_player_remove
-- setting ownership when app-registering endpoint/player objects
-
-This prevents heap-use-after-free in match_endpoint_by_path or
-match_player_by_path during service disconnect.
-
-Assisted-by: GPT:GPT-5.3-Codex
----
- profiles/audio/media.c | 16 ++++++++++++++++
- 1 file changed, 16 insertions(+)
-
-diff --git a/profiles/audio/media.c b/profiles/audio/media.c
-index 5d9ea2cbcbce..95f9580b08ff 100644
---- a/profiles/audio/media.c
-+++ b/profiles/audio/media.c
-@@ -136,12 +136,14 @@ struct media_endpoint {
- guint watch;
- GSList *requests;
- struct media_adapter *adapter;
-+ struct media_app *app;
- GSList *transports;
- struct endpoint_features features;
- };
-
- struct local_player {
- struct media_adapter *adapter;
-+ struct media_app *app;
- char *sender; /* Player DBus bus id */
- char *path; /* Player object path */
- GHashTable *settings; /* Player settings */
-@@ -305,6 +307,11 @@ static void media_endpoint_remove(void *data)
- }
- #endif
-
-+ if (endpoint->app) {
-+ queue_remove(endpoint->app->endpoints, endpoint);
-+ endpoint->app = NULL;
-+ }
-+
- info("Endpoint unregistered: sender=%s path=%s", endpoint->sender,
- endpoint->path);
-
-@@ -2076,6 +2083,11 @@ static void local_player_remove(void *data)
- {
- struct local_player *mp = data;
-
-+ if (mp->app) {
-+ queue_remove(mp->app->players, mp);
-+ mp->app = NULL;
-+ }
-+
- info("Player unregistered: sender=%s path=%s", mp->sender, mp->path);
-
- local_player_destroy(mp);
-@@ -3168,6 +3180,8 @@ static void app_register_endpoint(void *data, void *user_data)
- return;
- }
-
-+ endpoint->app = app;
-+
- queue_push_tail(app->endpoints, endpoint);
-
- return;
-@@ -3196,6 +3210,8 @@ static void app_register_player(void *data, void *user_data)
- if (!player)
- return;
-
-+ player->app = app;
-+
- if (g_dbus_proxy_get_property(proxy, "PlaybackStatus", &iter)) {
- if (!set_status(player, &iter))
- goto fail;
---
-2.54.0
-
-
-From b0abe23f58f1ecc44d9d0b6b328be72d2d2113b8 Mon Sep 17 00:00:00 2001
-From: Naga Bhavani Akella <naga.akella@oss.qualcomm.com>
-Date: Wed, 15 Jul 2026 14:09:56 +0530
-Subject: [PATCH 18/19] doc/org.bluez.ChannelSounding1:Add Used by reference
- and Examples
-
-Add :Used by: field linking to bluetoothctl cs submenu and
-Examples section showing corresponding bluetoothctl cs commands
-for D-Bus methods
----
- Makefile.am | 2 +
- doc/org.bluez.ChannelSounding1.rst | 534 +++++++++++++++++++++++++++++
- 2 files changed, 536 insertions(+)
- create mode 100644 doc/org.bluez.ChannelSounding1.rst
-
-diff --git a/Makefile.am b/Makefile.am
-index 76c4ab5d4f4e..23b96c2e303f 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -405,6 +405,7 @@ man_MANS += doc/org.bluez.Telephony.5 doc/org.bluez.Call.5
- man_MANS += doc/org.bluez.ThermometerManager.5 \
- doc/org.bluez.Thermometer.5 \
- doc/org.bluez.ThermometerWatcher.5
-+man_MANS += doc/org.bluez.ChannelSounding1.5
- endif
- manual_pages += doc/bluetoothd.8
- manual_pages += doc/hci.7 doc/mgmt.7 doc/l2cap.7 doc/rfcomm.7 doc/sco.7 \
-@@ -445,6 +446,7 @@ manual_pages += doc/org.bluez.Telephony.5 doc/org.bluez.Call.5
- manual_pages += doc/org.bluez.ThermometerManager.5 \
- doc/org.bluez.Thermometer.5 \
- doc/org.bluez.ThermometerWatcher.5
-+manual_pages += doc/org.bluez.ChannelSounding1.5
-
- EXTRA_DIST += src/genbuiltin src/bluetooth.conf \
- src/main.conf profiles/network/network.conf \
-diff --git a/doc/org.bluez.ChannelSounding1.rst b/doc/org.bluez.ChannelSounding1.rst
-new file mode 100644
-index 000000000000..c06e1d7285e0
---- /dev/null
-+++ b/doc/org.bluez.ChannelSounding1.rst
-@@ -0,0 +1,534 @@
-+==========================
-+org.bluez.ChannelSounding1
-+==========================
-+
-+----------------------------------------------
-+BlueZ D-Bus Channel Sounding API documentation
-+----------------------------------------------
-+
-+:Version: BlueZ
-+:Date: June 2026
-+:Manual section: 5
-+:Manual group: Linux System Administration
-+
-+Interface
-+=========
-+
-+:Service: org.bluez
-+:Interface: org.bluez.ChannelSounding1
-+:Object path: [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
-+:Used by: **bluetoothctl(1)**, **bluetoothctl-cs(1)**
-+
-+Methods
-+-------
-+
-+void StartMeasurement(dict params)
-+``````````````````````````````````
-+
-+Starts a Channel Sounding distance measurement procedure on the connected
-+device. All configuration is supplied in a single ``a{sv}`` dictionary.
-+Any key that is omitted retains its current value in the daemon.
-+
-+The device to measure is identified by the D-Bus object path on which
-+this method is called
-+(``[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX``).
-+Only one measurement per device object may be active at a time. Calling
-+**StartMeasurement** while a session is already active returns
-+``org.bluez.Error.InProgress``.
-+
-+When ``role`` is Reflector (``0x02``), this method does not start a CS
-+distance measurement: a Reflector never initiates a procedure locally,
-+so it cannot start one via this call. Instead, all given configuration
-+is applied and stored so the controller is ready to respond once a
-+remote Initiator begins a procedure, and the method returns success
-+without arming a local measurement session. The ``Active`` property
-+only transitions to ``true`` when a remote-initiated procedure
-+actually starts, which may happen well after this method returns (or
-+not at all, if the remote never initiates one). Clients that watch
-+``PropertiesChanged`` for ``Active`` (as **bluetoothctl-cs(1)** does)
-+are notified of both the remote-initiated start and its eventual stop.
-+
-+For Initiator role (or Both), ``duration_secs`` and the device object
-+path are the parameters that matter to start a measurement; every
-+other key below is optional configuration with a usable default. For
-+Reflector role, only ``role``, ``sync_ant_sel`` and ``max_tx_power``
-+are required or have any effect — the remaining keys are accepted but
-+otherwise unused, since no local procedure is armed.
-+
-+Supported dictionary keys:
-+
-+:uint32 duration_secs (Default: 0):
-+
-+ Duration in seconds before the measurement is stopped
-+ automatically. A value of 0 disables the automatic timeout.
-+
-+:byte role (Default: 0x03):
-+
-+ CS role to use for the measurement.
-+
-+ Possible values:
-+
-+ :0x01: Initiator
-+ :0x02: Reflector
-+ :0x03: Both (Initiator and Reflector)
-+
-+:byte sync_ant_sel (Default: 0xFF):
-+
-+ CS sync antenna selection. Values 0xFE and 0xFF are reserved
-+ by the Bluetooth specification.
-+
-+:byte max_tx_power (Default: 0x14):
-+
-+ Maximum TX power in dBm, treated as a signed value. Valid
-+ range is -127 to +20 dBm.
-+
-+:byte config_id:
-+
-+ CS configuration identifier.
-+
-+:byte main_mode_type:
-+
-+ Main CS mode used in the procedure.
-+
-+:byte sub_mode_type:
-+
-+ Sub-mode within the main mode. Set to 0xFF when unused.
-+
-+:byte main_mode_min_steps:
-+
-+ Minimum number of CS main mode steps per CS subevent.
-+
-+:byte main_mode_max_steps:
-+
-+ Maximum number of CS main mode steps per CS subevent.
-+
-+:byte main_mode_repetition:
-+
-+ Number of times the main mode steps are repeated in a
-+ subevent.
-+
-+:byte mode0_steps:
-+
-+ Number of CS Mode 0 steps at the beginning of each subevent.
-+
-+:byte rtt_types:
-+
-+ Round Trip Time measurement types for the configuration.
-+
-+:byte sync_phy:
-+
-+ PHY used for CS sync packets.
-+
-+ Possible values:
-+
-+ :0x01: LE 1M PHY
-+ :0x02: LE 2M PHY
-+
-+:array{byte} channel_map:
-+
-+ 10-byte channel map bitmap. Must be exactly 10 bytes.
-+
-+:byte channel_map_repetition:
-+
-+ Number of consecutive repetitions of the channel map.
-+
-+:byte channel_selection_type:
-+
-+ Algorithm used for CS channel selection.
-+
-+:byte channel_shape:
-+
-+ Shape used in the channel selection algorithm.
-+
-+:byte channel_jump:
-+
-+ Channel jump size used in the channel selection algorithm.
-+
-+:byte companion_signal_enable:
-+
-+ Set to 1 to transmit a companion signal alongside the CS
-+ tone, 0 to disable.
-+
-+:uint16 max_procedure_duration:
-+
-+ Maximum duration of a single CS measurement procedure.
-+
-+:uint16 min_period_between_procedures:
-+
-+ Minimum time between consecutive CS measurement procedures.
-+
-+:uint16 max_period_between_procedures:
-+
-+ Maximum time between consecutive CS measurement procedures.
-+
-+:uint16 max_procedure_count:
-+
-+ Maximum number of CS measurement procedures to run.
-+ A value of 0 means no limit.
-+
-+:array{byte} min_sub_event_len:
-+
-+ Minimum CS subevent length as a 3-byte little-endian value.
-+ Must be exactly 3 bytes.
-+
-+:array{byte} max_sub_event_len:
-+
-+ Maximum CS subevent length as a 3-byte little-endian value.
-+ Must be exactly 3 bytes.
-+
-+:byte tone_antenna_config_selection:
-+
-+ Antenna configuration used for CS tone exchanges.
-+
-+:byte phy:
-+
-+ PHY used during CS procedures.
-+
-+ Possible values:
-+
-+ :0x01: LE 1M PHY
-+ :0x02: LE 2M PHY
-+
-+:byte tx_power_delta:
-+
-+ Difference between remote and local TX power during CS
-+ procedures. 0x80 indicates not applicable.
-+
-+:byte preferred_peer_antenna:
-+
-+ Preferred antenna to be used by the peer device.
-+
-+:byte snr_control_initiator:
-+
-+ SNR control setting for the initiator role.
-+ 0xFF indicates no preference.
-+
-+:byte snr_control_reflector:
-+
-+ SNR control setting for the reflector role.
-+ 0xFF indicates no preference.
-+
-+Possible errors:
-+
-+:org.bluez.Error.InProgress:
-+:org.bluez.Error.InvalidArgs:
-+:org.freedesktop.DBus.Error.Failed:
-+
-+Examples:
-+
-+:bluetoothctl set role then start:
-+ | [cs] > role 0x01
-+ | [cs] > main_mode_type 2
-+ | [cs] > start AA:BB:CC:DD:EE:FF
-+:bluetoothctl start with defaults:
-+ | [cs] > start [dev_addr] [duration_secs]
-+:bluetoothctl configure as Reflector (applies settings, does not start a measurement):
-+ | [cs] > role 0x02
-+ | [cs] > start
-+
-+void StopMeasurement(void)
-+``````````````````````````
-+
-+Stops the active Channel Sounding distance measurement on this device.
-+The device is identified by the D-Bus object path on which this method
-+is called — no session identifier is required.
-+
-+Raises ``org.bluez.Error.NotConnected`` if no measurement is active.
-+
-+Possible errors:
-+
-+:org.bluez.Error.NotConnected:
-+:org.freedesktop.DBus.Error.Failed:
-+
-+In **bluetoothctl(1)**, the device address argument may be omitted only
-+when a single measurement is active; it is required when multiple
-+measurements are active.
-+
-+Examples:
-+
-+:bluetoothctl stop the only active measurement:
-+ | [cs] > stop
-+:bluetoothctl stop a specific device when multiple are active:
-+ | [cs] > stop AA:BB:CC:DD:EE:FF
-+
-+Signals
-+-------
-+
-+void ProcedureData(dict data)
-+``````````````````````````````
-+
-+Emitted when a Channel Sounding measurement procedure completes on this
-+device, carrying the raw CS procedure results as reported by the
-+controller. Consumers such as an external ranging estimation daemon
-+subscribe to this signal to compute distance estimates.
-+
-+:dict data:
-+
-+ :int32 procedureCounter:
-+
-+ Procedure counter value from the controller.
-+
-+ :int32 procedureSequence:
-+
-+ Sequence number of this procedure.
-+
-+ :byte initiatorSelectedTxPower:
-+
-+ TX power selected by the Initiator, treated as a signed
-+ value.
-+
-+ :byte reflectorSelectedTxPower:
-+
-+ TX power selected by the Reflector, treated as a signed
-+ value.
-+
-+ :uint32 initiatorSubeventCount:
-+
-+ Number of subevent results reported by the Initiator.
-+
-+ :array{dict} initiatorSubeventResults:
-+
-+ Present only when ``initiatorSubeventCount`` is greater
-+ than 0. One entry per Initiator subevent, each with the
-+ fields described in `Subevent Result`_ below.
-+
-+ :byte initiatorProcedureAbortReason:
-+
-+ Reason the Initiator's procedure was aborted, 0 if not
-+ aborted.
-+
-+ :uint32 reflectorSubeventCount:
-+
-+ Number of subevent results reported by the Reflector.
-+
-+ :array{dict} reflectorSubeventResults:
-+
-+ Present only when ``reflectorSubeventCount`` is greater
-+ than 0. One entry per Reflector subevent, each with the
-+ fields described in `Subevent Result`_ below.
-+
-+ :byte reflectorProcedureAbortReason:
-+
-+ Reason the Reflector's procedure was aborted, 0 if not
-+ aborted.
-+
-+ :dict procedureEnableConfig:
-+
-+ :byte toneAntennaConfigSelection:
-+
-+ Antenna configuration used for CS tone exchanges.
-+
-+ :uint32 subeventLenUs:
-+
-+ Subevent length in microseconds.
-+
-+ :byte subeventsPerEvent:
-+
-+ Number of subevents per event.
-+
-+ :uint32 subeventInterval:
-+
-+ Interval between subevents.
-+
-+ :uint32 eventInterval:
-+
-+ Interval between events.
-+
-+ :uint32 procedureInterval:
-+
-+ Interval between procedures.
-+
-+ :uint32 procedureCount:
-+
-+ Number of procedures configured.
-+
-+ :uint32 maxProcedureLen:
-+
-+ Maximum procedure length.
-+
-+ :dict csConfigParam:
-+
-+ :byte modeType:
-+
-+ Main CS mode used in the procedure.
-+
-+ :byte subModeType:
-+
-+ Sub-mode within the main mode.
-+
-+ :byte rttType:
-+
-+ Round Trip Time measurement type.
-+
-+ :array{byte} channelMap:
-+
-+ 10-byte channel map bitmap.
-+
-+ :byte minMainModeSteps:
-+ :byte maxMainModeSteps:
-+ :byte mainModeRepetition:
-+ :byte mode0Steps:
-+
-+ :byte role:
-+
-+ CS role in effect for the procedure (Initiator,
-+ Reflector, or Both).
-+
-+ :byte csSyncPhyType:
-+
-+ PHY used for CS sync packets.
-+
-+ :byte channelSelectionType:
-+ :byte ch3cShapeType:
-+ :byte ch3cJump:
-+ :byte channelMapRepetition:
-+ :byte tIp1TimeUs:
-+ :byte tIp2TimeUs:
-+ :byte tFcsTimeUs:
-+ :byte tPmTimeUs:
-+ :byte tSwTimeUsSupportedByLocal:
-+ :byte tSwTimeUsSupportedByRemote:
-+
-+ :uint32 bleConnInterval:
-+
-+ BLE connection interval in effect during the
-+ procedure.
-+
-+Subevent Result
-+~~~~~~~~~~~~~~~~
-+
-+Each element of ``initiatorSubeventResults`` and
-+``reflectorSubeventResults`` is a dict with the following fields:
-+
-+:int32 startAclConnEvtCounter:
-+
-+ ACL connection event counter at the start of the subevent.
-+
-+:int32 freqComp:
-+
-+ Frequency compensation value.
-+
-+:byte refPwrLvl:
-+
-+ Reference power level, treated as a signed value.
-+
-+:byte numAntPaths:
-+
-+ Number of antenna paths used.
-+
-+:byte subeventAbortReason:
-+
-+ Reason the subevent was aborted, 0 if not aborted.
-+
-+:uint64 timestampNanos:
-+
-+ Timestamp of the subevent result, in nanoseconds.
-+
-+:uint32 numSteps:
-+
-+ Number of steps reported in this subevent.
-+
-+:array{dict} stepData:
-+
-+ One entry per step. Each entry has:
-+
-+ :byte stepMode:
-+
-+ CS step mode (0-3).
-+
-+ :byte stepChannel:
-+
-+ Channel used for the step.
-+
-+ :dict modeZeroData:
-+
-+ Present when ``stepMode`` is 0.
-+
-+ :byte packetQuality:
-+ :byte packetRssiDbm:
-+ :byte packetAntenna:
-+
-+ :int32 initiatorMeasuredFreqOffset:
-+
-+ Frequency offset measured by the Initiator.
-+
-+ :dict modeOneData:
-+
-+ Present when ``stepMode`` is 1.
-+
-+ :byte packetQuality:
-+ :byte packetNadm:
-+ :byte packetRssiDbm:
-+
-+ :int32 toaTodInitiator:
-+
-+ Time of Arrival / Time of Departure at the
-+ Initiator.
-+
-+ :int32 todToaReflector:
-+
-+ Time of Departure / Time of Arrival at the
-+ Reflector.
-+
-+ :byte packetAntenna:
-+
-+ :array{int32} packetPct1:
-+
-+ In-phase/quadrature sample pair, as
-+ ``[i_sample, q_sample]``.
-+
-+ :array{int32} packetPct2:
-+
-+ In-phase/quadrature sample pair, as
-+ ``[i_sample, q_sample]``.
-+
-+ :dict modeTwoData:
-+
-+ Present when ``stepMode`` is 2.
-+
-+ :byte antennaPermutationIndex:
-+
-+ :array{int32} tonePctIQSamples:
-+
-+ Interleaved in-phase/quadrature tone samples, as
-+ ``[i_sample, q_sample, ...]`` — one pair per
-+ antenna path.
-+
-+ :array{byte} toneQualityIndicators:
-+
-+ One quality indicator byte per antenna path.
-+
-+ :dict modeThreeData:
-+
-+ Present when ``stepMode`` is 3. Contains the combined
-+ fields of both **modeOneData** and **modeTwoData**.
-+
-+Properties
-+----------
-+
-+boolean Active [readonly]
-+`````````````````````````
-+
-+Indicates whether a CS distance measurement procedure is currently
-+active on this device.
-+
-+Set to ``true`` when a procedure starts — either because the local
-+Initiator called **StartMeasurement** successfully, or because the
-+remote Initiator enabled a CS procedure on the local Reflector.
-+
-+Set to ``false`` when the procedure stops for any reason: the local
-+application called **StopMeasurement**, the measurement duration timer
-+expired, or the ACL connection was dropped.
-+
-+This property emits ``PropertiesChanged`` on every transition so that
-+clients can track measurement state without polling.
-+
-+RESOURCES
-+=========
-+
-+http://www.bluez.org
-+
-+REPORTING BUGS
-+==============
-+
-+linux-bluetooth@vger.kernel.org
---
-2.54.0
-
-
-From 30db66dc971bd1cd95d4a7b0eea296367ab65b3b Mon Sep 17 00:00:00 2001
-From: Naga Bhavani Akella <naga.akella@oss.qualcomm.com>
-Date: Wed, 15 Jul 2026 14:09:57 +0530
-Subject: [PATCH 19/19] doc/bluetoothctl-cs: Add :Uses: fields and document
- arguments
-
-Add :Uses: fields to link commands to their corresponding D-Bus API
-methods, and document command arguments with usage examples
----
- Makefile.tools | 5 +-
- doc/bluetoothctl-cs.rst | 546 ++++++++++++++++++++++++++++++++++++++++
- 2 files changed, 549 insertions(+), 2 deletions(-)
- create mode 100644 doc/bluetoothctl-cs.rst
-
-diff --git a/Makefile.tools b/Makefile.tools
-index 6188449f1565..0646d52e14dc 100644
---- a/Makefile.tools
-+++ b/Makefile.tools
-@@ -359,7 +359,7 @@ man_MANS += doc/rctest.1 doc/l2ping.1 doc/btattach.1 doc/isotest.1 \
- doc/bluetoothctl-gatt.1 doc/bluetoothctl-player.1 \
- doc/bluetoothctl-scan.1 doc/bluetoothctl-transport.1 \
- doc/bluetoothctl-assistant.1 doc/bluetoothctl-hci.1 \
-- doc/bluetoothctl-telephony.1
-+ doc/bluetoothctl-telephony.1 doc/bluetoothctl-cs.1
-
- endif
-
-@@ -465,7 +465,8 @@ manual_pages += doc/hciattach.1 doc/hciconfig.1 doc/hcitool.1 \
- doc/bluetoothctl-transport.1 \
- doc/bluetoothctl-assistant.1 \
- doc/bluetoothctl-hci.1 \
-- doc/bluetoothctl-telephony.1
-+ doc/bluetoothctl-telephony.1 \
-+ doc/bluetoothctl-cs.1
-
- if HID2HCI
- udevdir = $(UDEV_DIR)
-diff --git a/doc/bluetoothctl-cs.rst b/doc/bluetoothctl-cs.rst
-new file mode 100644
-index 000000000000..bbc1d8565f91
---- /dev/null
-+++ b/doc/bluetoothctl-cs.rst
-@@ -0,0 +1,546 @@
-+================
-+bluetoothctl-cs
-+================
-+
-+--------------------------
-+Channel Sounding Submenu
-+--------------------------
-+
-+:Version: BlueZ
-+:Copyright: Free use of this software is granted under the terms of the GNU
-+ Lesser General Public Licenses (LGPL).
-+:Date: June 2026
-+:Manual section: 1
-+:Manual group: Linux System Administration
-+
-+SYNOPSIS
-+========
-+
-+**bluetoothctl** [--options] [cs.commands]
-+
-+This submenu controls Bluetooth Channel Sounding (CS) distance measurement
-+using the **org.bluez.ChannelSounding1(5)** D-Bus interface. It allows
-+starting and stopping measurements and inspecting the current parameter
-+state and active session identifier.
-+
-+Each CS parameter has its own **cs.<param>** set command (see the
-+**CS Parameter Commands** section below). Overrides are applied to
-+the local parameter state immediately, so **show** reflects them
-+right away; **start** always uses whatever values are currently set.
-+
-+
-+Channel Sounding Commands
-+=========================
-+
-+start
-+-----
-+
-+Starts a distance measurement on the connected device using the
-+currently set CS parameters (see the **cs.<param>** commands below).
-+All configuration is sent to the daemon in a single **StartMeasurement**
-+call. On success the device path is printed to the console. Multiple
-+simultaneous sessions across different devices are supported; each is
-+tracked independently.
-+
-+Calling **start** on a device that already has an active measurement
-+returns an error without starting a second session on the same device.
-+
-+For Initiator role (or Both), the mandatory parameters are the
-+positional ``dev_addr`` and ``duration_secs``; every ``cs.<param>``
-+command below is optional configuration with a usable default.
-+
-+If ``role`` is set to Reflector (``0x02``, via **cs.role**), **start**
-+does not begin measuring distance: a Reflector never initiates a CS
-+procedure. It only pushes the current parameters to the daemon and
-+arms the device to respond once the remote Initiator starts one; the
-+call still succeeds. When the remote side starts or stops a procedure,
-+the console prints ``Measurement started``/``Measurement stopped`` for
-+that device — use **show** or watch the ``Active`` property to see
-+the same transition. In this role the only parameters that are
-+required or have any effect are ``role``, ``sync_ant_sel`` and
-+``max_tx_power``; every other **cs.<param>** command below is
-+accepted but unused.
-+
-+Positional arguments are optional:
-+
-+- ``dev_addr`` — Bluetooth address of the target device; uses the only
-+ available CS-capable device when omitted.
-+- ``duration_secs`` — auto-stop timeout in seconds; ``0`` (default) means
-+ no timeout.
-+
-+:Usage: **> start [dev_addr] [duration_secs]**
-+:Uses: **org.bluez.ChannelSounding1(5)** method **StartMeasurement**
-+:[dev_addr]: Bluetooth address of the target device (optional; uses the
-+ only available CS-capable device when omitted)
-+:[duration_secs]: Seconds before auto-stop (optional, default 0 = no timeout)
-+
-+:Example Start with all defaults, no timeout:
-+ | **> start**
-+:Example Start on a specific device:
-+ | **> start AA:BB:CC:DD:EE:FF**
-+:Example Start on a specific device with 10-second auto-stop:
-+ | **> start AA:BB:CC:DD:EE:FF 10**
-+:Example Start with 10-second auto-stop (single device, address omitted):
-+ | **> start 0 10**
-+:Example Start with 5-minute auto-stop:
-+ | **> start AA:BB:CC:DD:EE:FF 300**
-+
-+stop
-+----
-+
-+Stops an active CS distance measurement. When only one measurement is
-+running the device address may be omitted. When multiple measurements
-+are active the address is required to identify which one to stop.
-+
-+:Usage: **> stop [dev_addr]**
-+:Uses: **org.bluez.ChannelSounding1(5)** method **StopMeasurement**
-+:[dev_addr]: Bluetooth address of the device to stop (optional when
-+ only one session is active; required otherwise)
-+:Example Stop the only active measurement:
-+ | **> stop**
-+:Example Stop a specific device when multiple are active:
-+ | **> stop AA:BB:CC:DD:EE:FF**
-+:Example Stop a second device:
-+ | **> stop 11:22:33:44:55:66**
-+
-+show
-+----
-+
-+Displays all active measurements (device path for each) and the full
-+set of CS parameter values that will be used on the next **start** call.
-+When no measurements are active, ``none`` is shown.
-+
-+The parameter output is divided into three sections:
-+
-+- **Default Settings** — role, CS sync antenna selection, max TX power.
-+- **CS Config Params** — per-procedure configuration fields including
-+ mode type, step counts, PHY, and channel map.
-+- **CS Frequency Params** — procedure scheduling fields including
-+ duration, period, subevent lengths, and SNR control.
-+
-+:Usage: **> show**
-+:Example Show active session and all CS parameters:
-+ | **> show**
-+
-+CS Parameter Commands
-+======================
-+
-+Each CS parameter is set with its own command, of the form
-+``cs.<param> <value>``. Entering a param command with no value shows
-+its current setting. Overrides apply to the local parameter state
-+immediately, so **show** reflects them right away; **start** always
-+uses whatever values are currently set. Array-valued parameters
-+(``channel_map``, ``min_sub_event_len``, ``max_sub_event_len``) take
-+colon-separated hex bytes with no ``0x`` prefix.
-+
-+:Usage: **> <param> [value]**
-+
-+role
-+----
-+
-+Get/set the CS role.
-+
-+:Usage: **> role [0x01|0x02|0x03]**
-+:[0x01|0x02|0x03]: ``0x01`` Initiator, ``0x02`` Reflector, ``0x03`` Both
-+ (optional, shows current if omitted; default ``0x03``)
-+:Example Show current role:
-+ | **> role**
-+:Example Set role to Initiator only:
-+ | **> role 0x01**
-+:Example Set role to Reflector only (does not measure):
-+ | **> role 0x02**
-+:Example Set role to both Initiator and Reflector:
-+ | **> role 0x03**
-+
-+sync_ant_sel
-+------------
-+
-+Get/set the CS sync antenna selection.
-+
-+:Usage: **> sync_ant_sel [value]**
-+:[value]: CS sync antenna selection; ``0xFE``/``0xFF`` reserved
-+ (optional, shows current if omitted; default ``0xFF``)
-+:Example Show current antenna selection:
-+ | **> sync_ant_sel**
-+:Example Select antenna 1:
-+ | **> sync_ant_sel 0x01**
-+
-+max_tx_power
-+------------
-+
-+Get/set the maximum TX power.
-+
-+:Usage: **> max_tx_power [dBm]**
-+:[dBm]: Max TX power in dBm, signed (optional, shows current if
-+ omitted; range −127 to +20; default ``20``)
-+:Example Show current max TX power:
-+ | **> max_tx_power**
-+:Example Reduce max TX power to 10 dBm:
-+ | **> max_tx_power 10**
-+
-+config_id
-+---------
-+
-+Get/set the CS configuration identifier.
-+
-+:Usage: **> config_id [value]**
-+:[value]: CS configuration identifier (optional, shows current if
-+ omitted; default ``0``)
-+:Example Show current config id:
-+ | **> config_id**
-+:Example Set config id to 1:
-+ | **> config_id 1**
-+
-+main_mode_type
-+--------------
-+
-+Get/set the CS main mode type.
-+
-+:Usage: **> main_mode_type [1|2|3]**
-+:[1|2|3]: ``1`` Mode 1 (RTT), ``2`` Mode 2 (PBR), ``3`` Both (optional,
-+ shows current if omitted; default ``1``)
-+:Example Show current main mode type:
-+ | **> main_mode_type**
-+:Example Set main mode to Mode 2 (PBR):
-+ | **> main_mode_type 2**
-+:Example Set main mode to both RTT and PBR:
-+ | **> main_mode_type 3**
-+
-+sub_mode_type
-+-------------
-+
-+Get/set the CS sub-mode type within the main mode.
-+
-+:Usage: **> sub_mode_type [value]**
-+:[value]: Sub-mode within main mode; ``0xFF`` = unused (optional,
-+ shows current if omitted; default ``0xFF``)
-+:Example Show current sub-mode type:
-+ | **> sub_mode_type**
-+:Example Set sub-mode type to 0x01:
-+ | **> sub_mode_type 0x01**
-+
-+main_mode_min_steps
-+--------------------
-+
-+Get/set the minimum CS main mode steps per subevent.
-+
-+:Usage: **> main_mode_min_steps [value]**
-+:[value]: Min CS main mode steps per subevent (optional, shows
-+ current if omitted; default ``2``)
-+:Example Show current value:
-+ | **> main_mode_min_steps**
-+:Example Set minimum steps to 4:
-+ | **> main_mode_min_steps 4**
-+
-+main_mode_max_steps
-+--------------------
-+
-+Get/set the maximum CS main mode steps per subevent.
-+
-+:Usage: **> main_mode_max_steps [value]**
-+:[value]: Max CS main mode steps per subevent (optional, shows
-+ current if omitted; default ``3``)
-+:Example Show current value:
-+ | **> main_mode_max_steps**
-+:Example Set maximum steps to 8:
-+ | **> main_mode_max_steps 8**
-+
-+main_mode_repetition
-+---------------------
-+
-+Get/set how many times main mode steps are repeated in a subevent.
-+
-+:Usage: **> main_mode_repetition [value]**
-+:[value]: Repetition count (optional, shows current if omitted;
-+ default ``1``)
-+:Example Show current value:
-+ | **> main_mode_repetition**
-+:Example Repeat main mode steps twice:
-+ | **> main_mode_repetition 2**
-+
-+mode0_steps
-+-----------
-+
-+Get/set the number of CS Mode 0 steps at the beginning of each
-+subevent.
-+
-+:Usage: **> mode0_steps [value]**
-+:[value]: CS Mode 0 step count (optional, shows current if omitted;
-+ default ``2``)
-+:Example Show current value:
-+ | **> mode0_steps**
-+:Example Set Mode 0 steps to 3:
-+ | **> mode0_steps 3**
-+
-+rtt_types
-+---------
-+
-+Get/set the RTT measurement types bitmask.
-+
-+:Usage: **> rtt_types [value]**
-+:[value]: RTT measurement types bitmask (optional, shows current if
-+ omitted; default ``0``)
-+:Example Show current value:
-+ | **> rtt_types**
-+:Example Set RTT types bitmask:
-+ | **> rtt_types 0x01**
-+
-+sync_phy
-+--------
-+
-+Get/set the PHY used for CS sync.
-+
-+:Usage: **> sync_phy [0x01|0x02]**
-+:[0x01|0x02]: ``0x01`` LE 1M, ``0x02`` LE 2M (optional, shows current
-+ if omitted; default ``0x01``)
-+:Example Show current sync PHY:
-+ | **> sync_phy**
-+:Example Set CS sync PHY to LE 2M:
-+ | **> sync_phy 0x02**
-+
-+channel_map
-+-----------
-+
-+Get/set the 10-byte CS channel map bitmap.
-+
-+:Usage: **> channel_map [b0:b1:...:b9]**
-+:[b0:b1:...:b9]: 10 colon-separated hex bytes (optional, shows current
-+ if omitted; default ``FC:FF:7F:FC:FF:FF:FF:FF:FF:1F``)
-+:Example Show current channel map:
-+ | **> channel_map**
-+:Example Set a custom channel map (all enabled):
-+ | **> channel_map FF:FF:FF:FF:FF:FF:FF:FF:FF:FF**
-+
-+channel_map_repetition
-+-----------------------
-+
-+Get/set the number of consecutive repetitions of the channel map.
-+
-+:Usage: **> channel_map_repetition [value]**
-+:[value]: Repetition count (optional, shows current if omitted;
-+ default ``1``)
-+:Example Show current value:
-+ | **> channel_map_repetition**
-+:Example Repeat the channel map 3 times:
-+ | **> channel_map_repetition 3**
-+
-+channel_selection_type
-+-----------------------
-+
-+Get/set the CS channel selection algorithm.
-+
-+:Usage: **> channel_selection_type [value]**
-+:[value]: Channel selection algorithm (optional, shows current if
-+ omitted; default ``0``)
-+:Example Show current value:
-+ | **> channel_selection_type**
-+:Example Select algorithm 1:
-+ | **> channel_selection_type 1**
-+
-+channel_shape
-+-------------
-+
-+Get/set the shape used in the channel selection algorithm.
-+
-+:Usage: **> channel_shape [value]**
-+:[value]: Channel shape (optional, shows current if omitted; default
-+ ``0``)
-+:Example Show current value:
-+ | **> channel_shape**
-+:Example Set channel shape to 1:
-+ | **> channel_shape 1**
-+
-+channel_jump
-+------------
-+
-+Get/set the channel jump size.
-+
-+:Usage: **> channel_jump [value]**
-+:[value]: Channel jump size (optional, shows current if omitted;
-+ default ``2``)
-+:Example Show current value:
-+ | **> channel_jump**
-+:Example Set channel jump to 4:
-+ | **> channel_jump 4**
-+
-+companion_signal_enable
-+------------------------
-+
-+Get/set whether the companion signal is transmitted.
-+
-+:Usage: **> companion_signal_enable [0|1]**
-+:[0|1]: ``1`` to transmit companion signal, ``0`` to disable (optional,
-+ shows current if omitted; default ``0``)
-+:Example Show current value:
-+ | **> companion_signal_enable**
-+:Example Enable the companion signal:
-+ | **> companion_signal_enable 1**
-+
-+max_procedure_duration
-+-----------------------
-+
-+Get/set the maximum duration of one CS measurement procedure.
-+
-+:Usage: **> max_procedure_duration [value]**
-+:[value]: Maximum procedure duration (optional, shows current if
-+ omitted; default ``1600``)
-+:Example Show current value:
-+ | **> max_procedure_duration**
-+:Example Set max procedure duration to 800:
-+ | **> max_procedure_duration 800**
-+
-+min_period_between_procedures
-+-------------------------------
-+
-+Get/set the minimum time between consecutive procedures.
-+
-+:Usage: **> min_period_between_procedures [value]**
-+:[value]: Minimum period (optional, shows current if omitted; default
-+ ``30``)
-+:Example Show current value:
-+ | **> min_period_between_procedures**
-+:Example Set minimum period to 50:
-+ | **> min_period_between_procedures 50**
-+
-+max_period_between_procedures
-+-------------------------------
-+
-+Get/set the maximum time between consecutive procedures.
-+
-+:Usage: **> max_period_between_procedures [value]**
-+:[value]: Maximum period (optional, shows current if omitted; default
-+ ``150``)
-+:Example Show current value:
-+ | **> max_period_between_procedures**
-+:Example Set maximum period to 200:
-+ | **> max_period_between_procedures 200**
-+
-+max_procedure_count
-+--------------------
-+
-+Get/set the maximum number of procedures.
-+
-+:Usage: **> max_procedure_count [value]**
-+:[value]: Max procedure count; ``0`` = no limit (optional, shows
-+ current if omitted; default ``0``)
-+:Example Show current value:
-+ | **> max_procedure_count**
-+:Example Limit the procedure count to 100:
-+ | **> max_procedure_count 100**
-+
-+min_sub_event_len
-+------------------
-+
-+Get/set the minimum CS subevent length.
-+
-+:Usage: **> min_sub_event_len [b0:b1:b2]**
-+:[b0:b1:b2]: 3-byte LE value, colon-separated hex (optional, shows
-+ current if omitted; default ``00:20:00``)
-+:Example Show current value:
-+ | **> min_sub_event_len**
-+:Example Set minimum subevent length:
-+ | **> min_sub_event_len 00:10:00**
-+
-+max_sub_event_len
-+------------------
-+
-+Get/set the maximum CS subevent length.
-+
-+:Usage: **> max_sub_event_len [b0:b1:b2]**
-+:[b0:b1:b2]: 3-byte LE value, colon-separated hex (optional, shows
-+ current if omitted; default ``03:20:00``)
-+:Example Show current value:
-+ | **> max_sub_event_len**
-+:Example Set maximum subevent length:
-+ | **> max_sub_event_len 04:20:00**
-+
-+tone_antenna_config_selection
-+-------------------------------
-+
-+Get/set the antenna configuration for CS tone exchanges.
-+
-+:Usage: **> tone_antenna_config_selection [value]**
-+:[value]: Antenna config selection (optional, shows current if
-+ omitted; default ``0x07``)
-+:Example Show current value:
-+ | **> tone_antenna_config_selection**
-+:Example Set antenna config to 0x01:
-+ | **> tone_antenna_config_selection 0x01**
-+
-+phy
-+---
-+
-+Get/set the PHY used for CS procedures.
-+
-+:Usage: **> phy [0x01|0x02]**
-+:[0x01|0x02]: ``0x01`` LE 1M, ``0x02`` LE 2M (optional, shows current
-+ if omitted; default ``0x01``)
-+:Example Show current procedure PHY:
-+ | **> phy**
-+:Example Set CS procedure PHY to LE 2M:
-+ | **> phy 0x02**
-+
-+tx_power_delta
-+--------------
-+
-+Get/set the remote vs local TX power delta.
-+
-+:Usage: **> tx_power_delta [value]**
-+:[value]: TX power delta; ``0x80`` = not applicable (optional, shows
-+ current if omitted; default ``0x80``)
-+:Example Show current value:
-+ | **> tx_power_delta**
-+:Example Set TX power delta to 0x05:
-+ | **> tx_power_delta 0x05**
-+
-+preferred_peer_antenna
-+-----------------------
-+
-+Get/set the preferred antenna for the peer device.
-+
-+:Usage: **> preferred_peer_antenna [value]**
-+:[value]: Preferred peer antenna (optional, shows current if omitted;
-+ default ``0x03``)
-+:Example Show current value:
-+ | **> preferred_peer_antenna**
-+:Example Prefer antenna 1 on the peer:
-+ | **> preferred_peer_antenna 0x01**
-+
-+snr_control_initiator
-+----------------------
-+
-+Get/set the SNR control for the initiator.
-+
-+:Usage: **> snr_control_initiator [value]**
-+:[value]: SNR control; ``0xFF`` = no preference (optional, shows
-+ current if omitted; default ``0xFF``)
-+:Example Show current value:
-+ | **> snr_control_initiator**
-+:Example Prefer high SNR on the initiator:
-+ | **> snr_control_initiator 0x01**
-+
-+snr_control_reflector
-+----------------------
-+
-+Get/set the SNR control for the reflector.
-+
-+:Usage: **> snr_control_reflector [value]**
-+:[value]: SNR control; ``0xFF`` = no preference (optional, shows
-+ current if omitted; default ``0xFF``)
-+:Example Show current value:
-+ | **> snr_control_reflector**
-+:Example Prefer high SNR on the reflector:
-+ | **> snr_control_reflector 0x01**
-+:Example Prefer high SNR on both roles:
-+ | **> snr_control_initiator 0x01**
-+ | **> snr_control_reflector 0x01**
-+
-+RESOURCES
-+=========
-+
-+http://www.bluez.org
-+
-+REPORTING BUGS
-+==============
-+
-+linux-bluetooth@vger.kernel.org
---
-2.54.0
-
diff --git a/avrcp-getfolderitems.patch b/avrcp-getfolderitems.patch
deleted file mode 100644
index 666825d..0000000
--- a/avrcp-getfolderitems.patch
+++ /dev/null
@@ -1,148 +0,0 @@
-From bd8989620ed6e80755f06cfdb18f5b4a3913493c Mon Sep 17 00:00:00 2001
-From: Bastien Nocera <hadess@hadess.net>
-Date: Fri, 14 Aug 2026 16:01:18 +0200
-Subject: [PATCH] avrcp: Fix Out-of-Bounds Read in AVRCP GetFolderItems parsing
-
-If the "Displayable Name Length" is much longer than the size of the PDU
-packet we receive, then we might try to memcpy() past the end of the PDU
-packet.
-
-Be careful about clamping the name copying to the smallest of:
-- length specified in the PDU
-- left-over packet after the length field
-- size of the string we'll copy it into
-
-Reported-by: Elman Shahbazov <shahbazovelman97@gmail.com>
----
- profiles/audio/avrcp.c | 51 +++++++++++++++++++++++++-----------------
- 1 file changed, 31 insertions(+), 20 deletions(-)
-
-diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
-index 2194a913580f..23e959c76917 100644
---- a/profiles/audio/avrcp.c
-+++ b/profiles/audio/avrcp.c
-@@ -145,6 +145,8 @@
- #define AVRCP_SCOPE_SEARCH 0x02
- #define AVRCP_SCOPE_NOW_PLAYING 0x03
-
-+#define NAME_MAX_LEN 255
-+
- #if __BYTE_ORDER == __LITTLE_ENDIAN
-
- struct avrcp_header {
-@@ -2608,30 +2610,45 @@ static const char *subtype_to_string(uint32_t subtype)
- return "None";
- }
-
-+static gboolean parse_media_name(uint8_t *operands, uint16_t len,
-+ size_t name_len_offset,
-+ char *name, uint16_t *namelen)
-+{
-+ uint16_t namesize;
-+
-+ if (len < name_len_offset + 2)
-+ return FALSE;
-+
-+ memset(name, 0, NAME_MAX_LEN);
-+ namesize = MIN(get_be16(&operands[name_len_offset]),
-+ len - name_len_offset - 2);
-+ namesize = MIN(namesize, NAME_MAX_LEN - 1);
-+ if (*namelen > 0) {
-+ if (len < name_len_offset + 2 + namesize)
-+ return FALSE;
-+ memcpy(name, &operands[name_len_offset + 2], namesize);
-+ strtoutf8(name, namesize);
-+ }
-+ if (namelen)
-+ *namelen = namesize;
-+ return TRUE;
-+}
-+
- static struct media_item *parse_media_element(struct avrcp *session,
- uint8_t *operands, uint16_t len)
- {
- struct avrcp_player *player;
- struct media_player *mp;
- struct media_item *item;
-- uint16_t namelen, namesize;
-- char name[255];
-+ uint16_t namesize;
-+ char name[NAME_MAX_LEN];
- uint64_t uid;
- uint8_t count;
-
-- if (len < 13)
-+ if (!parse_media_name(operands, len, 11, name, &namesize))
- return NULL;
-
- uid = get_be64(&operands[0]);
--
-- memset(name, 0, sizeof(name));
-- namesize = get_be16(&operands[11]);
-- namelen = MIN(namesize, sizeof(name) - 1);
-- if (namelen > 0) {
-- memcpy(name, &operands[13], namelen);
-- strtoutf8(name, namelen);
-- }
--
- count = operands[13 + namesize];
-
- player = session->controller->player;
-@@ -2655,24 +2672,18 @@ static struct media_item *parse_media_folder(struct avrcp *session,
- struct avrcp_player *player = session->controller->player;
- struct media_player *mp = player->user_data;
- struct media_item *item;
-- uint16_t namelen;
-- char name[255];
-+ char name[NAME_MAX_LEN];
- uint64_t uid;
- uint8_t type;
- uint8_t playable;
-
-- if (len < 12)
-+ if (!parse_media_name(operands, len, 12, name, NULL))
- return NULL;
-
- uid = get_be64(&operands[0]);
- type = operands[8];
- playable = operands[9];
-
-- memset(name, 0, sizeof(name));
-- namelen = MIN(get_be16(&operands[12]), sizeof(name) - 1);
-- if (namelen > 0)
-- memcpy(name, &operands[14], namelen);
--
- item = media_player_create_folder(mp, name, type, uid);
- if (!item)
- return NULL;
---
-2.55.0
-
-From 8bf7fe4847833a76bd7e51d4d269b4b336389193 Mon Sep 17 00:00:00 2001
-From: Bastien Nocera <hadess@hadess.net>
-Date: Mon, 17 Aug 2026 10:11:19 +0200
-Subject: [PATCH] avrcp: Fix media/folder name not being set
-
-*namelen was used before being set.
-
-Fixes: bd8989620ed6 ("avrcp: Fix Out-of-Bounds Read in AVRCP GetFolderItems parsing")
----
- profiles/audio/avrcp.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
-index 23e959c76917..028c1c254b82 100644
---- a/profiles/audio/avrcp.c
-+++ b/profiles/audio/avrcp.c
-@@ -2623,7 +2623,7 @@ static gboolean parse_media_name(uint8_t *operands, uint16_t len,
- namesize = MIN(get_be16(&operands[name_len_offset]),
- len - name_len_offset - 2);
- namesize = MIN(namesize, NAME_MAX_LEN - 1);
-- if (*namelen > 0) {
-+ if (namesize > 0) {
- if (len < name_len_offset + 2 + namesize)
- return FALSE;
- memcpy(name, &operands[name_len_offset + 2], namesize);
---
-2.55.0
-
diff --git a/bluez.spec b/bluez.spec
index 4616e74..0757a6d 100644
--- a/bluez.spec
+++ b/bluez.spec
@@ -4,24 +4,22 @@
%bcond_with deprecated
%endif
+# Snapshot generated with:
+# git config tar.tar.xz.command "xz -c"
+# export SHA=`git rev-parse --short HEAD` ; export VERSION=5.87 ; git archive --format=tar.xz -o bluez-$VERSION+1.git$SHA.tar.xz --prefix=bluez-$VERSION+1.git$SHA/ HEAD
+# as a post-release snapshot, see:
+# https://fedoraproject.org/wiki/PackagingDrafts/TildeVersioning
+%global gitsha 8750129efca8
+
Name: bluez
-Version: 5.87
-Release: 6%{?dist}
+Version: 5.87+1.git%{gitsha}
+Release: 1%{?dist}
Summary: Bluetooth utilities
License: GPL-2.0-or-later
URL: http://www.bluez.org/
Source0: https://www.kernel.org/pub/linux/bluetooth/%{name}-%{version}.tar.xz
-# git format-patch --stdout 5.87...30db66dc971bd1cd95d4a7b0eea296367ab65b3b
-Patch1: 5.87-bug-fixes-1.patch
-# CVE-2026-75032
-Patch2: avrcp-getfolderitems.patch
-# CVE-2026-80186
-Patch3: name2utf8-overflow.patch
-# CVE-2026-80185
-Patch4: sdp-xml-type-confusion.patch
-
BuildRequires: dbus-devel >= 1.6
BuildRequires: glib2-devel
BuildRequires: libell-devel >= 0.39
@@ -180,7 +178,7 @@ install -m0755 tools/btsnoop $RPM_BUILD_ROOT%{_bindir}
# some issues and to set the MAC address on HCIs which don't have their
# MAC address configured
install -m0755 tools/btmgmt $RPM_BUILD_ROOT%{_bindir}
-install -m0644 doc/btmgmt.1 $RPM_BUILD_ROOT%{_mandir}/man1/
+rst2man doc/btmgmt.rst --no-datestamp --no-generator $RPM_BUILD_ROOT%{_mandir}/man1/btmgmt.1
# Remove libtool archive
find $RPM_BUILD_ROOT -name '*.la' -delete
@@ -346,6 +344,9 @@ install emulator/btvirt ${RPM_BUILD_ROOT}/%{_libexecdir}/bluetooth/
%{_userunitdir}/obex.service
%changelog
+* Wed Sep 09 2026 Bastien Nocera <bnocera@redhat.com> - 5.87+1.git8750129efca8-1
+- Rebase to latest upstream HEAD (Closes: #2528181, #2525293)
+
* Wed Aug 26 2026 Bastien Nocera <bnocera@redhat.com> - 5.87-6
- Fix CVE-2026-80185 (Closes: #2524397)
diff --git a/name2utf8-overflow.patch b/name2utf8-overflow.patch
deleted file mode 100644
index 47b2b6d..0000000
--- a/name2utf8-overflow.patch
+++ /dev/null
@@ -1,1347 +0,0 @@
-From 381b5d0d208972586282116d333865ba93b8dec2 Mon Sep 17 00:00:00 2001
-From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
-Date: Wed, 19 Aug 2026 16:02:47 -0400
-Subject: [PATCH 01/10] eir: Fix stack buffer overflow when parsing the remote
- name
-
-name2utf8() copies len bytes into a HCI_MAX_NAME_LENGTH + 2, so 250,
-byte stack buffer without clamping len first.
-
-eir_parse() only rejects a field once it runs past the end of the EIR
-data, and that data is up to 255 bytes, so field_len can be 254 and the
-data_len passed to name2utf8() can reach 253. strncpy() then writes 253
-bytes into the 250 byte buffer and leaves it unterminated, so the
-following g_strstrip() and g_strdup() also read past the end.
-
-The EIR data comes from a remote device, either in an extended inquiry
-response or in an advertising report, so the length is attacker
-controlled.
-
-Clamp len to HCI_MAX_NAME_LENGTH, which is what the local name is
-limited to anyway, and what ad_replace_name() already clamps to.
-
-Fixes: https://github.com/bluez/bluez/security/advisories/GHSA-68h6-5qgp-3975
-Assisted-by: Claude:claude-opus-5
----
- src/eir.c | 2 ++
- 1 file changed, 2 insertions(+)
-
-diff --git a/src/eir.c b/src/eir.c
-index 89c15995a546..4421b1662d65 100644
---- a/src/eir.c
-+++ b/src/eir.c
-@@ -137,6 +137,8 @@ static char *name2utf8(const uint8_t *name, uint8_t len)
- {
- char utf8_name[HCI_MAX_NAME_LENGTH + 2];
-
-+ len = MIN(len, HCI_MAX_NAME_LENGTH);
-+
- memset(utf8_name, 0, sizeof(utf8_name));
- strncpy(utf8_name, (char *) name, len);
- strtoutf8(utf8_name, len);
---
-2.55.0
-
-
-From 784203160e2fb906090059336d86a24f28349b02 Mon Sep 17 00:00:00 2001
-From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
-Date: Thu, 20 Aug 2026 13:42:12 -0400
-Subject: [PATCH 02/10] shared/ad: Fix reading past the name that was copied
-
-ad_replace_name() copies at most HCI_MAX_NAME_LENGTH bytes of the name
-into its buffer, but then hands the full iov_len to strisutf8() and
-strtoutf8().
-
-The advertising data is up to 255 bytes, so a complete local name field
-can hold 253 of them, and both end up reading 253 bytes out of a 250
-byte buffer, 3 of them past its end.
-
-Use the same clamped length throughout.
-
-Assisted-by: Claude:claude-opus-5
----
- src/shared/ad.c | 8 ++++----
- 1 file changed, 4 insertions(+), 4 deletions(-)
-
-diff --git a/src/shared/ad.c b/src/shared/ad.c
-index b1d1b84611aa..ebee078500c6 100644
---- a/src/shared/ad.c
-+++ b/src/shared/ad.c
-@@ -276,15 +276,15 @@ static bool ad_replace_uuid128(struct bt_ad *ad, struct iovec *iov)
- static bool ad_replace_name(struct bt_ad *ad, struct iovec *iov)
- {
- char utf8_name[HCI_MAX_NAME_LENGTH + 2];
-+ size_t len = MIN(iov->iov_len, (size_t) HCI_MAX_NAME_LENGTH);
-
- memset(utf8_name, 0, sizeof(utf8_name));
-- strncpy(utf8_name, (const char *)iov->iov_base,
-- MIN(iov->iov_len, HCI_MAX_NAME_LENGTH));
-+ strncpy(utf8_name, (const char *)iov->iov_base, len);
-
-- if (strisutf8(utf8_name, iov->iov_len))
-+ if (strisutf8(utf8_name, len))
- goto done;
-
-- strtoutf8(utf8_name, iov->iov_len);
-+ strtoutf8(utf8_name, len);
-
- /* Remove leading and trailing whitespace characters */
- strstrip(utf8_name);
---
-2.55.0
-
-
-From debd432ef13c5ea9ffab9ffcbfcb45372946920f Mon Sep 17 00:00:00 2001
-From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
-Date: Thu, 20 Aug 2026 13:28:38 -0400
-Subject: [PATCH 03/10] unit/test-eir: Add tests for the longest local names
-
-Nothing covered a name anywhere near the size of the buffer it is copied
-into, which is why the missing clamp went unnoticed.
-
-Add two tests. The first uses a name of HCI_MAX_NAME_LENGTH bytes, the
-longest one that fits, to pin the boundary down.
-
-The second uses a name of 253 bytes, as large as eir_parse() can be
-handed given the EIR length is a single byte, and which does not fit.
-Run against the code before the previous patch, it dies with
-
- *** buffer overflow detected ***: terminated
-
-Assisted-by: Claude:claude-opus-5
----
- unit/test-eir.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 62 insertions(+)
-
-diff --git a/unit/test-eir.c b/unit/test-eir.c
-index 62164ca993f6..326bc899e251 100644
---- a/unit/test-eir.c
-+++ b/unit/test-eir.c
-@@ -440,6 +440,64 @@ static const struct test_data iso_2022_jp_name_test = {
- .tx_power = 127,
- };
-
-+/*
-+ * A complete local name of HCI_MAX_NAME_LENGTH bytes, the longest one that
-+ * fits the buffer eir_parse() copies the name into.
-+ */
-+static unsigned char max_name_data[HCI_MAX_NAME_LENGTH + 2];
-+static char max_name[HCI_MAX_NAME_LENGTH + 1];
-+
-+static const struct test_data max_name_test = {
-+ .eir_data = max_name_data,
-+ .eir_size = sizeof(max_name_data),
-+ .name = max_name,
-+ .name_complete = true,
-+ .tx_power = 127,
-+};
-+
-+static void max_name_setup(const void *data)
-+{
-+ max_name_data[0] = sizeof(max_name_data) - 1;
-+ max_name_data[1] = EIR_NAME_COMPLETE;
-+ memset(max_name_data + 2, 'A', HCI_MAX_NAME_LENGTH);
-+
-+ memset(max_name, 'A', HCI_MAX_NAME_LENGTH);
-+ max_name[HCI_MAX_NAME_LENGTH] = '\0';
-+
-+ tester_setup_complete();
-+}
-+
-+/*
-+ * The longest complete local name eir_parse() can be handed at all, which is
-+ * bounded by the EIR length being a single byte. That is 253 bytes, more than
-+ * the buffer it is copied into, so this used to overflow it.
-+ */
-+static unsigned char long_name_data[255];
-+static char long_name[sizeof(long_name_data) - 2 + 1];
-+
-+/* The name does not fit, so it comes back clamped to HCI_MAX_NAME_LENGTH */
-+#define LONG_NAME_LEN HCI_MAX_NAME_LENGTH
-+
-+static const struct test_data long_name_test = {
-+ .eir_data = long_name_data,
-+ .eir_size = sizeof(long_name_data),
-+ .name = long_name,
-+ .name_complete = true,
-+ .tx_power = 127,
-+};
-+
-+static void long_name_setup(const void *data)
-+{
-+ long_name_data[0] = sizeof(long_name_data) - 1;
-+ long_name_data[1] = EIR_NAME_COMPLETE;
-+ memset(long_name_data + 2, 'B', sizeof(long_name_data) - 2);
-+
-+ memset(long_name, 'B', LONG_NAME_LEN);
-+ long_name[LONG_NAME_LEN] = '\0';
-+
-+ tester_setup_complete();
-+}
-+
- static const unsigned char bluesc_data[] = {
- 0x02, 0x01, 0x06, 0x03, 0x02, 0x16, 0x18, 0x12,
- 0x09, 0x57, 0x61, 0x68, 0x6f, 0x6f, 0x20, 0x42,
-@@ -756,6 +814,10 @@ int main(int argc, char *argv[])
- NULL);
- tester_add("/eir/iso-2022-jp-name", &iso_2022_jp_name_test, NULL,
- test_parsing, NULL);
-+ tester_add("/eir/max-name", &max_name_test, max_name_setup,
-+ test_parsing, NULL);
-+ tester_add("/eir/long-name", &long_name_test, long_name_setup,
-+ test_parsing, NULL);
- tester_add("/ad/bluesc", &bluesc_test, NULL, test_parsing, NULL);
- tester_add("/ad/wahooscale", &wahoo_scale_test, NULL, test_parsing,
- NULL);
---
-2.55.0
-
-
-From 3ad832a3c2a989ed9f14586cd3b56f40ad608679 Mon Sep 17 00:00:00 2001
-From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
-Date: Wed, 19 Aug 2026 16:10:36 -0400
-Subject: [PATCH 04/10] shared/util: Make strnlenutf8 reject ill-formed
- sequences
-
-strnlenutf8() only checks the shape of the lead byte and that the
-following bytes are continuation bytes, so it accepts sequences that are
-not well-formed UTF-8:
-
- C0 80 overlong encoding of U+0000
- C0 AF overlong encoding of '/'
- ED A0 80 UTF-16 surrogate U+D800
- F5 80 80 80 past the U+10FFFF limit
-
-strisutf8() and strtoutf8() are built on it, so a remote name containing
-any of those is considered valid and passed on unchanged, for instance
-to D-Bus, which does validate UTF-8 strictly and rejects them.
-
-Validate the sequences as defined by table 3-7 of the Unicode Standard
-instead, which constrains the range of the second byte for the E0, ED,
-F0 and F4 lead bytes and rejects the C0, C1 and F5 to FF ones outright.
-
-The decoding is split out into a helper that also reports the size of
-the maximal subpart of an ill-formed sequence, so that callers can skip
-over it, as recommended by section 3.9 of the Unicode Standard.
-
-Assisted-by: Claude:claude-opus-5
----
- src/shared/util.c | 90 ++++++++++++++++++++++++++++++++---------------
- 1 file changed, 62 insertions(+), 28 deletions(-)
-
-diff --git a/src/shared/util.c b/src/shared/util.c
-index 62dd1369b70d..e946214edbb9 100644
---- a/src/shared/util.c
-+++ b/src/shared/util.c
-@@ -2211,44 +2211,78 @@ char *strstrip(char *str)
- return str;
- }
-
--size_t strnlenutf8(const char *str, size_t len)
--
-+/*
-+ * Decode the UTF-8 sequence at str, as defined by table 3-7 of the Unicode
-+ * Standard, and return its size, or 0 if it is ill-formed.
-+ *
-+ * sublen is set to the size of the maximal subpart of the sequence, that is
-+ * the number of leading bytes that could still have formed a well-formed
-+ * sequence, which is what the caller needs to skip over.
-+ */
-+static size_t utf8_seqlen(const unsigned char *str, size_t len, size_t *sublen)
- {
-- size_t i = 0;
-+ unsigned char lo = 0x80, hi = 0xbf;
-+ size_t size, i;
-
-- while (i < len) {
-- unsigned char c = str[i];
-- size_t size = 0;
-+ if (str[0] <= 0x7f) {
-+ *sublen = 1;
-+ return 1;
-+ }
-
-- /* Check the first byte to determine the number of bytes in the
-- * UTF-8 character.
-+ if (str[0] >= 0xc2 && str[0] <= 0xdf) {
-+ size = 2;
-+ } else if (str[0] >= 0xe0 && str[0] <= 0xef) {
-+ size = 3;
-+ /* Reject the overlong encodings and the UTF-16 surrogates */
-+ if (str[0] == 0xe0)
-+ lo = 0xa0;
-+ else if (str[0] == 0xed)
-+ hi = 0x9f;
-+ } else if (str[0] >= 0xf0 && str[0] <= 0xf4) {
-+ size = 4;
-+ /* Reject the overlong encodings and anything past U+10FFFF */
-+ if (str[0] == 0xf0)
-+ lo = 0x90;
-+ else if (str[0] == 0xf4)
-+ hi = 0x8f;
-+ } else {
-+ /* C0 and C1 are overlong, F5 to FF are out of range, and a
-+ * continuation byte cannot start a sequence.
- */
-- if ((c & 0x80) == 0x00)
-- size = 1;
-- else if ((c & 0xE0) == 0xC0)
-- size = 2;
-- else if ((c & 0xF0) == 0xE0)
-- size = 3;
-- else if ((c & 0xF8) == 0xF0)
-- size = 4;
-- else
-- /* Invalid UTF-8 sequence */
-- goto done;
-+ *sublen = 1;
-+ return 0;
-+ }
-
-- /* Check the following bytes to ensure they have the correct
-- * format.
-- */
-- for (size_t j = 1; j < size; ++j) {
-- if (i + j >= len || (str[i + j] & 0xC0) != 0x80)
-- /* Invalid UTF-8 sequence */
-- goto done;
-+ for (i = 1; i < size; i++) {
-+ if (i >= len || str[i] < lo || str[i] > hi) {
-+ *sublen = i;
-+ return 0;
- }
-
-+ /* Only the second byte has a restricted range */
-+ lo = 0x80;
-+ hi = 0xbf;
-+ }
-+
-+ *sublen = size;
-+ return size;
-+}
-+
-+size_t strnlenutf8(const char *str, size_t len)
-+{
-+ size_t i = 0;
-+
-+ while (i < len) {
-+ size_t sublen;
-+
-+ if (!utf8_seqlen((const unsigned char *) str + i, len - i,
-+ &sublen))
-+ break;
-+
- /* Move to the next character */
-- i += size;
-+ i += sublen;
- }
-
--done:
- return i;
- }
-
---
-2.55.0
-
-
-From 11081f60d95f641ddbca4a53922d886972c87aa1 Mon Sep 17 00:00:00 2001
-From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
-Date: Wed, 19 Aug 2026 16:11:03 -0400
-Subject: [PATCH 05/10] shared/util: Add str2utf8
-
-There are five near copies of the same "turn a remote name into a UTF-8
-string" helper, in monitor/att.c, profiles/audio/mcp.c, profiles/gap/gas.c,
-src/eir.c and src/shared/ad.c, and they do not agree with each other.
-
-Most truncate at the first ill-formed sequence, which throws away the
-rest of the name, while the monitor replaces every non-ASCII byte with a
-space, which mangles perfectly valid UTF-8 names as soon as one bad byte
-appears. Most also copy into a fixed size stack buffer first, which is
-what made the missing clamp in src/eir.c a buffer overflow.
-
-Add a single helper they can share. It allocates the result, so there is
-no truncation to a buffer size, and replaces each ill-formed sequence
-with U+FFFD REPLACEMENT CHARACTER rather than dropping the rest of the
-string, matching what g_utf8_make_valid() and the WHATWG Encoding
-Standard do.
-
-The result has been checked byte for byte against Python's
-bytes.decode('utf-8', errors='replace') over all one and two byte
-sequences, a sample of the three byte ones and 200000 random inputs.
-
-Assisted-by: Claude:claude-opus-5
----
- src/shared/util.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++
- src/shared/util.h | 7 +++++++
- 2 files changed, 53 insertions(+)
-
-diff --git a/src/shared/util.c b/src/shared/util.c
-index e946214edbb9..8ec9b52e6401 100644
---- a/src/shared/util.c
-+++ b/src/shared/util.c
-@@ -2315,3 +2315,49 @@ char *strtoutf8(char *str, size_t len)
- memset(str + i, 0, len - i);
- return str;
- }
-+
-+char *str2utf8(const uint8_t *str, size_t len)
-+{
-+ char *utf8, *out, *stripped;
-+ size_t i = 0;
-+
-+ if (!str)
-+ return NULL;
-+
-+ /*
-+ * Invalid bytes are replaced with U+FFFD REPLACEMENT CHARACTER, which
-+ * is 3 bytes long, so that is the worst case size of the result.
-+ */
-+ utf8 = malloc(len * 3 + 1);
-+ if (!utf8)
-+ return NULL;
-+
-+ out = utf8;
-+
-+ while (i < len) {
-+ size_t sublen;
-+ size_t size = utf8_seqlen(str + i, len - i, &sublen);
-+
-+ if (size) {
-+ memcpy(out, str + i, size);
-+ out += size;
-+ i += size;
-+ continue;
-+ }
-+
-+ /* Replace the maximal subpart with U+FFFD */
-+ *out++ = 0xef;
-+ *out++ = 0xbf;
-+ *out++ = 0xbd;
-+ i += sublen;
-+ }
-+
-+ *out = '\0';
-+
-+ /* Remove leading and trailing whitespace characters */
-+ stripped = strstrip(utf8);
-+ if (stripped != utf8)
-+ memmove(utf8, stripped, strlen(stripped) + 1);
-+
-+ return utf8;
-+}
-diff --git a/src/shared/util.h b/src/shared/util.h
-index 562a5af31751..1984fb75f09e 100644
---- a/src/shared/util.h
-+++ b/src/shared/util.h
-@@ -143,6 +143,13 @@ bool strisutf8(const char *str, size_t length);
- bool argsisutf8(int argc, char *argv[]);
- char *strtoutf8(char *str, size_t len);
-
-+/*
-+ * Return a newly allocated, NUL terminated and whitespace stripped UTF-8
-+ * copy of the first len bytes of str, with each ill-formed sequence replaced
-+ * by U+FFFD REPLACEMENT CHARACTER. The result must be freed with free().
-+ */
-+char *str2utf8(const uint8_t *str, size_t len);
-+
- void *util_malloc(size_t size);
- void *util_memdup(const void *src, size_t size);
-
---
-2.55.0
-
-
-From 74c56dff2aa5d5f3cf41e446a0afeebb21ffc4e4 Mon Sep 17 00:00:00 2001
-From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
-Date: Wed, 19 Aug 2026 16:12:11 -0400
-Subject: [PATCH 06/10] unit/test-util: Add str2utf8 tests
-
-Cover the cases str2utf8() is meant to handle: well-formed input that
-has to be left alone, whitespace stripping, input that is not NUL
-terminated, and the ill-formed sequences that have to be replaced,
-including the overlong encodings, the UTF-16 surrogates and the code
-points past U+10FFFF.
-
-Also check that a maximal subpart is replaced by a single U+FFFD rather
-than one per byte, and that the result is always well-formed UTF-8.
-
-Assisted-by: Claude:claude-opus-5
----
- unit/test-util.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 83 insertions(+)
-
-diff --git a/unit/test-util.c b/unit/test-util.c
-index 1672b32eb39c..f0b1bb7994fb 100644
---- a/unit/test-util.c
-+++ b/unit/test-util.c
-@@ -83,6 +83,85 @@ static void test_min_max(const void *data)
- tester_test_passed();
- }
-
-+struct str2utf8_data {
-+ const char *input; /* Not NUL terminated, len bytes are used */
-+ size_t len;
-+ const char *expected;
-+};
-+
-+#define FFFD "\xef\xbf\xbd" /* U+FFFD REPLACEMENT CHARACTER */
-+
-+static const struct str2utf8_data str2utf8_tests[] = {
-+ /* Nothing to do */
-+ { "", 0, "" },
-+ { "Pixel 7", 7, "Pixel 7" },
-+ /* Well-formed multi-byte sequences are kept as they are */
-+ { "\xe2\x82\xac 5", 5, "\xe2\x82\xac 5" }, /* U+20AC */
-+ { "\xf0\x9f\x94\x8a", 4, "\xf0\x9f\x94\x8a" }, /* U+1F50A */
-+ /* Leading and trailing whitespace is removed */
-+ { " spaced ", 10, "spaced" },
-+ { "\t\r\nname\n\r\t", 10, "name" },
-+ { " ", 3, "" },
-+ /* The name is not NUL terminated, only len bytes are used */
-+ { "truncated", 4, "trun" },
-+ /* A byte that can never appear in UTF-8 */
-+ { "ab\xff""cd", 5, "ab" FFFD "cd" },
-+ /* A continuation byte cannot start a sequence */
-+ { "ab\x80""cd", 5, "ab" FFFD "cd" },
-+ /* One U+FFFD per maximal subpart, not per byte */
-+ { "ab\xe2\x82""cd", 6, "ab" FFFD "cd" },
-+ /* A sequence cut short by len is still one maximal subpart */
-+ { "ab\xe2\x82\xac", 4, "ab" FFFD },
-+ /* Latin-1 text is not valid UTF-8 */
-+ { "caf\xe9", 4, "caf" FFFD },
-+ /* Overlong encodings are rejected, C0 and C1 are never valid */
-+ { "\xc0\x80", 2, FFFD FFFD },
-+ { "\xc0\xaf", 2, FFFD FFFD },
-+ /* UTF-16 surrogates have no UTF-8 encoding */
-+ { "\xed\xa0\x80", 3, FFFD FFFD FFFD },
-+ /* U+10FFFF is the last code point, F5 to FF are out of range */
-+ { "\xf4\x90\x80\x80", 4, FFFD FFFD FFFD FFFD },
-+ { "\xf5\x80\x80\x80", 4, FFFD FFFD FFFD FFFD },
-+ /* The last code point itself is fine */
-+ { "\xf4\x8f\xbf\xbf", 4, "\xf4\x8f\xbf\xbf" },
-+ /* Replacement and stripping combined */
-+ { " \xff ", 3, FFFD },
-+};
-+
-+static void test_str2utf8(const void *data)
-+{
-+ size_t i;
-+
-+ for (i = 0; i < sizeof(str2utf8_tests) /
-+ sizeof(str2utf8_tests[0]); i++) {
-+ const struct str2utf8_data *test = &str2utf8_tests[i];
-+ char *str = str2utf8((const uint8_t *) test->input,
-+ test->len);
-+
-+ assert(str);
-+ if (strcmp(str, test->expected)) {
-+ printf("test %zu: expected \"%s\", got \"%s\"\n", i,
-+ test->expected, str);
-+ free(str);
-+ tester_test_failed();
-+ return;
-+ }
-+
-+ /* The result is always well-formed UTF-8 */
-+ assert(strisutf8(str, strlen(str)));
-+
-+ free(str);
-+ }
-+
-+ tester_test_passed();
-+}
-+
-+static void test_str2utf8_null(const void *data)
-+{
-+ assert(!str2utf8(NULL, 0));
-+ tester_test_passed();
-+}
-+
- int main(int argc, char *argv[])
- {
- tester_init(&argc, &argv);
-@@ -95,6 +174,10 @@ int main(int argc, char *argv[])
- test_cleanup_type, NULL);
- tester_add("/util/cleanup_fd", NULL, NULL,
- test_cleanup_fd, NULL);
-+ tester_add("/util/str2utf8", NULL, NULL,
-+ test_str2utf8, NULL);
-+ tester_add("/util/str2utf8_null", NULL, NULL,
-+ test_str2utf8_null, NULL);
-
- return tester_run();
- }
---
-2.55.0
-
-
-From 8c81ab108b09154b884b1b0549dc9c23ffe3ec6f Mon Sep 17 00:00:00 2001
-From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
-Date: Wed, 19 Aug 2026 16:18:38 -0400
-Subject: [PATCH 07/10] Replace the name2utf8 copies with str2utf8
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-monitor/att.c, profiles/audio/mcp.c, profiles/gap/gas.c and src/eir.c
-each carried their own name2utf8(), and src/shared/ad.c open coded the
-same thing in ad_replace_name(), with none of them agreeing.
-
-Use the shared helper instead, which drops around 120 lines and gives
-every caller the same behaviour.
-
-Two things change as a result. The monitor used to replace every
-non-ASCII byte with a space as soon as one bad byte appeared, mangling
-the valid part of the name, and now only the ill-formed sequences are
-replaced. Everything else used to truncate at the first ill-formed
-sequence, throwing away the rest of the name, and now keeps it.
-
-The unit/test-eir expectations are updated accordingly, and they show
-the improvement: the name that used to be reported as "test परी" is now
-reported as "test परी<U+FFFD>्षा invalid".
-
-str2utf8() returns memory from malloc(), so the callers that used
-g_free() now use free().
-
-Assisted-by: Claude:claude-opus-5
----
- monitor/att.c | 66 ++++++++++++++------------------------------
- profiles/audio/mcp.c | 24 ++--------------
- profiles/gap/gas.c | 20 ++------------
- src/eir.c | 22 ++-------------
- src/shared/ad.c | 20 ++++++--------
- unit/test-eir.c | 11 +++++---
- 6 files changed, 42 insertions(+), 121 deletions(-)
-
-diff --git a/monitor/att.c b/monitor/att.c
-index 7506dc528e85..44965a2aaf3b 100644
---- a/monitor/att.c
-+++ b/monitor/att.c
-@@ -15,7 +15,6 @@
- #endif
-
- #define _GNU_SOURCE
--#include <ctype.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
-@@ -2325,40 +2324,15 @@ static void vol_flag_notify(const struct l2cap_frame *frame)
- print_vcs_flag(frame);
- }
-
--static char *name2utf8(const uint8_t *name, uint16_t len)
--{
-- char utf8_name[HCI_MAX_NAME_LENGTH + 2];
-- int i;
--
-- if (g_utf8_validate((const char *) name, len, NULL))
-- return g_strndup((char *) name, len);
--
-- len = MIN(len, sizeof(utf8_name) - 1);
--
-- memset(utf8_name, 0, sizeof(utf8_name));
-- strncpy(utf8_name, (char *) name, len);
--
-- /* Assume ASCII, and replace all non-ASCII with spaces */
-- for (i = 0; utf8_name[i] != '\0'; i++) {
-- if (!isascii(utf8_name[i]))
-- utf8_name[i] = ' ';
-- }
--
-- /* Remove leading and trailing whitespace characters */
-- g_strstrip(utf8_name);
--
-- return g_strdup(utf8_name);
--}
--
- static void print_mp_name(const struct l2cap_frame *frame)
- {
- char *name;
-
-- name = name2utf8((uint8_t *)frame->data, frame->size);
-+ name = str2utf8(frame->data, frame->size);
-
- print_field(" Media Player Name: %s", name);
-
-- g_free(name);
-+ free(name);
- }
-
- static void mp_name_read(const struct l2cap_frame *frame)
-@@ -2385,11 +2359,11 @@ static void print_track_title(const struct l2cap_frame *frame)
- {
- char *name;
-
-- name = name2utf8((uint8_t *)frame->data, frame->size);
-+ name = str2utf8(frame->data, frame->size);
-
- print_field(" Track Title: %s", name);
-
-- g_free(name);
-+ free(name);
- }
-
- static void track_title_read(const struct l2cap_frame *frame)
-@@ -2520,11 +2494,11 @@ static void print_bearer_name(const struct l2cap_frame *frame)
- {
- char *name;
-
-- name = name2utf8((uint8_t *)frame->data, frame->size);
-+ name = str2utf8(frame->data, frame->size);
-
- print_field(" Bearer Name: %s", name);
-
-- g_free(name);
-+ free(name);
- }
-
- static void bearer_name_read(const struct l2cap_frame *frame)
-@@ -2541,11 +2515,11 @@ static void bearer_uci_read(const struct l2cap_frame *frame)
- {
- char *name;
-
-- name = name2utf8((uint8_t *)frame->data, frame->size);
-+ name = str2utf8(frame->data, frame->size);
-
- print_field(" Bearer Uci Name: %s", name);
-
-- g_free(name);
-+ free(name);
- }
-
- static void print_technology_name(const struct l2cap_frame *frame)
-@@ -2612,11 +2586,11 @@ static void print_uri_scheme_list(const struct l2cap_frame *frame)
- {
- char *name;
-
-- name = name2utf8((uint8_t *)frame->data, frame->size);
-+ name = str2utf8(frame->data, frame->size);
-
- print_field(" Uri scheme Name: %s", name);
-
-- g_free(name);
-+ free(name);
- }
-
- static void bearer_uri_schemes_list_read(const struct l2cap_frame *frame)
-@@ -2726,11 +2700,11 @@ static void print_call_list(const struct l2cap_frame *frame)
-
- print_field(" call_flag: 0x%x", call_flag);
-
-- call_uri = name2utf8((uint8_t *)frame->data, frame->size);
-+ call_uri = str2utf8(frame->data, frame->size);
-
- print_field(" call_uri: %s", call_uri);
-
-- g_free(call_uri);
-+ free(call_uri);
-
- done:
- if (frame->size)
-@@ -2816,11 +2790,11 @@ static void print_target_uri(const struct l2cap_frame *frame)
-
- print_field(" call_idx: %x", call_idx);
-
-- name = name2utf8((uint8_t *)frame->data, frame->size);
-+ name = str2utf8(frame->data, frame->size);
-
- print_field(" Uri: %s", name);
-
-- g_free(name);
-+ free(name);
-
- done:
- if (frame->size)
-@@ -2928,9 +2902,9 @@ static void print_call_cp(const struct l2cap_frame *frame)
- break;
- case 0x04:
- str = "Originate";
-- name = name2utf8((uint8_t *)frame->data, frame->size);
-+ name = str2utf8(frame->data, frame->size);
- print_field(" Operation: %s Uri: %s", str, name);
-- g_free(name);
-+ free(name);
- break;
- case 0x05:
- str = "Join";
-@@ -3124,11 +3098,11 @@ static void print_incom_call(const struct l2cap_frame *frame)
-
- print_field(" Call Index: %u", call_id);
-
-- name = name2utf8((uint8_t *)frame->data, frame->size);
-+ name = str2utf8(frame->data, frame->size);
-
- print_field(" call_string: %s", name);
-
-- g_free(name);
-+ free(name);
-
- done:
- if (frame->size)
-@@ -3157,11 +3131,11 @@ static void print_call_friendly_name(const struct l2cap_frame *frame)
-
- print_field(" Call Index: %u", call_id);
-
-- name = name2utf8((uint8_t *)frame->data, frame->size);
-+ name = str2utf8(frame->data, frame->size);
-
- print_field(" Friendly Name: %s", name);
-
-- g_free(name);
-+ free(name);
-
- done:
- if (frame->size)
-diff --git a/profiles/audio/mcp.c b/profiles/audio/mcp.c
-index 0c2e0de0b156..8adf814e8d73 100644
---- a/profiles/audio/mcp.c
-+++ b/profiles/audio/mcp.c
-@@ -73,26 +73,6 @@ struct remote_player {
- uint8_t playing_order;
- };
-
--static char *name2utf8(const uint8_t *name, uint16_t len)
--{
-- char *utf8_name;
--
-- utf8_name = malloc(len + 1);
-- if (!utf8_name)
-- return NULL;
--
-- if (len)
-- memcpy(utf8_name, name, len);
--
-- utf8_name[len] = 0;
-- strtoutf8(utf8_name, len);
--
-- /* Remove leading and trailing whitespace characters */
-- g_strstrip(utf8_name);
--
-- return utf8_name;
--}
--
- static const char *mcp_status_val_to_string(uint8_t status)
- {
- switch (status) {
-@@ -118,7 +98,7 @@ static void remote_media_player_name(void *data, const uint8_t *value,
- struct remote_player *remote = data;
- char *name;
-
-- name = name2utf8(value, length);
-+ name = str2utf8(value, length);
- if (!name)
- return;
-
-@@ -145,7 +125,7 @@ static void remote_track_title(void *data, const uint8_t *value,
- char *name;
- uint16_t len;
-
-- name = name2utf8(value, length);
-+ name = str2utf8(value, length);
- if (!name)
- return;
-
-diff --git a/profiles/gap/gas.c b/profiles/gap/gas.c
-index 0f41c9e6c2a5..5184d74e8f07 100644
---- a/profiles/gap/gas.c
-+++ b/profiles/gap/gas.c
-@@ -66,22 +66,6 @@ static void gas_free(struct gas *gas)
- g_free(gas);
- }
-
--static char *name2utf8(const uint8_t *name, uint16_t len)
--{
-- char utf8_name[HCI_MAX_NAME_LENGTH + 2];
--
-- len = MIN(len, sizeof(utf8_name) - 1);
--
-- memset(utf8_name, 0, sizeof(utf8_name));
-- strncpy(utf8_name, (char *) name, len);
-- strtoutf8(utf8_name, len);
--
-- /* Remove leading and trailing whitespace characters */
-- g_strstrip(utf8_name);
--
-- return g_strdup(utf8_name);
--}
--
- static void read_device_name_cb(bool success, uint8_t att_ecode,
- const uint8_t *value, uint16_t length,
- void *user_data)
-@@ -98,13 +82,13 @@ static void read_device_name_cb(bool success, uint8_t att_ecode,
- if (!length)
- return;
-
-- name = name2utf8(value, length);
-+ name = str2utf8(value, length);
-
- DBG("GAP Device Name: %s", name);
-
- btd_device_device_set_name(gas->device, name);
-
-- g_free(name);
-+ free(name);
- }
-
- static void handle_device_name(struct gas *gas, uint16_t value_handle)
-diff --git a/src/eir.c b/src/eir.c
-index 4421b1662d65..5c9ebe2af3a3 100644
---- a/src/eir.c
-+++ b/src/eir.c
-@@ -60,7 +60,7 @@ void eir_data_free(struct eir_data *eir)
- {
- queue_destroy(eir->services, g_free);
- eir->services = NULL;
-- g_free(eir->name);
-+ free(eir->name);
- eir->name = NULL;
- free(eir->hash);
- eir->hash = NULL;
-@@ -133,22 +133,6 @@ static void eir_parse_uuid128(struct eir_data *eir, const uint8_t *data,
- }
- }
-
--static char *name2utf8(const uint8_t *name, uint8_t len)
--{
-- char utf8_name[HCI_MAX_NAME_LENGTH + 2];
--
-- len = MIN(len, HCI_MAX_NAME_LENGTH);
--
-- memset(utf8_name, 0, sizeof(utf8_name));
-- strncpy(utf8_name, (char *) name, len);
-- strtoutf8(utf8_name, len);
--
-- /* Remove leading and trailing whitespace characters */
-- g_strstrip(utf8_name);
--
-- return g_strdup(utf8_name);
--}
--
- static void eir_parse_msd(struct eir_data *eir, const uint8_t *data,
- uint8_t len)
- {
-@@ -301,9 +285,9 @@ void eir_parse(struct eir_data *eir, const uint8_t *eir_data, uint8_t eir_len)
- while (data_len > 0 && data[data_len - 1] == '\0')
- data_len--;
-
-- g_free(eir->name);
-+ free(eir->name);
-
-- eir->name = name2utf8(data, data_len);
-+ eir->name = str2utf8(data, data_len);
- eir->name_complete = eir_data[1] != EIR_NAME_SHORT;
- break;
-
-diff --git a/src/shared/ad.c b/src/shared/ad.c
-index ebee078500c6..236e719507e4 100644
---- a/src/shared/ad.c
-+++ b/src/shared/ad.c
-@@ -275,22 +275,18 @@ static bool ad_replace_uuid128(struct bt_ad *ad, struct iovec *iov)
-
- static bool ad_replace_name(struct bt_ad *ad, struct iovec *iov)
- {
-- char utf8_name[HCI_MAX_NAME_LENGTH + 2];
-- size_t len = MIN(iov->iov_len, (size_t) HCI_MAX_NAME_LENGTH);
-+ char *utf8_name;
-+ bool ret;
-
-- memset(utf8_name, 0, sizeof(utf8_name));
-- strncpy(utf8_name, (const char *)iov->iov_base, len);
--
-- if (strisutf8(utf8_name, len))
-- goto done;
-+ utf8_name = str2utf8(iov->iov_base, iov->iov_len);
-+ if (!utf8_name)
-+ return false;
-
-- strtoutf8(utf8_name, len);
-+ ret = bt_ad_add_name(ad, utf8_name);
-
-- /* Remove leading and trailing whitespace characters */
-- strstrip(utf8_name);
-+ free(utf8_name);
-
--done:
-- return bt_ad_add_name(ad, utf8_name);
-+ return ret;
- }
-
- static bool ad_replace_uuid16_data(struct bt_ad *ad, struct iovec *iov)
-diff --git a/unit/test-eir.c b/unit/test-eir.c
-index 326bc899e251..380fcba2f38e 100644
---- a/unit/test-eir.c
-+++ b/unit/test-eir.c
-@@ -407,7 +407,8 @@ static const unsigned char invalid_utf8_name_data[] = {
- static const struct test_data invalid_utf8_name_test = {
- .eir_data = invalid_utf8_name_data,
- .eir_size = sizeof(invalid_utf8_name_data),
-- .name = "test परी",
-+ /* The truncated sequence is replaced by U+FFFD, the rest is kept */
-+ .name = "test परी" "\xef\xbf\xbd" "्षा invalid",
- .name_complete = true,
- .tx_power = 127,
- };
-@@ -435,7 +436,9 @@ static const unsigned char iso_2022_jp_name_data[] = {
- static const struct test_data iso_2022_jp_name_test = {
- .eir_data = iso_2022_jp_name_data,
- .eir_size = sizeof(iso_2022_jp_name_data),
-- .name = "test \033$B",
-+ /* The 4 JIS bytes are replaced by U+FFFD, the escapes are ASCII */
-+ .name = "test \033$B" "\xef\xbf\xbd\xef\xbf\xbd\xef\xbf\xbd\xef\xbf\xbd"
-+ "\033(B OK",
- .name_complete = true,
- .tx_power = 127,
- };
-@@ -475,8 +478,8 @@ static void max_name_setup(const void *data)
- static unsigned char long_name_data[255];
- static char long_name[sizeof(long_name_data) - 2 + 1];
-
--/* The name does not fit, so it comes back clamped to HCI_MAX_NAME_LENGTH */
--#define LONG_NAME_LEN HCI_MAX_NAME_LENGTH
-+/* str2utf8() does not clamp, so the whole name is kept */
-+#define LONG_NAME_LEN (sizeof(long_name_data) - 2)
-
- static const struct test_data long_name_test = {
- .eir_data = long_name_data,
---
-2.55.0
-
-
-From 2bf8286c4ebe6256b152c3adab6ea0fed8a834d9 Mon Sep 17 00:00:00 2001
-From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
-Date: Wed, 19 Aug 2026 16:27:07 -0400
-Subject: [PATCH 08/10] device: Fix the name truncation splitting UTF-8
- sequences
-
-btd_device_device_set_name() copies the name with
-
- strncpy(device->name, name, MAX_NAME_LENGTH);
-
-which cuts at 248 bytes without any regard for where the UTF-8
-characters start and end, so a longer name can be left with a partial
-sequence. The result is no longer valid UTF-8 and D-Bus rejects it when
-the Name property is emitted.
-
-A name made of 249 U+FFFD characters is 747 bytes long and cutting it at
-248 leaves a trailing "ef bf", two thirds of a character.
-
-Truncate on a character boundary instead. The same name now ends up 246
-bytes long and stays valid.
-
-This also means a name that is not valid UTF-8 to begin with, as can be
-had from the neard and sixaxis plugins, is now cut at the first
-ill-formed sequence rather than passed on as is.
-
-Assisted-by: Claude:claude-opus-5
----
- src/device.c | 12 +++++++++++-
- 1 file changed, 11 insertions(+), 1 deletion(-)
-
-diff --git a/src/device.c b/src/device.c
-index 65d84be56ca5..df607f718be1 100644
---- a/src/device.c
-+++ b/src/device.c
-@@ -5103,12 +5103,22 @@ char *btd_device_get_storage_path(struct btd_device *device, const char *name)
-
- void btd_device_device_set_name(struct btd_device *device, const char *name)
- {
-+ size_t len;
-+
- if (strncmp(name, device->name, MAX_NAME_LENGTH) == 0)
- return;
-
- DBG("%s %s", device->path, name);
-
-- strncpy(device->name, name, MAX_NAME_LENGTH);
-+ /*
-+ * Truncate on a character boundary, so that a name longer than
-+ * MAX_NAME_LENGTH does not end up with a partial sequence, which
-+ * would no longer be valid UTF-8 and would be rejected by D-Bus.
-+ */
-+ len = strnlenutf8(name, MIN(strlen(name), (size_t) MAX_NAME_LENGTH));
-+
-+ memcpy(device->name, name, len);
-+ device->name[len] = '\0';
-
- store_device_info(device);
-
---
-2.55.0
-
-
-From bef0faa312eeb83344bb2bead5bbbd0b538414e5 Mon Sep 17 00:00:00 2001
-From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
-Date: Wed, 19 Aug 2026 16:27:19 -0400
-Subject: [PATCH 09/10] device: Rename btd_device_device_set_name to
- btd_device_set_name
-
-The "device" was in there twice.
-
-Assisted-by: Claude:claude-opus-5
----
- plugins/neard.c | 2 +-
- plugins/sixaxis.c | 2 +-
- profiles/gap/gas.c | 2 +-
- src/adapter.c | 4 ++--
- src/device.c | 2 +-
- src/device.h | 2 +-
- 6 files changed, 7 insertions(+), 7 deletions(-)
-
-diff --git a/plugins/neard.c b/plugins/neard.c
-index edfc115373ef..1633dd576747 100644
---- a/plugins/neard.c
-+++ b/plugins/neard.c
-@@ -629,7 +629,7 @@ static void store_params(struct btd_adapter *adapter, struct btd_device *device,
-
- if (params->name) {
- device_store_cached_name(device, params->name);
-- btd_device_device_set_name(device, params->name);
-+ btd_device_set_name(device, params->name);
- }
-
- if (params->services)
-diff --git a/plugins/sixaxis.c b/plugins/sixaxis.c
-index a04a76d394eb..fc2b2a9d0156 100644
---- a/plugins/sixaxis.c
-+++ b/plugins/sixaxis.c
-@@ -362,7 +362,7 @@ static bool setup_device(int fd, const char *sysfs_path,
-
- info("sixaxis: setting up new device");
-
-- btd_device_device_set_name(device, cp->name);
-+ btd_device_set_name(device, cp->name);
- btd_device_set_pnpid(device, cp->source, cp->vid, cp->pid, cp->version);
- btd_device_set_temporary(device, true);
-
-diff --git a/profiles/gap/gas.c b/profiles/gap/gas.c
-index 5184d74e8f07..495799e641d9 100644
---- a/profiles/gap/gas.c
-+++ b/profiles/gap/gas.c
-@@ -86,7 +86,7 @@ static void read_device_name_cb(bool success, uint8_t att_ecode,
-
- DBG("GAP Device Name: %s", name);
-
-- btd_device_device_set_name(gas->device, name);
-+ btd_device_set_name(gas->device, name);
-
- free(name);
- }
-diff --git a/src/adapter.c b/src/adapter.c
-index c21b3e7fbcc2..cf59db4aa5a9 100644
---- a/src/adapter.c
-+++ b/src/adapter.c
-@@ -7628,7 +7628,7 @@ void btd_adapter_device_found(struct btd_adapter *adapter,
- name_known = device_name_known(dev);
-
- if (eir_data.name && (eir_data.name_complete || !name_known))
-- btd_device_device_set_name(dev, eir_data.name);
-+ btd_device_set_name(dev, eir_data.name);
-
- if (eir_data.class != 0)
- device_set_class(dev, eir_data.class);
-@@ -9814,7 +9814,7 @@ static void connected_callback(uint16_t index, uint16_t length,
-
- if (eir_data.name && (eir_data.name_complete || !name_known)) {
- device_store_cached_name(device, eir_data.name);
-- btd_device_device_set_name(device, eir_data.name);
-+ btd_device_set_name(device, eir_data.name);
- }
-
- if (eir_data.msd_list)
-diff --git a/src/device.c b/src/device.c
-index df607f718be1..9609a14f7883 100644
---- a/src/device.c
-+++ b/src/device.c
-@@ -5101,7 +5101,7 @@ char *btd_device_get_storage_path(struct btd_device *device, const char *name)
- return strdup(filename);
- }
-
--void btd_device_device_set_name(struct btd_device *device, const char *name)
-+void btd_device_set_name(struct btd_device *device, const char *name)
- {
- size_t len;
-
-diff --git a/src/device.h b/src/device.h
-index b890f23d4642..7683be82ee3f 100644
---- a/src/device.h
-+++ b/src/device.h
-@@ -23,7 +23,7 @@ char *btd_device_get_storage_path(struct btd_device *device,
- const char *filename);
-
-
--void btd_device_device_set_name(struct btd_device *device, const char *name);
-+void btd_device_set_name(struct btd_device *device, const char *name);
- void device_store_cached_name(struct btd_device *dev, const char *name);
- void device_get_name(struct btd_device *device, char *name, size_t len);
- bool device_name_known(struct btd_device *device);
---
-2.55.0
-
-
-From f0e40c5b3e6af6974c44077ccd0cdc01a2172f30 Mon Sep 17 00:00:00 2001
-From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
-Date: Wed, 19 Aug 2026 16:29:19 -0400
-Subject: [PATCH 10/10] unit/test-util: Cover strtoutf8 with the str2utf8 tests
-
-strtoutf8() and str2utf8() are the two ways of dealing with a name that
-is not valid UTF-8, so run them over the same inputs and keep the two
-expected results side by side, which documents how they differ:
-strtoutf8() truncates at the first ill-formed sequence and leaves the
-whitespace alone, str2utf8() replaces the ill-formed sequences and
-strips.
-
-The expected results were checked against Python, taking the longest
-prefix that decodes as strict UTF-8, over every one, two and three byte
-sequence, 16646655 of them, with no mismatch.
-
-Assisted-by: Claude:claude-opus-5
----
- unit/test-util.c | 94 +++++++++++++++++++++++++++++++++---------------
- 1 file changed, 66 insertions(+), 28 deletions(-)
-
-diff --git a/unit/test-util.c b/unit/test-util.c
-index f0b1bb7994fb..e605d17b6b56 100644
---- a/unit/test-util.c
-+++ b/unit/test-util.c
-@@ -83,65 +83,101 @@ static void test_min_max(const void *data)
- tester_test_passed();
- }
-
--struct str2utf8_data {
-+struct utf8_data {
- const char *input; /* Not NUL terminated, len bytes are used */
- size_t len;
-- const char *expected;
-+ const char *str2utf8; /* Ill-formed sequences replaced, stripped */
-+ const char *strtoutf8; /* Truncated at the first ill-formed one */
- };
-
- #define FFFD "\xef\xbf\xbd" /* U+FFFD REPLACEMENT CHARACTER */
-
--static const struct str2utf8_data str2utf8_tests[] = {
-+static const struct utf8_data utf8_tests[] = {
- /* Nothing to do */
-- { "", 0, "" },
-- { "Pixel 7", 7, "Pixel 7" },
-+ { "", 0, "", "" },
-+ { "Pixel 7", 7, "Pixel 7", "Pixel 7" },
- /* Well-formed multi-byte sequences are kept as they are */
-- { "\xe2\x82\xac 5", 5, "\xe2\x82\xac 5" }, /* U+20AC */
-- { "\xf0\x9f\x94\x8a", 4, "\xf0\x9f\x94\x8a" }, /* U+1F50A */
-+ { "\xe2\x82\xac 5", 5, "\xe2\x82\xac 5", /* U+20AC */
-+ "\xe2\x82\xac 5" },
-+ { "\xf0\x9f\x94\x8a", 4, "\xf0\x9f\x94\x8a", /* U+1F50A */
-+ "\xf0\x9f\x94\x8a" },
- /* Leading and trailing whitespace is removed */
-- { " spaced ", 10, "spaced" },
-- { "\t\r\nname\n\r\t", 10, "name" },
-- { " ", 3, "" },
-+ { " spaced ", 10, "spaced", " spaced " },
-+ { "\t\r\nname\n\r\t", 10, "name", "\t\r\nname\n\r\t" },
-+ { " ", 3, "", " " },
- /* The name is not NUL terminated, only len bytes are used */
-- { "truncated", 4, "trun" },
-+ { "truncated", 4, "trun", "trun" },
- /* A byte that can never appear in UTF-8 */
-- { "ab\xff""cd", 5, "ab" FFFD "cd" },
-+ { "ab\xff""cd", 5, "ab" FFFD "cd", "ab" },
- /* A continuation byte cannot start a sequence */
-- { "ab\x80""cd", 5, "ab" FFFD "cd" },
-+ { "ab\x80""cd", 5, "ab" FFFD "cd", "ab" },
- /* One U+FFFD per maximal subpart, not per byte */
-- { "ab\xe2\x82""cd", 6, "ab" FFFD "cd" },
-+ { "ab\xe2\x82""cd", 6, "ab" FFFD "cd", "ab" },
- /* A sequence cut short by len is still one maximal subpart */
-- { "ab\xe2\x82\xac", 4, "ab" FFFD },
-+ { "ab\xe2\x82\xac", 4, "ab" FFFD, "ab" },
- /* Latin-1 text is not valid UTF-8 */
-- { "caf\xe9", 4, "caf" FFFD },
-+ { "caf\xe9", 4, "caf" FFFD, "caf" },
- /* Overlong encodings are rejected, C0 and C1 are never valid */
-- { "\xc0\x80", 2, FFFD FFFD },
-- { "\xc0\xaf", 2, FFFD FFFD },
-+ { "\xc0\x80", 2, FFFD FFFD, "" },
-+ { "\xc0\xaf", 2, FFFD FFFD, "" },
- /* UTF-16 surrogates have no UTF-8 encoding */
-- { "\xed\xa0\x80", 3, FFFD FFFD FFFD },
-+ { "\xed\xa0\x80", 3, FFFD FFFD FFFD, "" },
- /* U+10FFFF is the last code point, F5 to FF are out of range */
-- { "\xf4\x90\x80\x80", 4, FFFD FFFD FFFD FFFD },
-- { "\xf5\x80\x80\x80", 4, FFFD FFFD FFFD FFFD },
-+ { "\xf4\x90\x80\x80", 4, FFFD FFFD FFFD FFFD, "" },
-+ { "\xf5\x80\x80\x80", 4, FFFD FFFD FFFD FFFD, "" },
- /* The last code point itself is fine */
-- { "\xf4\x8f\xbf\xbf", 4, "\xf4\x8f\xbf\xbf" },
-+ { "\xf4\x8f\xbf\xbf", 4, "\xf4\x8f\xbf\xbf",
-+ "\xf4\x8f\xbf\xbf" },
- /* Replacement and stripping combined */
-- { " \xff ", 3, FFFD },
-+ { " \xff ", 3, FFFD, " " },
- };
-
- static void test_str2utf8(const void *data)
- {
- size_t i;
-
-- for (i = 0; i < sizeof(str2utf8_tests) /
-- sizeof(str2utf8_tests[0]); i++) {
-- const struct str2utf8_data *test = &str2utf8_tests[i];
-+ for (i = 0; i < sizeof(utf8_tests) / sizeof(utf8_tests[0]); i++) {
-+ const struct utf8_data *test = &utf8_tests[i];
- char *str = str2utf8((const uint8_t *) test->input,
- test->len);
-
- assert(str);
-- if (strcmp(str, test->expected)) {
-+ if (strcmp(str, test->str2utf8)) {
- printf("test %zu: expected \"%s\", got \"%s\"\n", i,
-- test->expected, str);
-+ test->str2utf8, str);
-+ free(str);
-+ tester_test_failed();
-+ return;
-+ }
-+
-+ /* The result is always well-formed UTF-8 */
-+ assert(strisutf8(str, strlen(str)));
-+
-+ free(str);
-+ }
-+
-+ tester_test_passed();
-+}
-+
-+static void test_strtoutf8(const void *data)
-+{
-+ size_t i;
-+
-+ for (i = 0; i < sizeof(utf8_tests) / sizeof(utf8_tests[0]); i++) {
-+ const struct utf8_data *test = &utf8_tests[i];
-+ char *str;
-+
-+ /* strtoutf8() works in place, so it needs a writable copy */
-+ str = malloc(test->len + 1);
-+ assert(str);
-+ memcpy(str, test->input, test->len);
-+ str[test->len] = '\0';
-+
-+ assert(strtoutf8(str, test->len) == str);
-+
-+ if (strcmp(str, test->strtoutf8)) {
-+ printf("test %zu: expected \"%s\", got \"%s\"\n", i,
-+ test->strtoutf8, str);
- free(str);
- tester_test_failed();
- return;
-@@ -178,6 +214,8 @@ int main(int argc, char *argv[])
- test_str2utf8, NULL);
- tester_add("/util/str2utf8_null", NULL, NULL,
- test_str2utf8_null, NULL);
-+ tester_add("/util/strtoutf8", NULL, NULL,
-+ test_strtoutf8, NULL);
-
- return tester_run();
- }
---
-2.55.0
-
diff --git a/sdp-xml-type-confusion.patch b/sdp-xml-type-confusion.patch
deleted file mode 100644
index d57d172..0000000
--- a/sdp-xml-type-confusion.patch
+++ /dev/null
@@ -1,1172 +0,0 @@
-From e55ff8818f722dfbefc62b9646f69bad19014fdd Mon Sep 17 00:00:00 2001
-From: Bastien Nocera <hadess@hadess.net>
-Date: Wed, 12 Aug 2026 10:01:32 +0200
-Subject: [PATCH 1/9] unit: Add test for sdp_xml_parse_record()
-
-This adds 2 example XML files from other repositories, under a fair use
-license exception.
-Reported-by: Aisle Research
-Reported-by: Aisle Research
----
- Makefile.am | 13 +++
- unit/sdp-xml/Bluetooth_HID-sdp_record.xml | 123 ++++++++++++++++++++++
- unit/sdp-xml/qt-SerialPortSDPRecord.xml | 57 ++++++++++
- unit/test-sdp-xml.c | 85 +++++++++++++++
- 4 files changed, 278 insertions(+)
- create mode 100644 unit/sdp-xml/Bluetooth_HID-sdp_record.xml
- create mode 100644 unit/sdp-xml/qt-SerialPortSDPRecord.xml
- create mode 100644 unit/test-sdp-xml.c
-
-diff --git a/Makefile.am b/Makefile.am
-index 19c468d3a504..1ecb5e1ddaec 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -637,6 +637,19 @@ unit_test_sdp_SOURCES = unit/test-sdp.c \
- unit_test_sdp_LDADD = lib/libbluetooth-internal.la \
- src/libshared-glib.la $(GLIB_LIBS)
-
-+unit_tests += unit/test-sdp-xml
-+
-+unit_test_sdp_xml_SOURCES = unit/test-sdp-xml.c \
-+ src/sdp-xml.c src/sdp-xml.h \
-+ src/log.h src/log.c
-+unit_test_sdp_xml_LDADD = lib/libbluetooth-internal.la \
-+ src/libshared-glib.la $(GLIB_LIBS)
-+unit_test_sdp_xml_CFLAGS = $(AM_CFLAGS) $(GLIB_CFLAGS) -DTOP_SRCDIR=\""$(srcdir)"\"
-+unit_test_sdp_xml_CPPFLAGS = -I$(srcdir)/lib
-+
-+EXTRA_DIST += unit/sdp-xml/Bluetooth_HID-sdp_record.xml \
-+ unit/sdp-xml/qt-SerialPortSDPRecord.xml
-+
- unit_tests += unit/test-avdtp
-
- unit_test_avdtp_SOURCES = unit/test-avdtp.c \
-diff --git a/unit/sdp-xml/Bluetooth_HID-sdp_record.xml b/unit/sdp-xml/Bluetooth_HID-sdp_record.xml
-new file mode 100644
-index 000000000000..687b0b15b520
---- /dev/null
-+++ b/unit/sdp-xml/Bluetooth_HID-sdp_record.xml
-@@ -0,0 +1,123 @@
-+<?xml version="1.0" encoding="UTF-8" ?>
-+
-+<!--
-+
-+ From: https://github.com/AnesBenmerzoug/Bluetooth_HID/blob/master/sdp_record.xml
-+ A description of these fields can be found in the following links:
-+ http://www.bluecove.org/bluecove/apidocs/javax/bluetooth/ServiceRecord.html
-+ https://www.bluetooth.com/specifications/assigned-numbers/service-discovery
-+
-+ -->
-+
-+<record>
-+ <attribute id="0x0001"> <!-- Service Class ID List -->
-+ <sequence>
-+ <uuid value="0x1124" /> <!-- Human Interface Device -->
-+ </sequence>
-+ </attribute>
-+ <attribute id="0x0004"> <!-- Protocol Descriptor List -->
-+ <sequence>
-+ <sequence>
-+ <uuid value="0x0100" /> <!-- L2CAP -->
-+ <uint16 value="0x0011" /> <!-- HIDP -->
-+ </sequence>
-+ <sequence>
-+ <uuid value="0x0011" /> <!-- HIDP -->
-+ </sequence>
-+ </sequence>
-+ </attribute>
-+ <attribute id="0x0005"> <!-- Browse Group List -->
-+ <sequence>
-+ <uuid value="0x1002" />
-+ </sequence>
-+ </attribute>
-+ <attribute id="0x0006"> <!-- Language Based Attribute ID List -->
-+ <sequence>
-+ <uint16 value="0x656e" /> <!-- code_ISO639 -->
-+ <uint16 value="0x006a" /> <!-- encoding -->
-+ <uint16 value="0x0100" /> <!-- base_offset -->
-+ </sequence>
-+ </attribute>
-+ <attribute id="0x0009"> <!-- Bluetooth Profile Descriptor List -->
-+ <sequence>
-+ <sequence>
-+ <uuid value="0x1124" /> <!-- Human Interface Device -->
-+ <uint16 value="0x0100" /> <!-- L2CAP -->
-+ </sequence>
-+ </sequence>
-+ </attribute>
-+ <attribute id="0x000d"> <!-- Additional Protocol Descriptor Lists -->
-+ <sequence>
-+ <sequence>
-+ <sequence>
-+ <uuid value="0x0100" /> <!-- L2CAP -->
-+ <uint16 value="0x0013" />
-+ </sequence>
-+ <sequence>
-+ <uuid value="0x0011" /> <!-- HIDP -->
-+ </sequence>
-+ </sequence>
-+ </sequence>
-+ </attribute>
-+ <attribute id="0x0100">
-+ <text value="Bluetooth_Keyboard/Mouse" />
-+ </attribute>
-+ <attribute id="0x0101">
-+ <text value="USB > BT Keyboard/Mouse" />
-+ </attribute>
-+ <attribute id="0x0102">
-+ <text value="Raspberry Pi 3" />
-+ </attribute>
-+ <attribute id="0x0200">
-+ <uint16 value="0x0100" />
-+ </attribute>
-+ <attribute id="0x0201">
-+ <uint16 value="0x0111" />
-+ </attribute>
-+ <attribute id="0x0202">
-+ <uint8 value="0x40" />
-+ </attribute>
-+ <attribute id="0x0203">
-+ <uint8 value="0x00" />
-+ </attribute>
-+ <attribute id="0x0204">
-+ <boolean value="false" />
-+ </attribute>
-+ <attribute id="0x0205">
-+ <boolean value="false" />
-+ </attribute>
-+ <attribute id="0x0206">
-+ <sequence>
-+ <sequence>
-+ <uint8 value="0x22" />
-+ <text encoding="hex" value="05010906A1018501A100050719E029E71500250175019508810295017508810195087508150025650507190029658100C0C005010902A10185020901A1000509190129031500250175019503810275059501810105010930093109381581257F750895038106C0C0"/>
-+ </sequence>
-+ </sequence>
-+ </attribute>
-+ <attribute id="0x0207">
-+ <sequence>
-+ <sequence>
-+ <uint16 value="0x0409" />
-+ <uint16 value="0x0100" />
-+ </sequence>
-+ </sequence>
-+ </attribute>
-+ <attribute id="0x020b">
-+ <uint16 value="0x0100" />
-+ </attribute>
-+ <attribute id="0x020c">
-+ <uint16 value="0x0c80" />
-+ </attribute>
-+ <attribute id="0x020d">
-+ <boolean value="true" />
-+ </attribute>
-+ <attribute id="0x020e">
-+ <boolean value="false" />
-+ </attribute>
-+ <attribute id="0x020f">
-+ <uint16 value="0x0640" />
-+ </attribute>
-+ <attribute id="0x0210">
-+ <uint16 value="0x0320" />
-+ </attribute>
-+</record>
-diff --git a/unit/sdp-xml/qt-SerialPortSDPRecord.xml b/unit/sdp-xml/qt-SerialPortSDPRecord.xml
-new file mode 100644
-index 000000000000..1f62ccf11a41
---- /dev/null
-+++ b/unit/sdp-xml/qt-SerialPortSDPRecord.xml
-@@ -0,0 +1,57 @@
-+<?xml version="1.0" encoding="UTF-8" ?>
-+
-+<!--
-+ This is an XML file describing an SDP service record for a Serial Port
-+ service.
-+
-+ You can use the linux sdptool command to create a file like this for
-+ publishing your Bluetooth services. See the "Creating a Bluetooth service"
-+ tutorial for more details.
-+
-+ From: https://radekp.github.io/qtmoko/api/bluetooth-bluetoothservice-serialportsdprecord-xml.html
-+-->
-+
-+ <record>
-+ <attribute id="0x0001">
-+ <sequence>
-+ <uuid value="0x1101" />
-+ </sequence>
-+ </attribute>
-+ <attribute id="0x0004">
-+ <sequence>
-+ <sequence>
-+ <uuid value="0x0100" />
-+ </sequence>
-+ <sequence>
-+ <uuid value="0x0003" />
-+ <uint8 value="0x05" />
-+ </sequence>
-+ </sequence>
-+ </attribute>
-+ <attribute id="0x0005">
-+ <sequence>
-+ <uuid value="0x1002" />
-+ </sequence>
-+ </attribute>
-+ <attribute id="0x0006">
-+ <sequence>
-+ <uint16 value="0x656e" />
-+ <uint16 value="0x006a" />
-+ <uint16 value="0x0100" />
-+ </sequence>
-+ </attribute>
-+ <attribute id="0x0009">
-+ <sequence>
-+ <sequence>
-+ <uuid value="0x1101" />
-+ <uint16 value="0x0100" />
-+ </sequence>
-+ </sequence>
-+ </attribute>
-+ <attribute id="0x0100">
-+ <text value="Serial Port" />
-+ </attribute>
-+ <attribute id="0x0101">
-+ <text value="COM Port" />
-+ </attribute>
-+ </record>
-diff --git a/unit/test-sdp-xml.c b/unit/test-sdp-xml.c
-new file mode 100644
-index 000000000000..9af2f8c870f6
---- /dev/null
-+++ b/unit/test-sdp-xml.c
-@@ -0,0 +1,85 @@
-+// SPDX-License-Identifier: GPL-2.0-or-later
-+/*
-+ *
-+ * BlueZ - Bluetooth protocol stack for Linux
-+ *
-+ * Copyright (C) 2026 Intel Corporation. All rights reserved.
-+ *
-+ *
-+ */
-+
-+#ifdef HAVE_CONFIG_H
-+#include <config.h>
-+#endif
-+
-+#include <glib.h>
-+
-+#include "bluetooth/sdp.h"
-+#include "bluetooth/sdp_lib.h"
-+
-+#include "src/shared/util.h"
-+#include "src/shared/tester.h"
-+#include "src/log.h"
-+#include "src/sdp-xml.h"
-+
-+struct test_data {
-+ GString *s;
-+ char *filename;
-+ gboolean expected_result;
-+};
-+
-+static void parse_xml(gconstpointer data, gsize len, gboolean expected_result)
-+{
-+ sdp_record_t *rec = NULL;
-+ gboolean ret;
-+
-+ rec = sdp_xml_parse_record(data, len);
-+ ret = rec ? TRUE : FALSE;
-+ if (ret == expected_result)
-+ tester_test_passed();
-+ else
-+ tester_test_failed();
-+ if (rec)
-+ sdp_record_free(rec);
-+}
-+
-+static void parse_xml_for_filename(gconstpointer data)
-+{
-+ struct test_data *t = (struct test_data *) data;
-+ char *path = NULL;
-+ GError *error = NULL;
-+ char *contents = NULL;
-+ gsize len;
-+
-+ path = g_build_filename(TOP_SRCDIR, "unit", "sdp-xml",
-+ t->filename, NULL);
-+ if (!g_file_get_contents(path, &contents, &len, &error)) {
-+ g_warning("Can't load file '%s': %s", path, error->message);
-+ g_free(path);
-+ g_error_free(error);
-+ tester_test_failed();
-+ return;
-+ }
-+ parse_xml(contents, len, t->expected_result);
-+ g_free(contents);
-+ g_free(path);
-+}
-+
-+#define DEFINE_TEST(fname, res) { \
-+ data.expected_result = res; \
-+ data.filename = fname; \
-+ tester_add("/" fname, &data, NULL, \
-+ parse_xml_for_filename, NULL); \
-+ }
-+
-+int main(int argc, char *argv[])
-+{
-+ struct test_data data;
-+
-+ tester_init(&argc, &argv);
-+
-+ DEFINE_TEST("Bluetooth_HID-sdp_record.xml", TRUE);
-+ DEFINE_TEST("qt-SerialPortSDPRecord.xml", TRUE);
-+
-+ return tester_run();
-+}
---
-2.55.0
-
-
-From 985e643d78b09afc81d606bc0a08581fc05b1b15 Mon Sep 17 00:00:00 2001
-From: Bastien Nocera <hadess@hadess.net>
-Date: Wed, 12 Aug 2026 10:01:33 +0200
-Subject: [PATCH 2/9] sdp-xml: Fix crash caused by type confusion when parsing
- crafted SDP XML
-
-When element_end() processes </attribute>, it frees ctx_data->stack_head
-and clears the stack even if parsing is still nested inside a parent
-container.
-
-If a crafted ServiceRecord places a nested <attribute> inside <sequence>,
-a later sibling scalar element such as <uint64> can become the new stack
-head. When the closing </sequence> is then processed, compute_seq_size()
-is reached without first validating that the current node is actually
-a sequence.
-
-sdp_data_t.val stores both scalar members such as uint64 and the
-dataseq pointer in the same union. As a result, attacker-controlled
-scalar data can be reinterpreted as a linked-list pointer and traversed
-until bluetoothd crashes.
-
-See https://github.com/bluez/bluez/security/advisories/GHSA-7mmr-gwqx-vc34
-
-Reported-by: Aisle Research
-Co-authored-by: Aisle Research
----
- src/sdp-xml.c | 23 ++++++++++++++++++++++-
- 1 file changed, 22 insertions(+), 1 deletion(-)
-
-diff --git a/src/sdp-xml.c b/src/sdp-xml.c
-index e5b30e88505f..c8f9ed013b29 100644
---- a/src/sdp-xml.c
-+++ b/src/sdp-xml.c
-@@ -529,7 +529,9 @@ static void element_end(GMarkupParseContext *context,
- return;
-
- if (!strcmp(element_name, "attribute")) {
-- if (ctx_data->stack_head && ctx_data->stack_head->data) {
-+ /* Attributes are expected at top-level record scope. */
-+ if (ctx_data->stack_head && ctx_data->stack_head->data &&
-+ ctx_data->stack_head->next == NULL) {
- int ret = sdp_attr_add(ctx_data->record, ctx_data->attr_id,
- ctx_data->stack_head->data);
- if (ret == -1)
-@@ -539,6 +541,11 @@ static void element_end(GMarkupParseContext *context,
- ctx_data->stack_head->data = NULL;
- sdp_xml_data_free(ctx_data->stack_head);
- ctx_data->stack_head = NULL;
-+ } else if (ctx_data->stack_head && ctx_data->stack_head->next) {
-+ g_set_error(err, G_MARKUP_ERROR,
-+ G_MARKUP_ERROR_INVALID_CONTENT,
-+ "Nested <attribute> is invalid");
-+ return;
- } else {
- DBG("No data for attribute 0x%04x", ctx_data->attr_id);
- }
-@@ -558,6 +565,13 @@ static void element_end(GMarkupParseContext *context,
- }
-
- if (!strcmp(element_name, "sequence")) {
-+ if (!SDP_IS_SEQ(ctx_data->stack_head->data->dtd)) {
-+ g_set_error(err, G_MARKUP_ERROR,
-+ G_MARKUP_ERROR_INVALID_CONTENT,
-+ "Mismatched </sequence> close");
-+ return;
-+ }
-+
- ctx_data->stack_head->data->unitSize = compute_seq_size(ctx_data->stack_head->data);
-
- if (ctx_data->stack_head->data->unitSize > USHRT_MAX) {
-@@ -570,6 +584,13 @@ static void element_end(GMarkupParseContext *context,
- ctx_data->stack_head->data->unitSize += sizeof(uint8_t);
- }
- } else if (!strcmp(element_name, "alternate")) {
-+ if (!SDP_IS_ALT(ctx_data->stack_head->data->dtd)) {
-+ g_set_error(err, G_MARKUP_ERROR,
-+ G_MARKUP_ERROR_INVALID_CONTENT,
-+ "Mismatched </alternate> close");
-+ return;
-+ }
-+
- ctx_data->stack_head->data->unitSize = compute_seq_size(ctx_data->stack_head->data);
-
- if (ctx_data->stack_head->data->unitSize > USHRT_MAX) {
---
-2.55.0
-
-
-From a92683ce81ab92cd7dffd3350284579fbe0d4c30 Mon Sep 17 00:00:00 2001
-From: Bastien Nocera <hadess@hadess.net>
-Date: Wed, 12 Aug 2026 10:01:34 +0200
-Subject: [PATCH 3/9] unit: Add test for sdp-xml type-confusion bug
-
-See https://github.com/bluez/bluez/security/advisories/GHSA-7mmr-gwqx-vc34
-
-Co-authored-by: Aisle Research
----
- Makefile.am | 1 +
- unit/sdp-xml/compute-seq-size-type-confusion.xml | 9 +++++++++
- unit/test-sdp-xml.c | 2 ++
- 3 files changed, 12 insertions(+)
- create mode 100644 unit/sdp-xml/compute-seq-size-type-confusion.xml
-
-diff --git a/Makefile.am b/Makefile.am
-index 1ecb5e1ddaec..1d5f02b0d362 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -648,6 +648,7 @@ unit_test_sdp_xml_CFLAGS = $(AM_CFLAGS) $(GLIB_CFLAGS) -DTOP_SRCDIR=\""$(srcdir)
- unit_test_sdp_xml_CPPFLAGS = -I$(srcdir)/lib
-
- EXTRA_DIST += unit/sdp-xml/Bluetooth_HID-sdp_record.xml \
-+ unit/sdp-xml/compute-seq-size-type-confusion.xml \
- unit/sdp-xml/qt-SerialPortSDPRecord.xml
-
- unit_tests += unit/test-avdtp
-diff --git a/unit/sdp-xml/compute-seq-size-type-confusion.xml b/unit/sdp-xml/compute-seq-size-type-confusion.xml
-new file mode 100644
-index 000000000000..41d6478af276
---- /dev/null
-+++ b/unit/sdp-xml/compute-seq-size-type-confusion.xml
-@@ -0,0 +1,9 @@
-+<?xml version="1.0" encoding="UTF-8" ?>
-+<record>
-+<attribute id="0x0001">
-+<sequence>
-+<attribute id="0x0002"><uint8 value="0x01"/></attribute>
-+<uint64 value="0x4141414141414141"/>
-+</sequence>
-+</attribute>
-+</record>
-diff --git a/unit/test-sdp-xml.c b/unit/test-sdp-xml.c
-index 9af2f8c870f6..c8288ca10b8b 100644
---- a/unit/test-sdp-xml.c
-+++ b/unit/test-sdp-xml.c
-@@ -80,6 +80,8 @@ int main(int argc, char *argv[])
-
- DEFINE_TEST("Bluetooth_HID-sdp_record.xml", TRUE);
- DEFINE_TEST("qt-SerialPortSDPRecord.xml", TRUE);
-+ /* From https://github.com/bluez/bluez/security/advisories/GHSA-7mmr-gwqx-vc34 */
-+ DEFINE_TEST("compute-seq-size-type-confusion.xml", FALSE);
-
- return tester_run();
- }
---
-2.55.0
-
-
-From ae6c543e892f1fc55d16584ce1ef02e1969352af Mon Sep 17 00:00:00 2001
-From: Bastien Nocera <hadess@hadess.net>
-Date: Wed, 12 Aug 2026 10:01:35 +0200
-Subject: [PATCH 4/9] sdp-xml: Fix memory leak when adding duplicate attributes
-
-When sdp_attr_add() fails because an attribute is duplicated, don't
-unset its pointer in the parsing context data. As the attribute wasn't
-added to the record, the ownership of the attribute didn't get passed
-to the record either.
-
-Don't set the pointer to NULL so it gets freed when cleaning up the
-context.
-
-Fixes those 2 ASan warnings:
-Direct leak of 48 byte(s) in 1 object(s) allocated from:
- #0 0x7f896a8ef24f in calloc (/lib64/libasan.so.8+0xef24f) (BuildId: 5395ec74f54d9ec7bf97c06583dd39a96c230822)
- #1 0x562042b747f2 in sdp_data_alloc_with_length lib/bluetooth/sdp.c:350
-
-Indirect leak of 2 byte(s) in 1 object(s) allocated from:
- #0 0x7f896a8ef24f in calloc (/lib64/libasan.so.8+0xef24f) (BuildId: 5395ec74f54d9ec7bf97c06583dd39a96c230822)
- #1 0x562042b74bd7 in sdp_data_alloc_with_length lib/bluetooth/sdp.c:425
-
-See https://github.com/bluez/bluez/security/advisories/GHSA-75v6-6q44-57hc
-
-Reported-by: Aisle Research
-Co-authored-by: Aisle Research
----
- src/sdp-xml.c | 5 ++++-
- 1 file changed, 4 insertions(+), 1 deletion(-)
-
-diff --git a/src/sdp-xml.c b/src/sdp-xml.c
-index c8f9ed013b29..816d19611f8b 100644
---- a/src/sdp-xml.c
-+++ b/src/sdp-xml.c
-@@ -537,8 +537,11 @@ static void element_end(GMarkupParseContext *context,
- if (ret == -1)
- DBG("Could not add attribute 0x%04x",
- ctx_data->attr_id);
-+ else {
-+ /* ownership transferred to record */
-+ ctx_data->stack_head->data = NULL;
-+ }
-
-- ctx_data->stack_head->data = NULL;
- sdp_xml_data_free(ctx_data->stack_head);
- ctx_data->stack_head = NULL;
- } else if (ctx_data->stack_head && ctx_data->stack_head->next) {
---
-2.55.0
-
-
-From ab91b45282297e052b2e3be22c198265d19ac097 Mon Sep 17 00:00:00 2001
-From: Bastien Nocera <hadess@hadess.net>
-Date: Wed, 12 Aug 2026 10:01:36 +0200
-Subject: [PATCH 5/9] unit: Add test for sdp-xml duplicate attribute bug
-
-See https://github.com/bluez/bluez/security/advisories/GHSA-75v6-6q44-57hc
-
-Co-authored-by: Aisle Research
----
- Makefile.am | 1 +
- unit/sdp-xml/duplicate-attribute.xml | 4 ++++
- unit/test-sdp-xml.c | 2 ++
- 3 files changed, 7 insertions(+)
- create mode 100644 unit/sdp-xml/duplicate-attribute.xml
-
-diff --git a/Makefile.am b/Makefile.am
-index 1d5f02b0d362..3c6cf92ab403 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -649,6 +649,7 @@ unit_test_sdp_xml_CPPFLAGS = -I$(srcdir)/lib
-
- EXTRA_DIST += unit/sdp-xml/Bluetooth_HID-sdp_record.xml \
- unit/sdp-xml/compute-seq-size-type-confusion.xml \
-+ unit/sdp-xml/duplicate-attribute.xml \
- unit/sdp-xml/qt-SerialPortSDPRecord.xml
-
- unit_tests += unit/test-avdtp
-diff --git a/unit/sdp-xml/duplicate-attribute.xml b/unit/sdp-xml/duplicate-attribute.xml
-new file mode 100644
-index 000000000000..a30bc59d94ff
---- /dev/null
-+++ b/unit/sdp-xml/duplicate-attribute.xml
-@@ -0,0 +1,4 @@
-+<record>
-+<attribute id="0x0001"><text value="A"/></attribute>
-+<attribute id="0x0001"><text value="B"/></attribute>
-+</record>
-diff --git a/unit/test-sdp-xml.c b/unit/test-sdp-xml.c
-index c8288ca10b8b..9bf35235423b 100644
---- a/unit/test-sdp-xml.c
-+++ b/unit/test-sdp-xml.c
-@@ -82,6 +82,8 @@ int main(int argc, char *argv[])
- DEFINE_TEST("qt-SerialPortSDPRecord.xml", TRUE);
- /* From https://github.com/bluez/bluez/security/advisories/GHSA-7mmr-gwqx-vc34 */
- DEFINE_TEST("compute-seq-size-type-confusion.xml", FALSE);
-+ /* From https://github.com/bluez/bluez/security/advisories/GHSA-75v6-6q44-57hc */
-+ DEFINE_TEST("duplicate-attribute.xml", TRUE);
-
- return tester_run();
- }
---
-2.55.0
-
-
-From 308e3536688c0011e11b2d9bfdc94cb38e52c060 Mon Sep 17 00:00:00 2001
-From: Bastien Nocera <hadess@hadess.net>
-Date: Wed, 12 Aug 2026 10:01:37 +0200
-Subject: [PATCH 6/9] sdp-xml: Optimise parsing large sequences
-
-SDP sequences are stored as single-linked lists, so appending members
-to a sequence requires finding the tail of the list before the
-insertion.
-
-Finding the tail of the list always starts at the beginning of the list,
-so takes longer and longer as the list grows bigger.
-
-Keep track of the tail to avoid that problem. This cuts down the
-sequence_on_squared() test from around 3 to 4 seconds to less than
-0.1 seconds.
----
- src/sdp-xml.c | 18 +++++++++++++++---
- 1 file changed, 15 insertions(+), 3 deletions(-)
-
-diff --git a/src/sdp-xml.c b/src/sdp-xml.c
-index 816d19611f8b..fb8417b3fc60 100644
---- a/src/sdp-xml.c
-+++ b/src/sdp-xml.c
-@@ -44,6 +44,7 @@ struct sdp_xml_data {
- char type; /* 0 = Text or Hexadecimal */
- char *name; /* Name, optional in the dtd */
- /* TODO: What is it used for? */
-+ sdp_data_t *tail; /* Tail for O(1) dataseq append */
- };
-
- struct context_data {
-@@ -609,6 +610,7 @@ static void element_end(GMarkupParseContext *context,
-
- if (ctx_data->stack_head->next && ctx_data->stack_head->data &&
- ctx_data->stack_head->next->data) {
-+ sdp_data_t *tail;
- switch (ctx_data->stack_head->next->data->dtd) {
- case SDP_SEQ8:
- case SDP_SEQ16:
-@@ -616,10 +618,20 @@ static void element_end(GMarkupParseContext *context,
- case SDP_ALT8:
- case SDP_ALT16:
- case SDP_ALT32:
-- ctx_data->stack_head->next->data->val.dataseq =
-- sdp_seq_append(ctx_data->stack_head->next->data->val.dataseq,
-- ctx_data->stack_head->data);
-+ tail = ctx_data->stack_head->next->data->val.dataseq ?
-+ ctx_data->stack_head->next->tail : NULL;
-+ if (tail) {
-+ sdp_seq_append(tail,
-+ ctx_data->stack_head->data);
-+ } else {
-+ ctx_data->stack_head->next->data->val.dataseq =
-+ sdp_seq_append(NULL,
-+ ctx_data->stack_head->data);
-+ }
-+ ctx_data->stack_head->next->tail =
-+ ctx_data->stack_head->data;
- ctx_data->stack_head->data = NULL;
-+ ctx_data->stack_head->tail = NULL;
- break;
- }
-
---
-2.55.0
-
-
-From 95fa5735562e9f020c174199f19016cbb1c09ff2 Mon Sep 17 00:00:00 2001
-From: Bastien Nocera <hadess@hadess.net>
-Date: Wed, 12 Aug 2026 10:01:38 +0200
-Subject: [PATCH 7/9] unit: Add test for slow element_end() append
-
-This uses 40k iterations as this takes a visible amount of time on a
-pretty fast desktop machine (3-4 secs on an i9 9900k).
-
-See: https://github.com/bluez/bluez/security/advisories/GHSA-4p57-mrcv-r2jc
----
- unit/test-sdp-xml.c | 46 +++++++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 46 insertions(+)
-
-diff --git a/unit/test-sdp-xml.c b/unit/test-sdp-xml.c
-index 9bf35235423b..5aa98334eabc 100644
---- a/unit/test-sdp-xml.c
-+++ b/unit/test-sdp-xml.c
-@@ -65,6 +65,47 @@ static void parse_xml_for_filename(gconstpointer data)
- g_free(path);
- }
-
-+#define XML_START \
-+ "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" \
-+ "<record>\n" \
-+ " <attribute id=\"0x0004\">\n" \
-+ " <sequence>"
-+
-+#define XML_VALUE \
-+ " <uint8 value=\"0x01\" />\n"
-+
-+#define XML_END \
-+ " </sequence>\n" \
-+ " </attribute>\n" \
-+ "</record>"
-+
-+static void sequence_on_squared(gconstpointer data)
-+{
-+ struct test_data *t = (struct test_data *) data;
-+ parse_xml(t->s->str, t->s->len, TRUE);
-+}
-+
-+static void sequence_on_squared_setup(gconstpointer data)
-+{
-+ struct test_data *t = (struct test_data *) data;
-+ guint i;
-+
-+ t->s = g_string_new(XML_START);
-+ for (i = 0; i < 40000; i++)
-+ t->s = g_string_append(t->s, XML_VALUE);
-+ t->s = g_string_append(t->s, XML_END);
-+
-+ tester_setup_complete();
-+}
-+
-+static void sequence_on_squared_teardown(gconstpointer data)
-+{
-+ struct test_data *t = (struct test_data *) data;
-+
-+ g_string_free(t->s, TRUE);
-+ tester_teardown_complete();
-+}
-+
- #define DEFINE_TEST(fname, res) { \
- data.expected_result = res; \
- data.filename = fname; \
-@@ -85,5 +126,10 @@ int main(int argc, char *argv[])
- /* From https://github.com/bluez/bluez/security/advisories/GHSA-75v6-6q44-57hc */
- DEFINE_TEST("duplicate-attribute.xml", TRUE);
-
-+ tester_add("/sequence_on_squared", &data,
-+ sequence_on_squared_setup,
-+ sequence_on_squared,
-+ sequence_on_squared_teardown);
-+
- return tester_run();
- }
---
-2.55.0
-
-
-From e9caa7d3e2bf746203348e50654e7758fc84285d Mon Sep 17 00:00:00 2001
-From: Bastien Nocera <hadess@hadess.net>
-Date: Wed, 12 Aug 2026 10:01:39 +0200
-Subject: [PATCH 8/9] sdp-xml: Fix stack overflow when converting large
- sequences to XML
-
-Don't make convert_raw_data_to_xml() call itself recursively X times
-if there are X elements in a sequence.
-
-AddressSanitizer:DEADLYSIGNAL
-=================================================================
-==1684518==ERROR: AddressSanitizer: stack-overflow on address 0x7fff7fb40d98 (pc 0x7fbe1ee95c2b bp 0x7fff7fb41610 sp 0x7fff7fb40d70 T0)
- #0 0x7fbe1ee95c2b in printf_common(void*, char const*, __va_list_tag*) (/lib64/libasan.so.8+0x95c2b) (BuildId: 5395ec74f54d9ec7bf97c06583dd39a96c230822)
- #1 0x7fbe1eeb72d6 in vsnprintf (/lib64/libasan.so.8+0xb72d6) (BuildId: 5395ec74f54d9ec7bf97c06583dd39a96c230822)
- #2 0x7fbe1eeb94f4 in snprintf (/lib64/libasan.so.8+0xb94f4) (BuildId: 5395ec74f54d9ec7bf97c06583dd39a96c230822)
- #3 0x000000401fc2 in convert_raw_data_to_xml ../../../../Projects/jhbuild/bluez/src/sdp-xml.c:709
- #4 0x000000401c4f in convert_raw_data_to_xml ../../../../Projects/jhbuild/bluez/src/sdp-xml.c:994
-[...]
- #246 0x000000401c4f in convert_raw_data_to_xml ../../../../Projects/jhbuild/bluez/src/sdp-xml.c:994
-
-SUMMARY: AddressSanitizer: stack-overflow ../../../../Projects/jhbuild/bluez/src/sdp-xml.c:709 in convert_raw_data_to_xml
----
- src/sdp-xml.c | 16 +++++++++++++---
- 1 file changed, 13 insertions(+), 3 deletions(-)
-
-diff --git a/src/sdp-xml.c b/src/sdp-xml.c
-index fb8417b3fc60..5b448fe83410 100644
---- a/src/sdp-xml.c
-+++ b/src/sdp-xml.c
-@@ -682,9 +682,12 @@ sdp_record_t *sdp_xml_parse_record(const char *data, int size)
- return record;
- }
-
--
- static void convert_raw_data_to_xml(sdp_data_t *value, int indent_level,
-- void *data, void (*appender)(void *, const char *))
-+ void *data, void (*appender)(void *, const char *));
-+
-+static inline void convert_raw_data_to_xml_element(sdp_data_t *value,
-+ int indent_level,void *data,
-+ void (*appender)(void *, const char *))
- {
- int i, hex;
- char buf[STRBUFSIZE];
-@@ -1002,8 +1005,15 @@ static void convert_raw_data_to_xml(sdp_data_t *value, int indent_level,
-
- break;
- }
-+}
-
-- convert_raw_data_to_xml(value->next, indent_level, data, appender);
-+static void convert_raw_data_to_xml(sdp_data_t *value, int indent_level,
-+ void *data, void (*appender)(void *, const char *))
-+{
-+ for (; value != NULL; value = value->next) {
-+ convert_raw_data_to_xml_element(value, indent_level,
-+ data, appender);
-+ }
- }
-
- struct conversion_data {
---
-2.55.0
-
-
-From d01ba78b1d8a3294f82ae003479c922e1499ed49 Mon Sep 17 00:00:00 2001
-From: Bastien Nocera <hadess@hadess.net>
-Date: Wed, 12 Aug 2026 10:01:40 +0200
-Subject: [PATCH 9/9] unit: Add convert_sdp_record_to_xml() to SDP XML testing
-
-This tests SDP binary to XML conversion, including whether a fix for
-a stack overflow when dealing with large sequences, like in
-sequence_on_squared(), works correctly.
----
- unit/test-sdp-xml.c | 10 +++++++++-
- 1 file changed, 9 insertions(+), 1 deletion(-)
-
-diff --git a/unit/test-sdp-xml.c b/unit/test-sdp-xml.c
-index 5aa98334eabc..b338788aa295 100644
---- a/unit/test-sdp-xml.c
-+++ b/unit/test-sdp-xml.c
-@@ -28,6 +28,12 @@ struct test_data {
- gboolean expected_result;
- };
-
-+static void doprintf(void *data, const char *str)
-+{
-+ /* Do nothing for our tests */
-+ /* printf("%s", str); */
-+}
-+
- static void parse_xml(gconstpointer data, gsize len, gboolean expected_result)
- {
- sdp_record_t *rec = NULL;
-@@ -39,8 +45,10 @@ static void parse_xml(gconstpointer data, gsize len, gboolean expected_result)
- tester_test_passed();
- else
- tester_test_failed();
-- if (rec)
-+ if (rec) {
-+ convert_sdp_record_to_xml(rec, 0, doprintf);
- sdp_record_free(rec);
-+ }
- }
-
- static void parse_xml_for_filename(gconstpointer data)
---
-2.55.0
-
-From b21edc49b2c3675c7aea19286fdb2b887f29c0ff Mon Sep 17 00:00:00 2001
-From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
-Date: Fri, 14 Aug 2026 13:31:07 -0400
-Subject: [PATCH 1/4] sdp-xml: Use a queue to collect sequence members
-
-Appending a member to a sequence with sdp_seq_append() walks the
-single-linked list to find its tail, so building a sequence is O(n^2).
-
-This was previously worked around by caching the tail of the sequence in
-struct sdp_xml_data, which required the caller to pick between appending
-to the cached tail and initialising val.dataseq, and to keep the cache in
-sync on every append.
-
-Collect the members in a struct queue instead, which tracks its own tail,
-and link them into val.dataseq once the element is closed. Appending is a
-plain queue_push_tail(), and the queue is destroyed along with the rest
-of the element so members that were never linked, such as on malformed
-input, are still freed.
-
-The sequence_on_squared() test stays at less than 0.1 seconds.
-
-Assisted-by: Claude:claude-opus-5
----
- Makefile.tools | 4 +++-
- src/sdp-xml.c | 60 ++++++++++++++++++++++++++++++++++++--------------
- 2 files changed, 46 insertions(+), 18 deletions(-)
-
-diff --git a/Makefile.tools b/Makefile.tools
-index 1a4e5660813b..b3ef4ae1c3df 100644
---- a/Makefile.tools
-+++ b/Makefile.tools
-@@ -437,7 +437,9 @@ tools_hciconfig_LDADD = lib/libbluetooth-internal.la
- tools_hcitool_SOURCES = tools/hcitool.c src/oui.h src/oui.c
- tools_hcitool_LDADD = lib/libbluetooth-internal.la $(UDEV_LIBS)
-
--tools_sdptool_SOURCES = tools/sdptool.c src/sdp-xml.h src/sdp-xml.c
-+tools_sdptool_SOURCES = tools/sdptool.c src/sdp-xml.h src/sdp-xml.c \
-+ src/shared/queue.h src/shared/queue.c \
-+ src/shared/util.h src/shared/util.c
- tools_sdptool_LDADD = lib/libbluetooth-internal.la $(GLIB_LIBS)
-
- tools_ciptool_LDADD = lib/libbluetooth-internal.la
-diff --git a/src/sdp-xml.c b/src/sdp-xml.c
-index 5b448fe83410..bad9e289344f 100644
---- a/src/sdp-xml.c
-+++ b/src/sdp-xml.c
-@@ -25,6 +25,8 @@
- #include "bluetooth/sdp.h"
- #include "bluetooth/sdp_lib.h"
-
-+#include "shared/queue.h"
-+
- #include "sdp-xml.h"
-
- #define DBG(...) (void)(0)
-@@ -44,7 +46,7 @@ struct sdp_xml_data {
- char type; /* 0 = Text or Hexadecimal */
- char *name; /* Name, optional in the dtd */
- /* TODO: What is it used for? */
-- sdp_data_t *tail; /* Tail for O(1) dataseq append */
-+ struct queue *seq; /* Members of a dataseq, if any */
- };
-
- struct context_data {
-@@ -510,8 +512,37 @@ static void element_start(GMarkupParseContext *context,
- }
- }
-
-+/*
-+ * Link the members collected in elem->seq into elem->data->val.dataseq.
-+ *
-+ * Members are collected in a queue so that appending is O(1), sdp_seq_append()
-+ * would otherwise have to walk to the tail of the sequence on every append.
-+ */
-+static void sdp_xml_data_flush_seq(struct sdp_xml_data *elem)
-+{
-+ const struct queue_entry *entry;
-+ sdp_data_t *tail = NULL;
-+
-+ if (!elem->seq)
-+ return;
-+
-+ for (entry = queue_get_entries(elem->seq); entry; entry = entry->next) {
-+ if (tail)
-+ sdp_seq_append(tail, entry->data);
-+ else
-+ elem->data->val.dataseq = sdp_seq_append(NULL,
-+ entry->data);
-+ tail = entry->data;
-+ }
-+
-+ queue_destroy(elem->seq, NULL);
-+ elem->seq = NULL;
-+}
-+
- static void sdp_xml_data_free(struct sdp_xml_data *elem)
- {
-+ queue_destroy(elem->seq, (queue_destroy_func_t) sdp_data_free);
-+
- if (elem->data)
- sdp_data_free(elem->data);
-
-@@ -568,6 +599,8 @@ static void element_end(GMarkupParseContext *context,
- return;
- }
-
-+ sdp_xml_data_flush_seq(ctx_data->stack_head);
-+
- if (!strcmp(element_name, "sequence")) {
- if (!SDP_IS_SEQ(ctx_data->stack_head->data->dtd)) {
- g_set_error(err, G_MARKUP_ERROR,
-@@ -610,28 +643,21 @@ static void element_end(GMarkupParseContext *context,
-
- if (ctx_data->stack_head->next && ctx_data->stack_head->data &&
- ctx_data->stack_head->next->data) {
-- sdp_data_t *tail;
-- switch (ctx_data->stack_head->next->data->dtd) {
-+ struct sdp_xml_data *parent = ctx_data->stack_head->next;
-+
-+ switch (parent->data->dtd) {
- case SDP_SEQ8:
- case SDP_SEQ16:
- case SDP_SEQ32:
- case SDP_ALT8:
- case SDP_ALT16:
- case SDP_ALT32:
-- tail = ctx_data->stack_head->next->data->val.dataseq ?
-- ctx_data->stack_head->next->tail : NULL;
-- if (tail) {
-- sdp_seq_append(tail,
-- ctx_data->stack_head->data);
-- } else {
-- ctx_data->stack_head->next->data->val.dataseq =
-- sdp_seq_append(NULL,
-- ctx_data->stack_head->data);
-- }
-- ctx_data->stack_head->next->tail =
-- ctx_data->stack_head->data;
-- ctx_data->stack_head->data = NULL;
-- ctx_data->stack_head->tail = NULL;
-+ if (!parent->seq)
-+ parent->seq = queue_new();
-+
-+ if (queue_push_tail(parent->seq,
-+ ctx_data->stack_head->data))
-+ ctx_data->stack_head->data = NULL;
- break;
- }
-
---
-2.55.0
-
-
-From 97521ab4dd79a38b2c30d8d664b4e11f86680e88 Mon Sep 17 00:00:00 2001
-From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
-Date: Fri, 14 Aug 2026 13:40:51 -0400
-Subject: [PATCH 2/4] sdp-xml: Fix leaking the parse stack on malformed input
-
-sdp_xml_parse_record() frees its context but never the elements left on
-ctx_data->stack_head.
-
-element_end() returns early without popping the stack when it rejects a
-document, for instance on a mismatched </sequence> close, so a malformed
-record leaves its elements behind and they are never freed.
-
-Free the remaining stack elements before returning. Found with the
-compute-seq-size-type-confusion.xml test:
-
-56 (direct) + 1,072 (indirect) bytes in 1 blocks are definitely lost
- at calloc (vg_replace_malloc.c:1678)
- by sdp_xml_data_alloc (sdp-xml.c:73)
- by element_start (sdp-xml.c:473)
- by g_markup_parse_context_parse (gmarkup.c:1369)
- by sdp_xml_parse_record (sdp-xml.c:696)
-
-Assisted-by: Claude:claude-opus-5
----
- src/sdp-xml.c | 14 ++++++++++++++
- 1 file changed, 14 insertions(+)
-
-diff --git a/src/sdp-xml.c b/src/sdp-xml.c
-index bad9e289344f..bcd5785f87ca 100644
---- a/src/sdp-xml.c
-+++ b/src/sdp-xml.c
-@@ -551,6 +551,17 @@ static void sdp_xml_data_free(struct sdp_xml_data *elem)
- free(elem);
- }
-
-+/* Free the elements left on the stack, e.g. by a document that is malformed */
-+static void sdp_xml_data_free_stack(struct sdp_xml_data *elem)
-+{
-+ while (elem) {
-+ struct sdp_xml_data *next = elem->next;
-+
-+ sdp_xml_data_free(elem);
-+ elem = next;
-+ }
-+}
-+
- static void element_end(GMarkupParseContext *context,
- const char *element_name, gpointer user_data, GError **err)
- {
-@@ -696,6 +707,7 @@ sdp_record_t *sdp_xml_parse_record(const char *data, int size)
- if (g_markup_parse_context_parse(ctx, data, size, NULL) == FALSE) {
- error("XML parsing error");
- g_markup_parse_context_free(ctx);
-+ sdp_xml_data_free_stack(ctx_data->stack_head);
- sdp_record_free(record);
- free(ctx_data);
- return NULL;
-@@ -703,6 +715,8 @@ sdp_record_t *sdp_xml_parse_record(const char *data, int size)
-
- g_markup_parse_context_free(ctx);
-
-+ sdp_xml_data_free_stack(ctx_data->stack_head);
-+
- free(ctx_data);
-
- return record;
---
-2.55.0
-
-
-From 078ef10a4531e3fddbac332cf65077f317092555 Mon Sep 17 00:00:00 2001
-From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
-Date: Mon, 17 Aug 2026 14:56:16 -0400
-Subject: [PATCH 3/4] sdp: Fix memory leak when freeing alternates
-
-sdp_data_alloc_with_length() stores the members of SDP_ALT8, SDP_ALT16
-and SDP_ALT32 in val.dataseq, exactly like it does for the SDP_SEQ8,
-SDP_SEQ16 and SDP_SEQ32 sequences.
-
-sdp_data_free() only calls data_seq_free() for the sequences though, so
-freeing an alternate frees the alternate itself and leaks every one of
-its members, along with anything they own in turn:
-
-209 (48 direct, 161 indirect) bytes in 1 blocks are definitely lost
- at calloc (vg_replace_malloc.c:1678)
- by sdp_data_alloc_with_length (sdp.c:350)
- by sdp_data_alloc (sdp.c:486)
- by sdp_xml_parse_int (sdp-xml.c:243)
- by sdp_xml_parse_datatype (sdp-xml.c:421)
- by element_start (sdp-xml.c:507)
-
-Free the members of alternates as well.
-
-Assisted-by: Claude:claude-opus-5
----
- lib/bluetooth/sdp.c | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/lib/bluetooth/sdp.c b/lib/bluetooth/sdp.c
-index 8c0865398519..1e027f9ebe6d 100644
---- a/lib/bluetooth/sdp.c
-+++ b/lib/bluetooth/sdp.c
-@@ -972,6 +972,9 @@ void sdp_data_free(sdp_data_t *d)
- case SDP_SEQ8:
- case SDP_SEQ16:
- case SDP_SEQ32:
-+ case SDP_ALT8:
-+ case SDP_ALT16:
-+ case SDP_ALT32:
- data_seq_free(d);
- break;
- case SDP_URL_STR8:
---
-2.55.0
-
-
-From 5abc0045b84adcb47af26f88f699f33c0037a00c Mon Sep 17 00:00:00 2001
-From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
-Date: Mon, 17 Aug 2026 14:56:24 -0400
-Subject: [PATCH 4/4] unit/test-sdp-xml: Add a test parsing alternates
-
-None of the existing records contain an <alternate>, so nothing covered
-the SDP_ALT8, SDP_ALT16 and SDP_ALT32 handling.
-
-Add a record with an alternate holding an integer, a string and a nested
-sequence, which leaks its members under valgrind without the previous
-sdp_data_free() fix.
-
-Assisted-by: Claude:claude-opus-5
----
- Makefile.am | 1 +
- unit/sdp-xml/alternate.xml | 12 ++++++++++++
- unit/test-sdp-xml.c | 1 +
- 3 files changed, 14 insertions(+)
- create mode 100644 unit/sdp-xml/alternate.xml
-
-diff --git a/Makefile.am b/Makefile.am
-index 3c6cf92ab403..2754e1b7f2da 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -648,6 +648,7 @@ unit_test_sdp_xml_CFLAGS = $(AM_CFLAGS) $(GLIB_CFLAGS) -DTOP_SRCDIR=\""$(srcdir)
- unit_test_sdp_xml_CPPFLAGS = -I$(srcdir)/lib
-
- EXTRA_DIST += unit/sdp-xml/Bluetooth_HID-sdp_record.xml \
-+ unit/sdp-xml/alternate.xml \
- unit/sdp-xml/compute-seq-size-type-confusion.xml \
- unit/sdp-xml/duplicate-attribute.xml \
- unit/sdp-xml/qt-SerialPortSDPRecord.xml
-diff --git a/unit/sdp-xml/alternate.xml b/unit/sdp-xml/alternate.xml
-new file mode 100644
-index 000000000000..a35ebccbc71f
---- /dev/null
-+++ b/unit/sdp-xml/alternate.xml
-@@ -0,0 +1,12 @@
-+<?xml version="1.0" encoding="UTF-8" ?>
-+<record>
-+ <attribute id="0x0004">
-+ <alternate>
-+ <uint32 value="0x11223344" />
-+ <text value="alternate-member" />
-+ <sequence>
-+ <uint16 value="0x0100" />
-+ </sequence>
-+ </alternate>
-+ </attribute>
-+</record>
-diff --git a/unit/test-sdp-xml.c b/unit/test-sdp-xml.c
-index b338788aa295..cb5b91717fca 100644
---- a/unit/test-sdp-xml.c
-+++ b/unit/test-sdp-xml.c
-@@ -133,6 +133,7 @@ int main(int argc, char *argv[])
- DEFINE_TEST("compute-seq-size-type-confusion.xml", FALSE);
- /* From https://github.com/bluez/bluez/security/advisories/GHSA-75v6-6q44-57hc */
- DEFINE_TEST("duplicate-attribute.xml", TRUE);
-+ DEFINE_TEST("alternate.xml", TRUE);
-
- tester_add("/sequence_on_squared", &data,
- sequence_on_squared_setup,
---
-2.55.0
-
diff --git a/sources b/sources
index 050bd26..29dcf80 100644
--- a/sources
+++ b/sources
@@ -1 +1 @@
-SHA512 (bluez-5.87.tar.xz) = f1e3bede9b0bbc3b5cfe9fed5f7945be7cbcf7a299729b5f82f1cba4bbbd6c4d2e372c9685f61458dfc374c9dfee352b6c2933a2c0d7727b8171ff394055e169
+SHA512 (bluez-5.87+1.git8750129efca8.tar.xz) = a769c6e92af8bedfe554e292eb24e2750c04fe473f2f71e655e1a0bd16ce0970253015398f59840f8240f8b40e99072aa7aa43239f070f62e5c7ab47f8a3f001
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2026-09-09 12:44 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-09 12:44 [rpms/bluez] f44: Rebase to latest upstream HEAD (Closes: #2528181, #2525293) Bastien Nocera
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox