public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
* [rpms/bluez] f43: Rebase to latest upstream HEAD (Closes: #2528181, #2525293)
@ 2026-09-09 12:45 Bastien Nocera
  0 siblings, 0 replies; only message in thread
From: Bastien Nocera @ 2026-09-09 12:45 UTC (permalink / raw)
  To: git-commits

A new commit has been pushed.

Repo   : rpms/bluez
Branch : f43
Commit : e4d3cef156e60fe3083db2eac3f9633a21abae98
Author : Bastien Nocera <hadess@hadess.net>
Date   : 2026-09-09T14:44:39+02:00
Stats  : +14/-5057 in 6 file(s)
URL    : https://src.fedoraproject.org/rpms/bluez/c/e4d3cef156e60fe3083db2eac3f9633a21abae98?branch=f43

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 b7f0ecf..c1d6928 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:45 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:45 [rpms/bluez] f43: 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