From: Luiz Augusto von Dentz <luiz.dentz@gmail.com>
To: linux-bluetooth@vger.kernel.org
Subject: [PATCH BlueZ v1 5/8] Replace the name2utf8 copies with str2utf8
Date: Wed, 19 Aug 2026 16:40:05 -0400 [thread overview]
Message-ID: <20260819204008.2292225-6-luiz.dentz@gmail.com> (raw)
In-Reply-To: <20260819204008.2292225-1-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
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 | 7 +++--
6 files changed, 40 insertions(+), 119 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 95351d015323..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, 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 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 b1d1b84611aa..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];
+ char *utf8_name;
+ bool ret;
- memset(utf8_name, 0, sizeof(utf8_name));
- strncpy(utf8_name, (const char *)iov->iov_base,
- MIN(iov->iov_len, HCI_MAX_NAME_LENGTH));
+ utf8_name = str2utf8(iov->iov_base, iov->iov_len);
+ if (!utf8_name)
+ return false;
- if (strisutf8(utf8_name, iov->iov_len))
- goto done;
+ ret = bt_ad_add_name(ad, utf8_name);
- strtoutf8(utf8_name, iov->iov_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 62164ca993f6..a4b6743e48b7 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,
};
--
2.54.0
next prev parent reply other threads:[~2026-08-19 20:40 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-19 20:40 [PATCH BlueZ v1 0/8] Replace the name2utf8 copies with str2utf8 Luiz Augusto von Dentz
2026-08-19 20:40 ` [PATCH BlueZ v1 1/8] eir: Fix stack buffer overflow when parsing the remote name Luiz Augusto von Dentz
2026-08-19 21:27 ` Replace the name2utf8 copies with str2utf8 bluez.test.bot
2026-08-20 7:46 ` [PATCH BlueZ v1 1/8] eir: Fix stack buffer overflow when parsing the remote name Bastien Nocera
2026-08-20 8:14 ` Bastien Nocera
2026-08-19 20:40 ` [PATCH BlueZ v1 2/8] shared/util: Make strnlenutf8 reject ill-formed sequences Luiz Augusto von Dentz
2026-08-19 20:40 ` [PATCH BlueZ v1 3/8] shared/util: Add str2utf8 Luiz Augusto von Dentz
2026-08-19 20:40 ` [PATCH BlueZ v1 4/8] unit/test-util: Add str2utf8 tests Luiz Augusto von Dentz
2026-08-19 20:40 ` Luiz Augusto von Dentz [this message]
2026-08-19 20:40 ` [PATCH BlueZ v1 6/8] device: Fix the name truncation splitting UTF-8 sequences Luiz Augusto von Dentz
2026-08-19 20:40 ` [PATCH BlueZ v1 7/8] device: Rename btd_device_device_set_name to btd_device_set_name Luiz Augusto von Dentz
2026-08-19 20:40 ` [PATCH BlueZ v1 8/8] unit/test-util: Cover strtoutf8 with the str2utf8 tests Luiz Augusto von Dentz
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260819204008.2292225-6-luiz.dentz@gmail.com \
--to=luiz.dentz@gmail.com \
--cc=linux-bluetooth@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.