From 5ccabbbbc72fd4b5179a1d69291d9129e318f033 Mon Sep 17 00:00:00 2001 From: Mostafa Salman Date: Sun, 4 Aug 2024 11:07:38 +0300 Subject: [PATCH] Add Support for Annex K functions according to C11 Adding an implementation of the bounds-checking C functions (as specified in Annex K of the C11 standard) to the PicoLibc. These functions lower the risk of introducing security vulnerabilities such as buffer overflows and format string vulnerabilities into your code by providing clear and easy-to-use interfaces. For each C function a secure alternate function ending in a "_s" postfix is provided (e.g., strcpy_s). Use of these functions is recommended by security experts and secure coding standards. also, Implemented unit tests for the Annex-K functions to ensure their corrctness. Covered various scenarios including normal operation, boundary conditions, and error handling. Signed-off-by: Mostafa Salman --- newlib/libc/include/errno.h | 2 +- newlib/libc/include/stdint.h | 11 +- newlib/libc/include/stdlib.h | 23 ++- newlib/libc/include/string.h | 22 ++- newlib/libc/include/sys/_types.h | 11 ++ newlib/libc/include/sys/errno.h | 2 +- newlib/libc/stdlib/CMakeLists.txt | 1 + newlib/libc/stdlib/local_s.h | 52 ++++++ newlib/libc/stdlib/meson.build | 1 + newlib/libc/stdlib/set_constraint_handler_s.c | 61 ++++++ newlib/libc/string/CMakeLists.txt | 10 + newlib/libc/string/memcpy_s.c | 94 ++++++++++ newlib/libc/string/memmove_s.c | 86 +++++++++ newlib/libc/string/memset_s.c | 79 ++++++++ newlib/libc/string/meson.build | 11 ++ newlib/libc/string/strcat_s.c | 142 ++++++++++++++ newlib/libc/string/strcpy_s.c | 125 +++++++++++++ newlib/libc/string/strerror.c | 1 + newlib/libc/string/strerror_s.c | 85 +++++++++ newlib/libc/string/strerrorlen_s.c | 43 +++++ newlib/libc/string/string_private.h | 44 +++++ newlib/libc/string/strncat_s.c | 169 +++++++++++++++++ newlib/libc/string/strncpy_s.c | 147 +++++++++++++++ newlib/libc/string/strnlen_s.c | 60 ++++++ newlib/libc/tinystdio/CMakeLists.txt | 1 + newlib/libc/tinystdio/meson.build | 1 + newlib/libc/tinystdio/sprintf_s.c | 147 +++++++++++++++ newlib/libc/tinystdio/stdio.h | 12 ++ test/CMakeLists.txt | 11 ++ test/meson.build | 65 +++++++ test/test-memcpy_s.c | 151 +++++++++++++++ test/test-memmove_s.c | 150 +++++++++++++++ test/test-memset_s.c | 142 ++++++++++++++ test/test-sprintf_s.c | 152 +++++++++++++++ test/test-strcat_s.c | 161 ++++++++++++++++ test/test-strcpy_s.c | 155 ++++++++++++++++ test/test-strerror_s.c | 146 +++++++++++++++ test/test-strerrorlen_s.c | 125 +++++++++++++ test/test-strncat_s.c | 174 ++++++++++++++++++ test/test-strncpy_s.c | 154 ++++++++++++++++ test/test-strnlen_s.c | 150 +++++++++++++++ 41 files changed, 3173 insertions(+), 6 deletions(-) create mode 100644 newlib/libc/stdlib/local_s.h create mode 100644 newlib/libc/stdlib/set_constraint_handler_s.c create mode 100644 newlib/libc/string/memcpy_s.c create mode 100644 newlib/libc/string/memmove_s.c create mode 100644 newlib/libc/string/memset_s.c create mode 100644 newlib/libc/string/strcat_s.c create mode 100644 newlib/libc/string/strcpy_s.c create mode 100644 newlib/libc/string/strerror_s.c create mode 100644 newlib/libc/string/strerrorlen_s.c create mode 100644 newlib/libc/string/string_private.h create mode 100644 newlib/libc/string/strncat_s.c create mode 100644 newlib/libc/string/strncpy_s.c create mode 100644 newlib/libc/string/strnlen_s.c create mode 100644 newlib/libc/tinystdio/sprintf_s.c create mode 100644 test/test-memcpy_s.c create mode 100644 test/test-memmove_s.c create mode 100644 test/test-memset_s.c create mode 100644 test/test-sprintf_s.c create mode 100644 test/test-strcat_s.c create mode 100644 test/test-strcpy_s.c create mode 100644 test/test-strerror_s.c create mode 100644 test/test-strerrorlen_s.c create mode 100644 test/test-strncat_s.c create mode 100644 test/test-strncpy_s.c create mode 100644 test/test-strnlen_s.c diff --git a/newlib/libc/include/errno.h b/newlib/libc/include/errno.h index d52446d122..d6de5f05de 100644 --- a/newlib/libc/include/errno.h +++ b/newlib/libc/include/errno.h @@ -43,4 +43,4 @@ typedef int error_t; #include -#endif /* !__ERRNO_H__ */ +#endif /* !__ERRNO_H__ */ \ No newline at end of file diff --git a/newlib/libc/include/stdint.h b/newlib/libc/include/stdint.h index d7e9bf3666..ddfd00fe36 100644 --- a/newlib/libc/include/stdint.h +++ b/newlib/libc/include/stdint.h @@ -457,6 +457,13 @@ typedef __uint_least64_t uint_least64_t; #endif #endif -_END_STD_C +#if __STDC_WANT_LIB_EXT1__ == 1 + // could be defined by the user +#ifndef RSIZE_MAX +#define RSIZE_MAX SIZE_MAX +#endif +#endif + + _END_STD_C -#endif /* _STDINT_H */ +#endif /* _STDINT_H */ \ No newline at end of file diff --git a/newlib/libc/include/stdlib.h b/newlib/libc/include/stdlib.h index 7468be3a93..7571ed1ecb 100644 --- a/newlib/libc/include/stdlib.h +++ b/newlib/libc/include/stdlib.h @@ -408,10 +408,31 @@ char *__ldtoa (long double, int, int, int *, int *, char **); void __eprintf (const char *, const char *, unsigned int, const char *); #endif +#if __STDC_WANT_LIB_EXT1__ == 1 +#include + +#ifndef _ERRNO_T_DEFINED +typedef __errno_t errno_t; +#define _ERRNO_T_DEFINED +#endif + +#ifndef _RSIZE_T_DEFINED +typedef __rsize_t rsize_t; +#define _RSIZE_T_DEFINED +#endif + +typedef void (*constraint_handler_t)(const char *restrict msg, + void *restrict ptr, __errno_t error); + +constraint_handler_t set_constraint_handler_s(constraint_handler_t handler); +void abort_handler_s(const char *restrict msg, void *restrict ptr, + __errno_t error); +#endif + _END_STD_C #if __SSP_FORTIFY_LEVEL > 0 #include #endif -#endif /* _STDLIB_H_ */ +#endif /* _STDLIB_H_ */ \ No newline at end of file diff --git a/newlib/libc/include/string.h b/newlib/libc/include/string.h index d459f06bae..695048e90a 100644 --- a/newlib/libc/include/string.h +++ b/newlib/libc/include/string.h @@ -201,6 +201,26 @@ int timingsafe_bcmp (const void *, const void *, size_t); int timingsafe_memcmp (const void *, const void *, size_t); #endif +#if __STDC_WANT_LIB_EXT1__ == 1 +#include + +#ifndef _RSIZE_T_DEFINED +typedef __rsize_t rsize_t; +#define _RSIZE_T_DEFINED +#endif + +__errno_t memcpy_s(void *__restrict, rsize_t, const void *__restrict, rsize_t); +__errno_t memset_s(void *, rsize_t, int, rsize_t); +__errno_t memmove_s(void *, rsize_t, const void *, rsize_t); +__errno_t strcpy_s(char *__restrict, rsize_t, const char *__restrict); +__errno_t strcat_s(char *__restrict, rsize_t, const char *__restrict); +__errno_t strncpy_s(char *__restrict, rsize_t, const char *__restrict, rsize_t); +__errno_t strncat_s(char *__restrict, rsize_t, const char *__restrict, rsize_t); +size_t strnlen_s(const char *, size_t); +__errno_t strerror_s(char *, rsize_t, __errno_t); /* C11 */ +size_t strerrorlen_s(__errno_t); +#endif + #include _END_STD_C @@ -209,4 +229,4 @@ _END_STD_C #include #endif -#endif /* _STRING_H_ */ +#endif /* _STRING_H_ */ \ No newline at end of file diff --git a/newlib/libc/include/sys/_types.h b/newlib/libc/include/sys/_types.h index c38b7d1e60..66c8824457 100644 --- a/newlib/libc/include/sys/_types.h +++ b/newlib/libc/include/sys/_types.h @@ -258,4 +258,15 @@ typedef unsigned short __nlink_t; typedef long __suseconds_t; /* microseconds (signed) */ typedef unsigned long __useconds_t; /* microseconds (unsigned) */ +#ifdef __STDC_WANT_LIB_EXT1__ +#if (__STDC_WANT_LIB_EXT1__ != 0) && (__STDC_WANT_LIB_EXT1__ != 1) +#error Please define __STDC_WANT_LIB_EXT__ as 0 or 1 +#endif + +#if __STDC_WANT_LIB_EXT1__ == 1 +typedef size_t __rsize_t; +typedef int __errno_t; +#endif +#endif + #endif /* _SYS__TYPES_H */ diff --git a/newlib/libc/include/sys/errno.h b/newlib/libc/include/sys/errno.h index 7b0d8d33a9..4b5e56358a 100644 --- a/newlib/libc/include/sys/errno.h +++ b/newlib/libc/include/sys/errno.h @@ -223,4 +223,4 @@ extern NEWLIB_THREAD_LOCAL_ERRNO int errno; _END_STD_C -#endif /* _SYS_ERRNO_H */ +#endif /* _SYS_ERRNO_H */ \ No newline at end of file diff --git a/newlib/libc/stdlib/CMakeLists.txt b/newlib/libc/stdlib/CMakeLists.txt index 523243ba5f..8df4955c05 100644 --- a/newlib/libc/stdlib/CMakeLists.txt +++ b/newlib/libc/stdlib/CMakeLists.txt @@ -115,6 +115,7 @@ picolibc_sources( pico-exit.c pico-onexit.c pico-cxa-atexit.c + set_constraint_handler_s.c ) picolibc_sources_flags("-fno-builtin-malloc;-fno-builtin-free" diff --git a/newlib/libc/stdlib/local_s.h b/newlib/libc/stdlib/local_s.h new file mode 100644 index 0000000000..a50acc5864 --- /dev/null +++ b/newlib/libc/stdlib/local_s.h @@ -0,0 +1,52 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright © 2024, Synopsys Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _LOCAL_S_H_ +#define _LOCAL_S_H_ + +#if __STDC_WANT_LIB_EXT1__ == 1 +#include +#include + +#if RSIZE_MAX == SIZE_MAX +#define CHECK_RSIZE(s) 0 +#else +#define CHECK_RSIZE(s) (RSIZE_MAX == SIZE_MAX ? 0 : (s) > RSIZE_MAX) +#endif + +extern constraint_handler_t __cur_handler; + +#endif +#endif \ No newline at end of file diff --git a/newlib/libc/stdlib/meson.build b/newlib/libc/stdlib/meson.build index 2c31fd2c9b..fffe4c8398 100644 --- a/newlib/libc/stdlib/meson.build +++ b/newlib/libc/stdlib/meson.build @@ -161,6 +161,7 @@ srcs_stdlib = [ 'wctob.c', 'wctomb.c', 'wctomb_r.c', + 'set_constraint_handler_s.c', ] srcs_stdlib_stdio = [ diff --git a/newlib/libc/stdlib/set_constraint_handler_s.c b/newlib/libc/stdlib/set_constraint_handler_s.c new file mode 100644 index 0000000000..dd4c996389 --- /dev/null +++ b/newlib/libc/stdlib/set_constraint_handler_s.c @@ -0,0 +1,61 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright © 2024, Synopsys Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#define __STDC_WANT_LIB_EXT1__ 1 +#include "local_s.h" + +constraint_handler_t __cur_handler = abort_handler_s; + +void +abort_handler_s(const char *restrict msg, void *restrict ptr, __errno_t error) +{ + (void)msg; + (void)ptr; + (void)error; + abort(); +} + +constraint_handler_t +set_constraint_handler_s(constraint_handler_t handler) +{ + constraint_handler_t h = __cur_handler; + + if (handler == (constraint_handler_t)NULL) { + __cur_handler = abort_handler_s; // null restores to default handler + } else { + __cur_handler = handler; + } + + return h; +} diff --git a/newlib/libc/string/CMakeLists.txt b/newlib/libc/string/CMakeLists.txt index d31976e928..48ce38aa5c 100644 --- a/newlib/libc/string/CMakeLists.txt +++ b/newlib/libc/string/CMakeLists.txt @@ -135,4 +135,14 @@ picolibc_sources( wmempcpy.c wmemset.c xpg_strerror_r.c + memcpy_s.c + memmove_s.c + memset_s.c + strcat_s.c + strcpy_s.c + strerror_s.c + strerrorlen_s.c + strncat_s.c + strncpy_s.c + strnlen_s.c ) diff --git a/newlib/libc/string/memcpy_s.c b/newlib/libc/string/memcpy_s.c new file mode 100644 index 0000000000..84c5e91499 --- /dev/null +++ b/newlib/libc/string/memcpy_s.c @@ -0,0 +1,94 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright © 2024, Synopsys Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#define __STDC_WANT_LIB_EXT1__ 1 +#include +#include +#include "string_private.h" + +__errno_t +memcpy_s(void *restrict s1, rsize_t s1max, const void *restrict s2, rsize_t n) +{ + const char *msg = ""; + + if (s1 == NULL) { + msg = "memcpy_s: dest is NULL"; + goto handle_error; + } + + if (CHECK_RSIZE(s1max)) { + msg = "memcpy_s: buffer size exceeds RSIZE_MAX"; + goto handle_error; + } + + if (s2 == NULL) { + msg = "memcpy_s: source is NULL"; + goto handle_error; + } + + if (CHECK_RSIZE(n)) { + msg = "memcpy_s: copy count exceeds RSIZE_MAX"; + goto handle_error; + } + + if (n > s1max) { + msg = "memcpy_s: copy count exceeds buffer size"; + goto handle_error; + } + + const char *s1cp = (const char *)s1; + const char *s2cp = (const char *)s2; + const char *s1cp_limit = &s1cp[n]; + const char *s2cp_limit = &s2cp[n]; + + if (((s1cp_limit <= s2cp) || (s2cp_limit <= s1cp)) == false) { + msg = "memcpy_s: overlapping copy"; + goto handle_error; + } + + // Normal return path + (void)memcpy(s1, s2, n); + return 0; + +handle_error: + if (s1 != NULL) { + (void)memset(s1, (int32_t)'\0', s1max); + } + + if (__cur_handler != NULL) { + __cur_handler(msg, NULL, -1); + } + + return -1; +} diff --git a/newlib/libc/string/memmove_s.c b/newlib/libc/string/memmove_s.c new file mode 100644 index 0000000000..1b6b217f5d --- /dev/null +++ b/newlib/libc/string/memmove_s.c @@ -0,0 +1,86 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright © 2024, Synopsys Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#define __STDC_WANT_LIB_EXT1__ 1 +#include +#include +#include "string_private.h" + +__errno_t +memmove_s(void *s1, rsize_t s1max, const void *s2, rsize_t n) +{ + const char *msg = ""; + + if (s1 == NULL) { + msg = "memmove_s: dest is NULL"; + goto handle_error; + } + + if (CHECK_RSIZE(s1max)) { + msg = "memmove_s: buffer size exceeds RSIZE_MAX"; + goto handle_error; + } + + if (s2 == NULL) { + msg = "memmove_s: source is NULL"; + goto handle_error; + } + + if (CHECK_RSIZE(n)) { + msg = "memmove_s: copy count exceeds RSIZE_MAX"; + goto handle_error; + } + + if (n > s1max) { + msg = "memmove_s: copy count exceeds buffer size"; + goto handle_error; + } + + /* overlapping memory is allowed for memmove_s so no checks for that */ + + // Normal return path + (void)memmove(s1, s2, n); + return 0; + +handle_error: + if (s1 != NULL) { + (void)memset(s1, (int32_t)'\0', s1max); + } + + if (__cur_handler != NULL) { + __cur_handler(msg, NULL, -1); + } + + return -1; +} diff --git a/newlib/libc/string/memset_s.c b/newlib/libc/string/memset_s.c new file mode 100644 index 0000000000..ca8b1e117b --- /dev/null +++ b/newlib/libc/string/memset_s.c @@ -0,0 +1,79 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright © 2024, Synopsys Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#define __STDC_WANT_LIB_EXT1__ 1 +#include +#include +#include "string_private.h" + +__errno_t +memset_s(void *s, rsize_t smax, int c, rsize_t n) +{ + const char *msg = ""; + + if (s == NULL) { + msg = "memset_s: dest is NULL"; + goto handle_error; + } + + if (CHECK_RSIZE(smax)) { + msg = "memset_s: buffer size exceeds RSIZE_MAX"; + goto handle_error; + } + + if (CHECK_RSIZE(n)) { + msg = "memset_s: count exceeds RSIZE_MAX"; + goto handle_error; + } + + if (n > smax) { + msg = "memset_s: count exceeds buffer size"; + goto handle_error; + } + + // Normal return path + (void)memset(s, c, n); + return 0; + +handle_error: + if (s != NULL) { + (void)memset(s, c, smax); + } + + if (__cur_handler != NULL) { + __cur_handler(msg, NULL, -1); + } + + return -1; +} diff --git a/newlib/libc/string/meson.build b/newlib/libc/string/meson.build index 9aac49ea23..e7e9f264ce 100644 --- a/newlib/libc/string/meson.build +++ b/newlib/libc/string/meson.build @@ -134,11 +134,22 @@ srcs_string = [ 'wmempcpy.c', 'wmemset.c', 'xpg_strerror_r.c', + 'memcpy_s.c', + 'memmove_s.c', + 'memset_s.c', + 'strcat_s.c', + 'strcpy_s.c', + 'strerror_s.c', + 'strerrorlen_s.c', + 'strncat_s.c', + 'strncpy_s.c', + 'strnlen_s.c', ] hdrs_string = [ 'local.h', 'str-two-way.h', + 'string_private.h', ] srcs_strcmp = [ diff --git a/newlib/libc/string/strcat_s.c b/newlib/libc/string/strcat_s.c new file mode 100644 index 0000000000..64fabbe0d5 --- /dev/null +++ b/newlib/libc/string/strcat_s.c @@ -0,0 +1,142 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright © 2024, Synopsys Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#define __STDC_WANT_LIB_EXT1__ 1 +#include +#include +#include "string_private.h" + +__errno_t +strcat_s(char *restrict s1, rsize_t s1max, const char *restrict s2) +{ + const char *msg = ""; + size_t s1_len = 0; + bool write_null = true; + + if (s1 == NULL) { + msg = "strcat_s: dest is NULL"; + write_null = false; + goto handle_error; + } + + if ((s1max == 0) || (CHECK_RSIZE(s1max))) { + msg = "strcat_s: dest buffer size is 0 or exceeds RSIZE_MAX"; + write_null = false; + goto handle_error; + } + + if (s2 == NULL) { + msg = "strcat_s: source is NULL"; + goto handle_error; + } + + /* It is a constraint violation if s1max is not large enough to contain + * the concatenation s2: no truncation permitted. + * It is also a constraint violation if the string pointed to by s2 + * overlaps s1 in any way. + * The C11 Rationale says we are permitted to proceed with the copy and + * detect dest buffer overrun and overlapping memory blocks as a byproduct + * of performing the copy operation. This is to avoid calling strlen on + * s2 to detect these violations prior to attempting the copy. + */ + // compute chars available in s1 + s1_len = strnlen_s(s1, s1max); + if (s1_len == s1max) { + msg = "strcat_s: string 1 length exceeds buffer size"; + goto handle_error; + } + + const char *overlap_point; + bool check_s1_for_overlap; + unsigned m = s1max - s1_len; + char *s1cp = s1 + s1_len; + const char *s2cp = s2; + + if (s1 <= s2) { + // if we ever reach s2 when storing to s1 we have overlap + overlap_point = s2; + check_s1_for_overlap = true; + // make sure source does not lie within initial dest string. + if (s2 <= s1cp) { + msg = "strcat_s: overlapping copy"; + goto handle_error; + } + } else { + // if we ever reach s1 when reading from s2 we have overlap + overlap_point = s1; + check_s1_for_overlap = false; + // issue with checking initial dest string does not apply in this + // case, overlap will be detected only by hitting overlap_point. + } + + unsigned written = 0; + char c = '.'; + while (written < m) { + if (check_s1_for_overlap) { + if (s1cp == overlap_point) { + msg = "strcat_s: overlapping copy"; + goto handle_error; + } + } else if (s2cp == overlap_point) { + msg = "strcat_s: overlapping copy"; + goto handle_error; + } + + c = *s2cp++; + *s1cp++ = c; + written++; + if (c == '\0') { + break; + } + } + + if (c != '\0') { + msg = "strcat_s: dest buffer size insufficent to append string"; + goto handle_error; + } + + // Normal return path + return 0; + +handle_error: + if (write_null && s1 != NULL) { + *s1 = '\0'; + } + + if (__cur_handler != NULL) { + __cur_handler(msg, NULL, -1); + } + + return -1; +} diff --git a/newlib/libc/string/strcpy_s.c b/newlib/libc/string/strcpy_s.c new file mode 100644 index 0000000000..30e9438ae5 --- /dev/null +++ b/newlib/libc/string/strcpy_s.c @@ -0,0 +1,125 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright © 2024, Synopsys Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#define __STDC_WANT_LIB_EXT1__ 1 +#include +#include +#include "string_private.h" + +__errno_t +strcpy_s(char *restrict s1, rsize_t s1max, const char *restrict s2) +{ + const char *msg = ""; + bool write_null = true; + + if (s1 == NULL) { + msg = "strcpy_s: dest is NULL"; + write_null = false; + goto handle_error; + } + + if ((s1max == 0) || (CHECK_RSIZE(s1max))) { + msg = "strcpy_s: dest buffer size is 0 or exceeds RSIZE_MAX"; + write_null = false; + goto handle_error; + } + + if (s2 == NULL) { + msg = "strcpy_s: source is NULL"; + goto handle_error; + } + + /* It is a constraint violation if s1max is not large enough to contain + * s2: no truncation permitted. + * It is also a constraint violation if the string pointed to by s2 + * overlaps s1 in any way. + * The C11 Rationale says we are permitted to proceed with the copy and + * detect dest buffer overrun and overlapping memory blocks as a byproduct + * of performing the copy operation. This is to avoid calling strlen on + * s2 to detect these violations prior to attempting the copy. + */ + const char *overlap_point; + bool check_s1_for_overlap; + char *s1cp = s1; + const char *s2cp = s2; + if (s1 < s2) { + // if we ever reach s2 when storing to s1 we have overlap + overlap_point = s2; + check_s1_for_overlap = true; + } else { + // if we ever reach s1 when reading from s2 we have overlap + overlap_point = s1; + check_s1_for_overlap = false; + } + + unsigned written = 0; + char c = '.'; + while (written < s1max) { + if (check_s1_for_overlap) { + if (s1cp == overlap_point) { + msg = "strcpy_s: overlapping copy"; + goto handle_error; + } + } else if (s2cp == overlap_point) { + msg = "strcpy_s: overlapping copy"; + goto handle_error; + } + + c = *s2cp++; + *s1cp++ = c; + written++; + if (c == '\0') { + break; + } + } + + if (c != '\0') { + msg = "strcpy_s: dest buffer size insufficent to copy string"; + goto handle_error; + } + + // Normal return path + return 0; + +handle_error: + if (write_null && s1 != NULL) { + *s1 = '\0'; + } + + if (__cur_handler != NULL) { + __cur_handler(msg, NULL, -1); + } + + return -1; +} diff --git a/newlib/libc/string/strerror.c b/newlib/libc/string/strerror.c index 5788505b1b..9c3e38191d 100644 --- a/newlib/libc/string/strerror.c +++ b/newlib/libc/string/strerror.c @@ -400,6 +400,7 @@ QUICKREF #define _DEFAULT_SOURCE #include #include +#include "string_private.h" #include "local.h" extern char *_user_strerror (int, int, int *) _ATTRIBUTE((__weak__)); diff --git a/newlib/libc/string/strerror_s.c b/newlib/libc/string/strerror_s.c new file mode 100644 index 0000000000..8822168c10 --- /dev/null +++ b/newlib/libc/string/strerror_s.c @@ -0,0 +1,85 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright © 2024, Synopsys Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#define __STDC_WANT_LIB_EXT1__ 1 +#include +#include +#include +#include "string_private.h" + +/* C11 version; required by LLVM's C++11 library */ +__errno_t +strerror_s(char *buf, rsize_t buflen, __errno_t errnum) +{ + int32_t result = 0; + const char *msg = ""; + + if (buf == NULL) { + msg = "strerror_s: dest is NULL"; + goto handle_error; + } + + if ((buflen == 0u) || (CHECK_RSIZE(buflen))) { + msg = "strerror_s: dest buffer size is 0 or exceeds RSIZE_MAX"; + goto handle_error; + } + + const char *cp = _strerror_r(errnum, 0, NULL); + uint32_t len = strnlen_s(cp, MAX_ERROR_MSG); + + if (len < buflen) { + (void)strncpy(buf, cp, MAX_ERROR_MSG); + } else { + /* Standard allows truncation of error message with '...' to + indicate truncation. */ + (void)memcpy(buf, cp, (buflen - 1u)); + buf[(buflen - 1u)] = '\0'; + + if (buflen > 3u) { + (void)strncpy(&buf[(buflen - 4u)], "...", 4u); + } + + result = ERANGE; + } + + // Normal return path + return result; + +handle_error: + if (__cur_handler != NULL) { + __cur_handler(msg, NULL, -1); + } + + return -1; +} diff --git a/newlib/libc/string/strerrorlen_s.c b/newlib/libc/string/strerrorlen_s.c new file mode 100644 index 0000000000..b22215ccaf --- /dev/null +++ b/newlib/libc/string/strerrorlen_s.c @@ -0,0 +1,43 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright © 2024, Synopsys Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#define __STDC_WANT_LIB_EXT1__ 1 +#include +#include "string_private.h" + +size_t +strerrorlen_s(__errno_t errnum) +{ + return strnlen_s(_strerror_r(errnum, 0, NULL), MAX_ERROR_MSG); +} diff --git a/newlib/libc/string/string_private.h b/newlib/libc/string/string_private.h new file mode 100644 index 0000000000..8c5cd3a0b5 --- /dev/null +++ b/newlib/libc/string/string_private.h @@ -0,0 +1,44 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright © 2024, Synopsys Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef STRING_PRIVATE_H_ +#define STRING_PRIVATE_H_ + +#include "../stdlib/local_s.h" + +#define MAX_ERROR_MSG 100 + +char *_strerror_r(int errnum, int internal, int *errptr); + +#endif //STRING_PRIVATE_H_ diff --git a/newlib/libc/string/strncat_s.c b/newlib/libc/string/strncat_s.c new file mode 100644 index 0000000000..7290bdab64 --- /dev/null +++ b/newlib/libc/string/strncat_s.c @@ -0,0 +1,169 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright © 2024, Synopsys Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#define __STDC_WANT_LIB_EXT1__ 1 +#include +#include +#include "string_private.h" + +__errno_t +strncat_s(char *restrict s1, rsize_t s1max, const char *restrict s2, rsize_t n) +{ + const char *msg = ""; + size_t s1_len = 0; + bool write_null = true; + + s1_len = strnlen_s(s1, s1max); + + if (s1 == NULL) { + msg = "strncat_s: dest is NULL"; + write_null = false; + goto handle_error; + } else if ((s1max == 0u) || (CHECK_RSIZE(s1max))) { + msg = "strncat_s: dest buffer size is 0 or exceeds RSIZE_MAX"; + write_null = false; + goto handle_error; + } else if (s2 == NULL) { + msg = "strncat_s: source is NULL"; + goto handle_error; + } else if (CHECK_RSIZE(n)) { + msg = "strncat_s: copy count exceeds RSIZE_MAX"; + goto handle_error; + } + + /* It is a constraint violation if s1max is not large enough to contain + * the concatenation of s2. + * It is also a constraint violation if the string pointed to by s2 + * overlaps s1 in any way. + * The C11 Rationale says we are permitted to proceed with the copy and + * detect dest buffer overrun and overlapping memory blocks as a byproduct + * of performing the copy operation. This is to avoid calling strlen on + * s2 to detect these violations prior to attempting the copy. + */ + // compute chars available in s1 + + else if (s1_len == s1max) { + msg = "strncat_s: string 1 length exceeds buffer size"; + goto handle_error; + } else { + // compute chars available in s1 + uint32_t m = (s1max - s1_len); + uint32_t i = 0; + char *s1cp = s1; + + for (i = 0u; i < s1_len; i++) { + s1cp++; + } + + // Question; at this point should we just return + // strncpy_s(s1cp, m, s2, n) ? + // perhaps not since overlap check needs to be over entire s1 vs. s2? + + const char *overlap_point; + bool check_s1_for_overlap; + const char *s2cp = s2; + + if (s1 <= s2) { + // if we ever reach s2 when storing to s1 we have overlap + overlap_point = s2; + check_s1_for_overlap = true; + // make sure source does not lie within initial dest string. + if (s2 <= s1cp) { + msg = "strncat_s: overlapping copy"; + goto handle_error; + } + } else { + // if we ever reach s1 when reading from s2 we have overlap + overlap_point = s1; + check_s1_for_overlap = false; + } + + uint32_t written = 0; + char c = '.'; + + while ((written < m) && (written < n)) { + if (check_s1_for_overlap == true) { + if (s1cp == overlap_point) { + msg = "strncat_s: overlapping copy"; + goto handle_error; + } + } else if (s2cp == overlap_point) { + msg = "strncat_s: overlapping copy"; + goto handle_error; + } else { + /* Normal case*/ + } + + c = *s2cp; + s2cp++; + *s1cp = c; + s1cp++; + written++; + + if (c == '\0') { + break; + } + } + + if ((c != '\0') && (written == n) && (written < m)) { + // we copied n chars from s2 and there is room for null char in s1 + if ((check_s1_for_overlap == true) && (s1cp == overlap_point)) { + msg = "strncat_s: overlapping copy"; + goto handle_error; + } else { + c = '\0'; + *s1cp = '\0'; + } + } + + if (c != '\0') { + msg = "strncat_s: dest buffer size insufficent to copy string"; + goto handle_error; + } + } + + // Normal return path + return 0; + +handle_error: + if (write_null && s1 != NULL) { + *s1 = '\0'; + } + + if (__cur_handler != NULL) { + __cur_handler(msg, NULL, -1); + } + + return -1; +} diff --git a/newlib/libc/string/strncpy_s.c b/newlib/libc/string/strncpy_s.c new file mode 100644 index 0000000000..b35946404e --- /dev/null +++ b/newlib/libc/string/strncpy_s.c @@ -0,0 +1,147 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright © 2024, Synopsys Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#define __STDC_WANT_LIB_EXT1__ 1 +#include +#include +#include "string_private.h" + +__errno_t +strncpy_s(char *restrict s1, rsize_t s1max, const char *restrict s2, rsize_t n) +{ + const char *msg = ""; + bool write_null = true; + + if (s1 == NULL) { + msg = "strncpy_s: dest is NULL"; + write_null = false; + goto handle_error; + } else if ((s1max == 0u) || (CHECK_RSIZE(s1max))) { + msg = "strncpy_s: dest buffer size is 0 or exceeds RSIZE_MAX"; + write_null = false; + goto handle_error; + } + + else if (s2 == NULL) { + msg = "strncpy_s: source is NULL"; + goto handle_error; + } else if (CHECK_RSIZE(n)) { + msg = "strncpy_s: copy count exceeds RSIZE_MAX"; + goto handle_error; + } + + /* It is a constraint violation if s1max is not large enough to contain + * a copy of s2 (with n as upper bound on chars copied). + * It is also a constraint violation if the string pointed to by s2 + * overlaps s1 in any way. + * The C11 Rationale says we are permitted to proceed with the copy and + * detect dest buffer overrun and overlapping memory blocks as a byproduct + * of performing the copy operation. This is to avoid calling strlen on + * s2 to detect these violations prior to attempting the copy. + */ + + else { + const char *overlap_point; + bool check_s1_for_overlap; + char *s1cp = s1; + const char *s2cp = s2; + + if (s1 < s2) { + // if we ever reach s2 when storing to s1 we have overlap + overlap_point = s2; + check_s1_for_overlap = true; + } else { + // if we ever reach s1 when reading from s2 we have overlap + overlap_point = s1; + check_s1_for_overlap = false; + } + + uint32_t written = 0; + char c = '.'; + + while ((written < s1max) && (written < n)) { + if (check_s1_for_overlap == true) { + if (s1cp == overlap_point) { + msg = "strncpy_s: overlapping copy"; + goto handle_error; + } + } else if (s2cp == overlap_point) { + msg = "strncpy_s: overlapping copy"; + goto handle_error; + } else { + /* Normal case*/ + } + + c = *s2cp; + s2cp++; + *s1cp = c; + s1cp++; + written++; + + if (c == '\0') { + break; + } + } + + if ((c != '\0') && (written == n) && (written < s1max)) { + // we copied n chars from s2 and there is room for null char in s1 + if ((check_s1_for_overlap == true) && (s1cp == overlap_point)) { + msg = "strncpy_s: overlapping copy"; + goto handle_error; + } else { + c = '\0'; + *s1cp = '\0'; + } + } + + if (c != '\0') { + msg = "strncpy_s: dest buffer size insufficent to copy string"; + goto handle_error; + } + } + + // Normal return path + return 0; + +handle_error: + if (write_null && s1 != NULL) { + *s1 = '\0'; + } + + if (__cur_handler != NULL) { + __cur_handler(msg, NULL, -1); + } + + return -1; +} diff --git a/newlib/libc/string/strnlen_s.c b/newlib/libc/string/strnlen_s.c new file mode 100644 index 0000000000..bfbfb98618 --- /dev/null +++ b/newlib/libc/string/strnlen_s.c @@ -0,0 +1,60 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright © 2024, Synopsys Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#define __STDC_WANT_LIB_EXT1__ 1 +#include +#include "string_private.h" + +size_t +strnlen_s(const char *s, size_t maxsize) +{ + const void *s_end; + size_t rtn = 0; + + if (s == NULL) { + rtn = 0; + } else { + s_end = memchr((const void *)s, (int)'\0', maxsize); + + if (s_end == NULL) { + rtn = maxsize; + } else { + int s_size; + s_size = s_end - (const void *)s; + rtn = (size_t)s_size; + } + } + + return rtn; +} diff --git a/newlib/libc/tinystdio/CMakeLists.txt b/newlib/libc/tinystdio/CMakeLists.txt index 71f6318d16..97077ac6c5 100644 --- a/newlib/libc/tinystdio/CMakeLists.txt +++ b/newlib/libc/tinystdio/CMakeLists.txt @@ -166,6 +166,7 @@ picolibc_sources( vwscanf.c wprintf.c wscanf.c + sprintf_s.c ) picolibc_sources( diff --git a/newlib/libc/tinystdio/meson.build b/newlib/libc/tinystdio/meson.build index 8c5c8b40de..0f45fe0ec2 100644 --- a/newlib/libc/tinystdio/meson.build +++ b/newlib/libc/tinystdio/meson.build @@ -169,6 +169,7 @@ srcs_tinystdio = [ 'vwscanf.c', 'wprintf.c', 'wscanf.c', + 'sprintf_s.c', ] # exact float/string conversion code diff --git a/newlib/libc/tinystdio/sprintf_s.c b/newlib/libc/tinystdio/sprintf_s.c new file mode 100644 index 0000000000..14de845621 --- /dev/null +++ b/newlib/libc/tinystdio/sprintf_s.c @@ -0,0 +1,147 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright © 2024, Synopsys Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#define __STDC_WANT_LIB_EXT1__ 1 +#include "stdio_private.h" +#include +#include +#include +#include "../stdlib/local_s.h" + +int +sprintf_s(char *restrict s, rsize_t bufsize, const char *restrict fmt, ...) +{ + bool write_null = true; + const char *msg = ""; + va_list args; + int rc; + + if (s == NULL) { + write_null = false; + msg = "sprintf_s: dest buffer is null"; + goto handle_error; + } else if ((bufsize == 0) || (CHECK_RSIZE(bufsize))) { + write_null = false; + msg = "sprintf_s: invalid buffer size"; + goto handle_error; + } else if (fmt == NULL) { + msg = "sprintf_s: null format string"; + goto handle_error; + } else if (strstr(fmt, " %n") != NULL) { + msg = "sprintf_s: format string contains percent-n"; + goto handle_error; + } else { + va_start(args, fmt); + + va_list args_copy; + va_copy(args_copy, args); + + const char *check_ptr = fmt; + uint8_t null_str = 0; + while (*check_ptr && null_str == 0) { + if (check_ptr[0] == '%') { + switch (check_ptr[1]) { + case 's': { + char *str_arg = va_arg(args_copy, char *); + if (str_arg == NULL) { + msg = "sprintf_s: null string argument"; + va_end(args_copy); + va_end(args); + null_str = 1; + goto handle_error; + } + break; + } + case 'd': + case 'i': + case 'u': + case 'o': + case 'x': + case 'X': + case 'c': + case 'h': + case 'l': + case 'L': + case 'z': + case 't': + va_arg(args_copy, int); + break; + case 'f': + case 'F': + case 'e': + case 'E': + case 'g': + case 'G': + case 'a': + case 'A': + va_arg(args_copy, double); + break; + case 'p': + va_arg(args_copy, void *); + break; + default: + break; + } + } + check_ptr++; + } + + rc = vsnprintf(s, bufsize, fmt, args); + va_end(args_copy); + va_end(args); + } + + if (rc < 0 || rc >= (int)bufsize) { + msg = "sprintf_s: dest buffer overflow"; + goto handle_error; + } else { + s[rc] = 0; + } + + // Normal return path + return rc; + +handle_error: + if (__cur_handler != NULL) { + __cur_handler(msg, NULL, -1); + } + + rc = 0; /* standard stipulates this */ + + if (write_null && s != NULL) { + s[0] = '\0'; /* again, standard requires this */ + } + + return rc; +} \ No newline at end of file diff --git a/newlib/libc/tinystdio/stdio.h b/newlib/libc/tinystdio/stdio.h index 0b2c92e2db..c5274585af 100644 --- a/newlib/libc/tinystdio/stdio.h +++ b/newlib/libc/tinystdio/stdio.h @@ -375,6 +375,18 @@ int putchar_unlocked (int); #define putchar_unlocked(c, f) fgetc(c, stdin) #endif +#if __STDC_WANT_LIB_EXT1__ == 1 +#include + +#ifndef _RSIZE_T_DEFINED +typedef __rsize_t rsize_t; +#define _RSIZE_T_DEFINED +#endif + +int sprintf_s(char *__restrict __s, rsize_t __bufsize, + const char *__restrict __format, ...); +#endif + /* * The format of tmpnam names is TXXXXXX, which works with mktemp */ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index da55c79760..3fc61f4594 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -61,6 +61,17 @@ set(tests test-sprintf-percent-n malloc_stress posix-io + test-memcpy_s + test-memset_s + test-memmove_s + test-strcat_s + test-strcpy_s + test-strerror_s + test-strerrorlen_s + test-strncat_s + test-strncpy_s + test-strnlen_s + test-sprintf_s ) set(tests_fail diff --git a/test/meson.build b/test/meson.build index 8ddd9666a2..b8c0ecb9c1 100644 --- a/test/meson.build +++ b/test/meson.build @@ -583,6 +583,71 @@ foreach target : targets timeout: 60, env: test_env) + plain_tests = ['rand', 'regex', 'ungetc', 'fenv', + 'malloc', 'tls', + 'ffs', 'setjmp', 'atexit', 'on_exit', + 'math-funcs', 'timegm', 'time-tests', + 'test-strtod', 'test-strchr', + 'test-memset', 'test-put', + 'test-efcvt', 'test-atomic', + 'test-raise', + 'test-fma', + 'test-funopen', + ] + + if have_attr_ctor_dtor + plain_tests += 'constructor' + endif + + if have_complex + plain_tests += 'complex-funcs' + endif + + if newlib_nano_malloc or tests_enable_full_malloc_stress + plain_tests += 'malloc_stress' + endif + + if tinystdio + plain_tests += 't_fmemopen' + endif + + if (posix_io or not tinystdio) and tests_enable_posix_io + plain_tests += ['posix-io'] + + # legacy stdio doesn't work on semihosting, so just skip it + if tinystdio + plain_tests += ['test-fopen', + 'test-mktemp', + 'test-tmpnam', + 'test-fread-fwrite', + 'test-ungetc-ftell', + 'test-fgetc', + 'test-fgets-eof', + ] + endif + endif + + if tests_enable_stack_protector + plain_tests += 'stack-smash' + endif + + plain_tests += ['test-memcpy_s', + 'test-memset_s', + 'test-memmove_s', + 'test-strcat_s', + 'test-strcpy_s', + 'test-strerror_s', + 'test-strerrorlen_s', + 'test-strncat_s', + 'test-strncpy_s', + 'test-strnlen_s', + ] + + if tinystdio + plain_tests += 'test-sprintf_s' + endif + + foreach t1 : plain_tests t1_src = t1 + '.c' if target == '' diff --git a/test/test-memcpy_s.c b/test/test-memcpy_s.c new file mode 100644 index 0000000000..ac1d281307 --- /dev/null +++ b/test/test-memcpy_s.c @@ -0,0 +1,151 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright © 2024, Synopsys Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define __STDC_WANT_LIB_EXT1__ 1 +#include +#include +#include +#include + +#define MAX_ERROR_MSG 100 + +char handler_msg[MAX_ERROR_MSG] = ""; + +static void +custom_constraint_handler(const char *restrict msg, void *restrict ptr, + errno_t error) +{ + (void)ptr; + (void)error; + strcpy(handler_msg, msg); +} + +#define TEST_RES(cond, msg, handler_res, test_id) \ + if ((!(cond)) || (handler_res == 1)) { \ + printf("Test %d Failed: %s\n", test_id, msg); \ + return 1; \ + } else { \ + printf("Test %d Passed: %s\n", test_id, msg); \ + } + +static int +test_handler_called(int handler_called, char *expected_msg, int test_id) +{ + int ret = 0; + if (handler_called == 0) { + (void)expected_msg; + if (handler_msg[0] != '\0') { + printf( + "ERROR: Custom constraint handler called without error detiction!\n"); + printf("Test %d Failed: Error msg is incorrect\n", test_id); + ret = 1; + } + } else { + if (handler_msg[0] == '\0') { + (void)expected_msg; + printf("ERROR: Custom constraint handler not called\n"); + printf("Test %d Failed: Error msg is incorrect\n", test_id); + ret = 1; + } else { + if (strcmp(expected_msg, handler_msg) != 0) { + printf( + "ERROR: Custom constraint handler called with incorrect msg: %s\n", + handler_msg); + printf("Test %d Failed: Error msg is incorrect\n", test_id); + ret = 1; + } else { + (void)expected_msg; + printf( + "Custom constraint handler called with correct msg: %s\n", + handler_msg); + handler_msg[0] = '\0'; + ret = 0; + } + } + } + return ret; +} + +int +main(void) +{ + char src[] = "Hello, world!"; + char dest[50]; + int test_id = 0; + int handler_res = 0; + errno_t res; + + set_constraint_handler_s(custom_constraint_handler); + + // Test case 1: Normal copy + test_id++; + res = memcpy_s(dest, sizeof(dest), src, strlen(src) + 1); + handler_res = test_handler_called(0, "", test_id); + TEST_RES(res == 0, "Normal Copy", handler_res, test_id); + TEST_RES(strcmp(dest, "Hello, world!") == 0, "Normal Copy Contents", + handler_res, test_id); + + // Test case 2: Copy with insufficient destination size + test_id++; + res = memcpy_s(dest, 5, src, strlen(src) + 1); + handler_res = test_handler_called( + 1, "memcpy_s: copy count exceeds buffer size", test_id); + TEST_RES(res != 0, "Copy with insufficient destination size", handler_res, + test_id); + + // Test case 3: Copy with Null destination + test_id++; + res = memcpy_s(NULL, sizeof(dest), src, strlen(src) + 1); + handler_res = test_handler_called(1, "memcpy_s: dest is NULL", test_id); + TEST_RES(res != 0, "Copy with Null destination", handler_res, test_id); + + // Test case 4: Copy with Null source + test_id++; + res = memcpy_s(dest, sizeof(dest), NULL, strlen(src) + 1); + handler_res = test_handler_called(1, "memcpy_s: source is NULL", test_id); + TEST_RES(res != 0, "Copy with Null source", handler_res, test_id); + + // Test case 5: Copy with zero length + test_id++; + strcpy(dest, ""); + res = memcpy_s(dest, sizeof(dest), src, 0); + handler_res = test_handler_called(0, "", test_id); + TEST_RES(res == 0, "Copy with zero length", handler_res, test_id); + TEST_RES(dest[0] == '\0', "Copy with zero length Contents", handler_res, + test_id); + + printf("All memcpy_s tests passed!\n"); + return 0; +} \ No newline at end of file diff --git a/test/test-memmove_s.c b/test/test-memmove_s.c new file mode 100644 index 0000000000..83e8c0e3ab --- /dev/null +++ b/test/test-memmove_s.c @@ -0,0 +1,150 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright © 2024, Synopsys Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define __STDC_WANT_LIB_EXT1__ 1 +#include +#include +#include +#include + +#define MAX_ERROR_MSG 100 + +char handler_msg[MAX_ERROR_MSG] = ""; + +static void +custom_constraint_handler(const char *restrict msg, void *restrict ptr, + errno_t error) +{ + (void)ptr; + (void)error; + strcpy(handler_msg, msg); +} + +#define TEST_RES(cond, msg, handler_res, test_id) \ + if ((!(cond)) || (handler_res == 1)) { \ + printf("Test %d Failed: %s\n", test_id, msg); \ + return 1; \ + } else { \ + printf("Test %d Passed: %s\n", test_id, msg); \ + } + +static int +test_handler_called(int handler_called, char *expected_msg, int test_id) +{ + int ret = 0; + if (handler_called == 0) { + (void)expected_msg; + if (handler_msg[0] != '\0') { + printf( + "ERROR: Custom constraint handler called without error detiction!\n"); + printf("Test %d Failed: Error msg is incorrect\n", test_id); + ret = 1; + } + } else { + if (handler_msg[0] == '\0') { + (void)expected_msg; + printf("ERROR: Custom constraint handler not called\n"); + printf("Test %d Failed: Error msg is incorrect\n", test_id); + ret = 1; + } else { + if (strcmp(expected_msg, handler_msg) != 0) { + printf( + "ERROR: Custom constraint handler called with incorrect msg: %s\n", + handler_msg); + printf("Test %d Failed: Error msg is incorrect\n", test_id); + ret = 1; + } else { + (void)expected_msg; + printf( + "Custom constraint handler called with correct msg: %s\n", + handler_msg); + handler_msg[0] = '\0'; + ret = 0; + } + } + } + return ret; +} + +int +main(void) +{ + char buf[50] = "Hello, world!"; + int test_id = 0; + int handler_res = 0; + errno_t res; + + set_constraint_handler_s(custom_constraint_handler); + + // Test case 1: Normal move + test_id++; + res = memmove_s(buf + 7, sizeof(buf) - 7, buf, strlen(buf) + 1); + handler_res = test_handler_called(0, "", test_id); + TEST_RES(res == 0, "Normal move", handler_res, test_id); + TEST_RES(strcmp(buf, "Hello, Hello, world!") == 0, "Normal move Contents", + handler_res, test_id); + + // Test case 2: Move with insufficient destination size + test_id++; + res = memmove_s(buf + 7, 5, buf, strlen(buf) + 1); + handler_res = test_handler_called( + 1, "memmove_s: copy count exceeds buffer size", test_id); + TEST_RES(res != 0, "Move with insufficient destination size", handler_res, + test_id); + + // Test case 3: Move with Null destination + test_id++; + res = memmove_s(NULL, sizeof(buf), buf, strlen(buf) + 1); + handler_res = test_handler_called(1, "memmove_s: dest is NULL", test_id); + TEST_RES(res != 0, "Move with Null destination", handler_res, test_id); + + // Test case 4: Move with Null source + test_id++; + res = memmove_s(buf, sizeof(buf), NULL, strlen(buf) + 1); + handler_res = test_handler_called(1, "memmove_s: source is NULL", test_id); + TEST_RES(res != 0, "Move with Null source", handler_res, test_id); + + // Test case 5: Move with zero length + test_id++; + strcpy(buf, ""); + res = memmove_s(buf, sizeof(buf), buf, 0); + handler_res = test_handler_called(0, "", test_id); + TEST_RES(res == 0, "Move with zero length", handler_res, test_id); + TEST_RES(buf[0] == '\0', "Move with zero length Contents", handler_res, + test_id); + + printf("All memmove_s tests passed!\n"); + return 0; +} \ No newline at end of file diff --git a/test/test-memset_s.c b/test/test-memset_s.c new file mode 100644 index 0000000000..5b3b75085e --- /dev/null +++ b/test/test-memset_s.c @@ -0,0 +1,142 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright © 2024, Synopsys Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define __STDC_WANT_LIB_EXT1__ 1 +#include +#include +#include +#include + +#define MAX_ERROR_MSG 100 + +char handler_msg[MAX_ERROR_MSG] = ""; + +static void +custom_constraint_handler(const char *restrict msg, void *restrict ptr, + errno_t error) +{ + (void)ptr; + (void)error; + strcpy(handler_msg, msg); +} + +#define TEST_RES(cond, msg, handler_res, test_id) \ + if ((!(cond)) || (handler_res == 1)) { \ + printf("Test %d Failed: %s\n", test_id, msg); \ + return 1; \ + } else { \ + printf("Test %d Passed: %s\n", test_id, msg); \ + } + +static int +test_handler_called(int handler_called, char *expected_msg, int test_id) +{ + int ret = 0; + if (handler_called == 0) { + (void)expected_msg; + if (handler_msg[0] != '\0') { + printf( + "ERROR: Custom constraint handler called without error detiction!\n"); + printf("Test %d Failed: Error msg is incorrect\n", test_id); + ret = 1; + } + } else { + if (handler_msg[0] == '\0') { + (void)expected_msg; + printf("ERROR: Custom constraint handler not called\n"); + printf("Test %d Failed: Error msg is incorrect\n", test_id); + ret = 1; + } else { + if (strcmp(expected_msg, handler_msg) != 0) { + printf( + "ERROR: Custom constraint handler called with incorrect msg: %s\n", + handler_msg); + printf("Test %d Failed: Error msg is incorrect\n", test_id); + ret = 1; + } else { + (void)expected_msg; + printf( + "Custom constraint handler called with correct msg: %s\n", + handler_msg); + handler_msg[0] = '\0'; + ret = 0; + } + } + } + return ret; +} + +int +main(void) +{ + char buf[50] = "Hello, world!"; + int test_id = 0; + int handler_res = 0; + errno_t res; + + set_constraint_handler_s(custom_constraint_handler); + + // Test case 1: Normal Set + test_id++; + res = memset_s(buf, sizeof(buf), 'A', strlen("Hello, world!")); + handler_res = test_handler_called(0, "", test_id); + TEST_RES(res == 0, "Normal Set", handler_res, test_id); + TEST_RES(strcmp(buf, "AAAAAAAAAAAAA") == 0, "Normal Set Contents", + handler_res, test_id); + + // Test case 2: Zero-length Set + test_id++; + res = memset_s(buf, sizeof(buf), 'B', 0); + handler_res = test_handler_called(0, "", test_id); + TEST_RES(res == 0, "Zero-length Set", handler_res, test_id); + TEST_RES(strcmp(buf, "AAAAAAAAAAAAA") == 0, "Zero-length Set Contents", + handler_res, test_id); + + // Test case 3: Null pointers + test_id++; + res = memset_s(NULL, sizeof(buf), 'C', strlen("Hello, world!")); + handler_res = test_handler_called(1, "memset_s: dest is NULL", test_id); + TEST_RES(res != 0, "NULL Destination Pointer", handler_res, test_id); + + // Test case 4: Set with zero buffer size + test_id++; + res = memset_s(buf, 0, 'D', strlen("Hello, world!")); + handler_res = + test_handler_called(1, "memset_s: count exceeds buffer size", test_id); + TEST_RES(res != 0, "Set with zero buffer size", handler_res, test_id); + + printf("All memset_s tests passed!\n"); + return 0; +} \ No newline at end of file diff --git a/test/test-sprintf_s.c b/test/test-sprintf_s.c new file mode 100644 index 0000000000..51e6a7d88c --- /dev/null +++ b/test/test-sprintf_s.c @@ -0,0 +1,152 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright © 2024, Synopsys Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define __STDC_WANT_LIB_EXT1__ 1 +#include +#include +#include +#include + +#define MAX_ERROR_MSG 100 + +char handler_msg[MAX_ERROR_MSG] = ""; + +static void +custom_constraint_handler(const char *restrict msg, void *restrict ptr, + errno_t error) +{ + (void)ptr; + (void)error; + strcpy(handler_msg, msg); +} + +#define TEST_RES(cond, msg, handler_res, test_id) \ + if ((!(cond)) || (handler_res == 1)) { \ + printf("Test %d Failed: %s\n", test_id, msg); \ + return 1; \ + } else { \ + printf("Test %d Passed: %s\n", test_id, msg); \ + } + +static int +test_handler_called(int handler_called, char *expected_msg, int test_id) +{ + int ret = 0; + if (handler_called == 0) { + (void)expected_msg; + if (handler_msg[0] != '\0') { + printf( + "ERROR: Custom constraint handler called without error detiction!\n"); + printf("Test %d Failed: Error msg is incorrect\n", test_id); + ret = 1; + } + } else { + if (handler_msg[0] == '\0') { + (void)expected_msg; + printf("ERROR: Custom constraint handler not called\n"); + printf("Test %d Failed: Error msg is incorrect\n", test_id); + ret = 1; + } else { + if (strcmp(expected_msg, handler_msg) != 0) { + printf( + "ERROR: Custom constraint handler called with incorrect msg: %s\n", + handler_msg); + printf("Test %d Failed: Error msg is incorrect\n", test_id); + ret = 1; + } else { + (void)expected_msg; + printf( + "Custom constraint handler called with correct msg: %s\n", + handler_msg); + handler_msg[0] = '\0'; + ret = 0; + } + } + } + return ret; +} + +int +main(void) +{ + char buf[50]; + int test_id = 0; + int handler_res = 0; + errno_t res; + + set_constraint_handler_s(custom_constraint_handler); + + // Test case 1: Normal formatting + test_id++; + res = sprintf_s(buf, sizeof(buf), "Hello, %s!", "world"); + handler_res = test_handler_called(0, "", test_id); + TEST_RES(res == (int)strlen("Hello, world!"), "Normal formatting", + handler_res, test_id); + TEST_RES(strcmp(buf, "Hello, world!") == 0, "Normal formatting Contents", + handler_res, test_id); + + // Test case 2: Formatting with buffer overflow + test_id++; + res = sprintf_s(buf, 10, "Hello, %s!", "world"); + handler_res = + test_handler_called(1, "sprintf_s: dest buffer overflow", test_id); + TEST_RES(res == 0, "Formatting with buffer overflow", handler_res, test_id); + + // Test case 3: Formatting with Null buffer + test_id++; + res = sprintf_s(NULL, sizeof(buf), "Hello, %s!", "world"); + handler_res = + test_handler_called(1, "sprintf_s: dest buffer is null", test_id); + TEST_RES(res == 0, "Formatting with Null buffer", handler_res, test_id); + + // Test case 4: Formatting with Null format string + test_id++; + res = sprintf_s(buf, sizeof(buf), NULL, "world"); + handler_res = + test_handler_called(1, "sprintf_s: null format string", test_id); + TEST_RES(res == 0, "Formatting with Null format string", handler_res, + test_id); + + // Test case 5: Empty format string + test_id++; + res = sprintf_s(buf, sizeof(buf), "", "world"); + TEST_RES(res == 0, "Empty format string", handler_res, test_id); + handler_res = test_handler_called(0, "", test_id); + TEST_RES(strcmp(buf, "") == 0, "Empty format string Contents", handler_res, + test_id); + + printf("All sprintf_s tests passed!\n"); + return 0; +} \ No newline at end of file diff --git a/test/test-strcat_s.c b/test/test-strcat_s.c new file mode 100644 index 0000000000..dc6e7b47ef --- /dev/null +++ b/test/test-strcat_s.c @@ -0,0 +1,161 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright © 2024, Synopsys Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define __STDC_WANT_LIB_EXT1__ 1 +#include +#include +#include +#include + +#define MAX_ERROR_MSG 100 + +char handler_msg[MAX_ERROR_MSG] = ""; + +static void +custom_constraint_handler(const char *restrict msg, void *restrict ptr, + errno_t error) +{ + (void)ptr; + (void)error; + strcpy(handler_msg, msg); +} + +#define TEST_RES(cond, msg, handler_res, test_id) \ + if ((!(cond)) || (handler_res == 1)) { \ + printf("Test %d Failed: %s\n", test_id, msg); \ + return 1; \ + } else { \ + printf("Test %d Passed: %s\n", test_id, msg); \ + } + +static int +test_handler_called(int handler_called, char *expected_msg, int test_id) +{ + int ret = 0; + if (handler_called == 0) { + (void)expected_msg; + if (handler_msg[0] != '\0') { + printf( + "ERROR: Custom constraint handler called without error detiction!\n"); + printf("Test %d Failed: Error msg is incorrect\n", test_id); + ret = 1; + } + } else { + if (handler_msg[0] == '\0') { + (void)expected_msg; + printf("ERROR: Custom constraint handler not called\n"); + printf("Test %d Failed: Error msg is incorrect\n", test_id); + ret = 1; + } else { + if (strcmp(expected_msg, handler_msg) != 0) { + printf( + "ERROR: Custom constraint handler called with incorrect msg: %s\n", + handler_msg); + printf("Test %d Failed: Error msg is incorrect\n", test_id); + ret = 1; + } else { + (void)expected_msg; + printf( + "Custom constraint handler called with correct msg: %s\n", + handler_msg); + handler_msg[0] = '\0'; + ret = 0; + } + } + } + return ret; +} + +int +main(void) +{ + char dest[50] = "Hello"; + char src[] = ", world!"; + int test_id = 0; + int handler_res = 0; + errno_t res; + + set_constraint_handler_s(custom_constraint_handler); + + // Test case 1: Normal Concatenation + test_id++; + res = strcat_s(dest, sizeof(dest), src); + handler_res = test_handler_called(0, "", test_id); + TEST_RES(res == 0, "Normal Concatenation", handler_res, test_id); + TEST_RES(strcmp(dest, "Hello, world!") == 0, + "Normal Concatenation Contents", handler_res, test_id); + + // Test case 2: Concatenation with insufficient buffer + test_id++; + res = strcat_s(dest, 10, src); + handler_res = test_handler_called( + 1, "strcat_s: string 1 length exceeds buffer size", test_id); + TEST_RES(res != 0, "Concatenation with insufficient buffer", handler_res, + test_id); + + // Test case 3: Null pointers + test_id++; + res = strcat_s(NULL, sizeof(dest), src); + handler_res = test_handler_called(1, "strcat_s: dest is NULL", test_id); + TEST_RES(res != 0, "NULL Destination Pointer", handler_res, test_id); + res = strcat_s(dest, sizeof(dest), NULL); + handler_res = test_handler_called(1, "strcat_s: source is NULL", test_id); + TEST_RES(res != 0, "NULL Source Pointer", handler_res, test_id); + + // Test case 4: Concatenation of empty source string + test_id++; + strcpy(dest, "Hello"); + res = strcat_s(dest, sizeof(dest), ""); + handler_res = test_handler_called(0, "", test_id); + TEST_RES(res == 0, "Concatenation of empty source string", handler_res, + test_id); + TEST_RES(strcmp(dest, "Hello") == 0, + "Concatenation of empty source string Contents", handler_res, + test_id); + + // Test case 5: Concatenation with empty destination string + test_id++; + char dest2[50] = ""; + res = strcat_s(dest2, sizeof(dest2), ", World!"); + handler_res = test_handler_called(0, "", test_id); + TEST_RES(res == 0, "Concatenation of non-empty source to empty destination", + handler_res, test_id); + TEST_RES(strcmp(dest2, ", World!") == 0, + "Concatenation of non-empty source to empty destination Contents", + handler_res, test_id); + + printf("All strcat_s tests passed!\n"); + return 0; +} \ No newline at end of file diff --git a/test/test-strcpy_s.c b/test/test-strcpy_s.c new file mode 100644 index 0000000000..36e6f23b6d --- /dev/null +++ b/test/test-strcpy_s.c @@ -0,0 +1,155 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright © 2024, Synopsys Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define __STDC_WANT_LIB_EXT1__ 1 +#include +#include +#include +#include + +#define MAX_ERROR_MSG 100 + +char handler_msg[MAX_ERROR_MSG] = ""; + +static void +custom_constraint_handler(const char *restrict msg, void *restrict ptr, + errno_t error) +{ + (void)ptr; + (void)error; + strcpy(handler_msg, msg); +} + +#define TEST_RES(cond, msg, handler_res, test_id) \ + if ((!(cond)) || (handler_res == 1)) { \ + printf("Test %d Failed: %s\n", test_id, msg); \ + return 1; \ + } else { \ + printf("Test %d Passed: %s\n", test_id, msg); \ + } + +static int +test_handler_called(int handler_called, char *expected_msg, int test_id) +{ + int ret = 0; + if (handler_called == 0) { + (void)expected_msg; + if (handler_msg[0] != '\0') { + printf( + "ERROR: Custom constraint handler called without error detiction!\n"); + printf("Test %d Failed: Error msg is incorrect\n", test_id); + ret = 1; + } + } else { + if (handler_msg[0] == '\0') { + (void)expected_msg; + printf("ERROR: Custom constraint handler not called\n"); + printf("Test %d Failed: Error msg is incorrect\n", test_id); + ret = 1; + } else { + if (strcmp(expected_msg, handler_msg) != 0) { + printf( + "ERROR: Custom constraint handler called with incorrect msg: %s\n", + handler_msg); + printf("Test %d Failed: Error msg is incorrect\n", test_id); + ret = 1; + } else { + (void)expected_msg; + printf( + "Custom constraint handler called with correct msg: %s\n", + handler_msg); + handler_msg[0] = '\0'; + ret = 0; + } + } + } + return ret; +} + +int +main(void) +{ + char dest[50]; + const char *src = "Hello, world!"; + int test_id = 0; + int handler_res = 0; + errno_t res; + + set_constraint_handler_s(custom_constraint_handler); + + // Test case 1: Normal copy + test_id++; + res = strcpy_s(dest, sizeof(dest), src); + handler_res = test_handler_called(0, "", test_id); + TEST_RES(res == 0, "Normal Copy", handler_res, test_id); + TEST_RES(strcmp(dest, "Hello, world!") == 0, "Normal Copy Contents", + handler_res, test_id); + + // Test case 2: Copy with insufficient buffer + test_id++; + res = strcpy_s(dest, 5, src); + handler_res = test_handler_called( + 1, "strcpy_s: dest buffer size insufficent to copy string", test_id); + TEST_RES(res != 0, "Copy with insufficient buffer", handler_res, test_id); + + // Test case 3: Null pointers + test_id++; + res = strcpy_s(NULL, sizeof(dest), src); + handler_res = test_handler_called(1, "strcpy_s: dest is NULL", test_id); + TEST_RES(res != 0, "NULL Destination Pointer", handler_res, test_id); + res = strcpy_s(dest, sizeof(dest), NULL); + handler_res = test_handler_called(1, "strcpy_s: source is NULL", test_id); + TEST_RES(res != 0, "NULL Source Pointer", handler_res, test_id); + + // Test case 4: Copy of empty string + test_id++; + res = strcpy_s(dest, sizeof(dest), ""); + handler_res = test_handler_called(0, "", test_id); + TEST_RES(res == 0, "Copy of empty string", handler_res, test_id); + TEST_RES(strcmp(dest, "") == 0, "Copy of empty string Contents", + handler_res, test_id); + + // Test case 5: Copy to empty buffer + test_id++; + char buf2[50] = ""; + res = strcpy_s(buf2, sizeof(buf2), "world"); + handler_res = test_handler_called(0, "", test_id); + TEST_RES(res == 0, "Copy to empty buffer", handler_res, test_id); + TEST_RES(strcmp(buf2, "world") == 0, "Copy to empty buffer Contents", + handler_res, test_id); + + printf("All strcpy_s tests passed!\n"); + return 0; +} \ No newline at end of file diff --git a/test/test-strerror_s.c b/test/test-strerror_s.c new file mode 100644 index 0000000000..1011d197a2 --- /dev/null +++ b/test/test-strerror_s.c @@ -0,0 +1,146 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright © 2024, Synopsys Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define __STDC_WANT_LIB_EXT1__ 1 +#include +#include +#include +#include + +#define MAX_ERROR_MSG 100 + +char handler_msg[MAX_ERROR_MSG] = ""; + +static void +custom_constraint_handler(const char *restrict msg, void *restrict ptr, + errno_t error) +{ + (void)ptr; + (void)error; + strcpy(handler_msg, msg); +} + +#define TEST_RES(cond, msg, handler_res, test_id) \ + if ((!(cond)) || (handler_res == 1)) { \ + printf("Test %d Failed: %s\n", test_id, msg); \ + return 1; \ + } else { \ + printf("Test %d Passed: %s\n", test_id, msg); \ + } + +static int +test_handler_called(int handler_called, char *expected_msg, int test_id) +{ + int ret = 0; + if (handler_called == 0) { + (void)expected_msg; + if (handler_msg[0] != '\0') { + printf( + "ERROR: Custom constraint handler called without error detiction!\n"); + printf("Test %d Failed: Error msg is incorrect\n", test_id); + ret = 1; + } + } else { + if (handler_msg[0] == '\0') { + (void)expected_msg; + printf("ERROR: Custom constraint handler not called\n"); + printf("Test %d Failed: Error msg is incorrect\n", test_id); + ret = 1; + } else { + if (strcmp(expected_msg, handler_msg) != 0) { + printf( + "ERROR: Custom constraint handler called with incorrect msg: %s\n", + handler_msg); + printf("Test %d Failed: Error msg is incorrect\n", test_id); + ret = 1; + } else { + (void)expected_msg; + printf( + "Custom constraint handler called with correct msg: %s\n", + handler_msg); + handler_msg[0] = '\0'; + ret = 0; + } + } + } + return ret; +} + +int +main(void) +{ + char buf[100]; + int test_id = 0; + int handler_res = 0; + errno_t res; + + set_constraint_handler_s(custom_constraint_handler); + + // Test case 1: Normal error message + test_id++; + res = strerror_s(buf, sizeof(buf), EINVAL); + handler_res = test_handler_called(0, "", test_id); + TEST_RES(res == 0, "Normal error message", handler_res, test_id); + TEST_RES(strcmp(buf, "Invalid argument") == 0, + "Normal error message Contents", handler_res, test_id); + + // Test case 2: Buffer too small + test_id++; + res = strerror_s(buf, 10, EINVAL); + handler_res = test_handler_called(0, "", test_id); + TEST_RES(res == ERANGE, "Buffer too small", handler_res, test_id); + + // Test case 3: Null Destination Pointer + test_id++; + res = strerror_s(NULL, sizeof(buf), EINVAL); + handler_res = test_handler_called(1, "strerror_s: dest is NULL", test_id); + TEST_RES(res != 0, "Null Destination Pointer", handler_res, test_id); + + // Test case 4: Zero-length Buffer + test_id++; + res = strerror_s(buf, 0, EINVAL); + handler_res = test_handler_called( + 1, "strerror_s: dest buffer size is 0 or exceeds RSIZE_MAX", test_id); + TEST_RES(res != 0, "Zero-length Buffer", handler_res, test_id); + + // Test case 5: Unknown error code + test_id++; + res = strerror_s(buf, sizeof(buf), 12345); + handler_res = test_handler_called(0, "", test_id); + TEST_RES(res == 0, "Unknown error code", handler_res, test_id); + + printf("All strerror_s tests passed!\n"); + return 0; +} \ No newline at end of file diff --git a/test/test-strerrorlen_s.c b/test/test-strerrorlen_s.c new file mode 100644 index 0000000000..c1e88487ce --- /dev/null +++ b/test/test-strerrorlen_s.c @@ -0,0 +1,125 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright © 2024, Synopsys Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define __STDC_WANT_LIB_EXT1__ 1 +#include +#include +#include +#include + +#define MAX_ERROR_MSG 100 + +char handler_msg[MAX_ERROR_MSG] = ""; + +static void +custom_constraint_handler(const char *restrict msg, void *restrict ptr, + errno_t error) +{ + (void)ptr; + (void)error; + strcpy(handler_msg, msg); +} + +#define TEST_RES(cond, msg, handler_res, test_id) \ + if ((!(cond)) || (handler_res == 1)) { \ + printf("Test %d Failed: %s\n", test_id, msg); \ + return 1; \ + } else { \ + printf("Test %d Passed: %s\n", test_id, msg); \ + } + +static int +test_handler_called(int handler_called, char *expected_msg, int test_id) +{ + int ret = 0; + if (handler_called == 0) { + (void)expected_msg; + if (handler_msg[0] != '\0') { + printf( + "ERROR: Custom constraint handler called without error detiction!\n"); + printf("Test %d Failed: Error msg is incorrect\n", test_id); + ret = 1; + } + } else { + if (handler_msg[0] == '\0') { + (void)expected_msg; + printf("ERROR: Custom constraint handler not called\n"); + printf("Test %d Failed: Error msg is incorrect\n", test_id); + ret = 1; + } else { + if (strcmp(expected_msg, handler_msg) != 0) { + printf( + "ERROR: Custom constraint handler called with incorrect msg: %s\n", + handler_msg); + printf("Test %d Failed: Error msg is incorrect\n", test_id); + ret = 1; + } else { + (void)expected_msg; + printf( + "Custom constraint handler called with correct msg: %s\n", + handler_msg); + handler_msg[0] = '\0'; + ret = 0; + } + } + } + return ret; +} + +int +main(void) +{ + size_t length; + int test_id = 0; + int handler_res = 0; + + set_constraint_handler_s(custom_constraint_handler); + + // Test case 1: Normal error code + test_id++; + length = strerrorlen_s(EINVAL); + handler_res = test_handler_called(0, "", test_id); + TEST_RES(length == strlen("Invalid argument"), "Normal error code length", + handler_res, test_id); + + // Test case 2: Unknown error code + test_id++; + length = strerrorlen_s(12345); + handler_res = test_handler_called(0, "", test_id); + TEST_RES(length == 0, "Unknown error code length", handler_res, test_id); + + printf("All strerrorlen_s tests passed!\n"); + return 0; +} \ No newline at end of file diff --git a/test/test-strncat_s.c b/test/test-strncat_s.c new file mode 100644 index 0000000000..3093058a7d --- /dev/null +++ b/test/test-strncat_s.c @@ -0,0 +1,174 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright © 2024, Synopsys Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define __STDC_WANT_LIB_EXT1__ 1 +#include +#include +#include +#include + +#define MAX_ERROR_MSG 100 + +char handler_msg[MAX_ERROR_MSG] = ""; + +static void +custom_constraint_handler(const char *restrict msg, void *restrict ptr, + errno_t error) +{ + (void)ptr; + (void)error; + strcpy(handler_msg, msg); +} + +#define TEST_RES(cond, msg, handler_res, test_id) \ + if ((!(cond)) || (handler_res == 1)) { \ + printf("Test %d Failed: %s\n", test_id, msg); \ + return 1; \ + } else { \ + printf("Test %d Passed: %s\n", test_id, msg); \ + } + +static int +test_handler_called(int handler_called, char *expected_msg, int test_id) +{ + int ret = 0; + if (handler_called == 0) { + (void)expected_msg; + if (handler_msg[0] != '\0') { + printf( + "ERROR: Custom constraint handler called without error detiction!\n"); + printf("Test %d Failed: Error msg is incorrect\n", test_id); + ret = 1; + } + } else { + if (handler_msg[0] == '\0') { + (void)expected_msg; + printf("ERROR: Custom constraint handler not called\n"); + printf("Test %d Failed: Error msg is incorrect\n", test_id); + ret = 1; + } else { + if (strcmp(expected_msg, handler_msg) != 0) { + printf( + "ERROR: Custom constraint handler called with incorrect msg: %s\n", + handler_msg); + printf("Test %d Failed: Error msg is incorrect\n", test_id); + ret = 1; + } else { + (void)expected_msg; + printf( + "Custom constraint handler called with correct msg: %s\n", + handler_msg); + handler_msg[0] = '\0'; + ret = 0; + } + } + } + return ret; +} + +int +main(void) +{ + char dest[50] = "Hello"; + const char *src = ", world!"; + int test_id = 0; + int handler_res = 0; + errno_t res; + + set_constraint_handler_s(custom_constraint_handler); + + // Test case 1: Normal Concatenation + test_id++; + strcpy(dest, "Hello"); + res = strncat_s(dest, sizeof(dest), src, 8); + handler_res = test_handler_called(0, "", test_id); + TEST_RES(res == 0, "Normal Concatenation", handler_res, test_id); + TEST_RES(strcmp(dest, "Hello, world!") == 0, + "Normal Concatenation Contents", handler_res, test_id); + + // Test case 2: Concatenation with insufficient buffer + test_id++; + strcpy(dest, "Hello"); + res = strncat_s(dest, 10, src, 8); + handler_res = test_handler_called( + 1, "strncat_s: dest buffer size insufficent to copy string", test_id); + TEST_RES(res != 0, "Concatenation with insufficient buffer", handler_res, + test_id); + + // Test case 3: Null pointers + test_id++; + res = strncat_s(NULL, sizeof(dest), src, 8); + handler_res = test_handler_called(1, "strncat_s: dest is NULL", test_id); + TEST_RES(res != 0, "NULL Destination Pointer", handler_res, test_id); + res = strncat_s(dest, sizeof(dest), NULL, 8); + handler_res = test_handler_called(1, "strncat_s: source is NULL", test_id); + TEST_RES(res != 0, "NULL Source Pointer", handler_res, test_id); + + // Test case 4: Concatenation of empty source string + test_id++; + strcpy(dest, "Hello"); + res = strncat_s(dest, sizeof(dest), "", 0); + handler_res = test_handler_called(0, "", test_id); + TEST_RES(res == 0, "Concatenation of empty source string", handler_res, + test_id); + TEST_RES(strcmp(dest, "Hello") == 0, + "Concatenation of empty source string Contents", handler_res, + test_id); + + // Test case 5: Concatenation with empty destination string + test_id++; + char buf2[50] = ""; + res = strncat_s(buf2, sizeof(buf2), src, 8); + handler_res = test_handler_called(0, "", test_id); + TEST_RES(res == 0, "Concatenation of non-empty source to empty destination", + handler_res, test_id); + TEST_RES(strcmp(buf2, ", world!") == 0, + "Concatenation of non-empty source to empty destination Contents", + handler_res, test_id); + + // Test case 6: Concatenation with Zero Characters + test_id++; + strcpy(dest, "Hello"); + res = strncat_s(dest, sizeof(dest), src, 0); + handler_res = test_handler_called(0, "", test_id); + TEST_RES(res == 0, "Concatenation with Zero Characters", handler_res, + test_id); + TEST_RES(strcmp(dest, "Hello") == 0, + "Concatenation with Zero Characters Contents", handler_res, + test_id); + + printf("All strncat_s tests passed!\n"); + return 0; +} \ No newline at end of file diff --git a/test/test-strncpy_s.c b/test/test-strncpy_s.c new file mode 100644 index 0000000000..5d7f07566e --- /dev/null +++ b/test/test-strncpy_s.c @@ -0,0 +1,154 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright © 2024, Synopsys Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define __STDC_WANT_LIB_EXT1__ 1 +#include +#include +#include +#include + +#define MAX_ERROR_MSG 100 + +char handler_msg[MAX_ERROR_MSG] = ""; + +static void +custom_constraint_handler(const char *restrict msg, void *restrict ptr, + errno_t error) +{ + (void)ptr; + (void)error; + strcpy(handler_msg, msg); +} + +#define TEST_RES(cond, msg, handler_res, test_id) \ + if ((!(cond)) || (handler_res == 1)) { \ + printf("Test %d Failed: %s\n", test_id, msg); \ + return 1; \ + } else { \ + printf("Test %d Passed: %s\n", test_id, msg); \ + } + +static int +test_handler_called(int handler_called, char *expected_msg, int test_id) +{ + int ret = 0; + if (handler_called == 0) { + (void)expected_msg; + if (handler_msg[0] != '\0') { + printf( + "ERROR: Custom constraint handler called without error detiction!\n"); + printf("Test %d Failed: Error msg is incorrect\n", test_id); + ret = 1; + } + } else { + if (handler_msg[0] == '\0') { + (void)expected_msg; + printf("ERROR: Custom constraint handler not called\n"); + printf("Test %d Failed: Error msg is incorrect\n", test_id); + ret = 1; + } else { + if (strcmp(expected_msg, handler_msg) != 0) { + printf( + "ERROR: Custom constraint handler called with incorrect msg: %s\n", + handler_msg); + printf("Test %d Failed: Error msg is incorrect\n", test_id); + ret = 1; + } else { + (void)expected_msg; + printf( + "Custom constraint handler called with correct msg: %s\n", + handler_msg); + handler_msg[0] = '\0'; + ret = 0; + } + } + } + return ret; +} + +int +main(void) +{ + char dest[50]; + const char *src = "Hello, world!"; + int test_id = 0; + int handler_res = 0; + errno_t res; + + set_constraint_handler_s(custom_constraint_handler); + + // Test case 1: Normal copy + test_id++; + res = strncpy_s(dest, sizeof(dest), src, 13); + handler_res = test_handler_called(0, "", test_id); + TEST_RES(res == 0, "Normal Copy", handler_res, test_id); + TEST_RES(strcmp(dest, "Hello, world!") == 0, "Normal Copy Contents", + handler_res, test_id); + + // Test case 2: Copy with insufficient buffer + test_id++; + res = strncpy_s(dest, 5, src, 13); + handler_res = test_handler_called( + 1, "strncpy_s: dest buffer size insufficent to copy string", test_id); + TEST_RES(res != 0, "Copy with insufficient buffer", handler_res, test_id); + + // Test case 3: Null pointers + test_id++; + res = strncpy_s(NULL, sizeof(dest), src, 13); + handler_res = test_handler_called(1, "strncpy_s: dest is NULL", test_id); + TEST_RES(res != 0, "NULL Destination Pointer", handler_res, test_id); + res = strncpy_s(dest, sizeof(dest), NULL, 13); + handler_res = test_handler_called(1, "strncpy_s: source is NULL", test_id); + TEST_RES(res != 0, "NULL Source Pointer", handler_res, test_id); + + // Test case 4: Copy of empty string + test_id++; + res = strncpy_s(dest, sizeof(dest), "", 0); + handler_res = test_handler_called(0, "", test_id); + TEST_RES(res == 0, "Copy of empty string", handler_res, test_id); + TEST_RES(strcmp(dest, "") == 0, "Copy of empty string Contents", + handler_res, test_id); + + // Test case 5: Copy with zero Characters + test_id++; + res = strncpy_s(dest, sizeof(dest), "Hello, world!", 0); + handler_res = test_handler_called(0, "", test_id); + TEST_RES(res == 0, "Copy with zero Characters", handler_res, test_id); + TEST_RES(strcmp(dest, "") == 0, "Copy with zero Characters Contents", + handler_res, test_id); + + printf("All strncpy_s tests passed!\n"); + return 0; +} \ No newline at end of file diff --git a/test/test-strnlen_s.c b/test/test-strnlen_s.c new file mode 100644 index 0000000000..f2b6880073 --- /dev/null +++ b/test/test-strnlen_s.c @@ -0,0 +1,150 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright © 2024, Synopsys Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define __STDC_WANT_LIB_EXT1__ 1 +#include +#include +#include +#include + +#define MAX_ERROR_MSG 100 + +char handler_msg[MAX_ERROR_MSG] = ""; + +static void +custom_constraint_handler(const char *restrict msg, void *restrict ptr, + errno_t error) +{ + (void)ptr; + (void)error; + strcpy(handler_msg, msg); +} + +#define TEST_RES(cond, msg, handler_res, test_id) \ + if ((!(cond)) || (handler_res == 1)) { \ + printf("Test %d Failed: %s\n", test_id, msg); \ + return 1; \ + } else { \ + printf("Test %d Passed: %s\n", test_id, msg); \ + } + +static int +test_handler_called(int handler_called, char *expected_msg, int test_id) +{ + int ret = 0; + if (handler_called == 0) { + (void)expected_msg; + if (handler_msg[0] != '\0') { + printf( + "ERROR: Custom constraint handler called without error detiction!\n"); + printf("Test %d Failed: Error msg is incorrect\n", test_id); + ret = 1; + } + } else { + if (handler_msg[0] == '\0') { + (void)expected_msg; + printf("ERROR: Custom constraint handler not called\n"); + printf("Test %d Failed: Error msg is incorrect\n", test_id); + ret = 1; + } else { + if (strcmp(expected_msg, handler_msg) != 0) { + printf( + "ERROR: Custom constraint handler called with incorrect msg: %s\n", + handler_msg); + printf("Test %d Failed: Error msg is incorrect\n", test_id); + ret = 1; + } else { + (void)expected_msg; + printf( + "Custom constraint handler called with correct msg: %s\n", + handler_msg); + handler_msg[0] = '\0'; + ret = 0; + } + } + } + return ret; +} + +int +main(void) +{ + size_t length; + int test_id = 0; + int handler_res = 0; + + set_constraint_handler_s(custom_constraint_handler); + + // Test case 1: Normal length + test_id++; + length = strnlen_s("Hello, world!", 50); + handler_res = test_handler_called(0, "", test_id); + TEST_RES(length == 13, "Normal length", handler_res, test_id); + + // Test case 2: Length with exact buffer size + test_id++; + length = strnlen_s("Hello, world!", 13); + handler_res = test_handler_called(0, "", test_id); + TEST_RES(length == 13, "Length with exact buffer size", handler_res, + test_id); + + // Test case 3: Length with insufficient buffer + test_id++; + length = strnlen_s("Hello, world!", 5); + handler_res = test_handler_called(0, "", test_id); + TEST_RES(length == 5, "Length with insufficient buffer", handler_res, + test_id); + + // Test case 4: Length of empty string + test_id++; + length = strnlen_s("", 50); + handler_res = test_handler_called(0, "", test_id); + TEST_RES(length == 0, "Length of empty string", handler_res, test_id); + + // Test case 5: Length with Null string + test_id++; + length = strnlen_s(NULL, 50); + handler_res = test_handler_called(0, "", test_id); + TEST_RES(length == 0, "Length with Null string", handler_res, test_id); + + // Test case 6: Length with zero buffer size + test_id++; + length = strnlen_s("Hello, world!", 0); + handler_res = test_handler_called(0, "", test_id); + TEST_RES(length == 0, "Length with zero buffer size", handler_res, test_id); + + printf("All strnlen_s tests passed!\n"); + return 0; +} \ No newline at end of file