From 25ae592093bf7c79f36d486c6614f94ce337331b Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 23 Feb 2026 18:22:02 +0100 Subject: [PATCH 1/3] Fix strncasecmp bounds and alpha-only case folding + Added unit tests for string.c --- src/string.c | 13 +- tools/unit-tests/Makefile | 12 +- tools/unit-tests/unit-string.c | 358 +++++++++++++++++++++++++++++++++ 3 files changed, 376 insertions(+), 7 deletions(-) create mode 100644 tools/unit-tests/unit-string.c diff --git a/src/string.c b/src/string.c index ed85f8fa03..384c9cfb9b 100644 --- a/src/string.c +++ b/src/string.c @@ -117,6 +117,11 @@ int strcmp(const char *s1, const char *s2) } #endif /* Renesas CCRX */ +static int is_alpha(int c) +{ + return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')); +} + int strcasecmp(const char *s1, const char *s2) { int diff = 0; @@ -124,7 +129,8 @@ int strcasecmp(const char *s1, const char *s2) while (!diff && *s1) { diff = (int)*s1 - (int)*s2; - if ((diff == 'A' - 'a') || (diff == 'a' - 'A')) + if (((diff == 'A' - 'a') || (diff == 'a' - 'A')) && + (is_alpha((unsigned char)*s1) || is_alpha((unsigned char)*s2))) diff = 0; s1++; @@ -142,12 +148,13 @@ int strncasecmp(const char *s1, const char *s2, size_t n) while (!diff && *s1) { diff = (int)*s1 - (int)*s2; - if ((diff == 'A' - 'a') || (diff == 'a' - 'A')) + if (((diff == 'A' - 'a') || (diff == 'a' - 'A')) && + (is_alpha((unsigned char)*s1) || is_alpha((unsigned char)*s2))) diff = 0; s1++; s2++; - if (++i > n) + if (++i >= n) break; } return diff; diff --git a/tools/unit-tests/Makefile b/tools/unit-tests/Makefile index d8785fee55..72aafa4c37 100644 --- a/tools/unit-tests/Makefile +++ b/tools/unit-tests/Makefile @@ -25,10 +25,11 @@ LDFLAGS+=-ftest-coverage -TESTS:=unit-parser unit-extflash unit-spi-flash unit-aes128 unit-aes256 \ - unit-chacha20 unit-pci unit-mock-state unit-sectorflags unit-image \ - unit-nvm unit-nvm-flagshome unit-enc-nvm unit-enc-nvm-flagshome \ - unit-delta unit-update-flash unit-update-ram unit-pkcs11_store +TESTS:=unit-parser unit-extflash unit-string unit-spi-flash unit-aes128 \ + unit-aes256 unit-chacha20 unit-pci unit-mock-state unit-sectorflags \ + unit-image unit-nvm unit-nvm-flagshome unit-enc-nvm \ + unit-enc-nvm-flagshome unit-delta unit-update-flash unit-update-ram \ + unit-pkcs11_store all: $(TESTS) @@ -94,6 +95,9 @@ unit-extflash: ../../include/target.h unit-extflash.c unit-spi-flash: ../../include/target.h unit-spi-flash.c gcc -o $@ $^ $(CFLAGS) $(LDFLAGS) +unit-string: ../../include/target.h unit-string.c + gcc -o $@ $^ $(CFLAGS) -DDEBUG_UART -DPRINTF_ENABLED $(LDFLAGS) + unit-aes128: ../../include/target.h unit-extflash.c gcc -o $@ $^ $(CFLAGS) $(LDFLAGS) diff --git a/tools/unit-tests/unit-string.c b/tools/unit-tests/unit-string.c new file mode 100644 index 0000000000..c35f79c6a3 --- /dev/null +++ b/tools/unit-tests/unit-string.c @@ -0,0 +1,358 @@ +/* unit-string.c + * + * Unit tests for string.c. + * + * + * Copyright (C) 2025 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#define FAST_MEMCPY +#define DEBUG_UART +#define PRINTF_ENABLED + +#include +#include +#include + +#include "string.c" + +static char uart_buf[256]; +static size_t uart_len; + +void uart_write(const char* buf, unsigned int sz) +{ + size_t i; + for (i = 0; i < sz && uart_len + 1 < sizeof(uart_buf); i++) { + uart_buf[uart_len++] = buf[i]; + } + uart_buf[uart_len] = '\0'; +} + +static void reset_uart_buf(void) +{ + memset(uart_buf, 0, sizeof(uart_buf)); + uart_len = 0; +} + +START_TEST(test_strncasecmp_n_zero) +{ + ck_assert_int_eq(strncasecmp("ABC", "abc", 0), 0); + ck_assert_int_eq(strncasecmp("A", "B", 0), 0); +} +END_TEST + +START_TEST(test_strncasecmp_n_one) +{ + ck_assert_int_eq(strncasecmp("A", "a", 1), 0); + ck_assert_int_lt(strncasecmp("A", "b", 1), 0); + ck_assert_int_gt(strncasecmp("b", "A", 1), 0); +} +END_TEST + +START_TEST(test_strncasecmp_n_exact) +{ + ck_assert_int_eq(strncasecmp("AbC", "aBc", 3), 0); + ck_assert_int_eq(strncasecmp("AbCd", "aBcE", 3), 0); +} +END_TEST + +START_TEST(test_strncasecmp_diff_before_n) +{ + ck_assert_int_lt(strncasecmp("abc", "abd", 3), 0); + ck_assert_int_gt(strncasecmp("abd", "abc", 3), 0); + ck_assert_int_lt(strncasecmp("", "a", 1), 0); + ck_assert_int_eq(strncasecmp("", "a", 0), 0); +} +END_TEST + +START_TEST(test_case_insensitive_alpha_only) +{ + ck_assert_int_ne(strcasecmp("@", "`"), 0); + ck_assert_int_ne(strcasecmp("[", "{"), 0); + ck_assert_int_ne(strcasecmp("]", "}"), 0); + ck_assert_int_ne(strncasecmp("@", "`", 1), 0); + ck_assert_int_ne(strcasecmp("a@", "A`"), 0); + ck_assert_int_ne(strncasecmp("a@", "A`", 2), 0); +} +END_TEST + +START_TEST(test_isalpha_helpers) +{ + ck_assert_int_eq(islower('a'), 1); + ck_assert_int_eq(islower('Z'), 0); + ck_assert_int_eq(isupper('Z'), 1); + ck_assert_int_eq(isupper('a'), 0); + ck_assert_int_eq(isalpha('Q'), 1); + ck_assert_int_eq(isalpha('q'), 1); + ck_assert_int_eq(isalpha('1'), 0); + ck_assert_int_eq(tolower('A'), 'a'); + ck_assert_int_eq(tolower('a'), 'a'); + ck_assert_int_eq(toupper('a'), 'A'); + ck_assert_int_eq(toupper('A'), 'A'); + ck_assert_int_eq(tolower('1'), '1'); + ck_assert_int_eq(toupper('1'), '1'); +} +END_TEST + +START_TEST(test_memset_memcmp_memchr) +{ + uint8_t buf[8]; + uint8_t other[8]; + + memset(buf, 0xAA, sizeof(buf)); + memset(other, 0xAA, sizeof(other)); + ck_assert_int_eq(memcmp(buf, other, sizeof(buf)), 0); + + other[3] = 0xAB; + ck_assert_int_lt(memcmp(buf, other, sizeof(buf)), 0); + ck_assert_int_eq(memcmp(buf, other, 0), 0); + + ck_assert_ptr_eq(memchr(buf, 0xAA, sizeof(buf)), &buf[0]); + ck_assert_ptr_eq(memchr(buf, 0xAB, sizeof(buf)), NULL); + buf[6] = 0xAA; + ck_assert_ptr_eq(memchr(buf, 0xAA, 8), &buf[0]); + ck_assert_ptr_eq(memchr(buf, 0xAA, 1), &buf[0]); + ck_assert_ptr_eq(memchr(buf, 0xAA, 0), NULL); + ck_assert_ptr_eq(memchr(buf, 0xAA, 7), &buf[0]); +} +END_TEST + +START_TEST(test_strlen_strcmp) +{ + ck_assert_uint_eq(strlen(""), 0); + ck_assert_uint_eq(strlen("abc"), 3); + ck_assert_int_eq(strcmp("abc", "abc"), 0); + ck_assert_int_lt(strcmp("abc", "abd"), 0); + ck_assert_int_gt(strcmp("abe", "abd"), 0); + ck_assert_int_lt(strcmp("", "a"), 0); + ck_assert_int_gt(strcmp("a", ""), 0); +} +END_TEST + +START_TEST(test_strcpy_strncpy_strcat_strncat) +{ + char buf[8]; + char dest[8]; + char short_dest[4]; + + strcpy(buf, "hi"); + ck_assert_str_eq(buf, "hi"); + + memset(dest, 0, sizeof(dest)); + strncpy(dest, "abc", 4); + ck_assert_str_eq(dest, "abc"); + + memset(short_dest, 'X', sizeof(short_dest)); + strncpy(short_dest, "abcdef", 3); + ck_assert_int_eq(short_dest[0], 'a'); + ck_assert_int_eq(short_dest[1], 'b'); + ck_assert_int_eq(short_dest[2], 'c'); + ck_assert_int_eq(short_dest[3], 'X'); + + strcpy(dest, "a"); + strcat(dest, "b"); + ck_assert_str_eq(dest, "ab"); + + strcpy(dest, "a"); + strncat(dest, "bc", 3); + ck_assert_str_eq(dest, "ab"); + + strcpy(dest, "a"); + strncat(dest, "bc", 1); + ck_assert_str_eq(dest, "a"); + + strcpy(dest, ""); + strncat(dest, "x", 2); + ck_assert_str_eq(dest, "x"); +} +END_TEST + +START_TEST(test_strncmp) +{ + ck_assert_int_eq(strncmp("abc", "abc", 3), 0); + ck_assert_int_lt(strncmp("abc", "abd", 3), 0); + ck_assert_int_eq(strncmp("abc", "abd", 2), 0); + ck_assert_int_eq(strncmp("abc", "abc", 0), 0); + ck_assert_int_lt(strncmp("", "a", 1), 0); + ck_assert_int_gt(strncmp("a", "", 1), 0); +} +END_TEST + +START_TEST(test_memcpy_memmove) +{ + union { + unsigned long align; + unsigned char buf[32]; + } storage; + unsigned char src[16]; + unsigned char dst[16]; + unsigned char *p = (unsigned char *)&storage.align; + int i; + + for (i = 0; i < 16; i++) { + src[i] = (unsigned char)i; + dst[i] = 0; + p[i] = (unsigned char)i; + } + + memcpy(dst, src, 16); + ck_assert_int_eq(memcmp(src, dst, 16), 0); + + memmove(p + 2, p, 8); + ck_assert_int_eq(p[2], 0); + ck_assert_int_eq(p[3], 1); + + memmove(p, p + 2, 8); + ck_assert_int_eq(p[0], 0); + ck_assert_int_eq(p[1], 1); + + ck_assert_ptr_eq(memmove(p, p, 4), p); +} +END_TEST + +START_TEST(test_memcpy_fastpath_aligned) +{ + union { + unsigned long align; + unsigned char buf[64]; + } storage; + unsigned char *src = (unsigned char *)&storage.align; + unsigned char *dst = src + 16; + int i; + + for (i = 0; i < 32; i++) { + src[i] = (unsigned char)(i + 1); + dst[i] = 0; + } + + memcpy(dst, src, 32); + ck_assert_int_eq(memcmp(src, dst, 32), 0); +} +END_TEST + +START_TEST(test_uart_writenum_basic) +{ + reset_uart_buf(); + uart_writenum(0, 10, 0, 0); + ck_assert_str_eq(uart_buf, "0"); + + reset_uart_buf(); + uart_writenum(255, 16, 0, 0); + ck_assert_str_eq(uart_buf, "FF"); + + reset_uart_buf(); + uart_writenum(-5, 10, 0, 0); + ck_assert_str_eq(uart_buf, "-5"); + + reset_uart_buf(); + uart_writenum(7, 10, 1, 4); + ck_assert_str_eq(uart_buf, "0007"); + + reset_uart_buf(); + uart_writenum(1, 10, 1, 2); + ck_assert_str_eq(uart_buf, "01"); + + reset_uart_buf(); + uart_writenum(0x1234, 16, 1, 6); + ck_assert_str_eq(uart_buf, "001234"); + + reset_uart_buf(); + uart_writenum(1, 10, 1, 64); + ck_assert_int_eq(uart_buf[0], '0'); +} +END_TEST + +START_TEST(test_uart_printf_formats) +{ + reset_uart_buf(); + uart_printf("X=%u Y=%d Z=%x W=%X %% %s %c", 5U, -3, 0x2a, 0x2a, "ok", '!'); + ck_assert_str_eq(uart_buf, "X=5 Y=-3 Z=2A W=2A % ok !"); + + reset_uart_buf(); + uart_printf("%04u", 12U); + ck_assert_str_eq(uart_buf, "0012"); + + reset_uart_buf(); + uart_printf("%p", (void*)0x10); + ck_assert_str_eq(uart_buf, "0x10"); + + reset_uart_buf(); + uart_printf("A%08uB", 12U); + ck_assert_str_eq(uart_buf, "A00000012B"); + + reset_uart_buf(); + uart_printf("%12u", 3U); + ck_assert_str_eq(uart_buf, "3"); + + reset_uart_buf(); + uart_printf("%lu", (unsigned long)7); + ck_assert_str_eq(uart_buf, "7"); + + reset_uart_buf(); + uart_printf("%zu", (size_t)9); + ck_assert_str_eq(uart_buf, "9"); + + reset_uart_buf(); + uart_printf("bad%qend"); + ck_assert_str_eq(uart_buf, "badend"); + + reset_uart_buf(); + uart_printf("%i", -1); + ck_assert_str_eq(uart_buf, "-1"); +} +END_TEST + +Suite *string_suite(void) +{ + Suite *s = suite_create("String"); + TCase *tcase_strncasecmp = tcase_create("strncasecmp"); + TCase *tcase_misc = tcase_create("misc"); + + tcase_add_test(tcase_strncasecmp, test_strncasecmp_n_zero); + tcase_add_test(tcase_strncasecmp, test_strncasecmp_n_one); + tcase_add_test(tcase_strncasecmp, test_strncasecmp_n_exact); + tcase_add_test(tcase_strncasecmp, test_strncasecmp_diff_before_n); + tcase_add_test(tcase_strncasecmp, test_case_insensitive_alpha_only); + tcase_add_test(tcase_misc, test_isalpha_helpers); + tcase_add_test(tcase_misc, test_memset_memcmp_memchr); + tcase_add_test(tcase_misc, test_strlen_strcmp); + tcase_add_test(tcase_misc, test_strcpy_strncpy_strcat_strncat); + tcase_add_test(tcase_misc, test_strncmp); + tcase_add_test(tcase_misc, test_memcpy_memmove); + tcase_add_test(tcase_misc, test_uart_writenum_basic); + tcase_add_test(tcase_misc, test_uart_printf_formats); + + suite_add_tcase(s, tcase_strncasecmp); + suite_add_tcase(s, tcase_misc); + + return s; +} + +int main(void) +{ + int fails; + Suite *s = string_suite(); + SRunner *sr = srunner_create(s); + + srunner_run_all(sr, CK_NORMAL); + fails = srunner_ntests_failed(sr); + srunner_free(sr); + + return fails; +} From 6a5762d2410cffacfe6ad6bd1091732e161344b9 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 23 Feb 2026 19:38:14 +0100 Subject: [PATCH 2/3] Addressed copilot review comments - Converted 'strncat' to the standard POSIX behavior. Fixed fdt-parser to use the right len argument. --- src/string.c | 20 ++++++-------------- tools/fdt-parser/fdt-parser.c | 4 +++- tools/unit-tests/unit-string.c | 10 +++++----- 3 files changed, 14 insertions(+), 20 deletions(-) diff --git a/src/string.c b/src/string.c index 384c9cfb9b..7245ead8cd 100644 --- a/src/string.c +++ b/src/string.c @@ -117,11 +117,6 @@ int strcmp(const char *s1, const char *s2) } #endif /* Renesas CCRX */ -static int is_alpha(int c) -{ - return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')); -} - int strcasecmp(const char *s1, const char *s2) { int diff = 0; @@ -130,7 +125,7 @@ int strcasecmp(const char *s1, const char *s2) diff = (int)*s1 - (int)*s2; if (((diff == 'A' - 'a') || (diff == 'a' - 'A')) && - (is_alpha((unsigned char)*s1) || is_alpha((unsigned char)*s2))) + (isalpha((unsigned char)*s1) && isalpha((unsigned char)*s2))) diff = 0; s1++; @@ -149,7 +144,7 @@ int strncasecmp(const char *s1, const char *s2, size_t n) diff = (int)*s1 - (int)*s2; if (((diff == 'A' - 'a') || (diff == 'a' - 'A')) && - (is_alpha((unsigned char)*s1) || is_alpha((unsigned char)*s2))) + (isalpha((unsigned char)*s1) && isalpha((unsigned char)*s2))) diff = 0; s1++; @@ -163,16 +158,13 @@ int strncasecmp(const char *s1, const char *s2, size_t n) #if !defined(__CCRX__) /* Renesas CCRX */ char *strncat(char *dest, const char *src, size_t n) { - size_t i = 0; + size_t i; size_t j = strlen(dest); - for (i = 0; i < strlen(src); i++) { - if (j >= (n - 1)) { - break; - } - dest[j++] = src[i]; + for (i = 0; i < n && src[i] != '\0'; i++) { + dest[j + i] = src[i]; } - dest[j] = '\0'; + dest[j + i] = '\0'; return dest; } diff --git a/tools/fdt-parser/fdt-parser.c b/tools/fdt-parser/fdt-parser.c index 66e63a8dd9..c33c96d5ea 100644 --- a/tools/fdt-parser/fdt-parser.c +++ b/tools/fdt-parser/fdt-parser.c @@ -498,7 +498,9 @@ int main(int argc, char *argv[]) if (ret == 0) { char outfilename[PATH_MAX]; strncpy(outfilename, filename, sizeof(outfilename)-1); - strncat(outfilename, ".out", sizeof(outfilename)-1); + outfilename[sizeof(outfilename) - 1] = '\0'; + strncat(outfilename, ".out", + sizeof(outfilename) - strlen(outfilename) - 1); /* save updated binary file */ write_bin(outfilename, image, imageSz + UNIT_TEST_GROW_SIZE); diff --git a/tools/unit-tests/unit-string.c b/tools/unit-tests/unit-string.c index c35f79c6a3..f0fad0ec58 100644 --- a/tools/unit-tests/unit-string.c +++ b/tools/unit-tests/unit-string.c @@ -23,8 +23,6 @@ */ #define FAST_MEMCPY -#define DEBUG_UART -#define PRINTF_ENABLED #include #include @@ -86,7 +84,9 @@ START_TEST(test_case_insensitive_alpha_only) ck_assert_int_ne(strcasecmp("@", "`"), 0); ck_assert_int_ne(strcasecmp("[", "{"), 0); ck_assert_int_ne(strcasecmp("]", "}"), 0); + ck_assert_int_ne(strcasecmp("!", "A"), 0); ck_assert_int_ne(strncasecmp("@", "`", 1), 0); + ck_assert_int_ne(strncasecmp("!", "A", 1), 0); ck_assert_int_ne(strcasecmp("a@", "A`"), 0); ck_assert_int_ne(strncasecmp("a@", "A`", 2), 0); } @@ -171,11 +171,11 @@ START_TEST(test_strcpy_strncpy_strcat_strncat) strcpy(dest, "a"); strncat(dest, "bc", 3); - ck_assert_str_eq(dest, "ab"); + ck_assert_str_eq(dest, "abc"); strcpy(dest, "a"); strncat(dest, "bc", 1); - ck_assert_str_eq(dest, "a"); + ck_assert_str_eq(dest, "ab"); strcpy(dest, ""); strncat(dest, "x", 2); @@ -289,7 +289,7 @@ START_TEST(test_uart_printf_formats) ck_assert_str_eq(uart_buf, "0012"); reset_uart_buf(); - uart_printf("%p", (void*)0x10); + uart_printf("%p", (void*)(uintptr_t)0x10); ck_assert_str_eq(uart_buf, "0x10"); reset_uart_buf(); From 2244688d70e8546f35900f427bc0fde7c285e479 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 23 Feb 2026 20:59:55 +0100 Subject: [PATCH 3/3] Renamed function as suggested by copilot --- tools/unit-tests/unit-string.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/unit-tests/unit-string.c b/tools/unit-tests/unit-string.c index f0fad0ec58..f1479f6249 100644 --- a/tools/unit-tests/unit-string.c +++ b/tools/unit-tests/unit-string.c @@ -226,7 +226,7 @@ START_TEST(test_memcpy_memmove) } END_TEST -START_TEST(test_memcpy_fastpath_aligned) +START_TEST(test_memcpy_aligned_buffers) { union { unsigned long align; @@ -335,6 +335,7 @@ Suite *string_suite(void) tcase_add_test(tcase_misc, test_strcpy_strncpy_strcat_strncat); tcase_add_test(tcase_misc, test_strncmp); tcase_add_test(tcase_misc, test_memcpy_memmove); + tcase_add_test(tcase_misc, test_memcpy_aligned_buffers); tcase_add_test(tcase_misc, test_uart_writenum_basic); tcase_add_test(tcase_misc, test_uart_printf_formats);