diff --git a/src/modules/meson.build b/src/modules/meson.build index 439a37693..6bd108e95 100644 --- a/src/modules/meson.build +++ b/src/modules/meson.build @@ -828,7 +828,8 @@ if build_module_avb 'module-avb/msrp.c', 'module-avb/mvrp.c', 'module-avb/srp.c', - 'module-avb/stream.c' + 'module-avb/stream.c', + 'module-avb/strings.c' ], include_directories : [configinc], install : true, diff --git a/src/modules/module-avb/aecp-aem-cmds-resps/cmd-get-set-name.c b/src/modules/module-avb/aecp-aem-cmds-resps/cmd-get-set-name.c index f1482996c..fc51dbf0d 100644 --- a/src/modules/module-avb/aecp-aem-cmds-resps/cmd-get-set-name.c +++ b/src/modules/module-avb/aecp-aem-cmds-resps/cmd-get-set-name.c @@ -12,6 +12,7 @@ #include "../aecp-aem.h" #include "../aecp-aem-state.h" #include "../aecp-aem-descriptors.h" +#include "../strings.h" #include "cmd-get-set-name.h" @@ -145,7 +146,6 @@ int handle_cmd_get_name_common(struct aecp *aecp, int64_t now, /** * IEEE 1722.1-2021 7.4.17 SET_NAME - * For now this is not handling UTF characters, only ASCII */ int handle_cmd_set_name_common(struct aecp *aecp, int64_t now, const void *m, int len) @@ -156,7 +156,7 @@ int handle_cmd_set_name_common(struct aecp *aecp, int64_t now, const struct avb_packet_aecp_aem_setget_name *cmd; struct descriptor *desc; uint16_t desc_type, desc_id, name_index; - char *name_ptr; + unsigned char *name_ptr; int rc; cmd = (const struct avb_packet_aecp_aem_setget_name *)p->payload; @@ -169,7 +169,7 @@ int handle_cmd_set_name_common(struct aecp *aecp, int64_t now, return reply_status(aecp, AVB_AECP_AEM_STATUS_NO_SUCH_DESCRIPTOR, m, len); - name_ptr = get_name_ptr(desc_type, descriptor_body(desc), name_index); + name_ptr = (unsigned char *)get_name_ptr(desc_type, descriptor_body(desc), name_index); if (name_ptr == NULL) return reply_status(aecp, AVB_AECP_AEM_STATUS_BAD_ARGUMENTS, m, len); @@ -181,14 +181,17 @@ int handle_cmd_set_name_common(struct aecp *aecp, int64_t now, /** * IEEE 1722.1-2021: 7.4.17.1: The name does not contain a trailing NULL * but if the name is less than 64 bytes in length then it is zero - * padded + * padded. */ - memcpy(name_ptr, cmd->name, 64); + if (check_zero_padding(cmd->name, 64) == -1) + return reply_status(aecp, + AVB_AECP_AEM_STATUS_BAD_ARGUMENTS, m, len); - /** TODO: According to the specification, the string should alwasy be 0 - * terminated, the goal would be to check whether a string is UTF-8 and - * that it is correctly zero terminitaed if less than 64 char, if not - * then a simple memcpy is enough */ + if (validate_utf8(cmd->name, 64) == -1) + return reply_status(aecp, + AVB_AECP_AEM_STATUS_BAD_ARGUMENTS, m, len); + + memcpy(name_ptr, cmd->name, 64); rc = reply_success(aecp, m, len); if (rc < 0) diff --git a/src/modules/module-avb/aecp-aem.h b/src/modules/module-avb/aecp-aem.h index c94f416dc..997747ed8 100644 --- a/src/modules/module-avb/aecp-aem.h +++ b/src/modules/module-avb/aecp-aem.h @@ -250,7 +250,7 @@ struct avb_packet_aecp_aem_setget_name { uint16_t descriptor_index; uint16_t name_index; uint16_t configuration_index; - char name[64]; + unsigned char name[64]; } __attribute__ ((__packed__)); struct avb_packet_aecp_aem_setget_association_id { diff --git a/src/modules/module-avb/strings.c b/src/modules/module-avb/strings.c new file mode 100644 index 000000000..9877c1f8f --- /dev/null +++ b/src/modules/module-avb/strings.c @@ -0,0 +1,162 @@ +/* SPDX-FileCopyrightText: Copyright © 2026 Nils Tonnaett + * Nils Tonnaett */ +/* SPDX-License-Identifier: MIT */ + +#include "strings.h" + +#include + +typedef enum { + ST_START, + ST_A, + ST_B, + ST_C, + ST_D, + ST_E, + ST_F, + ST_G, +} UTF8_STATE; + +/* + * IEEE 1722.1 Section 7.4.17.1 + * + * We need to check if the buffer str of length len is valid UTF-8. + * The algorithm implemented here is based on the state machine by Frank Yung-Fong Tang + * described here at + * https://unicode.org/mail-arch/unicode-ml/y2003-m02/att-0467/01-The_Algorithm_to_Valide_an_UTF-8_String + */ +int validate_utf8 (const unsigned char *str, size_t len) +{ + UTF8_STATE state = ST_START; + bool err = false; + + for (unsigned int i = 0; i < len; ++i) + { + switch (state) + { + case ST_START: + if (str[i] <= 0x7F) + { + continue; + } + else if (str[i] >= 0xC2 && str[i] <= 0xDF) + { + state = ST_A; + } + else if (str[i] >= 0xE1 && str[i] <= 0xEC) + { + state = ST_B; + } + else if (str[i] >= 0xEE && str[i] <= 0xEF) + { + state = ST_B; + } + else if (str[i] == 0xE0) + { + state = ST_C; + } + else if (str[i] == 0xED) + { + state = ST_D; + } + else if (str[i] >= 0xF1 && str[i] <= 0xF3) + { + state = ST_E; + } + else if (str[i] == 0xF0) + { + state = ST_F; + } + else if (str[i] >= 0xF4) + { + state = ST_G; + } + else + { + err = true; + } + break; + case ST_A: + if (str[i] >= 0x80 && str[i] <= 0xBF) + state = ST_START; + else + err = true; + break; + case ST_B: + if (str[i] >= 0x80 && str[i] <= 0xBF) + state = ST_A; + else + err = true; + break; + case ST_C: + if (str[i] >= 0xA0 && str[i] <= 0xBF) + state = ST_A; + else + err = true; + break; + case ST_D: + if (str[i] >= 0x80 && str[i] <= 0x9F) + state = ST_A; + else + err = true; + break; + case ST_E: + if (str[i] >= 0x80 && str[i] <= 0xBF) + state = ST_B; + else + err = true; + break; + case ST_F: + if (str[i] >= 0x90 && str[i] <= 0xBF) + state = ST_B; + else + err = true; + break; + case ST_G: + if (str[i] >= 0x80 && str[i] <= 0x8F) + state = ST_B; + else + err = true; + break; + } + if (err == true) + { + return -1; + } + } + if (state != ST_START) + { + return -1; + } + else + { + return 0; + } +} + +/* + * For SET_NAME, strings need to be zero-padded if shorter than 64 bytes. A + * string of 64 bytes would NOT be nul-terminated. + * + * IEEE 1722.1 Section 7.4.17.1 + */ +int check_zero_padding (const unsigned char *str, size_t len) +{ + size_t str_len = strnlen ((char *)str, len); + /* String doesn't need to be null-terminated. Return success if there is no + * null in str */ + if (str_len == len) + { + return 0; + } + + for (unsigned int i = str_len; i < len; ++i) + { + if (str[i] != 0x00) + { + return -1; + } + } + + return 0; +} diff --git a/src/modules/module-avb/strings.h b/src/modules/module-avb/strings.h new file mode 100644 index 000000000..731b9e0eb --- /dev/null +++ b/src/modules/module-avb/strings.h @@ -0,0 +1,14 @@ +/* SPDX-FileCopyrightText: Copyright © 2026 Nils Tonnaett*/ +/* SPDX-License-Identifier: MIT */ + +#ifndef AVB_STRINGS_H +#define AVB_STRINGS_H + +#include +#include +#include + +int check_zero_padding(const unsigned char *str, size_t len); +int validate_utf8(const unsigned char *str, size_t len); + +#endif /* AVB_STRINGS_H */