diff options
Diffstat (limited to 'lib/libc/tests/string')
22 files changed, 1552 insertions, 50 deletions
diff --git a/lib/libc/tests/string/Makefile b/lib/libc/tests/string/Makefile index a7545b75e0e9..a019939c30af 100644 --- a/lib/libc/tests/string/Makefile +++ b/lib/libc/tests/string/Makefile @@ -1,19 +1,30 @@ - # ensure libc functions are tested, not clang's builtins CFLAGS+= -fno-builtin +ATF_TESTS_C+= bcmp_test ATF_TESTS_C+= ffs_test ATF_TESTS_C+= ffsl_test ATF_TESTS_C+= ffsll_test ATF_TESTS_C+= fls_test ATF_TESTS_C+= flsl_test ATF_TESTS_C+= flsll_test +ATF_TESTS_C+= memccpy_test ATF_TESTS_C+= memcmp_test +ATF_TESTS_C+= memrchr_test +ATF_TESTS_C+= memset2_test ATF_TESTS_C+= memset_s_test +ATF_TESTS_C+= strncmp_test ATF_TESTS_C+= stpncpy_test +ATF_TESTS_C+= strnlen_test +ATF_TESTS_C+= strcmp2_test +ATF_TESTS_C+= strcspn_test ATF_TESTS_C+= strerror2_test +ATF_TESTS_C+= strlcpy_test +ATF_TESTS_C+= strspn_test ATF_TESTS_C+= strverscmp_test ATF_TESTS_C+= strxfrm_test +ATF_TESTS_C+= timingsafe_bcmp_test +ATF_TESTS_C+= timingsafe_memcmp_test ATF_TESTS_C+= wcscasecmp_test ATF_TESTS_C+= wcscoll_test ATF_TESTS_C+= wcsnlen_test @@ -29,14 +40,14 @@ NETBSD_ATF_TESTS_C+= strchr_test NETBSD_ATF_TESTS_C+= strchrnul_test NETBSD_ATF_TESTS_C+= strcmp_test NETBSD_ATF_TESTS_C+= strcpy_test -NETBSD_ATF_TESTS_C+= strcspn_test NETBSD_ATF_TESTS_C+= strerror_test NETBSD_ATF_TESTS_C+= strlen_test NETBSD_ATF_TESTS_C+= strpbrk_test NETBSD_ATF_TESTS_C+= strrchr_test -NETBSD_ATF_TESTS_C+= strspn_test NETBSD_ATF_TESTS_C+= swab_test +SRCS.memset2_test= memset_test.c +SRCS.strcmp2_test= strcmp_test.c SRCS.strerror2_test= strerror_test.c .include "../Makefile.netbsd-tests" diff --git a/lib/libc/tests/string/bcmp_test.c b/lib/libc/tests/string/bcmp_test.c new file mode 100644 index 000000000000..fdf5e48b3eb4 --- /dev/null +++ b/lib/libc/tests/string/bcmp_test.c @@ -0,0 +1,32 @@ +/*- + * Copyright (c) 2023 The FreeBSD Foundation + * + * This software was developed by Robert Clausecker <fuz@FreeBSD.org> + * under sponsorship from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 MEMCMP bcmp +#define RES(x) ((x) != 0) + +#include "memcmp_test.c" diff --git a/lib/libc/tests/string/ffs_test.c b/lib/libc/tests/string/ffs_test.c index aea61254482e..4b59385d712e 100644 --- a/lib/libc/tests/string/ffs_test.c +++ b/lib/libc/tests/string/ffs_test.c @@ -25,7 +25,6 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE */ -#include <sys/cdefs.h> #include <atf-c.h> #include <limits.h> diff --git a/lib/libc/tests/string/fls_test.c b/lib/libc/tests/string/fls_test.c index 847977d260c9..55691b36617c 100644 --- a/lib/libc/tests/string/fls_test.c +++ b/lib/libc/tests/string/fls_test.c @@ -25,7 +25,6 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE */ -#include <sys/cdefs.h> #include <atf-c.h> #include <limits.h> diff --git a/lib/libc/tests/string/memccpy_test.c b/lib/libc/tests/string/memccpy_test.c new file mode 100644 index 000000000000..4784fee4ede5 --- /dev/null +++ b/lib/libc/tests/string/memccpy_test.c @@ -0,0 +1,229 @@ +/*- + * Copyright (c) 2009 David Schultz <das@FreeBSD.org> + * Copyright (c) 2023, 2024 The FreeBSD Foundation + * All rights reserved. + * + * Portions of this software were developed by Robert Clausecker + * <fuz@FreeBSD.org> under sponsorship from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + */ + +#include <sys/cdefs.h> +#include <sys/param.h> +#include <sys/mman.h> +#include <assert.h> +#include <dlfcn.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <atf-c.h> + +void *(*memccpy_fn)(void *restrict, const void *restrict, int, size_t); + +static char * +makebuf(size_t len, int guard_at_end) +{ + char *buf; + size_t alloc_size, page_size; + + page_size = getpagesize(); + alloc_size = roundup2(len, page_size) + page_size; + + buf = mmap(NULL, alloc_size, PROT_READ | PROT_WRITE, MAP_ANON, -1, 0); + assert(buf); + if (guard_at_end) { + assert(mprotect(buf + alloc_size - page_size, page_size, PROT_NONE) == 0); + return (buf + alloc_size - page_size - len); + } else { + assert(mprotect(buf, page_size, PROT_NONE) == 0); + return (buf + page_size); + } +} + +static void +freebuf(char * buf, size_t len, int guard_at_end) +{ + size_t alloc_size, page_size; + + page_size = getpagesize(); + alloc_size = roundup2(len, page_size) + page_size; + + if (guard_at_end) + munmap(buf + len + page_size - alloc_size, alloc_size); + else + munmap(buf - page_size, alloc_size); +} + +static void +test_memccpy(const char *s, size_t size) +{ + char *src, *dst, *expected; + size_t bufsize, x; + int i, j; + + for (i = 0; i <= 1; i++) { + for (j = 0; j <= 1; j++) { + for (bufsize = 0; bufsize <= size + 32; bufsize++) { + dst = makebuf(bufsize, j); + if (bufsize < size) { + src = makebuf(bufsize, i); + memcpy(src, s, bufsize); + expected = NULL; + } else { + src = makebuf(size, i); + memcpy(src, s, size); + expected = dst + size; + } + + memset(dst, 'X', bufsize); + assert(memccpy_fn(dst, src, s[size-1], bufsize) == expected); + assert(memcmp(src, dst, MIN(bufsize, size)) == 0); + for (x = size; x < bufsize; x++) + assert(dst[x] == 'X'); + + freebuf(dst, bufsize, j); + freebuf(src, bufsize < size ? bufsize : size, i); + } + } + } +} + +static void +test_sentinel(char *dest, char *src, size_t destlen, size_t srclen) +{ + size_t i, effective_len; + void *res, *wantres; + const char *fail = NULL; + char terminator; + + for (i = 0; i < srclen; i++) + /* src will never include (){} */ + src[i] = '0' + i; + + /* source sentinels: not to be copied */ + src[-1] = '('; + src[srclen] = ')'; + + memset(dest, '\xee', destlen); + + /* destination sentinels: not to be touched */ + dest[-1] = '{'; + dest[destlen] = '}'; + + effective_len = srclen < destlen ? srclen : destlen; + wantres = srclen <= destlen ? dest + srclen : NULL; + terminator = src[srclen-1]; + res = memccpy_fn(dest, src, terminator, destlen); + + if (dest[-1] != '{') + fail = "start sentinel overwritten"; + else if (dest[destlen] != '}') + fail = "end sentinel overwritten"; + else if (res != wantres) + fail = "incorrect return value"; + else if (destlen > 0 && memcmp(src, dest, effective_len) != 0) + fail = "string not copied correctly"; + else for (i = srclen; i < destlen; i++) + if (dest[i] != '\xee') { + fail = "buffer mutilated behind string"; + break; + } + + if (fail) + atf_tc_fail_nonfatal("%s\n" + "memccpy(%p \"%s\", %p \"%s\", %u '%c', %zu) = %p (want %p)\n", + fail, dest, dest, src, src, terminator, terminator, destlen, res, wantres); +} + +ATF_TC_WITHOUT_HEAD(null); +ATF_TC_BODY(null, tc) +{ + ATF_CHECK_EQ(memccpy_fn(NULL, "foo", 42, 0), NULL); +} + +ATF_TC(zero_extension); +ATF_TC_HEAD(zero_extension, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Ensure the upper bits of the terminator are ignored"); +} +ATF_TC_BODY(zero_extension, tc) +{ + int mask = -1 & ~UCHAR_MAX; + char buf[16]; + + memset(buf, 0xcc, sizeof(buf)); + ATF_CHECK_EQ(memccpy(buf, "foobar", 'r', sizeof(buf)), buf + sizeof("foobar") - 1); + ATF_CHECK_EQ(memcmp(buf, "foobar", sizeof("foobar") - 1), 0); + + memset(buf, 0xcc, sizeof(buf)); + ATF_CHECK_EQ(memccpy(buf, "foobar", mask | 'r', sizeof(buf)), buf + sizeof("foobar") - 1); + ATF_CHECK_EQ(memcmp(buf, "foobar", sizeof("foobar") - 1), 0); +} + +ATF_TC_WITHOUT_HEAD(bounds); +ATF_TC_BODY(bounds, tc) +{ + size_t i; + char buf[64]; + + for (i = 0; i < sizeof(buf) - 1; i++) { + buf[i] = ' ' + i; + buf[i+1] = '\0'; + test_memccpy(buf, i + 1); + } +} + +ATF_TC_WITHOUT_HEAD(alignments); +ATF_TC_BODY(alignments, tc) +{ + size_t srcalign, destalign, srclen, destlen; + char src[15+2+64]; /* 15 offsets + 64 max length + sentinels */ + char dest[15+2+64]; /* 15 offsets + 64 max length + sentinels */ + + for (srcalign = 0; srcalign < 16; srcalign++) + for (destalign = 0; destalign < 16; destalign++) + for (srclen = 1; srclen < 64; srclen++) + for (destlen = 0; destlen < 64; destlen++) + test_sentinel(dest+destalign+1, + src+srcalign+1, destlen, srclen); +} + +ATF_TP_ADD_TCS(tp) +{ + void *dl_handle; + + dl_handle = dlopen(NULL, RTLD_LAZY); + memccpy_fn = dlsym(dl_handle, "test_memccpy"); + if (memccpy_fn == NULL) + memccpy_fn = memccpy; + + ATF_TP_ADD_TC(tp, null); + ATF_TP_ADD_TC(tp, zero_extension); + ATF_TP_ADD_TC(tp, bounds); + ATF_TP_ADD_TC(tp, alignments); + + return (atf_no_error()); +} diff --git a/lib/libc/tests/string/memcmp_test.c b/lib/libc/tests/string/memcmp_test.c index ab782c92f255..5286a0b994f3 100644 --- a/lib/libc/tests/string/memcmp_test.c +++ b/lib/libc/tests/string/memcmp_test.c @@ -1,7 +1,11 @@ /*- * Copyright (c) 2016 Jilles Tjoelker <jilles@FreeBSD.org> + * Copyright (c) 2023 The FreeBSD Foundation * All rights reserved. * + * Portions of this software were developed by Robert Clausecker + * <fuz@FreeBSD.org> under sponsorship from the FreeBSD Foundation. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -24,20 +28,48 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <assert.h> +#include <dlfcn.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <atf-c.h> +#ifndef MEMCMP +#define MEMCMP memcmp +#endif + +/* + * On FreeBSD we demand that memcmp returns the difference between the + * characters at the first site of mismatch. However, ISO/IEC 9899:1990 + * only specifies that a number greater than, equal to, or less than + * zero shall be returned. If a unit test for this less strict + * behaviour is desired, define RES(x) to be (((x) > 0) - ((x) < 0)). + */ +#ifndef RES +#define RES(x) (x) +#endif + +static int (*memcmp_fn)(const void *, const void *, size_t); + +static void +check_memcmp(const char *a, const char *b, size_t len, int expected) +{ + int got; + + got = memcmp_fn(a, b, len); + ATF_CHECK_EQ_MSG(RES(expected), RES(got), + "%s(%p, %p, %zu) gave %d, but wanted %d", + __XSTRING(MEMCMP), a, b, len, got, expected); +} + ATF_TC_WITHOUT_HEAD(zero); ATF_TC_BODY(zero, tc) { - assert(memcmp("a", "b", 0) == 0); - assert(memcmp("", "", 0) == 0); + check_memcmp("a", "b", 0, 0); + check_memcmp("", "", 0, 0); } ATF_TC_WITHOUT_HEAD(eq); @@ -49,9 +81,9 @@ ATF_TC_BODY(eq, tc) for (i = 0; i < 256; i++) data1[i] = data2[i] = i ^ 0x55; for (i = 1; i < 256; i++) - assert(memcmp(data1, data2, i) == 0); + check_memcmp(data1, data2, i, 0); for (i = 1; i < 256; i++) - assert(memcmp(data1 + i, data2 + i, 256 - i) == 0); + check_memcmp(data1 + i, data2 + i, 256 - i, 0); } ATF_TC_WITHOUT_HEAD(neq); @@ -65,9 +97,9 @@ ATF_TC_BODY(neq, tc) data2[i] = i ^ 0x55; } for (i = 1; i < 256; i++) - assert(memcmp(data1, data2, i) != 0); + check_memcmp(data1, data2, i, -0x55); for (i = 1; i < 256; i++) - assert(memcmp(data1 + i, data2 + i, 256 - i) != 0); + check_memcmp(data1 + i, data2 + i, 256 - i, i - (i ^ 0x55)); } ATF_TC_WITHOUT_HEAD(diff); @@ -81,37 +113,43 @@ ATF_TC_BODY(diff, tc) data1[128] = 255; data2[128] = 0; for (i = 1; i < 66; i++) { - assert(memcmp(data1 + 128, data2 + 128, i) == 255); - assert(memcmp(data2 + 128, data1 + 128, i) == -255); - assert(memcmp(data1 + 129 - i, data2 + 129 - i, i) == 255); - assert(memcmp(data2 + 129 - i, data1 + 129 - i, i) == -255); - assert(memcmp(data1 + 129 - i, data2 + 129 - i, i * 2) == 255); - assert(memcmp(data2 + 129 - i, data1 + 129 - i, i * 2) == -255); + check_memcmp(data1 + 128, data2 + 128, i, 255); + check_memcmp(data2 + 128, data1 + 128, i, -255); + check_memcmp(data1 + 129 - i, data2 + 129 - i, i, 255); + check_memcmp(data2 + 129 - i, data1 + 129 - i, i, -255); + check_memcmp(data1 + 129 - i, data2 + 129 - i, i * 2, 255); + check_memcmp(data2 + 129 - i, data1 + 129 - i, i * 2, -255); } data1[128] = 'c'; data2[128] = 'e'; for (i = 1; i < 66; i++) { - assert(memcmp(data1 + 128, data2 + 128, i) == -2); - assert(memcmp(data2 + 128, data1 + 128, i) == 2); - assert(memcmp(data1 + 129 - i, data2 + 129 - i, i) == -2); - assert(memcmp(data2 + 129 - i, data1 + 129 - i, i) == 2); - assert(memcmp(data1 + 129 - i, data2 + 129 - i, i * 2) == -2); - assert(memcmp(data2 + 129 - i, data1 + 129 - i, i * 2) == 2); + check_memcmp(data1 + 128, data2 + 128, i, -2); + check_memcmp(data2 + 128, data1 + 128, i, 2); + check_memcmp(data1 + 129 - i, data2 + 129 - i, i, -2); + check_memcmp(data2 + 129 - i, data1 + 129 - i, i, 2); + check_memcmp(data1 + 129 - i, data2 + 129 - i, i * 2, -2); + check_memcmp(data2 + 129 - i, data1 + 129 - i, i * 2, 2); } memset(data1 + 129, 'A', sizeof(data1) - 129); memset(data2 + 129, 'Z', sizeof(data2) - 129); for (i = 1; i < 66; i++) { - assert(memcmp(data1 + 128, data2 + 128, i) == -2); - assert(memcmp(data2 + 128, data1 + 128, i) == 2); - assert(memcmp(data1 + 129 - i, data2 + 129 - i, i) == -2); - assert(memcmp(data2 + 129 - i, data1 + 129 - i, i) == 2); - assert(memcmp(data1 + 129 - i, data2 + 129 - i, i * 2) == -2); - assert(memcmp(data2 + 129 - i, data1 + 129 - i, i * 2) == 2); + check_memcmp(data1 + 128, data2 + 128, i, -2); + check_memcmp(data2 + 128, data1 + 128, i, 2); + check_memcmp(data1 + 129 - i, data2 + 129 - i, i, -2); + check_memcmp(data2 + 129 - i, data1 + 129 - i, i, 2); + check_memcmp(data1 + 129 - i, data2 + 129 - i, i * 2, -2); + check_memcmp(data2 + 129 - i, data1 + 129 - i, i * 2, 2); } } ATF_TP_ADD_TCS(tp) { + void *dl_handle; + + dl_handle = dlopen(NULL, RTLD_LAZY); + memcmp_fn = dlsym(dl_handle, "test_" __XSTRING(MEMCMP)); + if (memcmp_fn == NULL) + memcmp_fn = MEMCMP; ATF_TP_ADD_TC(tp, zero); ATF_TP_ADD_TC(tp, eq); diff --git a/lib/libc/tests/string/memrchr_test.c b/lib/libc/tests/string/memrchr_test.c new file mode 100644 index 000000000000..12f696c9dc1e --- /dev/null +++ b/lib/libc/tests/string/memrchr_test.c @@ -0,0 +1,116 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Robert Clausecker + */ + +#include <sys/cdefs.h> + +#include <dlfcn.h> +#include <limits.h> +#include <string.h> + +#include <atf-c.h> + +static void *(*memrchr_fn)(const void *, int, size_t); + +ATF_TC_WITHOUT_HEAD(null); +ATF_TC_BODY(null, tc) +{ + ATF_CHECK_EQ(memrchr_fn(NULL, 42, 0), NULL); +} + +ATF_TC_WITHOUT_HEAD(not_found); +ATF_TC_BODY(not_found, tc) +{ + size_t i, j; + char buf[1+15+64+1]; /* offset [0..15] + 64 buffer bytes + sentinels */ + + buf[0] = 'X'; + memset(buf + 1, '-', sizeof(buf) - 1); + + for (i = 0; i < 16; i++) + for (j = 0; j < 64; j++) { + buf[i + j + 1] = 'X'; + ATF_CHECK_EQ(memrchr_fn(buf + i + 1, 'X', j), NULL); + buf[i + j + 1] = '-'; + } +} + +static void +do_found_test(char buf[], size_t len, size_t first, size_t second) +{ + /* invariant: first <= second */ + + buf[first] = 'X'; + buf[second] = 'X'; + ATF_CHECK_EQ(memrchr_fn(buf, 'X', len), buf + second); + buf[first] = '-'; + buf[second] = '-'; +} + +ATF_TC_WITHOUT_HEAD(found); +ATF_TC_BODY(found, tc) +{ + size_t i, j, k, l; + char buf[1+15+64+1]; + + buf[0] = 'X'; + memset(buf + 1, '-', sizeof(buf) - 1); + + for (i = 0; i < 16; i++) + for (j = 0; j < 64; j++) + for (k = 0; k < j; k++) + for (l = 0; l <= k; l++) { + buf[i + j + 1] = 'X'; + do_found_test(buf + i + 1, j, l, k); + buf[i + j + 1] = '-'; + } +} + +/* check that the right character is found */ +static void +do_values_test(unsigned char buf[], size_t len, size_t i, int c) +{ + /* sentinels */ + buf[-1] = c; + buf[len] = c; + memset(buf, c + 1, len); + + if (i < len) { + buf[i] = c; + ATF_CHECK_EQ(memrchr_fn(buf, c, len), buf + i); + } else + ATF_CHECK_EQ(memrchr_fn(buf, c, len), NULL); +} + +ATF_TC_WITHOUT_HEAD(values); +ATF_TC_BODY(values, tc) +{ + size_t i, j, k; + int c; + unsigned char buf[1+15+64+1]; + + for (i = 0; i < 16; i++) + for (j = 0; j < 64; j++) + for (k = 0; k <= j; k++) + for (c = 0; c <= UCHAR_MAX; c++) + do_values_test(buf + i + 1, j, k, c); +} + +ATF_TP_ADD_TCS(tp) +{ + void *dl_handle; + + dl_handle = dlopen(NULL, RTLD_LAZY); + memrchr_fn = dlsym(dl_handle, "test_memrchr"); + if (memrchr_fn == NULL) + memrchr_fn = memrchr; + + ATF_TP_ADD_TC(tp, null); + ATF_TP_ADD_TC(tp, not_found); + ATF_TP_ADD_TC(tp, found); + ATF_TP_ADD_TC(tp, values); + + return (atf_no_error()); +} diff --git a/lib/libc/tests/string/memset_s_test.c b/lib/libc/tests/string/memset_s_test.c index 90a6958cb369..c822a2ee4554 100644 --- a/lib/libc/tests/string/memset_s_test.c +++ b/lib/libc/tests/string/memset_s_test.c @@ -23,7 +23,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <assert.h> #include <stdint.h> #include <stdlib.h> diff --git a/lib/libc/tests/string/memset_test.c b/lib/libc/tests/string/memset_test.c new file mode 100644 index 000000000000..b898ad5af251 --- /dev/null +++ b/lib/libc/tests/string/memset_test.c @@ -0,0 +1,29 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2024 Strahinja Stanisic <strajabot@FreeBSD.org> + */ + +#include <assert.h> +#include <string.h> + +#include <atf-c.h> + +ATF_TC_WITHOUT_HEAD(int_char_conv); +ATF_TC_BODY(int_char_conv, tc) +{ + char b[64]; + int c = 0xDEADBEEF; + memset(&b, c, 64); + for(int i = 0; i < 64; i++) { + assert(b[i] == (char)c); + } + +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, int_char_conv); + return (atf_no_error()); +} + diff --git a/lib/libc/tests/string/stpncpy_test.c b/lib/libc/tests/string/stpncpy_test.c index 6602bda0c2a9..8574b2d591be 100644 --- a/lib/libc/tests/string/stpncpy_test.c +++ b/lib/libc/tests/string/stpncpy_test.c @@ -1,7 +1,11 @@ /*- * Copyright (c) 2009 David Schultz <das@FreeBSD.org> + * Copyright (c) 2023 The FreeBSD Foundation * All rights reserved. * + * Portions of this software were developed by Robert Clausecker + * <fuz@FreeBSD.org> under sponsorship from the FreeBSD Foundation. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -24,16 +28,18 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/mman.h> #include <assert.h> +#include <dlfcn.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <atf-c.h> +static char *(*stpncpy_fn)(char *restrict, const char *restrict, size_t); + static char * makebuf(size_t len, int guard_at_end) { @@ -70,7 +76,7 @@ test_stpncpy(const char *s) dst = makebuf(bufsize, j); memset(dst, 'X', bufsize); len = (bufsize < size) ? bufsize : size - 1; - assert(stpncpy(dst, src, bufsize) == dst+len); + assert(stpncpy_fn(dst, src, bufsize) == dst+len); assert(memcmp(src, dst, len) == 0); for (x = len; x < bufsize; x++) assert(dst[x] == '\0'); @@ -79,33 +85,97 @@ test_stpncpy(const char *s) } } -ATF_TC_WITHOUT_HEAD(nul); -ATF_TC_BODY(nul, tc) +static void +test_sentinel(char *dest, char *src, size_t destlen, size_t srclen) { + size_t i; + const char *res, *wantres; + const char *fail = NULL; + + for (i = 0; i < srclen; i++) + /* src will never include (){} */ + src[i] = '0' + i; + src[srclen] = '\0'; + + /* source sentinels: not to be copied */ + src[-1] = '('; + src[srclen+1] = ')'; + + memset(dest, 0xee, destlen); + + /* destination sentinels: not to be touched */ + dest[-1] = '{'; + dest[destlen] = '}'; + + wantres = dest + (srclen > destlen ? destlen : srclen); + res = stpncpy_fn(dest, src, destlen); + + if (dest[-1] != '{') + fail = "start sentinel overwritten"; + else if (dest[destlen] != '}') + fail = "end sentinel overwritten"; + else if (strncmp(src, dest, destlen) != 0) + fail = "string not copied correctly"; + else if (res != wantres) + fail = "incorrect return value"; + else for (i = srclen; i < destlen; i++) + if (dest[i] != '\0') { + fail = "incomplete NUL padding"; + break; + } - test_stpncpy(""); + if (fail) + atf_tc_fail_nonfatal("%s\n" + "stpncpy(%p \"%s\", %p \"%s\", %zu) = %p (want %p)\n", + fail, dest, dest, src, src, destlen, res, wantres); } -ATF_TC_WITHOUT_HEAD(foo); -ATF_TC_BODY(foo, tc) +ATF_TC_WITHOUT_HEAD(null); +ATF_TC_BODY(null, tc) { - - test_stpncpy("foo"); + ATF_CHECK_EQ(stpncpy_fn(NULL, NULL, 0), NULL); } -ATF_TC_WITHOUT_HEAD(glorp); -ATF_TC_BODY(glorp, tc) +ATF_TC_WITHOUT_HEAD(bounds); +ATF_TC_BODY(bounds, tc) { + size_t i; + char buf[64+1]; - test_stpncpy("glorp"); + for (i = 0; i < sizeof(buf) - 1; i++) { + buf[i] = ' ' + i; + buf[i+1] = '\0'; + test_stpncpy(buf); + } +} + +ATF_TC_WITHOUT_HEAD(alignments); +ATF_TC_BODY(alignments, tc) +{ + size_t srcalign, destalign, srclen, destlen; + char src[15+3+64]; /* 15 offsets + 64 max length + NUL + sentinels */ + char dest[15+2+64]; /* 15 offsets + 64 max length + sentinels */ + + for (srcalign = 0; srcalign < 16; srcalign++) + for (destalign = 0; destalign < 16; destalign++) + for (srclen = 0; srclen < 64; srclen++) + for (destlen = 0; destlen < 64; destlen++) + test_sentinel(dest+destalign+1, + src+srcalign+1, destlen, srclen); } ATF_TP_ADD_TCS(tp) { + void *dl_handle; + + dl_handle = dlopen(NULL, RTLD_LAZY); + stpncpy_fn = dlsym(dl_handle, "test_stpncpy"); + if (stpncpy_fn == NULL) + stpncpy_fn = stpncpy; - ATF_TP_ADD_TC(tp, nul); - ATF_TP_ADD_TC(tp, foo); - ATF_TP_ADD_TC(tp, glorp); + ATF_TP_ADD_TC(tp, null); + ATF_TP_ADD_TC(tp, bounds); + ATF_TP_ADD_TC(tp, alignments); return (atf_no_error()); } diff --git a/lib/libc/tests/string/strcmp_test.c b/lib/libc/tests/string/strcmp_test.c new file mode 100644 index 000000000000..75ebcbcadda2 --- /dev/null +++ b/lib/libc/tests/string/strcmp_test.c @@ -0,0 +1,132 @@ +/*- + * Copyright (c) 2023 The FreeBSD Foundation + * + * This software was developed by Robert Clausecker <fuz@FreeBSD.org> + * under sponsorship from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 + */ + +#include <sys/cdefs.h> + +#include <atf-c.h> +#include <dlfcn.h> +#include <string.h> + +int (*volatile strcmp_fn)(const char *, const char *); + +ATF_TC(strcmp_alignments); +ATF_TC_HEAD(strcmp_alignments, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test strcmp(3) with various alignments"); +} + +static void +alignment_testcase(char *a, char *b, int want) +{ + int res; + + res = strcmp_fn(a, b); + ATF_CHECK_MSG(want == (res > 0) - (res < 0), + "strcmp(%p \"%s\", %p \"%s\") = %d != %d", + (void *)a, a, (void *)b, b, res, want); +} + +static void +check_strcmp_alignments(char a[], char b[], + size_t a_off, size_t b_off, size_t len, size_t pos) +{ + char *a_str, *b_str, a_orig, b_orig; + + a[a_off] = '\0'; + b[b_off] = '\0'; + + a_str = a + a_off + 1; + b_str = b + b_off + 1; + + a_str[len] = '\0'; + b_str[len] = '\0'; + a_str[len+1] = 'A'; + b_str[len+1] = 'B'; + + a_orig = a_str[pos]; + b_orig = b_str[pos]; + + alignment_testcase(a_str, b_str, 0); + + if (pos < len) { + a_str[pos] = '\0'; + alignment_testcase(a_str, b_str, -1); + a_str[pos] = a_orig; + b_str[pos] = '\0'; + alignment_testcase(a_str, b_str, 1); + b_str[pos] = b_orig; + } + + a_str[pos] = 'X'; + alignment_testcase(a_str, b_str, 1); + a_str[pos] = a_orig; + b_str[pos] = 'X'; + alignment_testcase(a_str, b_str, -1); + b_str[pos] = b_orig; + + a[a_off] = '-'; + b[b_off] = '-'; + a_str[len] = '-'; + b_str[len] = '-'; + a_str[len+1] = '-'; + b_str[len+1] = '-'; +} + +ATF_TC_BODY(strcmp_alignments, tc) +{ + size_t a_off, b_off, len, pos; + /* 16B alignment offset + 64B buffer + sentinel before/after + NUL */ + char a[64+16+3], b[64+16+3]; + + memset(a, '-', sizeof(a)); + memset(b, '-', sizeof(b)); + a[sizeof(a) - 1] = '\0'; + b[sizeof(b) - 1] = '\0'; + + /* check alignment offsets relevant for SSE routines */ + for (a_off = 0; a_off < 16; a_off++) + for (b_off = 0; b_off < 16; b_off++) + /* ensure main loop (@ 32B) is completed at least once */ + for (len = 1; len <= 64; len++) + for (pos = 0; pos <= len; pos++) + check_strcmp_alignments(a, b, a_off, b_off, len, pos); +} + +ATF_TP_ADD_TCS(tp) +{ + void *dl_handle; + + dl_handle = dlopen(NULL, RTLD_LAZY); + strcmp_fn = dlsym(dl_handle, "test_strcmp"); + if (strcmp_fn == NULL) + strcmp_fn = strcmp; + + ATF_TP_ADD_TC(tp, strcmp_alignments); + + return atf_no_error(); +} diff --git a/lib/libc/tests/string/strcspn_test.c b/lib/libc/tests/string/strcspn_test.c new file mode 100644 index 000000000000..293fc2bc8d4e --- /dev/null +++ b/lib/libc/tests/string/strcspn_test.c @@ -0,0 +1,269 @@ +/*- + * Copyright (c) 2023 The FreeBSD Foundation + * + * This software was developed by Robert Clausecker <fuz@FreeBSD.org> + * under sponsorship from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 + */ + + +#include <atf-c.h> +#include <assert.h> +#include <limits.h> +#include <stdbool.h> +#include <stddef.h> +#include <string.h> + +enum { + MAXALIGN = 16, /* test all offsets from this alignment */ + MAXBUF = 64, /* test up to this buffer length */ +}; + +enum { NOMATCH, MATCH }; + +#ifdef STRSPN +#define STRXSPN strspn +#else +#define STRXSPN strcspn +#endif + +static void +testcase(char *buf, size_t buflen, char *set, size_t setlen, int want_match) +{ + size_t i, outcome, expected; + + assert(setlen < UCHAR_MAX - 2); + + for (i = 0; i < buflen; i++) +#ifdef STRSPN + buf[i] = UCHAR_MAX - i % (setlen > 0 ? setlen : 1); +#else /* strcspn */ + buf[i] = 1 + i % (UCHAR_MAX - setlen - 1); +#endif + + buf[i] = '\0'; + + for (i = 0; i < setlen; i++) + set[i] = UCHAR_MAX - i; + + set[i] = '\0'; + +#ifdef STRSPN + if (setlen == 0) + expected = 0; + else if (want_match == MATCH && buflen > 0) { + buf[buflen - 1] = 1; + expected = buflen - 1; + } else + expected = buflen; +#else /* strcspn */ + if (want_match == MATCH && buflen > 0 && setlen > 0) { + buf[buflen - 1] = UCHAR_MAX; + expected = buflen - 1; + } else + expected = buflen; +#endif + + outcome = STRXSPN(buf, set); + ATF_CHECK_EQ_MSG(expected, outcome, "%s(%p[%zu], %p[%zu]) = %zu != %zu", + __XSTRING(STRXSPN), buf, buflen, set, setlen, outcome, expected); +} + +/* test set with all alignments and lengths of buf */ +static void +test_buf_alignments(char *set, size_t setlen, int want_match) +{ + char buf[MAXALIGN + MAXBUF + 1]; + size_t i, j; + + for (i = 0; i < MAXALIGN; i++) + for (j = 0; j <= MAXBUF; j++) + testcase(buf + i, j, set, setlen, want_match); +} + +/* test buf with all alignments and lengths of set */ +static void +test_set_alignments(char *buf, size_t buflen, int want_match) +{ + char set[MAXALIGN + MAXBUF + 1]; + size_t i, j; + + for (i = 0; i < MAXALIGN; i++) + for (j = 0; j <= MAXBUF; j++) + testcase(buf, buflen, set + i, j, want_match); +} + +ATF_TC_WITHOUT_HEAD(buf_alignments); +ATF_TC_BODY(buf_alignments, tc) +{ + char set[41]; + + test_buf_alignments(set, 0, MATCH); + test_buf_alignments(set, 1, MATCH); + test_buf_alignments(set, 5, MATCH); + test_buf_alignments(set, 20, MATCH); + test_buf_alignments(set, 40, MATCH); + + test_buf_alignments(set, 0, NOMATCH); + test_buf_alignments(set, 1, NOMATCH); + test_buf_alignments(set, 5, NOMATCH); + test_buf_alignments(set, 20, NOMATCH); + test_buf_alignments(set, 40, NOMATCH); +} + +ATF_TC_WITHOUT_HEAD(set_alignments); +ATF_TC_BODY(set_alignments, tc) +{ + char buf[31]; + + test_set_alignments(buf, 0, MATCH); + test_set_alignments(buf, 10, MATCH); + test_set_alignments(buf, 20, MATCH); + test_set_alignments(buf, 30, MATCH); + + test_set_alignments(buf, 0, NOMATCH); + test_set_alignments(buf, 10, NOMATCH); + test_set_alignments(buf, 20, NOMATCH); + test_set_alignments(buf, 30, NOMATCH); +} + +#ifndef STRSPN +/* test all positions in which set could match buf */ +static void +test_match_positions(char *buf, char *set, size_t buflen, size_t setlen) +{ + size_t i, j, outcome; + + memset(buf, '-', buflen); + + for (i = 0; i < setlen; i++) + set[i] = 'A' + i; + + buf[buflen] = '\0'; + set[setlen] = '\0'; + + /* + * Check for (mis)match at buffer position i + * against set position j. + */ + for (i = 0; i < buflen; i++) { + for (j = 0; j < setlen; j++) { + buf[i] = set[j]; + + outcome = strcspn(buf, set); + ATF_CHECK_EQ_MSG(i, outcome, + "strcspn(\"%s\", \"%s\") = %zu != %zu", + buf, set, outcome, i); + } + + buf[i] = '-'; + } +} + +ATF_TC_WITHOUT_HEAD(match_positions); +ATF_TC_BODY(match_positions, tc) +{ + char buf[129], set[65]; + + test_match_positions(buf, set, 128, 64); + test_match_positions(buf, set, 64, 64); + test_match_positions(buf, set, 32, 64); + test_match_positions(buf, set, 16, 64); + test_match_positions(buf, set, 8, 64); + test_match_positions(buf, set, 128, 32); + test_match_positions(buf, set, 64, 32); + test_match_positions(buf, set, 32, 32); + test_match_positions(buf, set, 16, 32); + test_match_positions(buf, set, 8, 32); + test_match_positions(buf, set, 128, 16); + test_match_positions(buf, set, 64, 16); + test_match_positions(buf, set, 32, 16); + test_match_positions(buf, set, 16, 16); + test_match_positions(buf, set, 8, 16); + test_match_positions(buf, set, 128, 8); + test_match_positions(buf, set, 64, 8); + test_match_positions(buf, set, 32, 8); + test_match_positions(buf, set, 16, 8); + test_match_positions(buf, set, 8, 8); +} + +/* if there are two matches, check that the earlier match is taken */ +static void +test_match_order(char *buf, char *set, size_t buflen, size_t setlen) +{ + size_t i, j, k, l, outcome; + + memset(buf, '-', buflen); + + for (i = 0; i < setlen; i++) + set[i] = 'A' + i; + + buf[buflen] = '\0'; + set[setlen] = '\0'; + + for (i = 0; i < setlen; i++) + for (j = 0; j < setlen; j++) + for (k = 0; k + 1 < buflen; k++) + for (l = k + 1; l < buflen; l++) { + buf[k] = set[i]; + buf[l] = set[j]; + outcome = strcspn(buf, set); + ATF_CHECK_EQ_MSG(k, outcome, + "strcspn(\"%s\", \"%s\") = %zu != %zu", + buf, set, outcome, k); + buf[k] = '-'; + buf[l] = '-'; + } +} + +ATF_TC_WITHOUT_HEAD(match_order); +ATF_TC_BODY(match_order, tc) +{ + char buf[33], set[65]; + + test_match_order(buf, set, 32, 64); + test_match_order(buf, set, 16, 64); + test_match_order(buf, set, 8, 64); + test_match_order(buf, set, 32, 32); + test_match_order(buf, set, 16, 32); + test_match_order(buf, set, 8, 32); + test_match_order(buf, set, 32, 16); + test_match_order(buf, set, 16, 16); + test_match_order(buf, set, 8, 16); + test_match_order(buf, set, 32, 8); + test_match_order(buf, set, 16, 8); + test_match_order(buf, set, 8, 8); +} +#endif /* !defined(STRSPN) */ + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, buf_alignments); + ATF_TP_ADD_TC(tp, set_alignments); +#ifndef STRSPN + ATF_TP_ADD_TC(tp, match_positions); + ATF_TP_ADD_TC(tp, match_order); +#endif + + return (atf_no_error()); +} diff --git a/lib/libc/tests/string/strlcpy_test.c b/lib/libc/tests/string/strlcpy_test.c new file mode 100644 index 000000000000..646bef42683e --- /dev/null +++ b/lib/libc/tests/string/strlcpy_test.c @@ -0,0 +1,183 @@ +/*- + * Copyright (c) 2009 David Schultz <das@FreeBSD.org> + * Copyright (c) 2023 The FreeBSD Foundation + * All rights reserved. + * + * Portions of this software were developed by Robert Clausecker + * <fuz@FreeBSD.org> under sponsorship from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + */ + +#include <sys/cdefs.h> +#include <sys/param.h> +#include <sys/mman.h> +#include <assert.h> +#include <dlfcn.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <atf-c.h> + +size_t (*strlcpy_fn)(char *restrict, const char *restrict, size_t); + +static char * +makebuf(size_t len, int guard_at_end) +{ + char *buf; + size_t alloc_size, page_size; + + page_size = getpagesize(); + alloc_size = roundup2(len, page_size) + page_size; + + buf = mmap(NULL, alloc_size, PROT_READ | PROT_WRITE, MAP_ANON, -1, 0); + assert(buf); + if (guard_at_end) { + assert(munmap(buf + alloc_size - page_size, page_size) == 0); + return (buf + alloc_size - page_size - len); + } else { + assert(munmap(buf, page_size) == 0); + return (buf + page_size); + } +} + +static void +test_strlcpy(const char *s) +{ + char *src, *dst; + size_t size, bufsize, x; + int i, j; + + size = strlen(s) + 1; + for (i = 0; i <= 1; i++) { + for (j = 0; j <= 1; j++) { + for (bufsize = 0; bufsize <= size + 10; bufsize++) { + src = makebuf(size, i); + memcpy(src, s, size); + dst = makebuf(bufsize, j); + memset(dst, 'X', bufsize); + assert(strlcpy_fn(dst, src, bufsize) == size-1); + assert(bufsize == 0 || strncmp(src, dst, bufsize - 1) == 0); + for (x = size; x < bufsize; x++) + assert(dst[x] == 'X'); + } + } + } +} + +static void +test_sentinel(char *dest, char *src, size_t destlen, size_t srclen) +{ + size_t i; + size_t res, wantres; + const char *fail = NULL; + + for (i = 0; i < srclen; i++) + /* src will never include (){} */ + src[i] = '0' + i; + src[srclen] = '\0'; + + /* source sentinels: not to be copied */ + src[-1] = '('; + src[srclen+1] = ')'; + + memset(dest, '\xee', destlen); + + /* destination sentinels: not to be touched */ + dest[-1] = '{'; + dest[destlen] = '}'; + + wantres = srclen; + res = strlcpy_fn(dest, src, destlen); + + if (dest[-1] != '{') + fail = "start sentinel overwritten"; + else if (dest[destlen] != '}') + fail = "end sentinel overwritten"; + else if (res != wantres) + fail = "incorrect return value"; + else if (destlen > 0 && strncmp(src, dest, destlen - 1) != 0) + fail = "string not copied correctly"; + else if (destlen > 0 && srclen >= destlen - 1 && dest[destlen-1] != '\0') + fail = "string not NUL terminated"; + else for (i = srclen + 1; i < destlen; i++) + if (dest[i] != '\xee') { + fail = "buffer mutilated behind string"; + break; + } + + if (fail) + atf_tc_fail_nonfatal("%s\n" + "strlcpy(%p \"%s\", %p \"%s\", %zu) = %zu (want %zu)\n", + fail, dest, dest, src, src, destlen, res, wantres); +} + +ATF_TC_WITHOUT_HEAD(null); +ATF_TC_BODY(null, tc) +{ + ATF_CHECK_EQ(strlcpy_fn(NULL, "foo", 0), 3); +} + +ATF_TC_WITHOUT_HEAD(bounds); +ATF_TC_BODY(bounds, tc) +{ + size_t i; + char buf[64+1]; + + for (i = 0; i < sizeof(buf) - 1; i++) { + buf[i] = ' ' + i; + buf[i+1] = '\0'; + test_strlcpy(buf); + } +} + +ATF_TC_WITHOUT_HEAD(alignments); +ATF_TC_BODY(alignments, tc) +{ + size_t srcalign, destalign, srclen, destlen; + char src[15+3+64]; /* 15 offsets + 64 max length + NUL + sentinels */ + char dest[15+2+64]; /* 15 offsets + 64 max length + sentinels */ + + for (srcalign = 0; srcalign < 16; srcalign++) + for (destalign = 0; destalign < 16; destalign++) + for (srclen = 0; srclen < 64; srclen++) + for (destlen = 0; destlen < 64; destlen++) + test_sentinel(dest+destalign+1, + src+srcalign+1, destlen, srclen); +} + +ATF_TP_ADD_TCS(tp) +{ + void *dl_handle; + + dl_handle = dlopen(NULL, RTLD_LAZY); + strlcpy_fn = dlsym(dl_handle, "test_strlcpy"); + if (strlcpy_fn == NULL) + strlcpy_fn = strlcpy; + + ATF_TP_ADD_TC(tp, null); + ATF_TP_ADD_TC(tp, bounds); + ATF_TP_ADD_TC(tp, alignments); + + return (atf_no_error()); +} diff --git a/lib/libc/tests/string/strncmp_test.c b/lib/libc/tests/string/strncmp_test.c new file mode 100644 index 000000000000..989c58bcfedf --- /dev/null +++ b/lib/libc/tests/string/strncmp_test.c @@ -0,0 +1,165 @@ +/*- + * Copyright (c) 2023 The FreeBSD Foundation + * + * This software was developed by Robert Clausecker <fuz@FreeBSD.org> + * under sponsorship from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 + */ + +#include <sys/cdefs.h> + +#include <atf-c.h> +#include <dlfcn.h> +#include <string.h> + +int (*volatile strncmp_fn)(const char *, const char *, size_t); + +static void +alignment_testcase(char *a, char *b, int want, size_t len) +{ + int res; + + res = strncmp_fn(a, b, len); + ATF_CHECK_MSG(want == (res > 0) - (res < 0), + "strcmp(%p \"%s\", %p \"%s\", %zu) = %d != %d", + (void *)a, a, (void *)b, b, len, res, want); +} + +static void +check_strncmp_alignments(char a[], char b[], + size_t a_off, size_t b_off, size_t len, size_t pos) +{ + char *a_str, *b_str, a_orig, b_orig; + + a[a_off] = '\0'; + b[b_off] = '\0'; + + a_str = a + a_off + 1; + b_str = b + b_off + 1; + + a_str[len] = '\0'; + b_str[len] = '\0'; + a_str[len+1] = 'A'; + b_str[len+1] = 'B'; + + a_orig = a_str[pos]; + b_orig = b_str[pos]; + + alignment_testcase(a_str, b_str, 0, len + 16); + alignment_testcase(a_str, b_str, 0, len + 1); + alignment_testcase(a_str, b_str, 0, len); + + if (pos < len) { + a_str[pos] = '\0'; + alignment_testcase(a_str, b_str, -1, len + 16); + alignment_testcase(a_str, b_str, -1, len + 1); + alignment_testcase(a_str, b_str, -1, len); + alignment_testcase(a_str, b_str, -1, pos + 1); + alignment_testcase(a_str, b_str, 0, pos); + a_str[pos] = a_orig; + + b_str[pos] = '\0'; + alignment_testcase(a_str, b_str, 1, len + 16); + alignment_testcase(a_str, b_str, 1, len + 1); + alignment_testcase(a_str, b_str, 1, len); + alignment_testcase(a_str, b_str, 1, pos + 1); + alignment_testcase(a_str, b_str, 0, pos); + b_str[pos] = b_orig; + } + + a_str[pos] = 'X'; + alignment_testcase(a_str, b_str, 1, len + 16); + alignment_testcase(a_str, b_str, 0, pos); + alignment_testcase(a_str, b_str, 1, pos + 1); + if (pos < len) { + alignment_testcase(a_str, b_str, 1, len); + alignment_testcase(a_str, b_str, 1, len + 1); + } + a_str[pos] = a_orig; + + b_str[pos] = 'X'; + alignment_testcase(a_str, b_str, -1, len + 16); + alignment_testcase(a_str, b_str, 0, pos); + alignment_testcase(a_str, b_str, -1, pos + 1); + if (pos < len) { + alignment_testcase(a_str, b_str, -1, len); + alignment_testcase(a_str, b_str, -1, len + 1); + } + b_str[pos] = b_orig; + + a[a_off] = '-'; + b[b_off] = '-'; + a_str[len] = '-'; + b_str[len] = '-'; + a_str[len+1] = '-'; + b_str[len+1] = '-'; +} + +ATF_TC(strncmp_alignments); +ATF_TC_HEAD(strncmp_alignments, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test strncmp(3) with various alignments"); +} + +ATF_TC_BODY(strncmp_alignments, tc) +{ + size_t a_off, b_off, len, pos; + char a[64+16+16+3], b[64+16+16+3]; + + memset(a, '-', sizeof(a)); + memset(b, '-', sizeof(b)); + a[sizeof(a) - 1] = '\0'; + b[sizeof(b) - 1] = '\0'; + + for (a_off = 0; a_off < 16; a_off++) + for (b_off = 0; b_off < 16; b_off++) + for (len = 1; len <= 64; len++) + for (pos = 0; pos <= len; pos++) + check_strncmp_alignments(a, b, a_off, b_off, len, pos); +} + +ATF_TC(strncmp_null); +ATF_TC_HEAD(strncmp_null, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test strncmp(3) with null pointers"); +} + +ATF_TC_BODY(strncmp_null, tc) +{ + alignment_testcase(NULL, NULL, 0, 0); +} + +ATF_TP_ADD_TCS(tp) +{ + void *dl_handle; + + dl_handle = dlopen(NULL, RTLD_LAZY); + strncmp_fn = dlsym(dl_handle, "test_strncmp"); + if (strncmp_fn == NULL) + strncmp_fn = strncmp; + + ATF_TP_ADD_TC(tp, strncmp_alignments); + ATF_TP_ADD_TC(tp, strncmp_null); + + return atf_no_error(); +} diff --git a/lib/libc/tests/string/strnlen_test.c b/lib/libc/tests/string/strnlen_test.c new file mode 100644 index 000000000000..31c2384bb30f --- /dev/null +++ b/lib/libc/tests/string/strnlen_test.c @@ -0,0 +1,141 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2024 Strahinja Stanisic <strajabot@FreeBSD.org> + */ + +#include <string.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdalign.h> +#include <stdint.h> + +#include <atf-c.h> + +ATF_TC(strnlen_alignments); +ATF_TC_HEAD(strnlen_alignments, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test strnlen(3) with different alignments"); +} + +ATF_TC_BODY(strnlen_alignments, tc) +{ + size_t (*strnlen_fn)(const char*, size_t) = strnlen; + char alignas(16) buffer[1 + 16 + 64 + 1 + 1]; + + memset(buffer, '/', sizeof(buffer)); + + for (int align = 1; align < 1 + 16; align++) { + char *s = buffer + align; + + for (size_t maxlen = 0; maxlen <= 64; maxlen++) { + for (size_t len = 0; len <= maxlen; len++) { + /* returns length */ + + /* without sentinels */ + s[len] = '\0'; + size_t val = strnlen_fn(s, maxlen); + if (val != len) { + fprintf(stderr, "align = %d, maxlen = %zu, len = %zu", + align, maxlen, len); + atf_tc_fail("returned incorrect len"); + } + + /* with sentinels */ + s[-1] = '\0'; + s[maxlen + 1] = '\0'; + val = strnlen_fn(s, maxlen); + if (val != len) { + fprintf(stderr, "align = %d, maxlen = %zu, len = %zu", + align, maxlen, len); + atf_tc_fail("returned incorrect len (sentinels)"); + } + + /* cleanup */ + s[-1] = '/'; + s[len] = '/'; + s[maxlen + 1] = '/'; + + } + + /* returns maxlen */ + + /* without sentinels */ + size_t val = strnlen_fn(s, maxlen); + if (val != maxlen) { + fprintf(stderr, "align = %d, maxlen = %zu", + align, maxlen); + atf_tc_fail("should return maxlen"); + } + + /* with sentinels */ + s[-1] = '\0'; + s[maxlen + 1] = '\0'; + val = strnlen_fn(s, maxlen); + if (val != maxlen) { + fprintf(stderr, "align = %d, maxlen = %zu", + align, maxlen); + atf_tc_fail("should return maxlen (sentinels)"); + } + + /* cleanup */ + s[-1] = '/'; + s[maxlen + 1] = '/'; + } + } +} + +ATF_TC(strnlen_size_max); +ATF_TC_HEAD(strnlen_size_max, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test strnlen(3) with maxlen=SIZE_MAX"); +} + +ATF_TC_BODY(strnlen_size_max, tc) +{ + size_t (*strnlen_fn)(const char*, size_t) = strnlen; + char alignas(16) buffer[1 + 16 + 64 + 1 + 1]; + + memset(buffer, '/', sizeof(buffer)); + + for (int align = 1; align < 1 + 16; align++) { + char* s = buffer + align; + + for (size_t len = 0; len <= 64; len++) { + /* returns length */ + + /* without sentinels */ + s[len] = '\0'; + size_t val = strnlen_fn(s, SIZE_MAX); + if (val != len) { + fprintf(stderr, "align = %d, maxlen = %zu, len = %zu", + align, SIZE_MAX, len); + atf_tc_fail("returned incorrect len (SIZE_MAX)"); + } + + /* with sentinels */ + s[-1] = '\0'; + val = strnlen_fn(s, SIZE_MAX); + if (val != len) { + fprintf(stderr, "align = %d, maxlen = %zu, len = %zu", + align, SIZE_MAX, len); + atf_tc_fail("returned incorrect len (sentinels) (SIZE_MAX)"); + } + + /* cleanup */ + s[-1] = '/'; + s[len] = '/'; + } + } +} + + + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, strnlen_alignments); + ATF_TP_ADD_TC(tp, strnlen_size_max); + + return atf_no_error(); +} diff --git a/lib/libc/tests/string/strspn_test.c b/lib/libc/tests/string/strspn_test.c new file mode 100644 index 000000000000..73a08ddefa1b --- /dev/null +++ b/lib/libc/tests/string/strspn_test.c @@ -0,0 +1,30 @@ +/*- + * Copyright (c) 2023 The FreeBSD Foundation + * + * This software was developed by Robert Clausecker <fuz@FreeBSD.org> + * under sponsorship from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 STRSPN +#include "strcspn_test.c" diff --git a/lib/libc/tests/string/strxfrm_test.c b/lib/libc/tests/string/strxfrm_test.c index 3c2ab58cb72b..becc620ba79c 100644 --- a/lib/libc/tests/string/strxfrm_test.c +++ b/lib/libc/tests/string/strxfrm_test.c @@ -24,7 +24,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <string.h> #include <locale.h> #include <stdio.h> diff --git a/lib/libc/tests/string/timingsafe_bcmp_test.c b/lib/libc/tests/string/timingsafe_bcmp_test.c new file mode 100644 index 000000000000..96bf789633f2 --- /dev/null +++ b/lib/libc/tests/string/timingsafe_bcmp_test.c @@ -0,0 +1,32 @@ +/*- + * Copyright (c) 2023 The FreeBSD Foundation + * + * This software was developed by Robert Clausecker <fuz@FreeBSD.org> + * under sponsorship from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 MEMCMP timingsafe_bcmp +#define RES(x) ((x) != 0) + +#include "memcmp_test.c" diff --git a/lib/libc/tests/string/timingsafe_memcmp_test.c b/lib/libc/tests/string/timingsafe_memcmp_test.c new file mode 100644 index 000000000000..5f97e41fcf8a --- /dev/null +++ b/lib/libc/tests/string/timingsafe_memcmp_test.c @@ -0,0 +1,32 @@ +/*- + * Copyright (c) 2023 The FreeBSD Foundation + * + * This software was developed by Robert Clausecker <fuz@FreeBSD.org> + * under sponsorship from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 MEMCMP timingsafe_memcmp +#define RES(x) (((x) > 0) - ((x) < 0)) + +#include "memcmp_test.c" diff --git a/lib/libc/tests/string/wcscasecmp_test.c b/lib/libc/tests/string/wcscasecmp_test.c index 5927345e7f4a..9a47c0d91b31 100644 --- a/lib/libc/tests/string/wcscasecmp_test.c +++ b/lib/libc/tests/string/wcscasecmp_test.c @@ -24,7 +24,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <assert.h> #include <locale.h> #include <stdio.h> diff --git a/lib/libc/tests/string/wcscoll_test.c b/lib/libc/tests/string/wcscoll_test.c index d64e03f0605a..2ce85bbb986b 100644 --- a/lib/libc/tests/string/wcscoll_test.c +++ b/lib/libc/tests/string/wcscoll_test.c @@ -26,7 +26,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <wchar.h> #include <locale.h> #include <stdlib.h> diff --git a/lib/libc/tests/string/wcsnlen_test.c b/lib/libc/tests/string/wcsnlen_test.c index 56a2ef1e3623..ba2e1c8d7c57 100644 --- a/lib/libc/tests/string/wcsnlen_test.c +++ b/lib/libc/tests/string/wcsnlen_test.c @@ -24,7 +24,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/mman.h> #include <assert.h> |