From d2e0cb60f2a4d31aec5fea1807ba98b5a1f211fc Mon Sep 17 00:00:00 2001 From: Emil Gydesen Date: Tue, 2 Sep 2025 11:19:37 +0200 Subject: [PATCH] DNM: Add checks for UTF when setting and getting broadcast name. Signed-off-by: Emil Gydesen --- include/zephyr/bluetooth/audio/audio.h | 17 +++-- subsys/bluetooth/audio/Kconfig.bap | 4 ++ subsys/bluetooth/audio/codec.c | 22 ++++++- tests/bluetooth/audio/codec/prj.conf | 1 + tests/bluetooth/audio/codec/src/main.c | 91 ++++++++++++++++++++++++++ 5 files changed, 130 insertions(+), 5 deletions(-) diff --git a/include/zephyr/bluetooth/audio/audio.h b/include/zephyr/bluetooth/audio/audio.h index 230f2735e4d57..2ee1898a70b73 100644 --- a/include/zephyr/bluetooth/audio/audio.h +++ b/include/zephyr/bluetooth/audio/audio.h @@ -49,11 +49,16 @@ extern "C" { #define BT_AUDIO_PD_MAX 0xFFFFFFU /** Indicates that the unicast server does not have a preference for any retransmission number */ #define BT_AUDIO_RTN_PREF_NONE 0xFFU -/** The minimum size of a Broadcast Name as defined by Bluetooth Assigned Numbers */ +/** The minimum size in octets of a Broadcast Name as defined by Bluetooth Assigned Numbers */ #define BT_AUDIO_BROADCAST_NAME_LEN_MIN 4 -/** The maximum size of a Broadcast Name as defined by Bluetooth Assigned Numbers */ +/** The maximum size in octets of a Broadcast Name as defined by Bluetooth Assigned Numbers */ #define BT_AUDIO_BROADCAST_NAME_LEN_MAX 128 +/** The minimum amount of characters of a Broadcast Name as defined by Bluetooth Assigned Numbers */ +#define BT_AUDIO_BROADCAST_NAME_CHAR_MIN 4 +/** The maximum amount of characters of a Broadcast Name as defined by Bluetooth Assigned Numbers */ +#define BT_AUDIO_BROADCAST_NAME_CHAR_MAX 32 + /** Size of the stream language value, e.g. "eng" */ #define BT_AUDIO_LANG_SIZE 3 @@ -1456,7 +1461,9 @@ int bt_audio_codec_cfg_meta_get_broadcast_name(const struct bt_audio_codec_cfg * * * @param codec_cfg The codec configuration to set data for. * @param broadcast_name The broadcast name to set. - * @param broadcast_name_len The length of @p broadcast_name. + * @param broadcast_name_len The length of @p broadcast_name. Shall be between + * @ref BT_AUDIO_BROADCAST_NAME_LEN_MIN and + * @ref BT_AUDIO_BROADCAST_NAME_LEN_MAX. * * @retval data_len The @p codec_cfg.data_len on success * @retval -EINVAL Arguments are invalid @@ -2058,7 +2065,9 @@ int bt_audio_codec_cap_meta_get_broadcast_name(const struct bt_audio_codec_cap * * * @param codec_cap The codec capability to set data for. * @param broadcast_name The broadcast name to set. - * @param broadcast_name_len The length of @p broadcast_name. + * @param broadcast_name_len The length of @p broadcast_name. Shall be between + * @ref BT_AUDIO_BROADCAST_NAME_LEN_MIN and + * @ref BT_AUDIO_BROADCAST_NAME_LEN_MAX. * * @retval data_len The @p codec_cap.data_len on success * @retval -EINVAL Arguments are invalid diff --git a/subsys/bluetooth/audio/Kconfig.bap b/subsys/bluetooth/audio/Kconfig.bap index 01ab717f379dc..42c90dc030c50 100644 --- a/subsys/bluetooth/audio/Kconfig.bap +++ b/subsys/bluetooth/audio/Kconfig.bap @@ -16,6 +16,7 @@ config BT_BAP_UNICAST_SERVER depends on BT_ISO_PERIPHERAL depends on BT_ASCS depends on BT_BONDABLE + depends on UTF8 select BT_PAC_SRC if BT_ASCS_ASE_SNK select BT_PAC_SNK if BT_ASCS_ASE_SRC help @@ -30,6 +31,7 @@ config BT_BAP_UNICAST_CLIENT depends on BT_CENTRAL depends on BT_ISO_CENTRAL depends on BT_BONDABLE + depends on UTF8 help This option enables support for Bluetooth Unicast Audio Client using Isochronous channels. @@ -121,6 +123,7 @@ endif # BT_BAP_UNICAST_CLIENT config BT_BAP_BROADCAST_SOURCE bool "Bluetooth Broadcast Source Audio Support" depends on BT_ISO_BROADCASTER + depends on UTF8 help This option enables support for Bluetooth Broadcast Source Audio using Isochronous channels. @@ -162,6 +165,7 @@ config BT_BAP_BROADCAST_SINK depends on BT_PAC_SNK depends on BT_PERIPHERAL depends on BT_BAP_SCAN_DELEGATOR + depends on UTF8 help This option enables support for Bluetooth Broadcast Sink Audio using Isochronous channels. diff --git a/subsys/bluetooth/audio/codec.c b/subsys/bluetooth/audio/codec.c index 0f0ba58e72e3b..c4ad2b27f59ad 100644 --- a/subsys/bluetooth/audio/codec.c +++ b/subsys/bluetooth/audio/codec.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -1057,7 +1058,8 @@ static int codec_meta_get_broadcast_name(const uint8_t meta[], size_t meta_len, } ret = codec_meta_get_val(meta, meta_len, BT_AUDIO_METADATA_TYPE_BROADCAST_NAME, &data); - if (data == NULL) { + if (data == NULL || + !IN_RANGE(ret, BT_AUDIO_BROADCAST_NAME_LEN_MIN, BT_AUDIO_BROADCAST_NAME_LEN_MAX)) { return -ENODATA; } @@ -1069,6 +1071,9 @@ static int codec_meta_get_broadcast_name(const uint8_t meta[], size_t meta_len, static int codec_meta_set_broadcast_name(uint8_t meta[], size_t meta_len, size_t meta_size, const uint8_t *broadcast_name, size_t broadcast_name_len) { + char broadcast_name_str[BT_AUDIO_BROADCAST_NAME_LEN_MAX + sizeof('\0')]; + ssize_t char_cnt; + CHECKIF(meta == NULL) { LOG_DBG("meta is NULL"); return -EINVAL; @@ -1079,6 +1084,21 @@ static int codec_meta_set_broadcast_name(uint8_t meta[], size_t meta_len, size_t return -EINVAL; } + if (!IN_RANGE(broadcast_name_len, BT_AUDIO_BROADCAST_NAME_LEN_MIN, + BT_AUDIO_BROADCAST_NAME_LEN_MAX)) { + LOG_DBG("Invalid broadcast name len %zu", broadcast_name_len); + return -EINVAL; + } + + (void)memcpy(broadcast_name_str, broadcast_name, broadcast_name_len); + broadcast_name_str[broadcast_name_len] = '\0'; + char_cnt = utf8_count_chars(broadcast_name_str); + if (!IN_RANGE(char_cnt, BT_AUDIO_BROADCAST_NAME_CHAR_MIN, + BT_AUDIO_BROADCAST_NAME_CHAR_MAX)) { + LOG_DBG("Invalid broadcast name %s", broadcast_name_str); + return -EINVAL; + } + return codec_meta_set_val(meta, meta_len, meta_size, BT_AUDIO_METADATA_TYPE_BROADCAST_NAME, broadcast_name, broadcast_name_len); } diff --git a/tests/bluetooth/audio/codec/prj.conf b/tests/bluetooth/audio/codec/prj.conf index a2358bde70595..af71c8a87f0f2 100644 --- a/tests/bluetooth/audio/codec/prj.conf +++ b/tests/bluetooth/audio/codec/prj.conf @@ -1,4 +1,5 @@ CONFIG_ZTEST=y +CONFIG_UTF8=y CONFIG_BT=y CONFIG_BT_SMP=y diff --git a/tests/bluetooth/audio/codec/src/main.c b/tests/bluetooth/audio/codec/src/main.c index e1deb97621259..df05c00fafde9 100644 --- a/tests/bluetooth/audio/codec/src/main.c +++ b/tests/bluetooth/audio/codec/src/main.c @@ -998,6 +998,66 @@ ZTEST(audio_codec_test_suite, test_bt_audio_codec_cfg_meta_set_broadcast_name) zassert_mem_equal(new_expected_data, broadcast_name, ARRAY_SIZE(new_expected_data)); } +static ZTEST(audio_codec_test_suite, test_bt_audio_codec_cfg_meta_set_broadcast_name_inval_min_len) +{ + const uint8_t new_data[] = {'n', 'e', 'w'}; + struct bt_audio_codec_cfg codec_cfg = + BT_AUDIO_CODEC_CFG(BT_HCI_CODING_FORMAT_LC3, 0x0000, 0x0000, {}, + {BT_AUDIO_CODEC_DATA(BT_AUDIO_METADATA_TYPE_BROADCAST_NAME, 'm', + 'y', ' ', 'b', 'c', 'a', 's', 't')}); + int ret; + + ret = bt_audio_codec_cfg_meta_set_broadcast_name(&codec_cfg, new_data, + ARRAY_SIZE(new_data)); + zassert_equal(ret, -EINVAL, "Unexpected return value %d", ret); +} + +static ZTEST(audio_codec_test_suite, test_bt_audio_codec_cfg_meta_set_broadcast_name_inval_max_len) +{ + const uint8_t new_data[] = {'T', 'h', 'i', 's', ' ', 'i', 's', ' ', 'a', ' ', 'v', + 'e', 'r', 'y', ' ', 'l', 'o', 'n', 'g', ' ', 's', 't', + 'r', 'i', 'n', 'g', ' ', 't', 'o', ' ', 'r', 'e', 't', + 'u', 'r', 'n', ' ', 'e', 'r', 'r', 'o', 'r'}; + struct bt_audio_codec_cfg codec_cfg = + BT_AUDIO_CODEC_CFG(BT_HCI_CODING_FORMAT_LC3, 0x0000, 0x0000, {}, + {BT_AUDIO_CODEC_DATA(BT_AUDIO_METADATA_TYPE_BROADCAST_NAME, 'm', + 'y', ' ', 'b', 'c', 'a', 's', 't')}); + int ret; + + ret = bt_audio_codec_cfg_meta_set_broadcast_name(&codec_cfg, new_data, + ARRAY_SIZE(new_data)); + zassert_equal(ret, -EINVAL, "Unexpected return value %d", ret); +} + +static ZTEST(audio_codec_test_suite, test_bt_audio_codec_cfg_meta_set_broadcast_name_inval_utf8) +{ + const uint8_t new_data[] = {0x80, 0x80, 0x80, 0x80, 0x80}; + struct bt_audio_codec_cfg codec_cfg = + BT_AUDIO_CODEC_CFG(BT_HCI_CODING_FORMAT_LC3, 0x0000, 0x0000, {}, + {BT_AUDIO_CODEC_DATA(BT_AUDIO_METADATA_TYPE_BROADCAST_NAME, 'm', + 'y', ' ', 'b', 'c', 'a', 's', 't')}); + int ret; + + ret = bt_audio_codec_cfg_meta_set_broadcast_name(&codec_cfg, new_data, + ARRAY_SIZE(new_data)); + zassert_equal(ret, -EINVAL, "Unexpected return value %d", ret); +} + +static ZTEST(audio_codec_test_suite, + test_bt_audio_codec_cfg_meta_set_broadcast_name_inval_char_cnt_min) +{ + const uint8_t new_data[] = "𠜎𠜎𠜎𠜎𠜎𠜎"; /* 2 4-octet characters */ + struct bt_audio_codec_cfg codec_cfg = + BT_AUDIO_CODEC_CFG(BT_HCI_CODING_FORMAT_LC3, 0x0000, 0x0000, {}, + {BT_AUDIO_CODEC_DATA(BT_AUDIO_METADATA_TYPE_BROADCAST_NAME, 'm', + 'y', ' ', 'b', 'c', 'a', 's', 't')}); + int ret; + + ret = bt_audio_codec_cfg_meta_set_broadcast_name(&codec_cfg, new_data, + ARRAY_SIZE(new_data)); + zassert_equal(ret, -EINVAL, "Unexpected return value %d", ret); +} + ZTEST(audio_codec_test_suite, test_bt_audio_codec_cfg_meta_get_extended) { const uint8_t expected_data[] = {0x00, 0x01, 0x02, 0x03}; @@ -1950,6 +2010,37 @@ ZTEST(audio_codec_test_suite, test_bt_audio_codec_cap_meta_set_broadcast_name) zassert_mem_equal(new_expected_data, broadcast_name, ARRAY_SIZE(new_expected_data)); } +static ZTEST(audio_codec_test_suite, test_bt_audio_codec_cap_meta_set_broadcast_name_inval_min_len) +{ + const uint8_t new_data[] = {'n', 'e', 'w'}; + struct bt_audio_codec_cap codec_cap = + BT_AUDIO_CODEC_CAP(BT_HCI_CODING_FORMAT_LC3, 0x0000, 0x0000, {}, + {BT_AUDIO_CODEC_DATA(BT_AUDIO_METADATA_TYPE_BROADCAST_NAME, 'm', + 'y', ' ', 'b', 'c', 'a', 's', 't')}); + int ret; + + ret = bt_audio_codec_cap_meta_set_broadcast_name(&codec_cap, new_data, + ARRAY_SIZE(new_data)); + zassert_equal(ret, -EINVAL, "Unexpected return value %d", ret); +} + +static ZTEST(audio_codec_test_suite, test_bt_audio_codec_cap_meta_set_broadcast_name_inval_max_len) +{ + const uint8_t new_data[] = {'T', 'h', 'i', 's', ' ', 'i', 's', ' ', 'a', ' ', 'v', + 'e', 'r', 'y', ' ', 'l', 'o', 'n', 'g', ' ', 's', 't', + 'r', 'i', 'n', 'g', ' ', 't', 'o', ' ', 'r', 'e', 't', + 'u', 'r', 'n', ' ', 'e', 'r', 'r', 'o', 'r'}; + struct bt_audio_codec_cap codec_cap = + BT_AUDIO_CODEC_CAP(BT_HCI_CODING_FORMAT_LC3, 0x0000, 0x0000, {}, + {BT_AUDIO_CODEC_DATA(BT_AUDIO_METADATA_TYPE_BROADCAST_NAME, 'm', + 'y', ' ', 'b', 'c', 'a', 's', 't')}); + int ret; + + ret = bt_audio_codec_cap_meta_set_broadcast_name(&codec_cap, new_data, + ARRAY_SIZE(new_data)); + zassert_equal(ret, -EINVAL, "Unexpected return value %d", ret); +} + ZTEST(audio_codec_test_suite, test_bt_audio_codec_cap_meta_get_extended) { const uint8_t expected_data[] = {0x00, 0x01, 0x02, 0x03};