[PATCH BlueZ v2 07/10] Replace the name2utf8 copies with str2utf8

Luiz Augusto von Dentz <[email protected]>
Newsgroups org.kernel.vger.linux-bluetooth
Message-ID <[email protected]>
From: Luiz Augusto von Dentz <[email protected]>

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);
+	utf8_name = str2utf8(iov->iov_base, iov->iov_len);
+	if (!utf8_name)
+		return false;
 
-	if (strisutf8(utf8_name, len))
-		goto done;
+	ret = bt_ad_add_name(ad, utf8_name);
 
-	strtoutf8(utf8_name, len);
+	free(utf8_name);
 
-	/* Remove leading and trailing whitespace characters */
-	strstrip(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.54.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.