diff options
Diffstat (limited to 'lib/libc')
| -rw-r--r-- | lib/libc/gen/Makefile.inc | 1 | ||||
| -rw-r--r-- | lib/libc/gen/h_ctype_abuse.c | 138 | ||||
| -rw-r--r-- | lib/libc/gen/h_execsig.c | 55 | ||||
| -rw-r--r-- | lib/libc/gen/t_arc4random.c | 670 | ||||
| -rw-r--r-- | lib/libc/gen/t_ctype.c | 1236 | ||||
| -rw-r--r-- | lib/libc/gen/t_timespec_get.c | 123 | ||||
| -rw-r--r-- | lib/libc/locale/t_c16rtomb.c | 287 | ||||
| -rw-r--r-- | lib/libc/locale/t_c32rtomb.c | 60 | ||||
| -rw-r--r-- | lib/libc/locale/t_c8rtomb.c | 355 | ||||
| -rw-r--r-- | lib/libc/locale/t_mbrtoc16.c | 364 | ||||
| -rw-r--r-- | lib/libc/locale/t_mbrtoc32.c | 61 | ||||
| -rw-r--r-- | lib/libc/locale/t_mbrtoc8.c | 415 | ||||
| -rw-r--r-- | lib/libc/locale/t_uchar.c | 81 | ||||
| -rw-r--r-- | lib/libc/misc/t_vis.c | 165 | ||||
| -rw-r--r-- | lib/libc/regex/t_regex_binary.c | 80 | ||||
| -rw-r--r-- | lib/libc/setjmp/t_sigstack.c | 389 | ||||
| -rw-r--r-- | lib/libc/ssp/h_getcwd2.c | 18 | ||||
| -rw-r--r-- | lib/libc/stdlib/h_sort.c | 225 | ||||
| -rw-r--r-- | lib/libc/stdlib/t_sort.sh | 115 | ||||
| -rw-r--r-- | lib/libc/sys/t_aio_cancel.c | 223 | ||||
| -rw-r--r-- | lib/libc/sys/t_aio_lio.c | 264 | ||||
| -rw-r--r-- | lib/libc/sys/t_aio_rw.c | 167 | ||||
| -rw-r--r-- | lib/libc/sys/t_aio_suspend.c | 170 | ||||
| -rw-r--r-- | lib/libc/sys/t_ptrace_kill.c | 131 |
24 files changed, 5793 insertions, 0 deletions
diff --git a/lib/libc/gen/Makefile.inc b/lib/libc/gen/Makefile.inc new file mode 100644 index 000000000000..01b5f23410c8 --- /dev/null +++ b/lib/libc/gen/Makefile.inc @@ -0,0 +1 @@ +.include "../Makefile.inc" diff --git a/lib/libc/gen/h_ctype_abuse.c b/lib/libc/gen/h_ctype_abuse.c new file mode 100644 index 000000000000..fdff8552f8f4 --- /dev/null +++ b/lib/libc/gen/h_ctype_abuse.c @@ -0,0 +1,138 @@ +/* $NetBSD: h_ctype_abuse.c,v 1.2 2025/09/15 17:32:01 riastradh Exp $ */ + +/*- + * Copyright (c) 2025 The NetBSD Foundation, Inc. + * All rights reserved. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/* + * Helper program to verify effects of ctype(3) abuse. + * + * NOTE: This program intentionally triggers undefined behaviour by + * passing int values to the ctype(3) functions which are neither EOF + * nor representable by unsigned char. The purpose is to verify that + * NetBSD's ctype(3) _does not_ trap the undefined behaviour when the + * environment variable LIBC_ALLOWCTYPEABUSE. (It does not check + * anything about the results, which are perforce nonsense -- just that + * it gets a result without crashing.) + */ + +#include <sys/cdefs.h> +__RCSID("$NetBSD: h_ctype_abuse.c,v 1.2 2025/09/15 17:32:01 riastradh Exp $"); + +#include <ctype.h> +#include <err.h> +#include <limits.h> +#include <locale.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define FOREACHCTYPE(M) \ + M(ISALPHA, isalpha) \ + M(ISUPPER, isupper) \ + M(ISLOWER, islower) \ + M(ISDIGIT, isdigit) \ + M(ISXDIGIT, isxdigit) \ + M(ISALNUM, isalnum) \ + M(ISSPACE, isspace) \ + M(ISPUNCT, ispunct) \ + M(ISPRINT, isprint) \ + M(ISGRAPH, isgraph) \ + M(ISCNTRL, iscntrl) \ + M(ISBLANK, isblank) \ + M(TOUPPER, toupper) \ + M(TOLOWER, tolower) + +int +main(int argc, char **argv) +{ + enum { +#define M(upper, lower) upper, + FOREACHCTYPE(M) +#undef M + } fn; + enum { + MACRO, + FUNCTION, + } mode; + int ch; + volatile int result; + + setprogname(argv[0]); + if (argc != 3 && argc != 4) { + errx(1, "Usage: %s <function> <mode> [<locale>]", + getprogname()); + } + +#define M(upper, lower) \ + else if (strcmp(argv[1], #lower) == 0) \ + fn = upper; + + if (0) + ; + FOREACHCTYPE(M) + else + errx(1, "unknown ctype function"); +#undef M + + if (strcmp(argv[2], "macro") == 0) + mode = MACRO; + else if (strcmp(argv[2], "function") == 0) + mode = FUNCTION; + else + errx(1, "unknown usage mode"); + + if (argc == 4) { + if (setlocale(LC_CTYPE, argv[3]) == NULL) + err(1, "setlocale"); + } + + /* + * Make sure we cover EOF as well. + */ + __CTASSERT(CHAR_MIN == 0 || CHAR_MIN <= EOF); + __CTASSERT(EOF <= UCHAR_MAX); + + for (ch = (CHAR_MIN == 0 ? EOF : CHAR_MIN); ch <= UCHAR_MAX; ch++) { + switch (fn) { +#define M(upper, lower) \ + case upper: \ + switch (mode) { \ + case MACRO: \ + result = lower(ch); \ + break; \ + case FUNCTION: \ + result = (lower)(ch); \ + break; \ + } \ + break; + FOREACHCTYPE(M) +#undef M + } + (void)result; + } + + return 0; +} diff --git a/lib/libc/gen/h_execsig.c b/lib/libc/gen/h_execsig.c new file mode 100644 index 000000000000..a954ca071094 --- /dev/null +++ b/lib/libc/gen/h_execsig.c @@ -0,0 +1,55 @@ +/* $NetBSD: h_execsig.c,v 1.1 2025/03/13 01:27:27 riastradh Exp $ */ + +/*- + * Copyright (c) 2025 The NetBSD Foundation, Inc. + * All rights reserved. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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> +__RCSID("$NetBSD: h_execsig.c,v 1.1 2025/03/13 01:27:27 riastradh Exp $"); + +/* + * Helper program for testing signal delivery during execve(2) and + * posix_spawn(2). The caller will: + * + * 1. fork and exec, or spawn + * 2. kill the child + * 3. write a byte to be read from the child's stdin + * + * Since the caller issues syscalls in that order, the signal should be + * delivered to the child first, and it should be interrupted by a + * signal before it returnsa byte from read(2). + */ + +#include <err.h> +#include <unistd.h> + +int +main(void) +{ + + if (read(STDIN_FILENO, (char[]){0}, 1) == -1) + err(1, "read"); + return 0; +} diff --git a/lib/libc/gen/t_arc4random.c b/lib/libc/gen/t_arc4random.c new file mode 100644 index 000000000000..b26c1b8a931a --- /dev/null +++ b/lib/libc/gen/t_arc4random.c @@ -0,0 +1,670 @@ +/* $NetBSD: t_arc4random.c,v 1.5 2025/03/09 18:11:55 riastradh Exp $ */ + +/*- + * Copyright (c) 2024 The NetBSD Foundation, Inc. + * All rights reserved. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 _REENTRANT + +#include <sys/cdefs.h> +__RCSID("$NetBSD: t_arc4random.c,v 1.5 2025/03/09 18:11:55 riastradh Exp $"); + +#include <sys/resource.h> +#include <sys/stat.h> +#include <sys/sysctl.h> +#include <sys/wait.h> + +#include <atf-c.h> +#include <err.h> +#include <fcntl.h> +#include <paths.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include "arc4random.h" +#include "reentrant.h" +#include "h_macros.h" + +/* + * iszero(buf, len) + * + * True if len bytes at buf are all zero, false if any one of them + * is nonzero. + */ +static bool +iszero(const void *buf, size_t len) +{ + const unsigned char *p = buf; + size_t i; + + for (i = 0; i < len; i++) { + if (p[i] != 0) + return false; + } + return true; +} + +/* + * arc4random_prng() + * + * Get a pointer to the current arc4random state, without updating + * any of the state, not even lazy initialization. + */ +static struct arc4random_prng * +arc4random_prng(void) +{ + struct arc4random_prng *prng = NULL; + + /* + * If arc4random has been initialized and there is a thread key + * (i.e., libc was built with _REENTRANT), get the thread-local + * arc4random state if there is one. + */ + if (arc4random_global.per_thread) + prng = thr_getspecific(arc4random_global.thread_key); + + /* + * If we couldn't get the thread-local state, get the global + * state instead. + */ + if (prng == NULL) + prng = &arc4random_global.prng; + + return prng; +} + +/* + * arc4random_global_buf(buf, len) + * + * Same as arc4random_buf, but force use of the global state. + * Must happen before any other use of arc4random. + */ +static void +arc4random_global_buf(void *buf, size_t len) +{ + struct rlimit rlim, orlim; + struct arc4random_prng *prng; + + /* + * Save the address space limit. + */ + RL(getrlimit(RLIMIT_AS, &orlim)); + memcpy(&rlim, &orlim, sizeof(rlim)); + + /* + * Get a sample while the address space limit is zero. This + * should try, and fail, to allocate a thread-local arc4random + * state with mmap(2). + */ + rlim.rlim_cur = 0; + RL(setrlimit(RLIMIT_AS, &rlim)); + arc4random_buf(buf, len); + RL(setrlimit(RLIMIT_AS, &orlim)); + + /* + * Restore the address space limit. + */ + RL(setrlimit(RLIMIT_AS, &orlim)); + + /* + * Verify the PRNG is the global one, not the thread-local one, + * and that it was initialized. + */ + prng = arc4random_prng(); + ATF_CHECK_EQ(prng, &arc4random_global.prng); + ATF_CHECK(!iszero(&prng->arc4_prng, sizeof(prng->arc4_prng))); + ATF_CHECK(prng->arc4_epoch != 0); +} + +/* + * arc4random_global_thread(cookie) + * + * Start routine for a thread that just grabs an output from the + * global state. + */ +static void * +arc4random_global_thread(void *cookie) +{ + unsigned char buf[32]; + + arc4random_global_buf(buf, sizeof(buf)); + + return NULL; +} + +ATF_TC(addrandom); +ATF_TC_HEAD(addrandom, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test arc4random_addrandom updates the state"); +} +ATF_TC_BODY(addrandom, tc) +{ + unsigned char buf[32], zero32[32] = {0}; + struct arc4random_prng *prng, copy; + + /* + * Get a sample to start things off. + */ + arc4random_buf(buf, sizeof(buf)); + ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */ + + /* + * By this point, the global state must be initialized -- if + * not, the process should have aborted. + */ + ATF_CHECK(arc4random_global.initialized); + + /* + * Get the PRNG, global or local. By this point, the PRNG + * state should be nonzero (with overwhelmingly high + * probability) and the epoch should also be nonzero. + */ + prng = arc4random_prng(); + ATF_CHECK(!iszero(&prng->arc4_prng, sizeof(prng->arc4_prng))); + ATF_CHECK(prng->arc4_epoch != 0); + + /* + * Save a copy and update the state with arc4random_addrandom. + */ + copy = *prng; + arc4random_addrandom(zero32, sizeof(zero32)); + + /* + * The state should have changed. (The epoch may or may not.) + */ + ATF_CHECK(memcmp(&prng->arc4_prng, ©.arc4_prng, + sizeof(copy.arc4_prng)) != 0); + + /* + * Save a copy and update the state with arc4random_stir. + */ + copy = *prng; + arc4random_stir(); + + /* + * The state should have changed. (The epoch may or may not.) + */ + ATF_CHECK(memcmp(&prng->arc4_prng, ©.arc4_prng, + sizeof(copy.arc4_prng)) != 0); +} + +ATF_TC(consolidate); +ATF_TC_HEAD(consolidate, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test consolidating entropy resets the epoch"); +} +ATF_TC_BODY(consolidate, tc) +{ + unsigned char buf[32]; + struct arc4random_prng *local, *global = &arc4random_global.prng; + unsigned localepoch, globalepoch; + const int consolidate = 1; + pthread_t thread; + + /* + * Get a sample from the global state to make sure the global + * state is initialized. Remember the epoch. + */ + arc4random_global_buf(buf, sizeof(buf)); + ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */ + ATF_CHECK(!iszero(&global->arc4_prng, sizeof(global->arc4_prng))); + ATF_CHECK((globalepoch = global->arc4_epoch) != 0); + + /* + * Get a sample from the local state too to make sure the local + * state is initialized. Remember the epoch. + */ + arc4random_buf(buf, sizeof(buf)); + ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */ + local = arc4random_prng(); + ATF_CHECK(!iszero(&local->arc4_prng, sizeof(local->arc4_prng))); + ATF_CHECK((localepoch = local->arc4_epoch) != 0); + + /* + * Trigger entropy consolidation. + */ + RL(sysctlbyname("kern.entropy.consolidate", /*oldp*/NULL, /*oldlen*/0, + &consolidate, sizeof(consolidate))); + + /* + * Verify the epoch cache isn't changed yet until we ask for + * more data. + */ + ATF_CHECK_EQ_MSG(globalepoch, global->arc4_epoch, + "global epoch was %u, now %u", globalepoch, global->arc4_epoch); + ATF_CHECK_EQ_MSG(localepoch, local->arc4_epoch, + "local epoch was %u, now %u", localepoch, local->arc4_epoch); + + /* + * Request new output and verify the local epoch cache has + * changed. + */ + arc4random_buf(buf, sizeof(buf)); + ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */ + ATF_CHECK_MSG(localepoch != local->arc4_epoch, + "local epoch unchanged from %u", localepoch); + + /* + * Create a new thread to grab output from the global state, + * wait for it to complete, and verify the global epoch cache + * has changed. (Now that we have already used the local state + * in this thread, we can't use the global state any more.) + */ + RZ(pthread_create(&thread, NULL, &arc4random_global_thread, NULL)); + RZ(pthread_join(thread, NULL)); + ATF_CHECK_MSG(globalepoch != global->arc4_epoch, + "global epoch unchanged from %u", globalepoch); +} + +ATF_TC(chroot); +ATF_TC_HEAD(chroot, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test arc4random in an empty chroot"); + atf_tc_set_md_var(tc, "require.user", "root"); +} +ATF_TC_BODY(chroot, tc) +{ + pid_t pid; + int status; + + /* + * Create an empty chroot. + */ + RL(mkdir("root", 0500)); + + /* + * In a child process, enter the chroot and verify that we + * can't open /dev/urandom but we can use arc4random. + * + * (atf gets unhappy if we chroot in the same process, when it + * later tries to create a results file.) + */ + RL(pid = fork()); + if (pid == 0) { + unsigned char buf[32] = {0}; + + if (chroot("root") == -1) + err(1, "chroot"); + if (open(_PATH_URANDOM, O_RDONLY) != -1) + errx(1, "open /dev/urandom must fail in empty chroot"); + if (errno != ENOENT) { + err(1, "expected open to fail with %d=ENOENT, not %d", + ENOENT, errno); + } + arc4random_buf(buf, sizeof(buf)); + if (iszero(buf, sizeof(buf))) /* Pr[fail] = 1/2^256 */ + errx(1, "arc4random returned all-zero"); + if (arc4random_prng()->arc4_epoch == 0) + errx(1, "arc4random failed to observe entropy epoch"); + _exit(0); + } + + /* + * Wait for the child process to finish. + */ + RL(waitpid(pid, &status, 0)); + ATF_CHECK_MSG(WIFEXITED(status) && WEXITSTATUS(status) == 0, + "child exited status 0x%x", status); +} + +ATF_TC(fdlimit); +ATF_TC_HEAD(fdlimit, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test arc4random works even if we have hit the fd limit"); +} +ATF_TC_BODY(fdlimit, tc) +{ + pid_t pid; + int status; + + /* + * In a child process, clamp down on the file descriptor + * resource limit and verify that we can't open /dev/urandom + * but we can use arc4random. + * + * (atf gets unhappy if we chroot in the same process, when it + * later tries to create a results file.) + */ + RL(pid = fork()); + if (pid == 0) { + struct rlimit rlim = {.rlim_cur = 0, .rlim_max = 0}; + unsigned char buf[32] = {0}; + + if (setrlimit(RLIMIT_NOFILE, &rlim) == -1) + err(1, "setrlimit(RLIMIT_NOFILE)"); + if (open(_PATH_URANDOM, O_RDONLY) != -1) + errx(1, "open must fail with zero RLIMIT_NOFILE"); + if (errno != EMFILE) { + err(1, "expected open to fail with %d=EMFILE, not %d", + EMFILE, errno); + } + arc4random_buf(buf, sizeof(buf)); + if (iszero(buf, sizeof(buf))) /* Pr[fail] = 1/2^256 */ + errx(1, "arc4random returned all-zero"); + if (arc4random_prng()->arc4_epoch == 0) + errx(1, "arc4random failed to observe entropy epoch"); + _exit(0); + } + + /* + * Wait for the child process to finish. + */ + RL(waitpid(pid, &status, 0)); + ATF_CHECK_MSG(WIFEXITED(status) && WEXITSTATUS(status) == 0, + "child exited status 0x%x", status); +} + +ATF_TC(fork); +ATF_TC_HEAD(fork, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test fork zeros the state and gets independent state"); +} +ATF_TC_BODY(fork, tc) +{ + unsigned char buf[32]; + struct arc4random_prng *local, *global = &arc4random_global.prng; + struct arc4random_prng childstate; + int fd[2]; + pid_t child, pid; + ssize_t nread; + int status; + + /* + * Get a sample from the global state to make sure the global + * state is initialized. + */ + arc4random_global_buf(buf, sizeof(buf)); + ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */ + ATF_CHECK(!iszero(&global->arc4_prng, sizeof(global->arc4_prng))); + ATF_CHECK(global->arc4_epoch != 0); + + /* + * Get a sample from the local state too to make sure the local + * state is initialized. + */ + arc4random_buf(buf, sizeof(buf)); + ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */ + local = arc4random_prng(); + ATF_CHECK(!iszero(&local->arc4_prng, sizeof(local->arc4_prng))); + ATF_CHECK(local->arc4_epoch != 0); + + /* + * Create a pipe to transfer the state from child to parent. + */ + RL(pipe(fd)); + + /* + * Fork a child. + */ + RL(child = fork()); + if (child == 0) { + status = 0; + + /* + * Verify the states have been zero'd on fork. + */ + if (!iszero(local, sizeof(*local))) { + fprintf(stderr, "failed to zero local state\n"); + status = 1; + } + if (!iszero(global, sizeof(*global))) { + fprintf(stderr, "failed to zero global state\n"); + status = 1; + } + + /* + * Verify we generate nonzero output. + */ + arc4random_buf(buf, sizeof(buf)); + if (iszero(buf, sizeof(buf))) { + fprintf(stderr, "failed to generate nonzero output\n"); + status = 1; + } + + /* + * Share the state to compare with parent. + */ + if ((size_t)write(fd[1], local, sizeof(*local)) != + sizeof(*local)) { + fprintf(stderr, "failed to share local state\n"); + status = 1; + } + _exit(status); + } + + /* + * Verify the global state has been zeroed as expected. (This + * way it is never available to the child, even shortly after + * the fork syscall returns before the atfork handler is + * called.) + */ + ATF_CHECK(iszero(global, sizeof(*global))); + + /* + * Read the state from the child. + */ + RL(nread = read(fd[0], &childstate, sizeof(childstate))); + ATF_CHECK_EQ_MSG(nread, sizeof(childstate), + "nread=%zu sizeof(childstate)=%zu", nread, sizeof(childstate)); + + /* + * Verify the child state is distinct. (The global state has + * been zero'd so it's OK it if coincides.) Check again after + * we grab another output. + */ + ATF_CHECK(memcmp(local, &childstate, sizeof(*local)) != 0); + arc4random_buf(buf, sizeof(buf)); + ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */ + ATF_CHECK(memcmp(local, &childstate, sizeof(*local)) != 0); + + /* + * Wait for the child to complete and verify it passed. + */ + RL(pid = waitpid(child, &status, 0)); + ATF_CHECK_EQ_MSG(status, 0, "child exited with nonzero status=%d", + status); +} + +ATF_TC(global_aslimit); +ATF_TC_HEAD(global_aslimit, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test the global state is used when address space limit is hit"); +} +ATF_TC_BODY(global_aslimit, tc) +{ + unsigned char buf[32], buf1[32]; + + /* + * Get a sample from the global state (and verify it was using + * the global state). + */ + arc4random_global_buf(buf, sizeof(buf)); + + /* + * Verify we got a sample. + */ + ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */ + + /* + * Get a sample from whatever state and make sure it wasn't + * repeated, which happens only with probability 1/2^256. + */ + arc4random_buf(buf1, sizeof(buf1)); + ATF_CHECK(!iszero(buf1, sizeof(buf1))); /* Pr[fail] = 1/2^256 */ + ATF_CHECK(memcmp(buf, buf1, sizeof(buf)) != 0); +} + +ATF_TC(global_threadkeylimit); +ATF_TC_HEAD(global_threadkeylimit, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test the global state is used we run out of thread keys"); +} +ATF_TC_BODY(global_threadkeylimit, tc) +{ + unsigned char buf[32], buf1[32]; + + /* + * Get a sample from the global state (and verify it was using + * the global state). + */ + arc4random_global_buf(buf, sizeof(buf)); + + /* + * Verify we got a sample. + */ + ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */ + + /* + * Artificially disable the per-thread state, make it an + * invalid thread key altogether, and clear the epoch. Make + * sure we're using the global PRNG state now. + */ + arc4random_global.per_thread = false; + memset(&arc4random_global.thread_key, 0x5a, + sizeof(arc4random_global.thread_key)); + arc4random_global.prng.arc4_epoch = 0; + ATF_CHECK(arc4random_prng() == &arc4random_global.prng); + + /* + * Get a sample again and make sure it wasn't repeated, which + * happens only with probability 1/2^256. + */ + arc4random_buf(buf1, sizeof(buf1)); + ATF_CHECK(!iszero(buf1, sizeof(buf1))); /* Pr[fail] = 1/2^256 */ + ATF_CHECK(memcmp(buf, buf1, sizeof(buf)) != 0); + + /* + * Verify this had the effect of updating the global epoch, + * meaning we used the global state and not the per-thread + * state. + */ + ATF_CHECK(arc4random_global.prng.arc4_epoch != 0); +} + +ATF_TC(local); +ATF_TC_HEAD(local, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test arc4random uses thread-local state"); + /* XXX skip if libc was built without _REENTRANT */ +} +ATF_TC_BODY(local, tc) +{ + unsigned char buf[32], buf1[32]; + struct arc4random_prng *prng; + + /* + * Get a sample to start things off. + */ + arc4random_buf(buf, sizeof(buf)); + ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */ + + /* + * Verify the arc4random state is _not_ the global state. + */ + prng = arc4random_prng(); + ATF_CHECK(prng != &arc4random_global.prng); + ATF_CHECK(!iszero(&prng->arc4_prng, sizeof(prng->arc4_prng))); + ATF_CHECK(prng->arc4_epoch != 0); + + /* + * Get another sample and make sure it wasn't repeated, which + * happens only with probability 1/2^256. + */ + arc4random_buf(buf1, sizeof(buf1)); + ATF_CHECK(!iszero(buf1, sizeof(buf1))); /* Pr[fail] = 1/2^256 */ + ATF_CHECK(memcmp(buf, buf1, sizeof(buf)) != 0); +} + +ATF_TC(stackfallback); +ATF_TC_HEAD(stackfallback, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test arc4random with pthread_atfork and thr_keycreate failure"); +} +ATF_TC_BODY(stackfallback, tc) +{ + unsigned char buf[32], buf1[32]; + struct arc4random_prng *local; + + /* + * Get a sample to start things off. This makes the library + * gets initialized. + */ + arc4random_buf(buf, sizeof(buf)); + ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */ + + /* + * Clear the arc4random global state, and the local state if it + * exists, and pretend pthread_atfork and thr_keycreate had + * both failed. + */ + memset(&arc4random_global.prng, 0, sizeof(arc4random_global.prng)); + if ((local = arc4random_prng()) != NULL) + memset(local, 0, sizeof(*local)); + arc4random_global.forksafe = false; + arc4random_global.per_thread = false; + + /* + * Make sure it still works to get a sample. + */ + arc4random_buf(buf1, sizeof(buf1)); + ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */ + ATF_CHECK(memcmp(buf, buf1, sizeof(buf)) != 0); + + /* + * Make sure the global and local epochs did not change. + */ + ATF_CHECK_EQ_MSG(arc4random_global.prng.arc4_epoch, 0, + "global epoch: %d", arc4random_global.prng.arc4_epoch); + if (local != NULL) { + ATF_CHECK_EQ_MSG(local->arc4_epoch, 0, + "local epoch: %d", local->arc4_epoch); + } +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, addrandom); + ATF_TP_ADD_TC(tp, chroot); + ATF_TP_ADD_TC(tp, consolidate); + ATF_TP_ADD_TC(tp, fdlimit); + ATF_TP_ADD_TC(tp, fork); + ATF_TP_ADD_TC(tp, global_aslimit); + ATF_TP_ADD_TC(tp, global_threadkeylimit); + ATF_TP_ADD_TC(tp, local); + ATF_TP_ADD_TC(tp, stackfallback); + + return atf_no_error(); +} diff --git a/lib/libc/gen/t_ctype.c b/lib/libc/gen/t_ctype.c new file mode 100644 index 000000000000..ee4db34304b7 --- /dev/null +++ b/lib/libc/gen/t_ctype.c @@ -0,0 +1,1236 @@ +/* $NetBSD: t_ctype.c,v 1.12 2025/09/15 00:11:55 riastradh Exp $ */ + +/*- + * Copyright (c) 2025 The NetBSD Foundation, Inc. + * All rights reserved. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/* + * Tests for the ctype(3) character classification macros. + * + * NOTE: These tests intentionally trigger undefined behaviour by + * passing int values to the ctype(3) functions which are neither EOF + * nor representable by unsigned char. The purpose is to verify + * NetBSD's intentional trapping of this undefined behaviour -- or + * intentional allowing of this undefined behaviour, when the + * environment variable LIBC_ALLOWCTYPEABUSE is set. + */ + +#include <sys/cdefs.h> +__RCSID("$NetBSD: t_ctype.c,v 1.12 2025/09/15 00:11:55 riastradh Exp $"); + +#include <sys/wait.h> + +#include <atf-c.h> +#include <ctype.h> +#include <locale.h> +#include <limits.h> +#include <setjmp.h> +#include <signal.h> +#include <stdio.h> +#include <unistd.h> + +#include "h_macros.h" + +#ifdef __CHAR_UNSIGNED__ +enum { CHAR_UNSIGNED = 1 }; +#else +enum { CHAR_UNSIGNED = 0 }; +#endif + +/* + * libc has a guard page for the LC_CTYPE=C ctype(3) tables only on + * some platforms. We skip it if char is unsigned (in which case the + * common kind of ctype(3) abuse is unlikely). We also skip it in + * static builds -- this is determined in the Makefile. + */ +#ifndef _CTYPE_GUARD_PAGE +# ifdef __CHAR_UNSIGNED__ +# define _CTYPE_GUARD_PAGE 0 +# else +# define _CTYPE_GUARD_PAGE 1 +# endif +#endif + +static const char *const locales[] = { "C.UTF-8", "fr_FR.ISO8859-1", "C" }; + +static int isalpha_wrapper(int ch) { return isalpha(ch); } +static int isupper_wrapper(int ch) { return isupper(ch); } +static int islower_wrapper(int ch) { return islower(ch); } +static int isdigit_wrapper(int ch) { return isdigit(ch); } +static int isxdigit_wrapper(int ch) { return isxdigit(ch); } +static int isalnum_wrapper(int ch) { return isalnum(ch); } +static int isspace_wrapper(int ch) { return isspace(ch); } +static int ispunct_wrapper(int ch) { return ispunct(ch); } +static int isprint_wrapper(int ch) { return isprint(ch); } +static int isgraph_wrapper(int ch) { return isgraph(ch); } +static int iscntrl_wrapper(int ch) { return iscntrl(ch); } +static int isblank_wrapper(int ch) { return isblank(ch); } +static int toupper_wrapper(int ch) { return toupper(ch); } +static int tolower_wrapper(int ch) { return tolower(ch); } + +jmp_buf env; + +static void +handle_signal(int signo) +{ + + longjmp(env, 1); +} + +static void +test_abuse(const char *name, int (*ctypefn)(int)) +{ + volatile int ch; /* for longjmp */ + + for (ch = CHAR_MIN; ch < 0; ch++) { + volatile int result; + + if (ch == EOF) + continue; + ATF_REQUIRE_MSG(ch != (int)(unsigned char)ch, "ch=%d", ch); + if (setjmp(env) == 0) { + REQUIRE_LIBC(signal(SIGABRT, &handle_signal), SIG_ERR); + REQUIRE_LIBC(signal(SIGSEGV, &handle_signal), SIG_ERR); + result = (*ctypefn)(ch); + REQUIRE_LIBC(signal(SIGABRT, SIG_DFL), SIG_ERR); + REQUIRE_LIBC(signal(SIGSEGV, SIG_DFL), SIG_ERR); + atf_tc_fail_nonfatal("%s failed to detect invalid %d," + " returned %d", + name, ch, result); + } else { + REQUIRE_LIBC(signal(SIGABRT, SIG_DFL), SIG_ERR); + REQUIRE_LIBC(signal(SIGSEGV, SIG_DFL), SIG_ERR); + } + } + + for (; ch <= CHAR_MAX; ch++) + ATF_REQUIRE_MSG(ch == (int)(unsigned char)ch, "ch=%d", ch); +} + +static void +test_abuse_in_locales(const char *name, int (*ctypefn)(int), bool macro) +{ + size_t i; + + for (i = 0; i < __arraycount(locales); i++) { + char buf[128]; + + if (!_CTYPE_GUARD_PAGE && macro && + strcmp(locales[i], "C") == 0) { + fprintf(stderr, "skip LC_CTYPE=C ctype(3) abuse --" + " no libc guard page on this platform\n"); + } + + ATF_REQUIRE_MSG(setlocale(LC_CTYPE, locales[i]) != NULL, + "locales[i]=%s", locales[i]); + snprintf(buf, sizeof(buf), "[%s]%s", locales[i], name); + test_abuse(buf, ctypefn); + } +} + +static void +test_use(const char *name, int (*ctypefn)(int)) +{ + volatile int ch; /* for longjmp */ + + for (ch = EOF; ch <= CHAR_MAX; ch = (ch == EOF ? 0 : ch + 1)) { + volatile int result; + + if (setjmp(env) == 0) { + REQUIRE_LIBC(signal(SIGABRT, &handle_signal), SIG_ERR); + REQUIRE_LIBC(signal(SIGSEGV, &handle_signal), SIG_ERR); + result = (*ctypefn)(ch); + REQUIRE_LIBC(signal(SIGABRT, SIG_DFL), SIG_ERR); + REQUIRE_LIBC(signal(SIGSEGV, SIG_DFL), SIG_ERR); + (void)result; + } else { + REQUIRE_LIBC(signal(SIGABRT, SIG_DFL), SIG_ERR); + REQUIRE_LIBC(signal(SIGSEGV, SIG_DFL), SIG_ERR); + atf_tc_fail_nonfatal("%s(%d) raised SIGSEGV", + name, ch); + } + } +} + +static void +test_isalpha_locale(const char *L, int (*ctypefn)(int)) +{ + + ATF_REQUIRE_MSG(setlocale(LC_CTYPE, L) != NULL, "L=%s", L); + test_use("isalpha", ctypefn); + ATF_CHECK(!(*ctypefn)(EOF)); +} + +static void +test_isupper_locale(const char *L, int (*ctypefn)(int)) +{ + + ATF_REQUIRE_MSG(setlocale(LC_CTYPE, L) != NULL, "L=%s", L); + test_use("isupper", ctypefn); + ATF_CHECK(!(*ctypefn)(EOF)); +} + +static void +test_islower_locale(const char *L, int (*ctypefn)(int)) +{ + + ATF_REQUIRE_MSG(setlocale(LC_CTYPE, L) != NULL, "L=%s", L); + test_use("islower", ctypefn); + ATF_CHECK(!(*ctypefn)(EOF)); +} + +static void +test_isdigit_locale(const char *L, int (*ctypefn)(int)) +{ + + ATF_REQUIRE_MSG(setlocale(LC_CTYPE, L) != NULL, "L=%s", L); + test_use("isdigit", ctypefn); + ATF_CHECK(!(*ctypefn)(EOF)); +} + +static void +test_isxdigit_locale(const char *L, int (*ctypefn)(int)) +{ + + ATF_REQUIRE_MSG(setlocale(LC_CTYPE, L) != NULL, "L=%s", L); + test_use("isxdigit", ctypefn); + ATF_CHECK(!(*ctypefn)(EOF)); +} + +static void +test_isalnum_locale(const char *L, int (*ctypefn)(int)) +{ + + ATF_REQUIRE_MSG(setlocale(LC_CTYPE, L) != NULL, "L=%s", L); + test_use("isalnum", ctypefn); + ATF_CHECK(!(*ctypefn)(EOF)); +} + +static void +test_isspace_locale(const char *L, int (*ctypefn)(int)) +{ + + ATF_REQUIRE_MSG(setlocale(LC_CTYPE, L) != NULL, "L=%s", L); + test_use("isspace", ctypefn); + ATF_CHECK(!(*ctypefn)(EOF)); +} + +static void +test_ispunct_locale(const char *L, int (*ctypefn)(int)) +{ + + ATF_REQUIRE_MSG(setlocale(LC_CTYPE, L) != NULL, "L=%s", L); + test_use("ispunct", ctypefn); + ATF_CHECK(!(*ctypefn)(EOF)); +} + +static void +test_isprint_locale(const char *L, int (*ctypefn)(int)) +{ + + ATF_REQUIRE_MSG(setlocale(LC_CTYPE, L) != NULL, "L=%s", L); + test_use("isprint", ctypefn); + ATF_CHECK(!(*ctypefn)(EOF)); +} + +static void +test_isgraph_locale(const char *L, int (*ctypefn)(int)) +{ + + ATF_REQUIRE_MSG(setlocale(LC_CTYPE, L) != NULL, "L=%s", L); + test_use("isgraph", ctypefn); + ATF_CHECK(!(*ctypefn)(EOF)); +} + +static void +test_iscntrl_locale(const char *L, int (*ctypefn)(int)) +{ + + ATF_REQUIRE_MSG(setlocale(LC_CTYPE, L) != NULL, "L=%s", L); + test_use("iscntrl", ctypefn); + ATF_CHECK(!(*ctypefn)(EOF)); +} + +static void +test_isblank_locale(const char *L, int (*ctypefn)(int)) +{ + + ATF_REQUIRE_MSG(setlocale(LC_CTYPE, L) != NULL, "L=%s", L); + test_use("isblank", ctypefn); + ATF_CHECK(!(*ctypefn)(EOF)); +} + +static void +test_toupper_locale(const char *L, int (*ctypefn)(int)) +{ + int result; + + ATF_REQUIRE_MSG(setlocale(LC_CTYPE, L) != NULL, "L=%s", L); + test_use("toupper", ctypefn); + ATF_CHECK_MSG((result = (*ctypefn)(EOF)) == EOF, + "result=%d, expected EOF=%d", result, EOF); +} + +static void +test_tolower_locale(const char *L, int (*ctypefn)(int)) +{ + int result; + + ATF_REQUIRE_MSG(setlocale(LC_CTYPE, L) != NULL, "L=%s", L); + test_use("tolower", ctypefn); + ATF_CHECK_MSG((result = (*ctypefn)(EOF)) == EOF, + "result=%d, expected EOF=%d", result, EOF); +} + +static void +test_isalpha_c(int (*ctypefn)(int)) +{ + int ch; + + ATF_CHECK(!(*ctypefn)(EOF)); + for (ch = 0; ch <= UCHAR_MAX; ch++) { + switch (ch) { + case 'a': case 'A': + case 'b': case 'B': + case 'c': case 'C': + case 'd': case 'D': + case 'e': case 'E': + case 'f': case 'F': + case 'g': case 'G': + case 'h': case 'H': + case 'i': case 'I': + case 'j': case 'J': + case 'k': case 'K': + case 'l': case 'L': + case 'm': case 'M': + case 'n': case 'N': + case 'o': case 'O': + case 'p': case 'P': + case 'q': case 'Q': + case 'r': case 'R': + case 's': case 'S': + case 't': case 'T': + case 'u': case 'U': + case 'v': case 'V': + case 'w': case 'W': + case 'x': case 'X': + case 'y': case 'Y': + case 'z': case 'Z': + ATF_CHECK_MSG((*ctypefn)(ch), "ch=0x%x", ch); + break; + default: + ATF_CHECK_MSG(!(*ctypefn)(ch), "ch=0x%x", ch); + break; + } + } +} + +static void +test_isupper_c(int (*ctypefn)(int)) +{ + int ch; + + ATF_CHECK(!(*ctypefn)(EOF)); + for (ch = 0; ch <= UCHAR_MAX; ch++) { + switch (ch) { + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + case 'G': + case 'H': + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + case 'Q': + case 'R': + case 'S': + case 'T': + case 'U': + case 'V': + case 'W': + case 'X': + case 'Y': + case 'Z': + ATF_CHECK_MSG((*ctypefn)(ch), "ch=0x%x", ch); + break; + default: + ATF_CHECK_MSG(!(*ctypefn)(ch), "ch=0x%x", ch); + break; + } + } +} + +static void +test_islower_c(int (*ctypefn)(int)) +{ + int ch; + + ATF_CHECK(!(*ctypefn)(EOF)); + for (ch = 0; ch <= UCHAR_MAX; ch++) { + switch (ch) { + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'g': + case 'h': + case 'i': + case 'j': + case 'k': + case 'l': + case 'm': + case 'n': + case 'o': + case 'p': + case 'q': + case 'r': + case 's': + case 't': + case 'u': + case 'v': + case 'w': + case 'x': + case 'y': + case 'z': + ATF_CHECK_MSG((*ctypefn)(ch), "ch=0x%x", ch); + break; + default: + ATF_CHECK_MSG(!(*ctypefn)(ch), "ch=0x%x", ch); + break; + } + } +} + +static void +test_isdigit_c(int (*ctypefn)(int)) +{ + int ch; + + ATF_CHECK(!(*ctypefn)(EOF)); + for (ch = 0; ch <= UCHAR_MAX; ch++) { + switch (ch) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + ATF_CHECK_MSG((*ctypefn)(ch), "ch=0x%x", ch); + break; + default: + ATF_CHECK_MSG(!(*ctypefn)(ch), "ch=0x%x", ch); + break; + } + } +} + +static void +test_isxdigit_c(int (*ctypefn)(int)) +{ + int ch; + + ATF_CHECK(!(*ctypefn)(EOF)); + for (ch = 0; ch <= UCHAR_MAX; ch++) { + switch (ch) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'a': case 'A': + case 'b': case 'B': + case 'c': case 'C': + case 'd': case 'D': + case 'e': case 'E': + case 'f': case 'F': + ATF_CHECK_MSG((*ctypefn)(ch), "ch=0x%x", ch); + break; + default: + ATF_CHECK_MSG(!(*ctypefn)(ch), "ch=0x%x", ch); + break; + } + } +} + +static void +test_isalnum_c(int (*ctypefn)(int)) +{ + int ch; + + ATF_CHECK(!(*ctypefn)(EOF)); + for (ch = 0; ch <= UCHAR_MAX; ch++) { + switch (ch) { + case 'a': case 'A': + case 'b': case 'B': + case 'c': case 'C': + case 'd': case 'D': + case 'e': case 'E': + case 'f': case 'F': + case 'g': case 'G': + case 'h': case 'H': + case 'i': case 'I': + case 'j': case 'J': + case 'k': case 'K': + case 'l': case 'L': + case 'm': case 'M': + case 'n': case 'N': + case 'o': case 'O': + case 'p': case 'P': + case 'q': case 'Q': + case 'r': case 'R': + case 's': case 'S': + case 't': case 'T': + case 'u': case 'U': + case 'v': case 'V': + case 'w': case 'W': + case 'x': case 'X': + case 'y': case 'Y': + case 'z': case 'Z': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + ATF_CHECK_MSG((*ctypefn)(ch), "ch=0x%x", ch); + break; + default: + ATF_CHECK_MSG(!(*ctypefn)(ch), "ch=0x%x", ch); + break; + } + } +} + +static void +test_isspace_c(int (*ctypefn)(int)) +{ + int ch; + + ATF_CHECK(!(*ctypefn)(EOF)); + for (ch = 0; ch <= UCHAR_MAX; ch++) { + switch (ch) { + case ' ': + case '\f': + case '\n': + case '\r': + case '\t': + case '\v': + ATF_CHECK_MSG((*ctypefn)(ch), "ch=0x%x", ch); + break; + default: + ATF_CHECK_MSG(!(*ctypefn)(ch), "ch=0x%x", ch); + break; + } + } +} + +static void +test_ispunct_c(int (*ctypefn)(int)) +{ + int ch; + + ATF_CHECK(!(*ctypefn)(EOF)); + for (ch = 0; ch <= UCHAR_MAX; ch++) { + switch (ch) { + default: + ATF_CHECK_MSG((*ctypefn)(ch), "ch=0x%x", ch); + break; + case 0 ... 0x1f: + case 0x20: /* space is printing but not punctuation */ + case 0x7f: + case 0x80 ... 0xff: + ATF_CHECK_MSG(!(*ctypefn)(ch), "ch=0x%x", ch); + break; + case 'a': case 'A': + case 'b': case 'B': + case 'c': case 'C': + case 'd': case 'D': + case 'e': case 'E': + case 'f': case 'F': + case 'g': case 'G': + case 'h': case 'H': + case 'i': case 'I': + case 'j': case 'J': + case 'k': case 'K': + case 'l': case 'L': + case 'm': case 'M': + case 'n': case 'N': + case 'o': case 'O': + case 'p': case 'P': + case 'q': case 'Q': + case 'r': case 'R': + case 's': case 'S': + case 't': case 'T': + case 'u': case 'U': + case 'v': case 'V': + case 'w': case 'W': + case 'x': case 'X': + case 'y': case 'Y': + case 'z': case 'Z': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + ATF_CHECK_MSG(!(*ctypefn)(ch), "ch=0x%x", ch); + break; + } + } +} + +static void +test_isprint_c(int (*ctypefn)(int)) +{ + int ch; + + ATF_CHECK(!(*ctypefn)(EOF)); + for (ch = 0; ch <= UCHAR_MAX; ch++) { + switch (ch) { + case 0x20: /* space is printing but not graphic */ + default: + ATF_CHECK_MSG((*ctypefn)(ch), "ch=0x%x", ch); + break; + case 0 ... 0x1f: + case 0x7f: + case 0x80 ... 0xff: + ATF_CHECK_MSG(!(*ctypefn)(ch), "ch=0x%x", ch); + break; + } + } +} + +static void +test_isgraph_c(int (*ctypefn)(int)) +{ + int ch; + + ATF_CHECK(!(*ctypefn)(EOF)); + for (ch = 0; ch <= UCHAR_MAX; ch++) { + switch (ch) { + default: + ATF_CHECK_MSG((*ctypefn)(ch), "ch=0x%x", ch); + break; + case 0 ... 0x1f: + case 0x20: /* space is printing but not graphic */ + case 0x7f: + case 0x80 ... 0xff: + ATF_CHECK_MSG(!(*ctypefn)(ch), "ch=0x%x", ch); + break; + } + } +} + +static void +test_iscntrl_c(int (*ctypefn)(int)) +{ + int ch; + + ATF_CHECK(!(*ctypefn)(EOF)); + for (ch = 0; ch <= UCHAR_MAX; ch++) { + switch (ch) { + case 0 ... 0x1f: + case 0x7f: + ATF_CHECK_MSG((*ctypefn)(ch), "ch=0x%x", ch); + break; + default: + ATF_CHECK_MSG(!(*ctypefn)(ch), "ch=0x%x", ch); + break; + } + } +} + +static void +test_isblank_c(int (*ctypefn)(int)) +{ + int ch; + + ATF_CHECK(!(*ctypefn)(EOF)); + for (ch = 0; ch <= UCHAR_MAX; ch++) { + switch (ch) { + case ' ': + case '\t': + ATF_CHECK_MSG((*ctypefn)(ch), "ch=0x%x", ch); + break; + default: + ATF_CHECK_MSG(!(*ctypefn)(ch), "ch=0x%x", ch); + break; + } + } +} + +static void +test_toupper_c(int (*ctypefn)(int)) +{ + int ch, result, expected; + + ATF_CHECK_MSG((result = (*ctypefn)(EOF)) == EOF, + "result=%d, expected EOF=%d", result, EOF); + for (ch = 0; ch <= UCHAR_MAX; ch++) { + switch (ch) { + case 'a': case 'A': expected = 'A'; break; + case 'b': case 'B': expected = 'B'; break; + case 'c': case 'C': expected = 'C'; break; + case 'd': case 'D': expected = 'D'; break; + case 'e': case 'E': expected = 'E'; break; + case 'f': case 'F': expected = 'F'; break; + case 'g': case 'G': expected = 'G'; break; + case 'h': case 'H': expected = 'H'; break; + case 'i': case 'I': expected = 'I'; break; + case 'j': case 'J': expected = 'J'; break; + case 'k': case 'K': expected = 'K'; break; + case 'l': case 'L': expected = 'L'; break; + case 'm': case 'M': expected = 'M'; break; + case 'n': case 'N': expected = 'N'; break; + case 'o': case 'O': expected = 'O'; break; + case 'p': case 'P': expected = 'P'; break; + case 'q': case 'Q': expected = 'Q'; break; + case 'r': case 'R': expected = 'R'; break; + case 's': case 'S': expected = 'S'; break; + case 't': case 'T': expected = 'T'; break; + case 'u': case 'U': expected = 'U'; break; + case 'v': case 'V': expected = 'V'; break; + case 'w': case 'W': expected = 'W'; break; + case 'x': case 'X': expected = 'X'; break; + case 'y': case 'Y': expected = 'Y'; break; + case 'z': case 'Z': expected = 'Z'; break; + default: + expected = ch; + break; + } + ATF_CHECK_MSG((result = (*ctypefn)(ch)) == expected, + "result=%d expected=%d", result, expected); + } +} + +static void +test_tolower_c(int (*ctypefn)(int)) +{ + int ch, result, expected; + + ATF_CHECK_MSG((result = (*ctypefn)(EOF)) == EOF, + "result=%d, expected EOF=%d", result, EOF); + for (ch = 0; ch <= UCHAR_MAX; ch++) { + switch (ch) { + case 'a': case 'A': expected = 'a'; break; + case 'b': case 'B': expected = 'b'; break; + case 'c': case 'C': expected = 'c'; break; + case 'd': case 'D': expected = 'd'; break; + case 'e': case 'E': expected = 'e'; break; + case 'f': case 'F': expected = 'f'; break; + case 'g': case 'G': expected = 'g'; break; + case 'h': case 'H': expected = 'h'; break; + case 'i': case 'I': expected = 'i'; break; + case 'j': case 'J': expected = 'j'; break; + case 'k': case 'K': expected = 'k'; break; + case 'l': case 'L': expected = 'l'; break; + case 'm': case 'M': expected = 'm'; break; + case 'n': case 'N': expected = 'n'; break; + case 'o': case 'O': expected = 'o'; break; + case 'p': case 'P': expected = 'p'; break; + case 'q': case 'Q': expected = 'q'; break; + case 'r': case 'R': expected = 'r'; break; + case 's': case 'S': expected = 's'; break; + case 't': case 'T': expected = 't'; break; + case 'u': case 'U': expected = 'u'; break; + case 'v': case 'V': expected = 'v'; break; + case 'w': case 'W': expected = 'w'; break; + case 'x': case 'X': expected = 'x'; break; + case 'y': case 'Y': expected = 'y'; break; + case 'z': case 'Z': expected = 'z'; break; + default: + expected = ch; + break; + } + ATF_CHECK_MSG((result = (*ctypefn)(ch)) == expected, + "result=%d expected=%d", result, expected); + } +} + +extern char **environ; + +static void +test_abuse_override(const struct atf_tc *tc, const char *fn, const char *mode, + const char *locale) +{ + char h_ctype_abuse[PATH_MAX]; + pid_t pid; + int status; + + RL(snprintf(h_ctype_abuse, sizeof(h_ctype_abuse), "%s/h_ctype_abuse", + atf_tc_get_config_var(tc, "srcdir"))); + + RL(setenv("LIBC_ALLOWCTYPEABUSE", "", 1)); + + RL(pid = vfork()); + if (pid == 0) { /* child */ + char *const argv[] = { + h_ctype_abuse, + __UNCONST(fn), + __UNCONST(mode), + __UNCONST(locale), + NULL, + }; + + if (execve(argv[0], argv, environ) == -1) + _exit(1); + _exit(2); + } + + RL(waitpid(pid, &status, 0)); + if (WIFSIGNALED(status)) { + atf_tc_fail_nonfatal("child exited on signal %d (%s)", + WTERMSIG(status), strsignal(WTERMSIG(status))); + } else if (!WIFEXITED(status)) { + atf_tc_fail_nonfatal("child exited status=0x%x", status); + } else { + ATF_CHECK_MSG(WEXITSTATUS(status) == 0, + "child exited with code %d", + WEXITSTATUS(status)); + } +} + +static void +test_abuse_override_in_locales(const struct atf_tc *tc, const char *fn, + const char *mode) +{ + size_t i; + + for (i = 0; i < __arraycount(locales); i++) { + fprintf(stderr, "# locale %s\n", locales[i]); + test_abuse_override(tc, fn, mode, locales[i]); + } +} + +#define ADD_TEST_ABUSE(TP, FN) do \ +{ \ + ATF_TP_ADD_TC(TP, abuse_##FN##_macro_c); \ + ATF_TP_ADD_TC(TP, abuse_##FN##_function_c); \ + ATF_TP_ADD_TC(TP, abuse_##FN##_macro_locale); \ + ATF_TP_ADD_TC(TP, abuse_##FN##_function_locale); \ +} while (0) + +#define DEF_TEST_ABUSE(FN) \ +ATF_TC(abuse_##FN##_macro_c); \ +ATF_TC_HEAD(abuse_##FN##_macro_c, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Test abusing "#FN" macro with default LC_CTYPE=C"); \ +} \ +ATF_TC_BODY(abuse_##FN##_macro_c, tc) \ +{ \ + if (CHAR_UNSIGNED) { \ + atf_tc_skip("runtime ctype(3) abuse is impossible with" \ + " unsigned char"); \ + } \ + if (!_CTYPE_GUARD_PAGE) \ + atf_tc_skip("no LC_CTYPE=C guard page on this platform"); \ + test_abuse(#FN, &FN##_wrapper); \ +} \ +ATF_TC(abuse_##FN##_function_c); \ +ATF_TC_HEAD(abuse_##FN##_function_c, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Test abusing "#FN" function with default LC_CTYPE=C"); \ +} \ +ATF_TC_BODY(abuse_##FN##_function_c, tc) \ +{ \ + if (CHAR_UNSIGNED) { \ + atf_tc_skip("runtime ctype(3) abuse is impossible with" \ + " unsigned char"); \ + } \ + if (!_CTYPE_GUARD_PAGE) \ + atf_tc_skip("no LC_CTYPE=C guard page on this platform"); \ + test_abuse(#FN, &FN); \ +} \ +ATF_TC(abuse_##FN##_macro_locale); \ +ATF_TC_HEAD(abuse_##FN##_macro_locale, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Test abusing "#FN" macro with locales"); \ +} \ +ATF_TC_BODY(abuse_##FN##_macro_locale, tc) \ +{ \ + if (CHAR_UNSIGNED) { \ + atf_tc_skip("runtime ctype(3) abuse is impossible with" \ + " unsigned char"); \ + } \ + test_abuse_in_locales(#FN, &FN##_wrapper, /*macro*/true); \ +} \ +ATF_TC(abuse_##FN##_function_locale); \ +ATF_TC_HEAD(abuse_##FN##_function_locale, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Test abusing "#FN" function with locales"); \ +} \ +ATF_TC_BODY(abuse_##FN##_function_locale, tc) \ +{ \ + if (CHAR_UNSIGNED) { \ + atf_tc_skip("runtime ctype(3) abuse is impossible with" \ + " unsigned char"); \ + } \ + test_abuse_in_locales(#FN, &FN, /*macro*/false); \ +} + +#define ADD_TEST_ABUSE_OVERRIDE(TP, FN) do \ +{ \ + ATF_TP_ADD_TC(TP, abuse_override_##FN##_macro_c); \ + ATF_TP_ADD_TC(TP, abuse_override_##FN##_function_c); \ + ATF_TP_ADD_TC(TP, abuse_override_##FN##_macro_locale); \ + ATF_TP_ADD_TC(TP, abuse_override_##FN##_function_locale); \ +} while (0) + +#define DEF_TEST_ABUSE_OVERRIDE(FN) \ +ATF_TC(abuse_override_##FN##_macro_c); \ +ATF_TC_HEAD(abuse_override_##FN##_macro_c, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Test allowing abuse of "#FN" macro with default LC_CTYPE=C"); \ +} \ +ATF_TC_BODY(abuse_override_##FN##_macro_c, tc) \ +{ \ + test_abuse_override(tc, #FN, "macro", NULL); \ +} \ +ATF_TC(abuse_override_##FN##_function_c); \ +ATF_TC_HEAD(abuse_override_##FN##_function_c, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Test allowing abuse "#FN" function with default LC_CTYPE=C"); \ +} \ +ATF_TC_BODY(abuse_override_##FN##_function_c, tc) \ +{ \ + test_abuse_override(tc, #FN, "function", NULL); \ +} \ +ATF_TC(abuse_override_##FN##_macro_locale); \ +ATF_TC_HEAD(abuse_override_##FN##_macro_locale, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Test allowing abuse of "#FN" macro with locales"); \ +} \ +ATF_TC_BODY(abuse_override_##FN##_macro_locale, tc) \ +{ \ + test_abuse_override_in_locales(tc, #FN, "macro"); \ +} \ +ATF_TC(abuse_override_##FN##_function_locale); \ +ATF_TC_HEAD(abuse_override_##FN##_function_locale, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Test allowing abuse of "#FN" function with locales"); \ +} \ +ATF_TC_BODY(abuse_override_##FN##_function_locale, tc) \ +{ \ + test_abuse_override_in_locales(tc, #FN, "function"); \ +} + +#define ADD_TEST_USE(TP, FN) do \ +{ \ + ATF_TP_ADD_TC(TP, FN##_macro_c); \ + ATF_TP_ADD_TC(TP, FN##_function_c); \ + ATF_TP_ADD_TC(TP, FN##_macro_locale); \ + ATF_TP_ADD_TC(TP, FN##_function_locale); \ +} while (0) + +#define DEF_TEST_USE(FN) \ +ATF_TC(FN##_macro_c); \ +ATF_TC_HEAD(FN##_macro_c, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Test "#FN" macro with default LC_CTYPE=C"); \ +} \ +ATF_TC_BODY(FN##_macro_c, tc) \ +{ \ + test_##FN##_c(&FN##_wrapper); \ +} \ +ATF_TC(FN##_function_c); \ +ATF_TC_HEAD(FN##_function_c, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Test "#FN" function with default LC_CTYPE=C"); \ +} \ +ATF_TC_BODY(FN##_function_c, tc) \ +{ \ + test_##FN##_c(&FN); \ +} \ +ATF_TC(FN##_macro_locale); \ +ATF_TC_HEAD(FN##_macro_locale, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Test "#FN" macro with locales"); \ +} \ +ATF_TC_BODY(FN##_macro_locale, tc) \ +{ \ + size_t i; \ + \ + for (i = 0; i < __arraycount(locales); i++) \ + test_##FN##_locale(locales[i], &FN##_wrapper); \ +} \ +ATF_TC(FN##_function_locale); \ +ATF_TC_HEAD(FN##_function_locale, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Test "#FN" function with locales"); \ +} \ +ATF_TC_BODY(FN##_function_locale, tc) \ +{ \ + size_t i; \ + \ + for (i = 0; i < __arraycount(locales); i++) \ + test_##FN##_locale(locales[i], &FN); \ +} + +DEF_TEST_ABUSE(isalpha) +DEF_TEST_ABUSE(isupper) +DEF_TEST_ABUSE(islower) +DEF_TEST_ABUSE(isdigit) +DEF_TEST_ABUSE(isxdigit) +DEF_TEST_ABUSE(isalnum) +DEF_TEST_ABUSE(isspace) +DEF_TEST_ABUSE(ispunct) +DEF_TEST_ABUSE(isprint) +DEF_TEST_ABUSE(isgraph) +DEF_TEST_ABUSE(iscntrl) +DEF_TEST_ABUSE(isblank) +DEF_TEST_ABUSE(toupper) +DEF_TEST_ABUSE(tolower) + +DEF_TEST_ABUSE_OVERRIDE(isalpha) +DEF_TEST_ABUSE_OVERRIDE(isupper) +DEF_TEST_ABUSE_OVERRIDE(islower) +DEF_TEST_ABUSE_OVERRIDE(isdigit) +DEF_TEST_ABUSE_OVERRIDE(isxdigit) +DEF_TEST_ABUSE_OVERRIDE(isalnum) +DEF_TEST_ABUSE_OVERRIDE(isspace) +DEF_TEST_ABUSE_OVERRIDE(ispunct) +DEF_TEST_ABUSE_OVERRIDE(isprint) +DEF_TEST_ABUSE_OVERRIDE(isgraph) +DEF_TEST_ABUSE_OVERRIDE(iscntrl) +DEF_TEST_ABUSE_OVERRIDE(isblank) +DEF_TEST_ABUSE_OVERRIDE(toupper) +DEF_TEST_ABUSE_OVERRIDE(tolower) + +DEF_TEST_USE(isalpha) +DEF_TEST_USE(isupper) +DEF_TEST_USE(islower) +DEF_TEST_USE(isdigit) +DEF_TEST_USE(isxdigit) +DEF_TEST_USE(isalnum) +DEF_TEST_USE(isspace) +DEF_TEST_USE(ispunct) +DEF_TEST_USE(isprint) +DEF_TEST_USE(isgraph) +DEF_TEST_USE(iscntrl) +DEF_TEST_USE(isblank) +DEF_TEST_USE(toupper) +DEF_TEST_USE(tolower) + +ATF_TC(eof_confusion_iso8859_1); +ATF_TC_HEAD(eof_confusion_iso8859_1, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test potential confusion with EOF in ISO-8859-1"); +} +ATF_TC_BODY(eof_confusion_iso8859_1, tc) +{ + int ydots = 0xff; /* ÿ, LATIN SMALL LETTER Y WITH DIAERESIS */ + int ch; + + /* + * The LATIN SMALL LETTER Y WITH DIAERESIS code point 0xff in + * ISO-8859-1 is curious primarily because its bit pattern + * coincides with an 8-bit signed -1, which is to say, EOF as + * an 8-bit quantity; of course, for EOF, all of the is* + * functions are supposed to return false (as we test above). + * It also has the curious property that it lacks any + * corresponding uppercase code point in ISO-8859-1, so we + * can't distinguish it from EOF by tolower/toupper. + */ + ATF_REQUIRE(setlocale(LC_CTYPE, "fr_FR.ISO8859-1") != NULL); + ATF_CHECK(isalpha(ydots)); + ATF_CHECK(!isupper(ydots)); + ATF_CHECK(islower(ydots)); + ATF_CHECK(!isdigit(ydots)); + ATF_CHECK(!isxdigit(ydots)); + ATF_CHECK(isalnum(ydots)); + ATF_CHECK(!isspace(ydots)); + ATF_CHECK(!ispunct(ydots)); + ATF_CHECK(isprint(ydots)); + ATF_CHECK(isgraph(ydots)); + ATF_CHECK(!iscntrl(ydots)); + ATF_CHECK(!isblank(ydots)); + ATF_CHECK_MSG((ch = toupper(ydots)) == ydots, "ch=0x%x", ch); + ATF_CHECK_MSG((ch = tolower(ydots)) == ydots, "ch=0x%x", ch); +} + +ATF_TC(eof_confusion_koi8_u); +ATF_TC_HEAD(eof_confusion_koi8_u, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test potential confusion with EOF in KOI8-U"); +} +ATF_TC_BODY(eof_confusion_koi8_u, tc) +{ + int Hard = 0xff; /* Ъ, CYRILLIC CAPITAL LETTER HARD SIGN */ + int hard = 0xdf; /* ъ, CYRILLIC SMALL LETTER HARD SIGN */ + int ch; + + /* + * The CYRILLIC CAPITAL LETTER HARD SIGN code point 0xff in + * KOI8-U (and KOI8-R) also coincides with the bit pattern of + * an 8-bit signed -1. Unlike LATIN SMALL LETTER Y WITH + * DIAERESIS, it has a lowercase equivalent in KOI8-U. + */ + ATF_REQUIRE(setlocale(LC_CTYPE, "uk_UA.KOI8-U") != NULL); + ATF_CHECK(isalpha(Hard)); + ATF_CHECK(isupper(Hard)); + ATF_CHECK(!islower(Hard)); + ATF_CHECK(!isdigit(Hard)); + ATF_CHECK(!isxdigit(Hard)); + ATF_CHECK(isalnum(Hard)); + ATF_CHECK(!isspace(Hard)); + ATF_CHECK(!ispunct(Hard)); + ATF_CHECK(isprint(Hard)); + ATF_CHECK(isgraph(Hard)); + ATF_CHECK(!iscntrl(Hard)); + ATF_CHECK(!isblank(Hard)); + ATF_CHECK_MSG((ch = toupper(Hard)) == Hard, "ch=0x%x", ch); + ATF_CHECK_MSG((ch = tolower(Hard)) == hard, "ch=0x%x", ch); +} + +ATF_TC(eof_confusion_pt154); +ATF_TC_HEAD(eof_confusion_pt154, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test potential confusion with EOF in PT154"); +} +ATF_TC_BODY(eof_confusion_pt154, tc) +{ + int ya = 0xff; /* я, CYRILLIC SMALL LETTER YA */ + int Ya = 0xdf; /* Я, CYRILLIC CAPITAL LETTER YA */ + int ch; + + /* + * The CYRILLIC SMALL LETTER YA code point 0xff in PT154 also + * coincides with the bit pattern of an 8-bit signed -1, and is + * lowercase with a corresponding uppercase code point in + * PT154. + */ + ATF_REQUIRE(setlocale(LC_CTYPE, "kk_KZ.PT154") != NULL); + ATF_CHECK(isalpha(ya)); + ATF_CHECK(!isupper(ya)); + ATF_CHECK(islower(ya)); + ATF_CHECK(!isdigit(ya)); + ATF_CHECK(!isxdigit(ya)); + ATF_CHECK(isalnum(ya)); + ATF_CHECK(!isspace(ya)); + ATF_CHECK(!ispunct(ya)); + ATF_CHECK(isprint(ya)); + ATF_CHECK(isgraph(ya)); + ATF_CHECK(!iscntrl(ya)); + ATF_CHECK(!isblank(ya)); + ATF_CHECK_MSG((ch = toupper(ya)) == Ya, "ch=0x%x", ch); + ATF_CHECK_MSG((ch = tolower(ya)) == ya, "ch=0x%x", ch); +} + +ATF_TP_ADD_TCS(tp) +{ + + ADD_TEST_ABUSE(tp, isalpha); + ADD_TEST_ABUSE(tp, isupper); + ADD_TEST_ABUSE(tp, islower); + ADD_TEST_ABUSE(tp, isdigit); + ADD_TEST_ABUSE(tp, isxdigit); + ADD_TEST_ABUSE(tp, isalnum); + ADD_TEST_ABUSE(tp, isspace); + ADD_TEST_ABUSE(tp, ispunct); + ADD_TEST_ABUSE(tp, isprint); + ADD_TEST_ABUSE(tp, isgraph); + ADD_TEST_ABUSE(tp, iscntrl); + ADD_TEST_ABUSE(tp, isblank); + ADD_TEST_ABUSE(tp, toupper); + ADD_TEST_ABUSE(tp, tolower); + + ADD_TEST_ABUSE_OVERRIDE(tp, isalpha); + ADD_TEST_ABUSE_OVERRIDE(tp, isupper); + ADD_TEST_ABUSE_OVERRIDE(tp, islower); + ADD_TEST_ABUSE_OVERRIDE(tp, isdigit); + ADD_TEST_ABUSE_OVERRIDE(tp, isxdigit); + ADD_TEST_ABUSE_OVERRIDE(tp, isalnum); + ADD_TEST_ABUSE_OVERRIDE(tp, isspace); + ADD_TEST_ABUSE_OVERRIDE(tp, ispunct); + ADD_TEST_ABUSE_OVERRIDE(tp, isprint); + ADD_TEST_ABUSE_OVERRIDE(tp, isgraph); + ADD_TEST_ABUSE_OVERRIDE(tp, iscntrl); + ADD_TEST_ABUSE_OVERRIDE(tp, isblank); + ADD_TEST_ABUSE_OVERRIDE(tp, toupper); + ADD_TEST_ABUSE_OVERRIDE(tp, tolower); + + ADD_TEST_USE(tp, isalpha); + ADD_TEST_USE(tp, isupper); + ADD_TEST_USE(tp, islower); + ADD_TEST_USE(tp, isdigit); + ADD_TEST_USE(tp, isxdigit); + ADD_TEST_USE(tp, isalnum); + ADD_TEST_USE(tp, isspace); + ADD_TEST_USE(tp, ispunct); + ADD_TEST_USE(tp, isprint); + ADD_TEST_USE(tp, isgraph); + ADD_TEST_USE(tp, iscntrl); + ADD_TEST_USE(tp, isblank); + ADD_TEST_USE(tp, toupper); + ADD_TEST_USE(tp, tolower); + + ATF_TP_ADD_TC(tp, eof_confusion_iso8859_1); + ATF_TP_ADD_TC(tp, eof_confusion_koi8_u); + ATF_TP_ADD_TC(tp, eof_confusion_pt154); + + return atf_no_error(); +} diff --git a/lib/libc/gen/t_timespec_get.c b/lib/libc/gen/t_timespec_get.c new file mode 100644 index 000000000000..b51625311032 --- /dev/null +++ b/lib/libc/gen/t_timespec_get.c @@ -0,0 +1,123 @@ +/*- + * Copyright (c) 2025 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Nia Alarie. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 <limits.h> +#include <time.h> + +ATF_TC(timespec_getres); +ATF_TC_HEAD(timespec_getres, tc) +{ + atf_tc_set_md_var(tc, "descr", "Resolution tests for timespec_getres"); +} + +ATF_TC_BODY(timespec_getres, tc) +{ + struct timespec ts, ts2; + + ATF_REQUIRE_EQ(timespec_getres(&ts, TIME_MONOTONIC), TIME_MONOTONIC); + ATF_REQUIRE_EQ(clock_getres(CLOCK_MONOTONIC, &ts2), 0); + + ATF_REQUIRE_EQ(ts.tv_sec, ts2.tv_sec); + ATF_REQUIRE_EQ(ts.tv_nsec, ts2.tv_nsec); + + ATF_REQUIRE_EQ(timespec_getres(&ts, TIME_UTC), TIME_UTC); + ATF_REQUIRE_EQ(clock_getres(CLOCK_REALTIME, &ts2), 0); + + ATF_REQUIRE_EQ(ts.tv_sec, ts2.tv_sec); + ATF_REQUIRE_EQ(ts.tv_nsec, ts2.tv_nsec); + + /* now an invalid value */ + ATF_REQUIRE_EQ(timespec_getres(&ts, INT_MAX), 0); +} + +ATF_TC(timespec_get); +ATF_TC_HEAD(timespec_get, tc) +{ + atf_tc_set_md_var(tc, "descr", "Basic tests for timespec_get"); +} + +ATF_TC_BODY(timespec_get, tc) +{ + struct timespec ts, ts2; + + ATF_REQUIRE_EQ(timespec_get(&ts, TIME_UTC), TIME_UTC); + ATF_REQUIRE_EQ(clock_gettime(CLOCK_REALTIME, &ts2), 0); + + /* + * basically test that these clocks (which should be the same source) + * aren't too wildly apart... + */ + + if (ts2.tv_sec >= ts.tv_sec) { + ATF_REQUIRE((ts2.tv_sec - ts.tv_sec) < 86400); + } else { + ATF_REQUIRE((ts.tv_sec - ts2.tv_sec) < 86400); + } + + /* now an invalid value */ + ATF_REQUIRE_EQ(timespec_get(&ts, INT_MAX), 0); +} + +ATF_TC(timespec_get_monotonic); +ATF_TC_HEAD(timespec_get_monotonic, tc) +{ + atf_tc_set_md_var(tc, "descr", "Monotonic tests for timespec_getres"); +} + +ATF_TC_BODY(timespec_get_monotonic, tc) +{ + struct timespec ts, ts2; + + ATF_REQUIRE_EQ(timespec_get(&ts, TIME_MONOTONIC), TIME_MONOTONIC); + ATF_REQUIRE_EQ(clock_gettime(CLOCK_MONOTONIC, &ts2), 0); + + /* + * basically test that these clocks (which should be the same source) + * aren't too wildly apart... + */ + + ATF_REQUIRE(ts2.tv_sec >= ts.tv_sec); + ATF_REQUIRE((ts2.tv_sec - ts.tv_sec) < 86400); + + /* test that it's actually monotonic */ + ATF_REQUIRE_EQ(timespec_get(&ts2, TIME_MONOTONIC), TIME_MONOTONIC); + ATF_REQUIRE(ts2.tv_sec >= ts.tv_sec); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, timespec_getres); + ATF_TP_ADD_TC(tp, timespec_get); + ATF_TP_ADD_TC(tp, timespec_get_monotonic); + + return atf_no_error(); +} + diff --git a/lib/libc/locale/t_c16rtomb.c b/lib/libc/locale/t_c16rtomb.c new file mode 100644 index 000000000000..3d695db8dc97 --- /dev/null +++ b/lib/libc/locale/t_c16rtomb.c @@ -0,0 +1,287 @@ +/* $NetBSD: t_c16rtomb.c,v 1.6 2024/08/19 16:22:10 riastradh Exp $ */ + +/*- + * Copyright (c) 2002 Tim J. Robbins + * All rights reserved. + * + * Copyright (c) 2013 Ed Schouten <ed@FreeBSD.org> + * All rights reserved. + * + * 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. + */ +/* + * Test program for c16rtomb() as specified by ISO/IEC 9899:2011. + */ + +#include <sys/cdefs.h> +__RCSID("$NetBSD: t_c16rtomb.c,v 1.6 2024/08/19 16:22:10 riastradh Exp $"); + +#include <errno.h> +#include <limits.h> +#include <locale.h> +#include <stdio.h> +#include <string.h> +#include <uchar.h> + +#include <atf-c.h> + +static void +require_lc_ctype(const char *locale_name) +{ + char *lc_ctype_set; + + lc_ctype_set = setlocale(LC_CTYPE, locale_name); + if (lc_ctype_set == NULL) + atf_tc_fail("setlocale(LC_CTYPE, \"%s\") failed; errno=%d", + locale_name, errno); + + ATF_REQUIRE_EQ_MSG(strcmp(lc_ctype_set, locale_name), 0, + "lc_ctype_set=%s locale_name=%s", lc_ctype_set, locale_name); +} + +static mbstate_t s; +static char buf[7*MB_LEN_MAX + 1]; + +ATF_TC_WITHOUT_HEAD(c16rtomb_c_locale_test); +ATF_TC_BODY(c16rtomb_c_locale_test, tc) +{ + size_t n; + + require_lc_ctype("C"); + + /* + * If the buffer argument is NULL, c16 is implicitly 0, + * c16rtomb() resets its internal state. + */ + ATF_CHECK_EQ_MSG((n = c16rtomb(NULL, L'\0', NULL)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c16rtomb(NULL, 0xdc00, NULL)), 1, "n=%zu", n); + + /* Null wide character. */ + memset(&s, 0, sizeof(s)); + memset(buf, 0xcc, sizeof(buf)); + ATF_CHECK_EQ_MSG((n = c16rtomb(buf, 0, &s)), 1, "n=%zu", n); + ATF_CHECK_MSG(((unsigned char)buf[0] == 0 && + (unsigned char)buf[1] == 0xcc), + "buf=[%02x %02x]", buf[0], buf[1]); + + /* Latin letter A, internal state. */ + ATF_CHECK_EQ_MSG((n = c16rtomb(NULL, L'\0', NULL)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c16rtomb(NULL, L'A', NULL)), 1, "n=%zu", n); + + /* Latin letter A. */ + memset(&s, 0, sizeof(s)); + memset(buf, 0xcc, sizeof(buf)); + ATF_CHECK_EQ_MSG((n = c16rtomb(buf, L'A', &s)), 1, "n=%zu", n); + ATF_CHECK_MSG(((unsigned char)buf[0] == 'A' && + (unsigned char)buf[1] == 0xcc), + "buf=[%02x %02x]", buf[0], buf[1]); + + /* Unicode character 'Pile of poo'. */ + memset(&s, 0, sizeof(s)); + memset(buf, 0xcc, sizeof(buf)); + ATF_CHECK_EQ_MSG((n = c16rtomb(buf, 0xd83d, &s)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c16rtomb(buf, 0xdca9, &s)), (size_t)-1, + "n=%zu", n); + ATF_CHECK_EQ_MSG(errno, EILSEQ, "errno=%d", errno); + ATF_CHECK_EQ_MSG((unsigned char)buf[0], 0xcc, "buf=[%02x]", buf[0]); + + /* Incomplete Unicode character 'Pile of poo', interrupted by NUL. */ + memset(&s, 0, sizeof(s)); + memset(buf, 0xcc, sizeof(buf)); + ATF_CHECK_EQ_MSG((n = c16rtomb(buf, 0xd83d, &s)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c16rtomb(buf, L'\0', &s)), 1, "n=%zu", n); + ATF_CHECK_MSG(((unsigned char)buf[0] == '\0' && + (unsigned char)buf[1] == 0xcc), + "buf=[%02x %02x]", buf[0], buf[1]); +} + +ATF_TC_WITHOUT_HEAD(c16rtomb_iso2022jp_locale_test); +ATF_TC_BODY(c16rtomb_iso2022jp_locale_test, tc) +{ + char *p; + size_t n; + + require_lc_ctype("ja_JP.ISO-2022-JP"); + + /* + * If the buffer argument is NULL, c16 is implicitly 0, + * c16rtomb() resets its internal state. + */ + ATF_CHECK_EQ_MSG((n = c16rtomb(NULL, L'\0', NULL)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c16rtomb(NULL, 0xdc00, NULL)), 1, "n=%zu", n); + + /* Null wide character. */ + memset(&s, 0, sizeof(s)); + memset(buf, 0xcc, sizeof(buf)); + ATF_CHECK_EQ_MSG((n = c16rtomb(buf, 0, &s)), 1, "n=%zu", n); + ATF_CHECK_MSG(((unsigned char)buf[0] == 0 && + (unsigned char)buf[1] == 0xcc), + "buf=[%02x %02x]", buf[0], buf[1]); + + /* Latin letter A, internal state. */ + ATF_CHECK_EQ_MSG((n = c16rtomb(NULL, L'\0', NULL)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c16rtomb(NULL, L'A', NULL)), 1, "n=%zu", n); + + /* + * 1. U+0042 LATIN CAPITAL LETTER A + * 2. U+00A5 YEN SIGN + * 3. U+00A5 YEN SIGN (again, no shift needed) + * 4. U+30A2 KATAKANA LETTER A + * 5. U+30A2 KATAKANA LETTER A (again, no shift needed) + * 6. incomplete UTF-16 surrogate pair -- no output + * 7. U+0000 NUL (plus shift sequence to initial state) + */ + memset(&s, 0, sizeof(s)); + memset(buf, 0xcc, sizeof(buf)); + p = buf; + ATF_CHECK_EQ_MSG((n = c16rtomb(p, L'A', &s)), 1, "n=%zu", n); /* 1 */ + p += 1; + ATF_CHECK_EQ_MSG((n = c16rtomb(p, 0xa5, &s)), 4, "n=%zu", n); /* 2 */ + p += 4; + ATF_CHECK_EQ_MSG((n = c16rtomb(p, 0xa5, &s)), 1, "n=%zu", n); /* 3 */ + p += 1; + ATF_CHECK_EQ_MSG((n = c16rtomb(p, 0x30a2, &s)), 5, "n=%zu", n); /* 4 */ + p += 5; + ATF_CHECK_EQ_MSG((n = c16rtomb(p, 0x30a2, &s)), 2, "n=%zu", n); /* 5 */ + p += 2; + ATF_CHECK_EQ_MSG((n = c16rtomb(p, 0xd800, &s)), 0, "n=%zu", n); /* 6 */ + ATF_CHECK_EQ_MSG((n = c16rtomb(p, L'\0', &s)), 4, "n=%zu", n); /* 7 */ + p += 4; + ATF_CHECK_MSG(((unsigned char)buf[0] == 'A' && + (unsigned char)buf[1] == 0x1b && /* shift ISO/IEC 646:JP */ + (unsigned char)buf[2] == '(' && + (unsigned char)buf[3] == 'J' && + (unsigned char)buf[4] == 0x5c && /* YEN SIGN */ + (unsigned char)buf[5] == 0x5c && /* YEN SIGN */ + (unsigned char)buf[6] == 0x1b && /* shift JIS X 0208 */ + (unsigned char)buf[7] == '$' && + (unsigned char)buf[8] == 'B' && + (unsigned char)buf[9] == 0x25 && /* KATAKANA LETTER A */ + (unsigned char)buf[10] == 0x22 && + (unsigned char)buf[11] == 0x25 && /* KATAKANA LETTER A */ + (unsigned char)buf[12] == 0x22 && + (unsigned char)buf[13] == 0x1b && /* shift US-ASCII */ + (unsigned char)buf[14] == '(' && + (unsigned char)buf[15] == 'B' && + (unsigned char)buf[16] == '\0' && + (unsigned char)buf[17] == 0xcc), + "buf=[%02x %02x %02x %02x %02x %02x %02x %02x " + " %02x %02x %02x %02x %02x %02x %02x %02x " + " %02x %02x]", + buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6], buf[7], + buf[8], buf[9], buf[10], buf[11], + buf[12], buf[13], buf[14], buf[15], + buf[16], buf[17]); +} + +ATF_TC_WITHOUT_HEAD(c16rtomb_iso_8859_1_test); +ATF_TC_BODY(c16rtomb_iso_8859_1_test, tc) +{ + size_t n; + + require_lc_ctype("en_US.ISO8859-1"); + + /* Unicode character 'Euro sign'. */ + memset(&s, 0, sizeof(s)); + memset(buf, 0xcc, sizeof(buf)); + ATF_CHECK_EQ_MSG((n = c16rtomb(buf, 0x20ac, &s)), (size_t)-1, + "n=%zu", n); + ATF_CHECK_EQ_MSG(errno, EILSEQ, "errno=%d", errno); + ATF_CHECK_EQ_MSG((unsigned char)buf[0], 0xcc, "buf=[%02x]", buf[0]); +} + +ATF_TC_WITHOUT_HEAD(c16rtomb_iso_8859_15_test); +ATF_TC_BODY(c16rtomb_iso_8859_15_test, tc) +{ + size_t n; + + require_lc_ctype("en_US.ISO8859-15"); + + /* Unicode character 'Euro sign'. */ + memset(&s, 0, sizeof(s)); + memset(buf, 0xcc, sizeof(buf)); + ATF_CHECK_EQ_MSG((n = c16rtomb(buf, 0x20ac, &s)), 1, "n=%zu", n); + ATF_CHECK_MSG(((unsigned char)buf[0] == 0xa4 && + (unsigned char)buf[1] == 0xcc), + "buf=[%02x %02x]", buf[0], buf[1]); +} + +ATF_TC_WITHOUT_HEAD(c16rtomb_utf_8_test); +ATF_TC_BODY(c16rtomb_utf_8_test, tc) +{ + size_t n; + + require_lc_ctype("en_US.UTF-8"); + + /* Unicode character 'Pile of poo'. */ + memset(&s, 0, sizeof(s)); + memset(buf, 0xcc, sizeof(buf)); + ATF_CHECK_EQ_MSG((n = c16rtomb(buf, 0xd83d, &s)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c16rtomb(buf, 0xdca9, &s)), 4, "n=%zu", n); + ATF_CHECK_MSG(((unsigned char)buf[0] == 0xf0 && + (unsigned char)buf[1] == 0x9f && + (unsigned char)buf[2] == 0x92 && + (unsigned char)buf[3] == 0xa9 && + (unsigned char)buf[4] == 0xcc), + "buf=[%02x %02x %02x %02x %02x]", + buf[0], buf[1], buf[2], buf[3], buf[4]); + + /* Invalid code; 'Pile of poo' without the trail surrogate. */ + memset(&s, 0, sizeof(s)); + memset(buf, 0xcc, sizeof(buf)); + ATF_CHECK_EQ_MSG((n = c16rtomb(buf, 0xd83d, &s)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c16rtomb(buf, L'A', &s)), (size_t)-1, + "n=%zu", n); + ATF_CHECK_EQ_MSG(errno, EILSEQ, "errno=%d", errno); + ATF_CHECK_EQ_MSG((unsigned char)buf[0], 0xcc, "buf=[%02x]", buf[0]); + + /* Invalid code; 'Pile of poo' without the lead surrogate. */ + memset(&s, 0, sizeof(s)); + memset(buf, 0xcc, sizeof(buf)); + ATF_CHECK_EQ_MSG((n = c16rtomb(buf, 0xdca9, &s)), (size_t)-1, + "n=%zu", n); + ATF_CHECK_EQ_MSG(errno, EILSEQ, "errno=%d", errno); + ATF_CHECK_EQ_MSG((unsigned char)buf[0], 0xcc, "buf=[%02x]", buf[0]); + + /* Incomplete Unicode character 'Pile of poo', interrupted by NUL. */ + memset(&s, 0, sizeof(s)); + memset(buf, 0xcc, sizeof(buf)); + ATF_CHECK_EQ_MSG((n = c16rtomb(buf, 0xd83d, &s)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c16rtomb(buf, L'\0', &s)), 1, + "n=%zu", n); + ATF_CHECK_MSG(((unsigned char)buf[0] == '\0' && + (unsigned char)buf[1] == 0xcc), + "buf=[%02x %02x]", buf[0], buf[1]); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, c16rtomb_c_locale_test); + ATF_TP_ADD_TC(tp, c16rtomb_iso2022jp_locale_test); + ATF_TP_ADD_TC(tp, c16rtomb_iso_8859_1_test); + ATF_TP_ADD_TC(tp, c16rtomb_iso_8859_15_test); + ATF_TP_ADD_TC(tp, c16rtomb_utf_8_test); + + return (atf_no_error()); +} diff --git a/lib/libc/locale/t_c32rtomb.c b/lib/libc/locale/t_c32rtomb.c new file mode 100644 index 000000000000..7233e14f7967 --- /dev/null +++ b/lib/libc/locale/t_c32rtomb.c @@ -0,0 +1,60 @@ +/* $NetBSD: t_c32rtomb.c,v 1.1 2024/08/15 14:16:34 riastradh Exp $ */ + +/*- + * Copyright (c) 2024 The NetBSD Foundation, Inc. + * All rights reserved. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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> +__RCSID("$NetBSD: t_c32rtomb.c,v 1.1 2024/08/15 14:16:34 riastradh Exp $"); + +#include <atf-c.h> +#include <locale.h> +#include <uchar.h> + +#include "h_macros.h" + +ATF_TC(c32rtomb_null); +ATF_TC_HEAD(c32rtomb_null, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test null string output to c32rtomb"); +} +ATF_TC_BODY(c32rtomb_null, tc) +{ + char *locale; + mbstate_t ps = {0}; + size_t n; + + REQUIRE_LIBC((locale = setlocale(LC_ALL, "C")), NULL); + ATF_REQUIRE_EQ_MSG(strcmp(locale, "C"), 0, "locale=%s", locale); + + ATF_CHECK_EQ_MSG((n = c32rtomb(NULL, L'x', &ps)), 1, "n=%zu", n); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, c32rtomb_null); + return atf_no_error(); +} diff --git a/lib/libc/locale/t_c8rtomb.c b/lib/libc/locale/t_c8rtomb.c new file mode 100644 index 000000000000..ce03e6ed1d20 --- /dev/null +++ b/lib/libc/locale/t_c8rtomb.c @@ -0,0 +1,355 @@ +/* $NetBSD: t_c8rtomb.c,v 1.7 2024/08/19 16:22:10 riastradh Exp $ */ + +/*- + * Copyright (c) 2002 Tim J. Robbins + * All rights reserved. + * + * Copyright (c) 2013 Ed Schouten <ed@FreeBSD.org> + * All rights reserved. + * + * 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. + */ +/* + * Test program for c8rtomb() as specified by C23. + */ + +#include <sys/cdefs.h> +__RCSID("$NetBSD: t_c8rtomb.c,v 1.7 2024/08/19 16:22:10 riastradh Exp $"); + +#include <errno.h> +#include <limits.h> +#include <locale.h> +#include <stdio.h> +#include <string.h> +#include <uchar.h> + +#include <atf-c.h> + +static void +require_lc_ctype(const char *locale_name) +{ + char *lc_ctype_set; + + lc_ctype_set = setlocale(LC_CTYPE, locale_name); + if (lc_ctype_set == NULL) + atf_tc_fail("setlocale(LC_CTYPE, \"%s\") failed; errno=%d", + locale_name, errno); + + ATF_REQUIRE_EQ_MSG(strcmp(lc_ctype_set, locale_name), 0, + "lc_ctype_set=%s locale_name=%s", lc_ctype_set, locale_name); +} + +static mbstate_t s; +static char buf[7*MB_LEN_MAX + 1]; + +ATF_TC_WITHOUT_HEAD(c8rtomb_c_locale_test); +ATF_TC_BODY(c8rtomb_c_locale_test, tc) +{ + size_t n; + + require_lc_ctype("C"); + + /* + * If the buffer argument is NULL, c8 is implicitly 0, + * c8rtomb() resets its internal state. + */ + ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, '\0', NULL)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0x80, NULL)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0xc0, NULL)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0xe0, NULL)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0xf0, NULL)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0xf8, NULL)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0xfc, NULL)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0xfe, NULL)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0xff, NULL)), 1, "n=%zu", n); + + /* Null wide character. */ + memset(&s, 0, sizeof(s)); + memset(buf, 0xcc, sizeof(buf)); + ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0, &s)), 1, "n=%zu", n); + ATF_CHECK_MSG(((unsigned char)buf[0] == 0 && + (unsigned char)buf[1] == 0xcc), + "buf=[%02x %02x]", buf[0], buf[1]); + + /* Latin letter A, internal state. */ + ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, '\0', NULL)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 'A', NULL)), 1, "n=%zu", n); + + /* Latin letter A. */ + memset(&s, 0, sizeof(s)); + memset(buf, 0xcc, sizeof(buf)); + ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 'A', &s)), 1, "n=%zu", n); + ATF_CHECK_MSG(((unsigned char)buf[0] == 'A' && + (unsigned char)buf[1] == 0xcc), + "buf=[%02x %02x]", buf[0], buf[1]); + + /* Unicode character 'Pile of poo'. */ + memset(&s, 0, sizeof(s)); + memset(buf, 0xcc, sizeof(buf)); + ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xf0, &s)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x9f, &s)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x92, &s)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xa9, &s)), (size_t)-1, + "n=%zu", n); + ATF_CHECK_EQ_MSG(errno, EILSEQ, "errno=%d", errno); + ATF_CHECK_EQ_MSG((unsigned char)buf[0], 0xcc, "buf=[%02x]", buf[0]); + + /* Incomplete Unicode character 'Pile of poo', interrupted by NUL. */ + memset(&s, 0, sizeof(s)); + memset(buf, 0xcc, sizeof(buf)); + ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xf0, &s)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(buf, '\0', &s)), 1, "n=%zu", n); + ATF_CHECK_MSG(((unsigned char)buf[0] == '\0' && + (unsigned char)buf[1] == 0xcc), + "buf=[%02x %02x]", buf[0], buf[1]); + + memset(&s, 0, sizeof(s)); + memset(buf, 0xcc, sizeof(buf)); + ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xf0, &s)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x9f, &s)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(buf, '\0', &s)), 1, "n=%zu", n); + ATF_CHECK_MSG(((unsigned char)buf[0] == '\0' && + (unsigned char)buf[1] == 0xcc), + "buf=[%02x %02x]", buf[0], buf[1]); + + memset(&s, 0, sizeof(s)); + memset(buf, 0xcc, sizeof(buf)); + ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xf0, &s)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x9f, &s)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x92, &s)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(buf, '\0', &s)), 1, "n=%zu", n); + ATF_CHECK_MSG(((unsigned char)buf[0] == '\0' && + (unsigned char)buf[1] == 0xcc), + "buf=[%02x %02x]", buf[0], buf[1]); +} + +ATF_TC_WITHOUT_HEAD(c8rtomb_iso2022jp_locale_test); +ATF_TC_BODY(c8rtomb_iso2022jp_locale_test, tc) +{ + char *p; + size_t n; + + require_lc_ctype("ja_JP.ISO-2022-JP"); + + /* + * If the buffer argument is NULL, c8 is implicitly 0, + * c8rtomb() resets its internal state. + */ + ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, '\0', NULL)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0x80, NULL)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0xc0, NULL)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0xe0, NULL)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0xf0, NULL)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0xf8, NULL)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0xfc, NULL)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0xfe, NULL)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0xff, NULL)), 1, "n=%zu", n); + + /* Null wide character. */ + memset(&s, 0, sizeof(s)); + memset(buf, 0xcc, sizeof(buf)); + ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0, &s)), 1, "n=%zu", n); + ATF_CHECK_MSG(((unsigned char)buf[0] == 0 && + (unsigned char)buf[1] == 0xcc), + "buf=[%02x %02x]", buf[0], buf[1]); + + /* Latin letter A, internal state. */ + ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, '\0', NULL)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 'A', NULL)), 1, "n=%zu", n); + + /* + * 1. U+0042 LATIN CAPITAL LETTER A + * 2. U+00A5 YEN SIGN + * 3. U+00A5 YEN SIGN (again, no shift needed) + * 4. U+30A2 KATAKANA LETTER A + * 5. U+30A2 KATAKANA LETTER A (again, no shift needed) + * 6. incomplete UTF-8 multibyte sequence -- no output + * 7. U+0000 NUL (plus shift sequence to initial state) + */ + memset(&s, 0, sizeof(s)); + memset(buf, 0xcc, sizeof(buf)); + p = buf; + ATF_CHECK_EQ_MSG((n = c8rtomb(p, 'A', &s)), 1, "n=%zu", n); /* 1 */ + p += 1; + ATF_CHECK_EQ_MSG((n = c8rtomb(p, 0xc2, &s)), 0, "n=%zu", n); /* 2 */ + ATF_CHECK_EQ_MSG((n = c8rtomb(p, 0xa5, &s)), 4, "n=%zu", n); + p += 4; + ATF_CHECK_EQ_MSG((n = c8rtomb(p, 0xc2, &s)), 0, "n=%zu", n); /* 3 */ + ATF_CHECK_EQ_MSG((n = c8rtomb(p, 0xa5, &s)), 1, "n=%zu", n); + p += 1; + ATF_CHECK_EQ_MSG((n = c8rtomb(p, 0xe3, &s)), 0, "n=%zu", n); /* 4 */ + ATF_CHECK_EQ_MSG((n = c8rtomb(p, 0x82, &s)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(p, 0xa2, &s)), 5, "n=%zu", n); + p += 5; + ATF_CHECK_EQ_MSG((n = c8rtomb(p, 0xe3, &s)), 0, "n=%zu", n); /* 5 */ + ATF_CHECK_EQ_MSG((n = c8rtomb(p, 0x82, &s)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(p, 0xa2, &s)), 2, "n=%zu", n); + p += 2; + ATF_CHECK_EQ_MSG((n = c8rtomb(p, 0xe3, &s)), 0, "n=%zu", n); /* 6 */ + ATF_CHECK_EQ_MSG((n = c8rtomb(p, 0x82, &s)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(p, '\0', &s)), 4, "n=%zu", n); /* 7 */ + p += 4; + ATF_CHECK_MSG(((unsigned char)buf[0] == 'A' && + (unsigned char)buf[1] == 0x1b && /* shift ISO/IEC 646:JP */ + (unsigned char)buf[2] == '(' && + (unsigned char)buf[3] == 'J' && + (unsigned char)buf[4] == 0x5c && /* YEN SIGN */ + (unsigned char)buf[5] == 0x5c && /* YEN SIGN */ + (unsigned char)buf[6] == 0x1b && /* shift JIS X 0208 */ + (unsigned char)buf[7] == '$' && + (unsigned char)buf[8] == 'B' && + (unsigned char)buf[9] == 0x25 && /* KATAKANA LETTER A */ + (unsigned char)buf[10] == 0x22 && + (unsigned char)buf[11] == 0x25 && /* KATAKANA LETTER A */ + (unsigned char)buf[12] == 0x22 && + (unsigned char)buf[13] == 0x1b && /* shift US-ASCII */ + (unsigned char)buf[14] == '(' && + (unsigned char)buf[15] == 'B' && + (unsigned char)buf[16] == '\0' && + (unsigned char)buf[17] == 0xcc), + "buf=[%02x %02x %02x %02x %02x %02x %02x %02x " + " %02x %02x %02x %02x %02x %02x %02x %02x " + " %02x %02x]", + buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6], buf[7], + buf[8], buf[9], buf[10], buf[11], + buf[12], buf[13], buf[14], buf[15], + buf[16], buf[17]); +} + +ATF_TC_WITHOUT_HEAD(c8rtomb_iso_8859_1_test); +ATF_TC_BODY(c8rtomb_iso_8859_1_test, tc) +{ + size_t n; + + require_lc_ctype("en_US.ISO8859-1"); + + /* Unicode character 'Euro sign'. */ + memset(&s, 0, sizeof(s)); + memset(buf, 0xcc, sizeof(buf)); + ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xe2, &s)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x82, &s)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xac, &s)), (size_t)-1, + "n=%zu", n); + ATF_CHECK_EQ_MSG(errno, EILSEQ, "errno=%d", errno); + ATF_CHECK_EQ_MSG((unsigned char)buf[0], 0xcc, "buf=[%02x]", buf[0]); +} + +ATF_TC_WITHOUT_HEAD(c8rtomb_iso_8859_15_test); +ATF_TC_BODY(c8rtomb_iso_8859_15_test, tc) +{ + size_t n; + + require_lc_ctype("en_US.ISO8859-15"); + + /* Unicode character 'Euro sign'. */ + memset(&s, 0, sizeof(s)); + memset(buf, 0xcc, sizeof(buf)); + ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xe2, &s)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x82, &s)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xac, &s)), 1, "n=%zu", n); + ATF_CHECK_MSG(((unsigned char)buf[0] == 0xa4 && + (unsigned char)buf[1] == 0xcc), + "buf=[%02x %02x]", buf[0], buf[1]); +} + +ATF_TC_WITHOUT_HEAD(c8rtomb_utf_8_test); +ATF_TC_BODY(c8rtomb_utf_8_test, tc) +{ + size_t n; + + require_lc_ctype("en_US.UTF-8"); + + /* Unicode character 'Pile of poo'. */ + memset(&s, 0, sizeof(s)); + memset(buf, 0xcc, sizeof(buf)); + ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xf0, &s)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x9f, &s)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x92, &s)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xa9, &s)), 4, "n=%zu", n); + ATF_CHECK_MSG(((unsigned char)buf[0] == 0xf0 && + (unsigned char)buf[1] == 0x9f && + (unsigned char)buf[2] == 0x92 && + (unsigned char)buf[3] == 0xa9 && + (unsigned char)buf[4] == 0xcc), + "buf=[%02x %02x %02x %02x %02x]", + buf[0], buf[1], buf[2], buf[3], buf[4]); + + /* Invalid code; 'Pile of poo' without the last byte. */ + memset(&s, 0, sizeof(s)); + memset(buf, 0xcc, sizeof(buf)); + ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xf0, &s)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x9f, &s)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x92, &s)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 'A', &s)), (size_t)-1, + "n=%zu", n); + ATF_CHECK_EQ_MSG(errno, EILSEQ, "errno=%d", errno); + ATF_CHECK_EQ_MSG((unsigned char)buf[0], 0xcc, "buf=[%02x]", buf[0]); + + /* Invalid code; 'Pile of poo' without the first byte. */ + memset(&s, 0, sizeof(s)); + memset(buf, 0xcc, sizeof(buf)); + ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x9f, &s)), (size_t)-1, + "n=%zu", n); + ATF_CHECK_EQ_MSG(errno, EILSEQ, "errno=%d", errno); + ATF_CHECK_EQ_MSG((unsigned char)buf[0], 0xcc, "buf=[%02x]", buf[0]); + + /* Incomplete Unicode character 'Pile of poo', interrupted by NUL. */ + memset(&s, 0, sizeof(s)); + memset(buf, 0xcc, sizeof(buf)); + ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xf0, &s)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(buf, '\0', &s)), 1, "n=%zu", n); + ATF_CHECK_MSG(((unsigned char)buf[0] == '\0' && + (unsigned char)buf[1] == 0xcc), + "buf=[%02x %02x]", buf[0], buf[1]); + + memset(&s, 0, sizeof(s)); + memset(buf, 0xcc, sizeof(buf)); + ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xf0, &s)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x9f, &s)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(buf, '\0', &s)), 1, "n=%zu", n); + ATF_CHECK_MSG(((unsigned char)buf[0] == '\0' && + (unsigned char)buf[1] == 0xcc), + "buf=[%02x %02x]", buf[0], buf[1]); + + memset(&s, 0, sizeof(s)); + memset(buf, 0xcc, sizeof(buf)); + ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xf0, &s)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x9f, &s)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x92, &s)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = c8rtomb(buf, '\0', &s)), 1, "n=%zu", n); + ATF_CHECK_MSG(((unsigned char)buf[0] == '\0' && + (unsigned char)buf[1] == 0xcc), + "buf=[%02x %02x]", buf[0], buf[1]); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, c8rtomb_c_locale_test); + ATF_TP_ADD_TC(tp, c8rtomb_iso2022jp_locale_test); + ATF_TP_ADD_TC(tp, c8rtomb_iso_8859_1_test); + ATF_TP_ADD_TC(tp, c8rtomb_iso_8859_15_test); + ATF_TP_ADD_TC(tp, c8rtomb_utf_8_test); + + return (atf_no_error()); +} diff --git a/lib/libc/locale/t_mbrtoc16.c b/lib/libc/locale/t_mbrtoc16.c new file mode 100644 index 000000000000..cac804c84eed --- /dev/null +++ b/lib/libc/locale/t_mbrtoc16.c @@ -0,0 +1,364 @@ +/* $NetBSD: t_mbrtoc16.c,v 1.3 2024/08/20 17:43:09 riastradh Exp $ */ + +/*- + * Copyright (c) 2002 Tim J. Robbins + * All rights reserved. + * + * Copyright (c) 2013 Ed Schouten <ed@FreeBSD.org> + * All rights reserved. + * + * 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. + */ +/* + * Test program for mbrtoc16() as specified by ISO/IEC 9899:2011. + */ + +#include <sys/cdefs.h> +__RCSID("$NetBSD: t_mbrtoc16.c,v 1.3 2024/08/20 17:43:09 riastradh Exp $"); + +#include <errno.h> +#include <inttypes.h> +#include <limits.h> +#include <locale.h> +#include <string.h> +#include <uchar.h> + +#include <atf-c.h> + +static void +require_lc_ctype(const char *locale_name) +{ + char *lc_ctype_set; + + lc_ctype_set = setlocale(LC_CTYPE, locale_name); + if (lc_ctype_set == NULL) + atf_tc_fail("setlocale(LC_CTYPE, \"%s\") failed; errno=%d", + locale_name, errno); + + ATF_REQUIRE_EQ_MSG(strcmp(lc_ctype_set, locale_name), 0, + "lc_ctype_set=%s locale_name=%s", lc_ctype_set, locale_name); +} + +static mbstate_t s; +static char16_t c16; + +ATF_TC_WITHOUT_HEAD(mbrtoc16_c_locale_test); +ATF_TC_BODY(mbrtoc16_c_locale_test, tc) +{ + size_t n; + + require_lc_ctype("C"); + + /* Null wide character, internal state. */ + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "", 1, NULL)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, 0, "c16=U+%"PRIx16, (uint16_t)c16); + + /* Null wide character. */ + memset(&s, 0, sizeof(s)); + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "", 1, &s)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, 0, "c16=U+%"PRIx16, (uint16_t)c16); + + /* Latin letter A, internal state. */ + ATF_CHECK_EQ_MSG((n = mbrtoc16(NULL, 0, 0, NULL)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "A", 1, NULL)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, L'A', "c16=U+%"PRIx16" L'A'=U+%"PRIx16, + (uint16_t)c16, (uint16_t)L'A'); + + /* Latin letter A. */ + memset(&s, 0, sizeof(s)); + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "A", 1, &s)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, L'A', "c16=U+%"PRIx16" L'A'=U+%"PRIx16, + (uint16_t)c16, (uint16_t)L'A'); + + /* Incomplete character sequence. */ + c16 = L'z'; + memset(&s, 0, sizeof(s)); + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "", 0, &s)), (size_t)-2, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, L'z', "c16=U+%"PRIx16" L'z'=U+%"PRIx16, + (uint16_t)c16, (uint16_t)L'z'); + + /* Check that mbrtoc16() doesn't access the buffer when n == 0. */ + c16 = L'z'; + memset(&s, 0, sizeof(s)); + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "", 0, &s)), (size_t)-2, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, L'z', "c16=U+%"PRIx16" L'z'=U+%"PRIx16, + (uint16_t)c16, (uint16_t)L'z'); + + /* Check that mbrtoc16() doesn't read ahead too aggressively. */ + memset(&s, 0, sizeof(s)); + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "AB", 2, &s)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, L'A', "c16=U+%"PRIx16" L'A'=U+%"PRIx16, + (uint16_t)c16, (uint16_t)L'A'); + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "C", 1, &s)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, L'C', "c16=U+%"PRIx16" L'C'=U+%"PRIx16, + (uint16_t)c16, (uint16_t)L'C'); +} + +ATF_TC_WITHOUT_HEAD(mbrtoc16_iso2022jp_locale_test); +ATF_TC_BODY(mbrtoc16_iso2022jp_locale_test, tc) +{ + size_t n; + + require_lc_ctype("ja_JP.ISO-2022-JP"); + + /* Null wide character, internal state. */ + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "", 1, NULL)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, 0, "c16=U+%04"PRIx16, (uint16_t)c16); + + /* Null wide character. */ + memset(&s, 0, sizeof(s)); + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "", 1, &s)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, 0, "c16=U+%04"PRIx16, (uint16_t)c16); + + /* Latin letter A, internal state. */ + ATF_CHECK_EQ_MSG((n = mbrtoc16(NULL, 0, 0, NULL)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "A", 1, NULL)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, L'A', "c16=U+%04"PRIx16" L'A'=U+%04"PRIx16, + (uint16_t)c16, (uint16_t)L'A'); + + /* Latin letter A. */ + memset(&s, 0, sizeof(s)); + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "A", 1, &s)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, L'A', "c16=U+%04"PRIx16" L'A'=U+%04"PRIx16, + (uint16_t)c16, (uint16_t)L'A'); + + /* Incomplete character sequence. */ + c16 = L'z'; + memset(&s, 0, sizeof(s)); + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "", 0, &s)), (size_t)-2, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, L'z', "c16=U+%04"PRIx16" L'z'=U+%04"PRIx16, + (uint16_t)c16, (uint16_t)L'z'); + + /* Check that mbrtoc16() doesn't access the buffer when n == 0. */ + c16 = L'z'; + memset(&s, 0, sizeof(s)); + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "", 0, &s)), (size_t)-2, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, L'z', "c16=U+%04"PRIx16" L'z'=U+%04"PRIx16, + (uint16_t)c16, (uint16_t)L'z'); + + /* Check that mbrtoc16() doesn't read ahead too aggressively. */ + memset(&s, 0, sizeof(s)); + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "AB", 2, &s)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, L'A', "c16=U+%04"PRIx16" L'A'=U+%04"PRIx16, + (uint16_t)c16, (uint16_t)L'A'); + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "C", 1, &s)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, L'C', "c16=U+%04"PRIx16" L'C'=U+%04"PRIx16, + (uint16_t)c16, (uint16_t)L'C'); + + /* Incomplete character sequence (shift sequence only). */ + memset(&s, 0, sizeof(s)); + c16 = 0; + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\x1b(J", 3, &s)), (size_t)-2, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, 0, "c16=U+%04"PRIx16, (uint16_t)c16); + + /* Same as above, but complete (U+00A5 YEN SIGN). */ + memset(&s, 0, sizeof(s)); + c16 = 0; + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\x1b(J\x5c", 4, &s)), 4, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, 0xa5, "c16=U+%04"PRIx16, (uint16_t)c16); + + /* Test restarting behaviour. */ + memset(&s, 0, sizeof(s)); + c16 = 0; + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\x1b(", 2, &s)), (size_t)-2, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, 0, "c16=U+%04"PRIx16, (uint16_t)c16); + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "J\x5c", 2, &s)), 2, "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, 0xa5, "c16=U+%04"PRIx16, (uint16_t)c16); + + /* + * Test shift sequence state in various increments: + * 1. U+0042 LATIN CAPITAL LETTER A + * 2. (shift ISO/IEC 646:JP) U+00A5 YEN SIGN + * 3. U+00A5 YEN SIGN + * 4. (shift JIS X 0208) U+30A2 KATAKANA LETTER A + * 5. U+30A2 KATAKANA LETTER A + * 6. (shift to initial state) U+0000 NUL + */ + memset(&s, 0, sizeof(s)); + c16 = 0; + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "A\x1b(J", 4, &s)), 1, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, L'A', "c16=U+%04"PRIx16, (uint16_t)c16); + c16 = 0; + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\x1b(J", 3, &s)), (size_t)-2, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, 0, "c16=U+%04"PRIx16, (uint16_t)c16); + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\x5c\x5c", 2, &s)), 1, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, 0x00a5, "c16=U+%04"PRIx16, (uint16_t)c16); + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\x5c\x1b$", 3, &s)), 1, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, 0x00a5, "c16=U+%04"PRIx16, (uint16_t)c16); + c16 = 0x1234; + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\x1b", 1, &s)), (size_t)-2, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, 0x1234, "c16=U+%04"PRIx16, (uint16_t)c16); + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "$B\x25\x22", 4, &s)), 4, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, 0x30a2, "c16=U+%04"PRIx16, (uint16_t)c16); + c16 = 0; + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\x25", 1, &s)), (size_t)-2, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, 0, "c16=U+%04"PRIx16, (uint16_t)c16); + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\x22\x1b(B\x00", 5, &s)), 1, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, 0x30a2, "c16=U+%04"PRIx16, (uint16_t)c16); + c16 = 0; + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\x1b(", 2, &s)), (size_t)-2, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, 0, "c16=U+%04"PRIx16, (uint16_t)c16); + c16 = 42; + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "B\x00", 2, &s)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, 0, "c16=U+%04"PRIx16, (uint16_t)c16); +} + +ATF_TC_WITHOUT_HEAD(mbrtoc16_iso_8859_1_test); +ATF_TC_BODY(mbrtoc16_iso_8859_1_test, tc) +{ + size_t n; + + require_lc_ctype("en_US.ISO8859-1"); + + /* Currency sign. */ + memset(&s, 0, sizeof(s)); + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\xa4", 1, &s)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, 0xa4, "c16=U+%"PRIx16, (uint16_t)c16); +} + +ATF_TC_WITHOUT_HEAD(mbrtoc16_iso_8859_15_test); +ATF_TC_BODY(mbrtoc16_iso_8859_15_test, tc) +{ + size_t n; + + require_lc_ctype("en_US.ISO8859-15"); + + /* Euro sign. */ + memset(&s, 0, sizeof(s)); + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\xa4", 1, &s)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, 0x20ac, "c16=U+%"PRIx16, (uint16_t)c16); +} + +ATF_TC_WITHOUT_HEAD(mbrtoc16_utf_8_test); +ATF_TC_BODY(mbrtoc16_utf_8_test, tc) +{ + size_t n; + + require_lc_ctype("en_US.UTF-8"); + + /* Null wide character, internal state. */ + ATF_CHECK_EQ_MSG((n = mbrtoc16(NULL, 0, 0, NULL)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "", 1, NULL)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, 0, "c16=U+%"PRIx16, (uint16_t)c16); + + /* Null wide character. */ + memset(&s, 0, sizeof(s)); + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "", 1, &s)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, 0, "c16=U+%"PRIx16, (uint16_t)c16); + + /* Latin letter A, internal state. */ + ATF_CHECK_EQ_MSG((n = mbrtoc16(NULL, 0, 0, NULL)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "A", 1, NULL)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, L'A', "c16=U+%"PRIx16" L'A'=U+%"PRIx16, + (uint16_t)c16, (uint16_t)L'A'); + + /* Latin letter A. */ + memset(&s, 0, sizeof(s)); + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "A", 1, &s)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, L'A', "c16=U+%"PRIx16" L'A'=U+%"PRIx16, + (uint16_t)c16, (uint16_t)L'A'); + + /* Incomplete character sequence (zero length). */ + c16 = L'z'; + memset(&s, 0, sizeof(s)); + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "", 0, &s)), (size_t)-2, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, L'z', "c16=U+%"PRIx16" L'z'=U+%"PRIx16, + (uint16_t)c16, (uint16_t)L'z'); + + /* Incomplete character sequence (truncated double-byte). */ + memset(&s, 0, sizeof(s)); + c16 = 0; + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\xc3", 1, &s)), (size_t)-2, + "n=%zu", n); + + /* Same as above, but complete. */ + memset(&s, 0, sizeof(s)); + c16 = 0; + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\xc3\x84", 2, &s)), 2, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, 0xc4, "c16=U+%"PRIx16, (uint16_t)c16); + + /* Test restarting behaviour. */ + memset(&s, 0, sizeof(s)); + c16 = 0; + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\xc3", 1, &s)), (size_t)-2, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, 0, "c16=U+%"PRIx16, (uint16_t)c16); + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\xb7", 1, &s)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, 0xf7, "c16=U+%"PRIx16, (uint16_t)c16); + + /* Surrogate pair. */ + memset(&s, 0, sizeof(s)); + c16 = 0; + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\xf0\x9f\x92\xa9", 4, &s)), 4, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, 0xd83d, "c16=U+%"PRIx16, (uint16_t)c16); + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "", 0, &s)), (size_t)-3, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, 0xdca9, "c16=U+%"PRIx16, (uint16_t)c16); + + /* Letter e with acute, precomposed. */ + memset(&s, 0, sizeof(s)); + c16 = 0; + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\xc3\xa9", 2, &s)), 2, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, 0xe9, "c16=U+%"PRIx16, (uint16_t)c16); + + /* Letter e with acute, combined. */ + memset(&s, 0, sizeof(s)); + c16 = 0; + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\x65\xcc\x81", 3, &s)), 1, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, 0x65, "c16=U+%"PRIx16, (uint16_t)c16); + ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\xcc\x81", 2, &s)), 2, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c16, 0x301, "c16=U+%"PRIx16, (uint16_t)c16); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, mbrtoc16_c_locale_test); + ATF_TP_ADD_TC(tp, mbrtoc16_iso2022jp_locale_test); + ATF_TP_ADD_TC(tp, mbrtoc16_iso_8859_1_test); + ATF_TP_ADD_TC(tp, mbrtoc16_iso_8859_15_test); + ATF_TP_ADD_TC(tp, mbrtoc16_utf_8_test); + + return (atf_no_error()); +} diff --git a/lib/libc/locale/t_mbrtoc32.c b/lib/libc/locale/t_mbrtoc32.c new file mode 100644 index 000000000000..8ccc421400a8 --- /dev/null +++ b/lib/libc/locale/t_mbrtoc32.c @@ -0,0 +1,61 @@ +/* $NetBSD: t_mbrtoc32.c,v 1.1 2024/08/15 14:16:34 riastradh Exp $ */ + +/*- + * Copyright (c) 2024 The NetBSD Foundation, Inc. + * All rights reserved. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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> +__RCSID("$NetBSD: t_mbrtoc32.c,v 1.1 2024/08/15 14:16:34 riastradh Exp $"); + +#include <atf-c.h> +#include <locale.h> +#include <uchar.h> + +#include "h_macros.h" + +ATF_TC(mbrtoc32_null); +ATF_TC_HEAD(mbrtoc32_null, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test null string input to mbrtoc32"); +} +ATF_TC_BODY(mbrtoc32_null, tc) +{ + char *locale; + char32_t c32; + mbstate_t ps = {0}; + size_t n; + + REQUIRE_LIBC((locale = setlocale(LC_ALL, "C")), NULL); + ATF_REQUIRE_EQ_MSG(strcmp(locale, "C"), 0, "locale=%s", locale); + + ATF_CHECK_EQ_MSG((n = mbrtoc32(&c32, NULL, 0, &ps)), 0, "n=%zu", n); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, mbrtoc32_null); + return atf_no_error(); +} diff --git a/lib/libc/locale/t_mbrtoc8.c b/lib/libc/locale/t_mbrtoc8.c new file mode 100644 index 000000000000..760f19a62f08 --- /dev/null +++ b/lib/libc/locale/t_mbrtoc8.c @@ -0,0 +1,415 @@ +/* $NetBSD: t_mbrtoc8.c,v 1.3 2024/08/20 17:43:09 riastradh Exp $ */ + +/*- + * Copyright (c) 2002 Tim J. Robbins + * All rights reserved. + * + * Copyright (c) 2013 Ed Schouten <ed@FreeBSD.org> + * All rights reserved. + * + * 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. + */ +/* + * Test program for mbrtoc8() as specified by C23. + */ + +#include <sys/cdefs.h> +__RCSID("$NetBSD: t_mbrtoc8.c,v 1.3 2024/08/20 17:43:09 riastradh Exp $"); + +#include <errno.h> +#include <inttypes.h> +#include <limits.h> +#include <locale.h> +#include <string.h> +#include <uchar.h> + +#include <atf-c.h> + +static void +require_lc_ctype(const char *locale_name) +{ + char *lc_ctype_set; + + lc_ctype_set = setlocale(LC_CTYPE, locale_name); + if (lc_ctype_set == NULL) + atf_tc_fail("setlocale(LC_CTYPE, \"%s\") failed; errno=%d", + locale_name, errno); + + ATF_REQUIRE_EQ_MSG(strcmp(lc_ctype_set, locale_name), 0, + "lc_ctype_set=%s locale_name=%s", lc_ctype_set, locale_name); +} + +static mbstate_t s; +static char8_t c8; + +ATF_TC_WITHOUT_HEAD(mbrtoc8_c_locale_test); +ATF_TC_BODY(mbrtoc8_c_locale_test, tc) +{ + size_t n; + + require_lc_ctype("C"); + + /* Null wide character, internal state. */ + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 1, NULL)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0, "c8=0x%"PRIx8, (uint8_t)c8); + + /* Null wide character. */ + memset(&s, 0, sizeof(s)); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 1, &s)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0, "c8=0x%"PRIx8, (uint8_t)c8); + + /* Latin letter A, internal state. */ + ATF_CHECK_EQ_MSG((n = mbrtoc8(NULL, 0, 0, NULL)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "A", 1, NULL)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 'A', "c8=0x%"PRIx8" 'A'=0x%"PRIx8, + (uint8_t)c8, (uint8_t)'A'); + + /* Latin letter A. */ + memset(&s, 0, sizeof(s)); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "A", 1, &s)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 'A', "c8=0x%"PRIx8" 'A'=0x%"PRIx8, + (uint8_t)c8, (uint8_t)'A'); + + /* Incomplete character sequence. */ + c8 = 'z'; + memset(&s, 0, sizeof(s)); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-2, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 'z', "c8=0x%"PRIx8" 'z'=0x%"PRIx8, + (uint8_t)c8, (uint8_t)'z'); + + /* Check that mbrtoc8() doesn't access the buffer when n == 0. */ + c8 = 'z'; + memset(&s, 0, sizeof(s)); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-2, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 'z', "c8=0x%"PRIx8" 'z'=0x%"PRIx8, + (uint8_t)c8, (uint8_t)'z'); + + /* Check that mbrtoc8() doesn't read ahead too aggressively. */ + memset(&s, 0, sizeof(s)); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "AB", 2, &s)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 'A', "c8=0x%"PRIx8" 'A'=0x%"PRIx8, + (uint8_t)c8, (uint8_t)'A'); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "C", 1, &s)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 'C', "c8=0x%"PRIx8" 'C'=0x%"PRIx8, + (uint8_t)c8, (uint8_t)'C'); + +} + +ATF_TC_WITHOUT_HEAD(mbrtoc8_iso2022jp_locale_test); +ATF_TC_BODY(mbrtoc8_iso2022jp_locale_test, tc) +{ + size_t n; + + require_lc_ctype("ja_JP.ISO-2022-JP"); + + /* Null wide character, internal state. */ + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 1, NULL)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0, "c8=0x%"PRIx8, (uint8_t)c8); + + /* Null wide character. */ + memset(&s, 0, sizeof(s)); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 1, &s)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0, "c8=0x%"PRIx8, (uint8_t)c8); + + /* Latin letter A, internal state. */ + ATF_CHECK_EQ_MSG((n = mbrtoc8(NULL, 0, 0, NULL)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "A", 1, NULL)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 'A', "c8=0x%"PRIx8" 'A'=0x%"PRIx8, + (uint8_t)c8, (uint8_t)'A'); + + /* Latin letter A. */ + memset(&s, 0, sizeof(s)); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "A", 1, &s)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 'A', "c8=0x%"PRIx8" 'A'=0x%"PRIx8, + (uint8_t)c8, (uint8_t)'A'); + + /* Incomplete character sequence. */ + c8 = 'z'; + memset(&s, 0, sizeof(s)); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-2, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 'z', "c8=0x%"PRIx8" 'z'=0x%"PRIx8, + (uint8_t)c8, (uint8_t)'z'); + + /* Check that mbrtoc8() doesn't access the buffer when n == 0. */ + c8 = 'z'; + memset(&s, 0, sizeof(s)); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-2, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 'z', "c8=0x%"PRIx8" 'z'=0x%"PRIx8, + (uint8_t)c8, (uint8_t)'z'); + + /* Check that mbrtoc8() doesn't read ahead too aggressively. */ + memset(&s, 0, sizeof(s)); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "AB", 2, &s)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 'A', "c8=0x%"PRIx8" 'A'=0x%"PRIx8, + (uint8_t)c8, (uint8_t)'A'); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "C", 1, &s)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 'C', "c8=0x%"PRIx8" 'C'=0x%"PRIx8, + (uint8_t)c8, (uint8_t)'C'); + + /* Incomplete character sequence (shift sequence only). */ + memset(&s, 0, sizeof(s)); + c8 = 0; + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x1b(J", 3, &s)), (size_t)-2, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0, "c8=0x%"PRIx8, (uint8_t)c8); + + /* Same as above, but complete (U+00A5 YEN SIGN). */ + memset(&s, 0, sizeof(s)); + c8 = 0; + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x1b(J\x5c", 4, &s)), 4, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0xc2, "c8=0x%"PRIx8, (uint8_t)c8); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-3, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0xa5, "c8=0x%"PRIx8, (uint8_t)c8); + + /* Test restarting behaviour. */ + memset(&s, 0, sizeof(s)); + c8 = 0; + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x1b(", 2, &s)), (size_t)-2, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0, "c8=0x%"PRIx8, (uint8_t)c8); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "J\x5c", 2, &s)), 2, "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0xc2, "c8=0x%"PRIx8, (uint8_t)c8); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-3, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0xa5, "c8=0x%"PRIx8, (uint8_t)c8); + + /* + * Test shift sequence state in various increments: + * 1. U+0042 LATIN CAPITAL LETTER A + * 2. (shift ISO/IEC 646:JP) U+00A5 YEN SIGN + * 3. U+00A5 YEN SIGN + * 4. (shift JIS X 0208) U+30A2 KATAKANA LETTER A + * 5. U+30A2 KATAKANA LETTER A + * 6. (shift to initial state) U+0000 NUL + */ + memset(&s, 0, sizeof(s)); + c8 = 0; + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "A\x1b(J", 4, &s)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 'A', "c8=0x%"PRIx8, (uint8_t)c8); + c8 = 0; + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x1b(J", 3, &s)), (size_t)-2, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0, "c8=0x%"PRIx8, (uint8_t)c8); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x5c\x5c", 2, &s)), 1, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0xc2, "c8=0x%"PRIx8, (uint8_t)c8); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x5c\x1b$", 3, &s)), (size_t)-3, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0xa5, "c8=0x%"PRIx8, (uint8_t)c8); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x5c\x1b$", 3, &s)), 1, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0xc2, "c8=0x%"PRIx8, (uint8_t)c8); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x1b", 1, &s)), (size_t)-3, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0xa5, "c8=0x%"PRIx8, (uint8_t)c8); + c8 = 0xff; + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x1b", 1, &s)), (size_t)-2, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0xff, "c8=0x%"PRIx8, (uint8_t)c8); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "$B\x25\x22", 4, &s)), 4, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0xe3, "c8=0x%"PRIx8, (uint8_t)c8); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x25", 1, &s)), (size_t)-3, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0x82, "c8=0x%"PRIx8, (uint8_t)c8); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x25", 1, &s)), (size_t)-3, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0xa2, "c8=0x%"PRIx8, (uint8_t)c8); + c8 = 0; + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x25", 1, &s)), (size_t)-2, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0, "c8=0x%"PRIx8, (uint8_t)c8); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x22\x1b(B\x00", 5, &s)), 1, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0xe3, "c8=0x%"PRIx8, (uint8_t)c8); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x1b(", 2, &s)), (size_t)-3, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0x82, "c8=0x%"PRIx8, (uint8_t)c8); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x1b(", 2, &s)), (size_t)-3, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0xa2, "c8=0x%"PRIx8, (uint8_t)c8); + c8 = 0; + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x1b(", 2, &s)), (size_t)-2, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0, "c8=0x%"PRIx8, (uint8_t)c8); + c8 = 42; + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "B\x00", 2, &s)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0, "c8=0x%"PRIx8, (uint8_t)c8); +} + +ATF_TC_WITHOUT_HEAD(mbrtoc8_iso_8859_1_test); +ATF_TC_BODY(mbrtoc8_iso_8859_1_test, tc) +{ + size_t n; + + require_lc_ctype("en_US.ISO8859-1"); + + /* Currency sign. */ + memset(&s, 0, sizeof(s)); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\xa4", 1, &s)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0xc2, "c8=0x%"PRIx8, (uint8_t)c8); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-3, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0xa4, "c8=0x%"PRIx8, (uint8_t)c8); +} + +ATF_TC_WITHOUT_HEAD(mbrtoc8_iso_8859_15_test); +ATF_TC_BODY(mbrtoc8_iso_8859_15_test, tc) +{ + size_t n; + + require_lc_ctype("en_US.ISO8859-15"); + + /* Euro sign. */ + memset(&s, 0, sizeof(s)); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\xa4", 1, &s)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0xe2, "c8=0x%"PRIx8, (uint8_t)c8); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-3, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0x82, "c8=0x%"PRIx8, (uint8_t)c8); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-3, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0xac, "c8=0x%"PRIx8, (uint8_t)c8); +} + +ATF_TC_WITHOUT_HEAD(mbrtoc8_utf_8_test); +ATF_TC_BODY(mbrtoc8_utf_8_test, tc) +{ + size_t n; + + require_lc_ctype("en_US.UTF-8"); + + /* Null wide character, internal state. */ + ATF_CHECK_EQ_MSG((n = mbrtoc8(NULL, 0, 0, NULL)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 1, NULL)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0, "c8=0x%"PRIx8, (uint8_t)c8); + + /* Null wide character. */ + memset(&s, 0, sizeof(s)); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 1, &s)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0, "c8=0x%"PRIx8, (uint8_t)c8); + + /* Latin letter A, internal state. */ + ATF_CHECK_EQ_MSG((n = mbrtoc8(NULL, 0, 0, NULL)), 0, "n=%zu", n); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "A", 1, NULL)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 'A', "c8=0x%"PRIx8" 'A'=0x%"PRIx8, + (uint8_t)c8, (uint8_t)'A'); + + /* Latin letter A. */ + memset(&s, 0, sizeof(s)); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "A", 1, &s)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 'A', "c8=0x%"PRIx8" 'A'=0x%"PRIx8, + (uint8_t)c8, (uint8_t)'A'); + + /* Incomplete character sequence (zero length). */ + c8 = 'z'; + memset(&s, 0, sizeof(s)); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-2, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 'z', "c8=0x%"PRIx8" 'z'=0x%"PRIx8, + (uint8_t)c8, (uint8_t)'z'); + + /* Incomplete character sequence (truncated double-byte). */ + memset(&s, 0, sizeof(s)); + c8 = 0; + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\xc3", 1, &s)), (size_t)-2, + "n=%zu", n); + + /* Same as above, but complete. */ + memset(&s, 0, sizeof(s)); + c8 = 0; + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\xc3\x84", 2, &s)), 2, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0xc3, "c8=0x%"PRIx8, (uint8_t)c8); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-3, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0x84, "c8=0x%"PRIx8, (uint8_t)c8); + + /* Test restarting behaviour. */ + memset(&s, 0, sizeof(s)); + c8 = 0; + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\xc3", 1, &s)), (size_t)-2, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0, "c8=0x%"PRIx8, (uint8_t)c8); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\xb7", 1, &s)), 1, "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0xc3, "c8=0x%"PRIx8, (uint8_t)c8); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-3, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0xb7, "c8=0x%"PRIx8, (uint8_t)c8); + + /* Four-byte sequence. */ + memset(&s, 0, sizeof(s)); + c8 = 0; + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\xf0\x9f\x92\xa9", 4, &s)), 4, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0xf0, "c8=0x%"PRIx8, (uint8_t)c8); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-3, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0x9f, "c8=0x%"PRIx8, (uint8_t)c8); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-3, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0x92, "c8=0x%"PRIx8, (uint8_t)c8); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-3, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0xa9, "c8=0x%"PRIx8, (uint8_t)c8); + + /* Letter e with acute, precomposed. */ + memset(&s, 0, sizeof(s)); + c8 = 0; + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\xc3\xa9", 2, &s)), 2, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0xc3, "c8=0x%"PRIx8, (uint8_t)c8); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-3, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0xa9, "c8=0x%"PRIx8, (uint8_t)c8); + + /* Letter e with acute, combined. */ + memset(&s, 0, sizeof(s)); + c8 = 0; + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x65\xcc\x81", 3, &s)), 1, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0x65, "c8=0x%"PRIx8, (uint8_t)c8); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\xcc\x81", 2, &s)), 2, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0xcc, "c8=0x%"PRIx8, (uint8_t)c8); + ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-3, + "n=%zu", n); + ATF_CHECK_EQ_MSG(c8, 0x81, "c8=0x%"PRIx8, (uint8_t)c8); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, mbrtoc8_c_locale_test); + ATF_TP_ADD_TC(tp, mbrtoc8_iso2022jp_locale_test); + ATF_TP_ADD_TC(tp, mbrtoc8_iso_8859_1_test); + ATF_TP_ADD_TC(tp, mbrtoc8_iso_8859_15_test); + ATF_TP_ADD_TC(tp, mbrtoc8_utf_8_test); + + return (atf_no_error()); +} diff --git a/lib/libc/locale/t_uchar.c b/lib/libc/locale/t_uchar.c new file mode 100644 index 000000000000..66e4830fb88a --- /dev/null +++ b/lib/libc/locale/t_uchar.c @@ -0,0 +1,81 @@ +/* $NetBSD: t_uchar.c,v 1.3 2024/10/14 06:02:14 rillig Exp $ */ + +/*- + * Copyright (c) 2024 The NetBSD Foundation, Inc. + * All rights reserved. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 <uchar.h> first to verify it declares everything we need. + */ +#include <uchar.h> +typedef mbstate_t nbtest_mbstate_t; +typedef size_t nbtest_size_t; +typedef char8_t nbtest_char8_t; +typedef char16_t nbtest_char16_t; +typedef char32_t nbtest_char32_t; +static size_t (*nbtest_mbrtoc8)(char8_t *restrict, const char *restrict, + size_t, mbstate_t *restrict) __unused = &mbrtoc8; +static size_t (*nbtest_c8rtomb)(char *restrict, char8_t, + mbstate_t *restrict) __unused = &c8rtomb; +static size_t (*nbtest_mbrtoc16)(char16_t *restrict, const char *restrict, + size_t, mbstate_t *restrict) __unused = &mbrtoc16; +static size_t (*nbtest_c16rtomb)(char *restrict, char16_t, + mbstate_t *restrict) __unused = &c16rtomb; +static size_t (*nbtest_mbrtoc32)(char32_t *restrict, const char *restrict, + size_t, mbstate_t *restrict) __unused = mbrtoc32; +static size_t (*nbtest_c32rtomb)(char *restrict, char32_t, + mbstate_t *restrict) __unused = &c32rtomb; + +#include <sys/cdefs.h> +__RCSID("$NetBSD: t_uchar.c,v 1.3 2024/10/14 06:02:14 rillig Exp $"); + +#include <atf-c.h> +#include <stdint.h> + +ATF_TC(uchartypes); +ATF_TC_HEAD(uchartypes, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test <uchar.h> types are reasonable"); +} +ATF_TC_BODY(uchartypes, tc) +{ + + ATF_CHECK_EQ_MSG(sizeof(char8_t), sizeof(unsigned char), + "char8_t %zu, unsigned char %zu", + sizeof(char8_t), sizeof(unsigned char)); + ATF_CHECK_EQ_MSG(sizeof(char16_t), sizeof(uint_least16_t), + "char16_t %zu, uint_least16_t %zu", + sizeof(char16_t), sizeof(uint_least16_t)); + ATF_CHECK_EQ_MSG(sizeof(char32_t), sizeof(uint_least32_t), + "char32_t %zu, uint_least32_t %zu", + sizeof(char32_t), sizeof(uint_least32_t)); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, uchartypes); + return atf_no_error(); +} diff --git a/lib/libc/misc/t_vis.c b/lib/libc/misc/t_vis.c new file mode 100644 index 000000000000..a712ffa2f9d2 --- /dev/null +++ b/lib/libc/misc/t_vis.c @@ -0,0 +1,165 @@ +/* $NetBSD: t_vis.c,v 1.2 2025/11/16 12:43:25 nia Exp $ */ + +/*- + * Copyright (c) 2025 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Nia Alarie. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 <string.h> +#include <stdio.h> +#include "vis_types.h" +#include "vis_proto.h" + +ATF_TC(vis_test_addsub); + +ATF_TC_HEAD(vis_test_addsub, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test 32-bit packed add/subtract"); +} + +ATF_TC_BODY(vis_test_addsub, tc) +{ + vis_d64 v1, v2, v3; + vis_f32 f1, f2; + vis_u32 u1, u2; + + v1 = vis_to_double(8, 16); + v2 = vis_to_double(16, 8); + + v3 = vis_fpadd32(v1, v2); + + f1 = vis_read_lo(v3); + memcpy(&u1, &f1, sizeof(f1)); + f2 = vis_read_hi(v3); + memcpy(&u2, &f2, sizeof(f2)); + + ATF_REQUIRE(u1 == 24 && u2 == 24); + + v2 = vis_to_double(4, 4); + v3 = vis_fpsub32(v3, v2); + + f1 = vis_read_lo(v3); + memcpy(&u1, &f1, sizeof(f1)); + f2 = vis_read_hi(v3); + memcpy(&u2, &f2, sizeof(f2)); + + ATF_REQUIRE(u1 == 20 && u2 == 20); +} + +ATF_TC(vis_test_bitwise); + +ATF_TC_HEAD(vis_test_bitwise, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test 32-bit packed bitwise"); +} + +ATF_TC_BODY(vis_test_bitwise, tc) +{ + static vis_u8 testbytes1[8] = { 1, 0, 1, 1, 1, 0, 1, 1 }; + static vis_u8 testbytes2[8] = { 1, 1, 0, 1, 1, 1, 0, 1 }; + static vis_u8 test_and[8] = { 1, 0, 0, 1, 1, 0, 0, 1 }; + static vis_u8 test_or[8] = { 1, 1, 1, 1, 1, 1, 1, 1 }; + static vis_u8 test_zero[8] = { 0 }; + static vis_u64 test_ones = 0xffffffffffffffff; + double v1, v2, v3; + + memcpy(&v1, testbytes1, sizeof(v1)); + memcpy(&v2, testbytes2, sizeof(v2)); + + v3 = vis_fand(v1, v2); + + ATF_REQUIRE(memcmp(&v3, test_and, sizeof(v3)) == 0); + + v3 = vis_fone(); + + ATF_REQUIRE(memcmp(&v3, &test_ones, sizeof(v3)) == 0); + + v3 = vis_for(v1, v2); + + ATF_REQUIRE(memcmp(&v3, test_or, sizeof(v3)) == 0); + + v3 = vis_fzero(); + + ATF_REQUIRE(memcmp(&v3, test_zero, sizeof(v3)) == 0); +} + +ATF_TC(vis_test_fcmpeq16); + +ATF_TC_HEAD(vis_test_fcmpeq16, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test 16-bit packed compare"); +} + +ATF_TC_BODY(vis_test_fcmpeq16, tc) +{ + static vis_u16 testshort1[4] = { 16000, 16000, 16000, 16000 }; + static vis_u16 testshort2[4] = { 32000, 16000, 32000, 16000 }; + static vis_u16 testshort3[4] = { 48000, 48000, 48000, 48000 }; + vis_d64 v1, v2, v3; + + memcpy(&v1, testshort1, sizeof(v1)); + memcpy(&v2, testshort2, sizeof(v2)); + memcpy(&v3, testshort3, sizeof(v3)); + + ATF_REQUIRE((!!vis_fcmpeq16(v1, v2)) != 0); + ATF_REQUIRE((!!vis_fcmpeq16(v1, v3)) == 0); + ATF_REQUIRE((!!vis_fcmpne16(v1, v3)) != 0); +} + +ATF_TC(vis_test_fcmpeq32); + +ATF_TC_HEAD(vis_test_fcmpeq32, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test 32-bit packed compare"); +} + +ATF_TC_BODY(vis_test_fcmpeq32, tc) +{ + static vis_u32 testlong1[2] = { 16000, 16000 }; + static vis_u32 testlong2[2] = { 32000, 16000 }; + static vis_u32 testlong3[2] = { 48000, 48000 }; + vis_d64 v1, v2, v3; + + memcpy(&v1, testlong1, sizeof(v1)); + memcpy(&v2, testlong2, sizeof(v2)); + memcpy(&v3, testlong3, sizeof(v3)); + + ATF_REQUIRE((!!vis_fcmpeq32(v1, v2)) != 0); + ATF_REQUIRE((!!vis_fcmpeq32(v1, v3)) == 0); + ATF_REQUIRE((!!vis_fcmpne32(v1, v3)) != 0); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, vis_test_addsub); + ATF_TP_ADD_TC(tp, vis_test_bitwise); + ATF_TP_ADD_TC(tp, vis_test_fcmpeq16); + ATF_TP_ADD_TC(tp, vis_test_fcmpeq32); + + return atf_no_error(); +} diff --git a/lib/libc/regex/t_regex_binary.c b/lib/libc/regex/t_regex_binary.c new file mode 100644 index 000000000000..0c3d8cb9b908 --- /dev/null +++ b/lib/libc/regex/t_regex_binary.c @@ -0,0 +1,80 @@ +/* $NetBSD: t_regex_binary.c,v 1.1 2025/01/01 18:13:48 christos Exp $ */ + +/*- + * Copyright (c) 2024 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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> +__RCSID("$NetBSD: t_regex_binary.c,v 1.1 2025/01/01 18:13:48 christos Exp $"); + +#include <atf-c.h> +#include <regex.h> + +ATF_TC(negative_ranges); +ATF_TC_HEAD(negative_ranges, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test negative ranges compilation"); +} +ATF_TC_BODY(negative_ranges, tc) +{ + regex_t re; + char msg[1024]; + int e; + + if ((e = regcomp(&re, "[\xe0-\xf1][\xa0-\xd1].*", REG_EXTENDED)) != 0) { + regerror(e, &re, msg, sizeof(msg)); + ATF_REQUIRE_MSG(0, "regcomp failed %s", msg); + } +} + +ATF_TC(negative_char); +ATF_TC_HEAD(negative_char, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test negative char in braces compilation"); +} +ATF_TC_BODY(negative_char, tc) +{ + regex_t re; + char msg[1024]; + int e; + + /* PR/58910 */ + if ((e = regcomp(&re, ": j:[]j:[]j:[\xd9j:[]", REG_EXTENDED)) != 0) { + regerror(e, &re, msg, sizeof(msg)); + ATF_REQUIRE_MSG(0, "regcomp failed %s", msg); + } +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, negative_ranges); + ATF_TP_ADD_TC(tp, negative_char); + return atf_no_error(); +} diff --git a/lib/libc/setjmp/t_sigstack.c b/lib/libc/setjmp/t_sigstack.c new file mode 100644 index 000000000000..ea827da9545c --- /dev/null +++ b/lib/libc/setjmp/t_sigstack.c @@ -0,0 +1,389 @@ +/* $NetBSD: t_sigstack.c,v 1.25 2025/05/12 14:46:19 christos Exp $ */ + +/*- + * Copyright (c) 2024 The NetBSD Foundation, Inc. + * All rights reserved. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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> +__RCSID("$NetBSD: t_sigstack.c,v 1.25 2025/05/12 14:46:19 christos Exp $"); + +#include <dlfcn.h> +#include <setjmp.h> +#include <signal.h> +#include <stddef.h> +#include <stdlib.h> +#include <ucontext.h> + +#include "h_macros.h" + +struct sigaltstack ss[3]; +jmp_buf jmp; +sigjmp_buf sigjmp; +unsigned nentries; +const char *bailname; +void (*bailfn)(void) __dead; + +/* + * Optional compat13 functions from when sigcontext was expanded. + * Fortunately the only change visible to the caller is that the size + * of jmp_buf increased, so we can always use the old symbols with new + * jmp_buf arrays. + */ +int (*compat13_sigsetjmp)(sigjmp_buf, int); +void (*compat13_siglongjmp)(sigjmp_buf, int) __dead; +int (*compat13_setjmp)(jmp_buf); +void (*compat13_longjmp)(jmp_buf, int) __dead; + +/* + * compatsigsys(signo) + * + * Signal handler for SIGSYS in case compat_13_sigreturn13 is not + * implemented by the kernel -- we will just skip the test in that + * case. + */ +static void +compatsigsys(int signo) +{ + + atf_tc_skip("no compat syscalls to test"); +} + +static void +compatsetup(void) +{ + + /* + * Grab the libc library symbols if available. + */ + if ((compat13_sigsetjmp = dlsym(RTLD_SELF, "sigsetjmp")) == NULL || + (compat13_siglongjmp = dlsym(RTLD_SELF, "siglongjmp")) == NULL || + (compat13_setjmp = dlsym(RTLD_SELF, "setjmp")) == NULL || + (compat13_longjmp = dlsym(RTLD_SELF, "longjmp")) == NULL) + atf_tc_skip("no compat functions to test"); + + /* + * Arrange for SIGSYS to skip the test -- this happens if the + * libc stub has the function, but the kernel isn't built with + * support for the compat13 sigreturn syscall for longjmp. + */ + REQUIRE_LIBC(signal(SIGSYS, &compatsigsys), SIG_ERR); +} + +static void +on_sigusr1(int signo, siginfo_t *si, void *ctx) +{ + ucontext_t *uc = ctx; + void *sp = (void *)(uintptr_t)_UC_MACHINE_SP(uc); + void *fp = __builtin_frame_address(0); + struct sigaltstack *ssp; + + /* + * Ensure we haven't re-entered the signal handler too many + * times. We should enter only twice. + */ + ATF_REQUIRE_MSG(nentries < 2, + "%u recursive signal handler entries is too many in this test", + nentries + 1); + + /* + * Ensure that the signal handler was called in the alternate + * signal stack. + */ + ssp = &ss[nentries]; + ATF_REQUIRE_MSG((fp >= ssp->ss_sp && + fp < (void *)((char *)ssp->ss_sp + ssp->ss_size)), + "sigaltstack failed to take effect on entry %u --" + " signal handler's frame pointer %p doesn't lie in sigaltstack" + " [%p, %p), size 0x%zx", + nentries, + fp, ssp->ss_sp, (char *)ssp->ss_sp + ssp->ss_size, ssp->ss_size); + + /* + * Ensure that if we enter the signal handler, we are entering + * it from the original stack, not from any of the alternate + * signal stacks. + */ + for (ssp = &ss[0]; ssp < &ss[__arraycount(ss)]; ssp++) { + ATF_REQUIRE_MSG((sp < ssp->ss_sp || + sp >= (void *)((char *)ssp->ss_sp + ssp->ss_size)), + "%s failed to restore stack" + " before allowing signal on entry %u --" + " interrupted stack pointer %p lies in sigaltstack %zd" + " [%p, %p), size 0x%zx", + bailname, + nentries, + sp, ssp - ss, + ssp->ss_sp, (char *)ssp->ss_sp + ssp->ss_size, + ssp->ss_size); + } + + /* + * First time through, we want to test whether longjmp restores + * the signal mask first, or restores the stack pointer first. + * The signal should be blocked at this point, so we re-raise + * the signal to queue it up for delivery as soon as it is + * unmasked -- which should wait until the stack pointer has + * been restored in longjmp. + */ + if (nentries++ == 0) + RL(raise(SIGUSR1)); + + /* + * Set up the next sigaltstack. We can't reuse the current one + * for the next signal handler re-entry until the system clears + * the SS_ONSTACK process state -- which normal return from + * signal handler does, but which longjmp does not. So to keep + * it simple (ha), we just use another sigaltstack. + */ + RL(sigaltstack(&ss[nentries], NULL)); + + /* + * Jump back to the original context. + */ + (*bailfn)(); +} + +static void +go(const char *name, void (*fn)(void) __dead) +{ + struct sigaction sa; + unsigned i; + + bailname = name; + bailfn = fn; + + /* + * Allocate a stack for the signal handler to run in, and + * configure the system to use the first one. + * + * XXX Should maybe use a guard page but this is simpler. + */ + for (i = 0; i < __arraycount(ss); i++) { + ss[i].ss_size = SIGSTKSZ; + REQUIRE_LIBC(ss[i].ss_sp = malloc(ss[i].ss_size), NULL); + } + RL(sigaltstack(&ss[0], NULL)); + + /* + * Set up a test signal handler for SIGUSR1. Allow all + * signals, except SIGUSR1 (which is masked by default) -- that + * way we don't inadvertently obscure weird crashes in the + * signal handler. + * + * Set SA_SIGINFO so the system will pass siginfo -- and, more + * to the point, ucontext, so the signal handler can determine + * the stack pointer of the logic it interrupted. + * + * Set SA_ONSTACK so the system will use the alternate signal + * stack to call the signal handler -- that way, it can tell + * whether the stack was restored before the second time + * around. + */ + memset(&sa, 0, sizeof(sa)); + sa.sa_sigaction = &on_sigusr1; + RL(sigemptyset(&sa.sa_mask)); + sa.sa_flags = SA_SIGINFO|SA_ONSTACK; + RL(sigaction(SIGUSR1, &sa, NULL)); + + /* + * Raise the signal to enter the signal handler the first time. + */ + RL(raise(SIGUSR1)); + + /* + * If we ever reach this point, something went seriously wrong. + */ + atf_tc_fail("unreachable"); +} + +static void __dead +bail_longjmp(void) +{ + + longjmp(jmp, 1); +} + +ATF_TC(setjmp); +ATF_TC_HEAD(setjmp, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test longjmp restores stack first, then signal mask"); +} +ATF_TC_BODY(setjmp, tc) +{ + +#if defined __ia64__ + atf_tc_expect_fail("PR lib/57946:" + " longjmp fails to restore stack first before" + " restoring signal mask on most architectures"); +#endif + + /* + * Set up a return point for the signal handler: when the + * signal handler does longjmp(jmp, 1), it comes flying out of + * here. + */ + if (setjmp(jmp) == 1) + return; + + /* + * Run the test with longjmp. + */ + go("longjmp", &bail_longjmp); +} + +static void __dead +bail_compat13_longjmp(void) +{ + + (*compat13_longjmp)(jmp, 1); +} + +ATF_TC(compat13_setjmp); +ATF_TC_HEAD(compat13_setjmp, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test compat13 longjmp restores stack first, then signal mask"); +} +ATF_TC_BODY(compat13_setjmp, tc) +{ + + compatsetup(); + +#if defined __arm__ || defined __i386__ || defined __sh3__ +#ifndef __arm__ /* will be exposed once PR 59351 is fixed */ + atf_tc_expect_fail("PR lib/57946:" + " longjmp fails to restore stack first before" + " restoring signal mask on most architectures"); +#endif +#endif +#ifdef __arm__ + atf_tc_expect_signal(-1, "PR port-arm/59351: compat_setjmp is busted"); +#endif + + /* + * Set up a return point for the signal handler: when the + * signal handler does (*compat13_longjmp)(jmp, 1), it comes + * flying out of here. + */ + if ((*compat13_setjmp)(jmp) == 1) + return; + + /* + * Run the test with compat13_longjmp. + */ + go("longjmp", &bail_compat13_longjmp); +} + +static void __dead +bail_siglongjmp(void) +{ + + siglongjmp(sigjmp, 1); +} + +ATF_TC(sigsetjmp); +ATF_TC_HEAD(sigsetjmp, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test siglongjmp restores stack first, then signal mask"); +} +ATF_TC_BODY(sigsetjmp, tc) +{ + +#if defined __ia64__ + atf_tc_expect_fail("PR lib/57946:" + " longjmp fails to restore stack first before" + " restoring signal mask on most architectures"); +#endif + + /* + * Set up a return point for the signal handler: when the + * signal handler does siglongjmp(sigjmp, 1), it comes flying + * out of here. + */ + if (sigsetjmp(sigjmp, /*savesigmask*/1) == 1) + return; + + /* + * Run the test with siglongjmp. + */ + go("siglongjmp", &bail_siglongjmp); +} + +static void __dead +bail_compat13_siglongjmp(void) +{ + + (*compat13_siglongjmp)(sigjmp, 1); +} + +ATF_TC(compat13_sigsetjmp); +ATF_TC_HEAD(compat13_sigsetjmp, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test compat13 siglongjmp restores stack first," + " then signal mask"); +} +ATF_TC_BODY(compat13_sigsetjmp, tc) +{ + + compatsetup(); + +#if defined __arm__ || defined __i386__ || defined __sh3__ +#ifndef __arm__ /* will be exposed once PR 59351 is fixed */ + atf_tc_expect_fail("PR lib/57946:" + " longjmp fails to restore stack first before" + " restoring signal mask on most architectures"); +#endif +#endif +#ifdef __arm__ + atf_tc_expect_signal(-1, "PR port-arm/59351: compat_setjmp is busted"); +#endif + + /* + * Set up a return point for the signal handler: when the + * signal handler does (*compat13_siglongjmp)(sigjmp, 1), it + * comes flying out of here. + */ + if ((*compat13_sigsetjmp)(sigjmp, /*savesigmask*/1) == 1) + return; + + /* + * Run the test with compat13_siglongjmp. + */ + go("siglongjmp", &bail_compat13_siglongjmp); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, compat13_setjmp); + ATF_TP_ADD_TC(tp, compat13_sigsetjmp); + ATF_TP_ADD_TC(tp, setjmp); + ATF_TP_ADD_TC(tp, sigsetjmp); + + return atf_no_error(); +} diff --git a/lib/libc/ssp/h_getcwd2.c b/lib/libc/ssp/h_getcwd2.c new file mode 100644 index 000000000000..86cfd09f2bb0 --- /dev/null +++ b/lib/libc/ssp/h_getcwd2.c @@ -0,0 +1,18 @@ +#include <err.h> +#include <errno.h> +#include <unistd.h> + +char* +getcwd(char* buf, size_t buflen) +{ + errno = ENOSYS; + return NULL; +} +int +main(void) +{ + char buf[256]; + if (getcwd(buf, sizeof buf) == NULL) + err(1, "getcwd failed"); + return 0; +} diff --git a/lib/libc/stdlib/h_sort.c b/lib/libc/stdlib/h_sort.c new file mode 100644 index 000000000000..1e5f4b3b0798 --- /dev/null +++ b/lib/libc/stdlib/h_sort.c @@ -0,0 +1,225 @@ +/* $NetBSD: h_sort.c,v 1.3 2025/03/02 23:11:19 riastradh Exp $ */ + +/*- + * Copyright (c) 2025 The NetBSD Foundation, Inc. + * All rights reserved. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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> +__RCSID("$NetBSD: h_sort.c,v 1.3 2025/03/02 23:11:19 riastradh Exp $"); + +#include <assert.h> +#include <err.h> +#include <getopt.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +static void +heapsort_r_wrapper(void *a, size_t n, size_t sz, + int (*cmp)(const void *, const void *, void *), void *cookie) +{ + + if (heapsort_r(a, n, sz, cmp, cookie) == -1) + err(1, "heapsort_r"); +} + +static void +mergesort_r_wrapper(void *a, size_t n, size_t sz, + int (*cmp)(const void *, const void *, void *), void *cookie) +{ + + if (mergesort_r(a, n, sz, cmp, cookie) == -1) + err(1, "mergesort_r"); +} + +struct context { + const char *buf; + const size_t *linepos; +}; + +static int +cmp(const void *va, const void *vb, void *cookie) +{ + const struct context *C = cookie; + const size_t *a = va; + const size_t *b = vb; + + return strcmp(C->buf + C->linepos[*a], C->buf + C->linepos[*b]); +} + +static void __dead +usage(void) +{ + + fprintf(stderr, "Usage: %s [-n] <sortfn>\n", getprogname()); + exit(1); +} + +int +main(int argc, char **argv) +{ + int ch; + int nflag = 0; + void (*sortfn)(void *, size_t, size_t, + int (*)(const void *, const void *, void *), void *); + char *buf = NULL; + size_t nbuf; + size_t *linepos = NULL; + size_t nlines; + size_t *permutation = NULL; + size_t off; + ssize_t nread; + char *p; + size_t i; + int error; + + /* + * Parse arguments. + */ + setprogname(argv[0]); + while ((ch = getopt(argc, argv, "hn")) != -1) { + switch (ch) { + case 'n': + nflag = 1; + break; + case '?': + case 'h': + default: + usage(); + } + } + argc -= optind; + argv += optind; + if (argc != 1) + usage(); + if (strcmp(argv[0], "heapsort_r") == 0) + sortfn = &heapsort_r_wrapper; + else if (strcmp(argv[0], "mergesort_r") == 0) + sortfn = &mergesort_r_wrapper; + else if (strcmp(argv[0], "qsort_r") == 0) + sortfn = &qsort_r; + else + errx(1, "unknown sort: %s", argv[0]); + + /* + * Allocate an initial 4K buffer. + */ + nbuf = 0x1000; + error = reallocarr(&buf, nbuf, 1); + if (error) + errc(1, error, "reallocarr"); + + /* + * Read the input into a contiguous buffer. Reject input with + * embedded NULs so we can use strcmp(3) to compare lines. + */ + off = 0; + while ((nread = read(STDIN_FILENO, buf + off, nbuf - off - 1)) != 0) { + if (nread == -1) + err(1, "read"); + if ((size_t)nread > nbuf - off - 1) + errx(1, "overlong read: %zu", (size_t)nread); + if (memchr(buf + off, '\0', (size_t)nread) != NULL) + errx(1, "NUL byte in input"); + off += (size_t)nread; + + /* + * If we filled the buffer, reallocate it with double + * the size. Bail if that would overflow. + */ + if (nbuf - off == 1) { + if (nbuf > SIZE_MAX/2) + errx(1, "input overflow"); + nbuf *= 2; + error = reallocarr(&buf, nbuf, 1); + if (error) + errc(1, error, "reallocarr"); + } + } + + /* + * If the input was empty, nothing to do. + */ + if (off == 0) + return 0; + + /* + * NUL-terminate the input and count the lines. The last line + * may have no trailing \n. + */ + buf[off] = '\0'; + nlines = 1; + for (p = buf; (p = strchr(p, '\n')) != NULL;) { + if (*++p == '\0') + break; + nlines++; + } + + /* + * Create an array of line positions to sort. NUL-terminate + * each line so we can use strcmp(3). + */ + error = reallocarr(&linepos, nlines, sizeof(linepos[0])); + if (error) + errc(1, error, "reallocarr"); + i = 0; + for (p = buf; linepos[i++] = p - buf, (p = strchr(p, '\n')) != NULL;) { + *p = '\0'; + if (*++p == '\0') + break; + } + assert(i == nlines); + + /* + * Create an array of permuted line numbers. + */ + error = reallocarr(&permutation, nlines, sizeof(permutation[0])); + if (error) + errc(1, error, "reallocarr"); + for (i = 0; i < nlines; i++) + permutation[i] = i; + + /* + * Sort the lines via comparison function that consults the + * buffer as a cookie. + */ + (*sortfn)(permutation, nlines, sizeof(permutation[0]), &cmp, + &(struct context) { .buf = buf, .linepos = linepos }); + + /* + * Print the lines in sorted order with the original line + * numbers. + */ + for (i = 0; i < nlines; i++) { + const size_t j = permutation[i]; + if (nflag) + printf("%zu %s\n", j, buf + linepos[j]); + else + printf("%s\n", buf + linepos[j]); + } + fflush(stdout); + return ferror(stdout); +} diff --git a/lib/libc/stdlib/t_sort.sh b/lib/libc/stdlib/t_sort.sh new file mode 100644 index 000000000000..8987ac1ec623 --- /dev/null +++ b/lib/libc/stdlib/t_sort.sh @@ -0,0 +1,115 @@ +# $NetBSD: t_sort.sh,v 1.2 2025/03/02 20:00:32 riastradh Exp $ +# +# Copyright (c) 2025 The NetBSD Foundation, Inc. +# All rights reserved. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + +check_sort() +{ + local sortfn + + set -eu + + sortfn="$1" + + printf 'foo\nbar\nbaz\nquux' >in1 + printf '1 bar\n2 baz\n0 foo\n3 quux\n' >out1 + atf_check -s exit:0 -o file:out1 \ + "$(atf_get_srcdir)"/h_sort -n "$sortfn" <in1 + + atf_check -s exit:0 -o empty sh -c 'exec shuffle -f - <in1 >in2' + printf 'bar\nbaz\nfoo\nquux\n' >out2 + atf_check -s exit:0 -o file:out2 \ + "$(atf_get_srcdir)"/h_sort "$sortfn" <in2 +} + +check_stablesort() +{ + local sortfn + + set -eu + + sortfn="$1" + + printf 'foo\nfoo\nfoo\nfoo\nfoo' >in1 + printf '0 foo\n1 foo\n2 foo\n3 foo\n4 foo\n' >out1 + atf_check -s exit:0 -o file:out1 \ + "$(atf_get_srcdir)"/h_sort -n "$sortfn" <in1 + + printf 'foo\nfoo\nfoo\nfoo\nfoo\nbar\nbar\nbar\nbar\nbar' >in2 + printf '5 bar\n6 bar\n7 bar\n8 bar\n9 bar\n' >out2 + printf '0 foo\n1 foo\n2 foo\n3 foo\n4 foo\n' >>out2 + atf_check -s exit:0 -o file:out2 \ + "$(atf_get_srcdir)"/h_sort -n "$sortfn" <in2 + + printf 'foo\nfoo\nbar\nbaz\nquux' >in3 + printf '2 bar\n3 baz\n0 foo\n1 foo\n4 quux\n' >out3 + atf_check -s exit:0 -o file:out3 \ + "$(atf_get_srcdir)"/h_sort -n "$sortfn" <in3 + + printf 'foo\nbar\nbar\nbaz\nquux' >in4 + printf '1 bar\n2 bar\n3 baz\n0 foo\n4 quux\n' >out4 + atf_check -s exit:0 -o file:out4 \ + "$(atf_get_srcdir)"/h_sort -n "$sortfn" <in4 + + printf 'foo\nbar\nbaz\nbaz\nquux' >in5 + printf '1 bar\n2 baz\n3 baz\n0 foo\n4 quux\n' >out5 + atf_check -s exit:0 -o file:out5 \ + "$(atf_get_srcdir)"/h_sort -n "$sortfn" <in5 + + printf 'foo\nbar\nbaz\nquux\nquux' >in6 + printf '1 bar\n2 baz\n0 foo\n3 quux\n4 quux\n' >out6 + atf_check -s exit:0 -o file:out6 \ + "$(atf_get_srcdir)"/h_sort -n "$sortfn" <in6 +} + +sortfn_case() +{ + local sortfn + + sortfn="$1" + + eval "${sortfn}_head() { atf_set descr \"Test ${sortfn}\"; }" + eval "${sortfn}_body() { check_sort $sortfn; }" + atf_add_test_case "$sortfn" +} + +stablesortfn_case() +{ + local sortfn + + sortfn="$1" + + eval "${sortfn}_stable_head() { atf_set descr \"Test ${sortfn}\"; }" + eval "${sortfn}_stable_body() { check_stablesort $sortfn; }" + atf_add_test_case "${sortfn}_stable" +} + +atf_init_test_cases() +{ + + sortfn_case heapsort_r + sortfn_case mergesort_r + sortfn_case qsort_r + stablesortfn_case mergesort_r +} diff --git a/lib/libc/sys/t_aio_cancel.c b/lib/libc/sys/t_aio_cancel.c new file mode 100644 index 000000000000..43965cada251 --- /dev/null +++ b/lib/libc/sys/t_aio_cancel.c @@ -0,0 +1,223 @@ +/* $NetBSD: t_aio_cancel.c,v 1.1 2025/10/10 15:53:55 christos Exp $ */ + +/* + * Copyright (c) 2025 The NetBSD Foundation, Inc. + * All rights reserved. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 <aio.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <time.h> +#include <unistd.h> + +static int mktemp_file(char *, size_t); +static void fill_pattern(uint8_t *, size_t, uint8_t); +static void wait_all(const struct aiocb * const [], size_t); + +static int +mktemp_file(char *path, size_t pathlen) +{ + int fd, n; + + n = snprintf(path, pathlen, "t_aio_cancel.XXXXXX"); + ATF_REQUIRE(n > 0 && (size_t)n < pathlen); + + fd = mkstemp(path); + ATF_REQUIRE(fd >= 0); + + return fd; +} + +static void +fill_pattern(uint8_t *buf, size_t len, uint8_t seed) +{ + size_t i; + + for (i = 0; i < len; i++) { + buf[i] = (uint8_t)(seed + (i & 0xff)); + } +} + +static void +wait_all(const struct aiocb * const list[], size_t nent) +{ + size_t i; + int pending, rv; + + for (;;) { + pending = 0; + + for (i = 0; i < nent; i++) { + int err; + + if (list[i] == NULL) { + continue; + } + + err = aio_error(list[i]); + if (err == EINPROGRESS) { + pending = 1; + } + } + + if (!pending) { + break; + } + + rv = aio_suspend(list, (int)nent, NULL); + ATF_REQUIRE_EQ_MSG(0, rv, "aio_suspend failed: %s", + strerror(errno)); + } +} + +ATF_TC_WITHOUT_HEAD(cancel_active_write); +ATF_TC_BODY(cancel_active_write, tc) +{ + char path[64]; + int fd, rv, crv, err; + const size_t blksz = 0x1000; + uint8_t *wbuf; + struct aiocb cb; + const struct aiocb *list[1]; + + fd = mktemp_file(path, sizeof(path)); + + wbuf = malloc(blksz); + ATF_REQUIRE(wbuf != NULL); + fill_pattern(wbuf, blksz, 0x33); + + memset(&cb, 0, sizeof(cb)); + cb.aio_fildes = fd; + cb.aio_buf = wbuf; + cb.aio_nbytes = blksz; + cb.aio_offset = 0; + + rv = aio_write(&cb); + ATF_REQUIRE_EQ(0, rv); + + crv = aio_cancel(fd, &cb); + ATF_REQUIRE(crv == AIO_CANCELED || crv == AIO_NOTCANCELED + || crv == AIO_ALLDONE); + + if (crv == AIO_CANCELED) { + do { + err = aio_error(&cb); + } while (err == EINPROGRESS); + ATF_REQUIRE_EQ(ECANCELED, err); + ATF_REQUIRE_EQ(-1, aio_return(&cb)); + } else if (crv == AIO_NOTCANCELED) { + list[0] = &cb; + wait_all(list, 1); + ATF_REQUIRE_EQ(0, aio_error(&cb)); + ATF_REQUIRE_EQ((ssize_t)blksz, aio_return(&cb)); + } else { + do { + err = aio_error(&cb); + } while (err == EINPROGRESS); + ATF_REQUIRE_EQ(0, err); + ATF_REQUIRE_EQ((ssize_t)blksz, aio_return(&cb)); + } + + rv = close(fd); + ATF_REQUIRE_EQ(0, rv); + rv = unlink(path); + ATF_REQUIRE_EQ(0, rv); + + free(wbuf); +} + +ATF_TC_WITHOUT_HEAD(cancel_completed_request); +ATF_TC_BODY(cancel_completed_request, tc) +{ + char path[64]; + int fd, rv, crv; + const size_t blksz = 4096; + uint8_t *wbuf; + struct aiocb cb; + const struct aiocb *list[1]; + + fd = mktemp_file(path, sizeof(path)); + + wbuf = malloc(blksz); + ATF_REQUIRE(wbuf != NULL); + memset(wbuf, 0x7E, blksz); + + memset(&cb, 0, sizeof(cb)); + cb.aio_fildes = fd; + cb.aio_buf = wbuf; + cb.aio_nbytes = blksz; + cb.aio_offset = 0; + + rv = aio_write(&cb); + ATF_REQUIRE_EQ(0, rv); + + list[0] = &cb; + wait_all(list, 1); + ATF_REQUIRE_EQ(0, aio_error(&cb)); + ATF_REQUIRE_EQ((ssize_t)blksz, aio_return(&cb)); + + crv = aio_cancel(fd, &cb); + ATF_REQUIRE_EQ(AIO_ALLDONE, crv); + + rv = close(fd); + ATF_REQUIRE_EQ(0, rv); + rv = unlink(path); + ATF_REQUIRE_EQ(0, rv); + + free(wbuf); +} + +ATF_TC_WITHOUT_HEAD(cancel_invalid_fd); +ATF_TC_BODY(cancel_invalid_fd, tc) +{ + struct aiocb cb; + int crv; + + memset(&cb, 0, sizeof(cb)); + cb.aio_fildes = -1; + + errno = 0; + crv = aio_cancel(-1, &cb); + ATF_REQUIRE_EQ(-1, crv); + ATF_REQUIRE_EQ(EBADF, errno); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, cancel_active_write); + ATF_TP_ADD_TC(tp, cancel_completed_request); + ATF_TP_ADD_TC(tp, cancel_invalid_fd); + return atf_no_error(); +} diff --git a/lib/libc/sys/t_aio_lio.c b/lib/libc/sys/t_aio_lio.c new file mode 100644 index 000000000000..991db8d5a7cd --- /dev/null +++ b/lib/libc/sys/t_aio_lio.c @@ -0,0 +1,264 @@ +/* $NetBSD: t_aio_lio.c,v 1.1 2025/10/10 15:53:55 christos Exp $ */ + +/* + * Copyright (c) 2025 The NetBSD Foundation, Inc. + * All rights reserved. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 <aio.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +static int mktemp_file(char *, size_t); +static void fill_pattern(uint8_t *, size_t, uint8_t); +static void wait_all(const struct aiocb * const [], size_t); + +static int +mktemp_file(char *path, size_t pathlen) +{ + int fd, n; + + n = snprintf(path, pathlen, "t_aio_lio.XXXXXX"); + ATF_REQUIRE(n > 0 && (size_t)n < pathlen); + + fd = mkstemp(path); + ATF_REQUIRE(fd >= 0); + + return fd; +} + +static void +fill_pattern(uint8_t *buf, size_t len, uint8_t seed) +{ + size_t i; + + for (i = 0; i < len; i++) { + buf[i] = (uint8_t)(seed + (i & 0xff)); + } +} + +static void +wait_all(const struct aiocb * const list[], size_t nent) +{ + size_t i; + int pending, rv; + + for (;;) { + pending = 0; + + for (i = 0; i < nent; i++) { + int err; + + if (list[i] == NULL) { + continue; + } + + err = aio_error(list[i]); + if (err == EINPROGRESS) { + pending = 1; + } + } + + if (!pending) { + break; + } + + rv = aio_suspend(list, (int)nent, NULL); + ATF_REQUIRE_EQ_MSG(0, rv, "aio_suspend failed: %s", + strerror(errno)); + } +} + +ATF_TC_WITHOUT_HEAD(lio_nowait); +ATF_TC_BODY(lio_nowait, tc) +{ + char path[64]; + int fd, rv; +#define NW_REQS 8 +#define NW_BLKSIZ 8192 + uint8_t *bufs[NW_REQS]; + struct aiocb cbs[NW_REQS]; + struct aiocb *list[NW_REQS]; + off_t off; + size_t i; + + fd = mktemp_file(path, sizeof(path)); + + off = 0; + for (i = 0; i < NW_REQS; i++) { + bufs[i] = malloc(NW_BLKSIZ); + ATF_REQUIRE(bufs[i] != NULL); + + fill_pattern(bufs[i], NW_BLKSIZ, (uint8_t)i); + + memset(&cbs[i], 0, sizeof(cbs[i])); + cbs[i].aio_fildes = fd; + cbs[i].aio_buf = bufs[i]; + cbs[i].aio_nbytes = NW_BLKSIZ; + cbs[i].aio_offset = off; + cbs[i].aio_lio_opcode = LIO_WRITE; + + list[i] = &cbs[i]; + off += (off_t)NW_BLKSIZ; + } + + rv = lio_listio(LIO_NOWAIT, list, (int)NW_REQS, NULL); + ATF_REQUIRE_EQ_MSG(0, rv, "lio_listio failed: %s", + strerror(errno)); + + wait_all((const struct aiocb * const *)list, NW_REQS); + + for (i = 0; i < NW_REQS; i++) { + int err; + ssize_t done; + + err = aio_error(&cbs[i]); + ATF_REQUIRE_EQ(0, err); + + done = aio_return(&cbs[i]); + ATF_REQUIRE_EQ(NW_BLKSIZ, done); + + free(bufs[i]); + } + + rv = close(fd); + ATF_REQUIRE_EQ(0, rv); + rv = unlink(path); + ATF_REQUIRE_EQ(0, rv); +} + +ATF_TC_WITHOUT_HEAD(lio_wait_write_then_read); +ATF_TC_BODY(lio_wait_write_then_read, tc) +{ + char path[64]; + int fd, rv; +#define WWTR_REQS 4 +#define WWTR_BLKSIZ 4096 + + uint8_t *wbufs[WWTR_REQS]; + struct aiocb wcbs[WWTR_REQS]; + struct aiocb *wlist[WWTR_REQS]; + + uint8_t *rbufs[WWTR_REQS]; + struct aiocb rcbs[WWTR_REQS]; + struct aiocb *rlist[WWTR_REQS]; + + size_t i; + off_t off; + + fd = mktemp_file(path, sizeof(path)); + + off = 0; + for (i = 0; i < WWTR_REQS; i++) { + wbufs[i] = malloc(WWTR_BLKSIZ); + ATF_REQUIRE(wbufs[i] != NULL); + + fill_pattern(wbufs[i], WWTR_BLKSIZ, (uint8_t)(0xA0 + i)); + + memset(&wcbs[i], 0, sizeof(wcbs[i])); + wcbs[i].aio_fildes = fd; + wcbs[i].aio_buf = wbufs[i]; + wcbs[i].aio_nbytes = WWTR_BLKSIZ; + wcbs[i].aio_offset = off; + wcbs[i].aio_lio_opcode = LIO_WRITE; + + wlist[i] = &wcbs[i]; + off += WWTR_BLKSIZ; + } + + rv = lio_listio(LIO_WAIT, wlist, (int)WWTR_REQS, NULL); + ATF_REQUIRE_EQ_MSG(0, rv, "lio_listio write failed: %s", + strerror(errno)); + + for (i = 0; i < WWTR_REQS; i++) { + int err; + ssize_t done; + + err = aio_error(&wcbs[i]); + ATF_REQUIRE_EQ(0, err); + + done = aio_return(&wcbs[i]); + ATF_REQUIRE_EQ(WWTR_BLKSIZ, done); + } + + for (i = 0; i < WWTR_REQS; i++) { + rbufs[i] = calloc(1, WWTR_BLKSIZ); + ATF_REQUIRE(rbufs[i] != NULL); + + memset(&rcbs[i], 0, sizeof(rcbs[i])); + rcbs[i].aio_fildes = fd; + rcbs[i].aio_buf = rbufs[i]; + rcbs[i].aio_nbytes = WWTR_BLKSIZ; + rcbs[i].aio_offset = (off_t)i * WWTR_BLKSIZ; + rcbs[i].aio_lio_opcode = LIO_READ; + + rlist[i] = &rcbs[i]; + } + + rv = lio_listio(LIO_NOWAIT, rlist, WWTR_REQS, NULL); + ATF_REQUIRE_EQ_MSG(0, rv, "lio_listio read failed: %s", + strerror(errno)); + + wait_all((const struct aiocb * const *)rlist, WWTR_REQS); + + for (i = 0; i < WWTR_REQS; i++) { + int err; + ssize_t done; + + err = aio_error(&rcbs[i]); + ATF_REQUIRE_EQ(0, err); + + done = aio_return(&rcbs[i]); + ATF_REQUIRE_EQ(WWTR_BLKSIZ, done); + + ATF_REQUIRE_EQ(0, memcmp(wbufs[i], rbufs[i], WWTR_BLKSIZ)); + + free(wbufs[i]); + free(rbufs[i]); + } + + rv = close(fd); + ATF_REQUIRE_EQ(0, rv); + rv = unlink(path); + ATF_REQUIRE_EQ(0, rv); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, lio_nowait); + ATF_TP_ADD_TC(tp, lio_wait_write_then_read); + + return atf_no_error(); +} diff --git a/lib/libc/sys/t_aio_rw.c b/lib/libc/sys/t_aio_rw.c new file mode 100644 index 000000000000..e7a5b4fa67d1 --- /dev/null +++ b/lib/libc/sys/t_aio_rw.c @@ -0,0 +1,167 @@ +/* $NetBSD: t_aio_rw.c,v 1.1 2025/10/10 15:53:55 christos Exp $ */ + +/* + * Copyright (c) 2025 The NetBSD Foundation, Inc. + * All rights reserved. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 <aio.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <time.h> +#include <unistd.h> + +static int mktemp_file(char *, size_t); +static void fill_pattern(uint8_t *, size_t, uint8_t); +static void wait_all(const struct aiocb * const [], size_t); + +static int +mktemp_file(char *path, size_t pathlen) +{ + int fd, n; + + n = snprintf(path, pathlen, "t_aio_rw.XXXXXX"); + ATF_REQUIRE(n > 0 && (size_t)n < pathlen); + + fd = mkstemp(path); + ATF_REQUIRE(fd >= 0); + + return fd; +} + +static void +fill_pattern(uint8_t *buf, size_t len, uint8_t seed) +{ + size_t i; + + for (i = 0; i < len; i++) { + buf[i] = (uint8_t)(seed + (i & 0xff)); + } +} + +static void +wait_all(const struct aiocb * const list[], size_t nent) +{ + size_t i; + int pending, rv, error; + + for (;;) { + pending = 0; + + for (i = 0; i < nent; i++) { + if (list[i] == NULL) { + continue; + } + + error = aio_error(list[i]); + if (error == EINPROGRESS) { + pending = 1; + } + } + + if (!pending) { + break; + } + + rv = aio_suspend(list, (int)nent, NULL); + ATF_REQUIRE_EQ_MSG(0, rv, "aio_suspend failed: %s", + strerror(errno)); + } +} + +/* + * write_then_read_back + * Write a block then read it back asynchronously and compare. + */ +ATF_TC_WITHOUT_HEAD(write_then_read_back); +ATF_TC_BODY(write_then_read_back, tc) +{ + char path[64]; + int fd, rv; + const size_t blksz = 0x2000; + uint8_t *wbuf, *rbuf; + struct aiocb wcb, rcb; + const struct aiocb *wlist[1], *rlist[1]; + + fd = mktemp_file(path, sizeof(path)); + + wbuf = malloc(blksz); + rbuf = calloc(1, blksz); + ATF_REQUIRE(wbuf != NULL && rbuf != NULL); + + fill_pattern(wbuf, blksz, 0xA0); + + memset(&wcb, 0, sizeof(wcb)); + wcb.aio_fildes = fd; + wcb.aio_buf = wbuf; + wcb.aio_nbytes = blksz; + wcb.aio_offset = 0; + + rv = aio_write(&wcb); + ATF_REQUIRE_EQ(0, rv); + wlist[0] = &wcb; + wait_all(wlist, 1); + + ATF_REQUIRE_EQ(0, aio_error(&wcb)); + ATF_REQUIRE_EQ((ssize_t)blksz, aio_return(&wcb)); + + memset(&rcb, 0, sizeof(rcb)); + rcb.aio_fildes = fd; + rcb.aio_buf = rbuf; + rcb.aio_nbytes = blksz; + rcb.aio_offset = 0; + + rv = aio_read(&rcb); + ATF_REQUIRE_EQ(0, rv); + rlist[0] = &rcb; + wait_all(rlist, 1); + + ATF_REQUIRE_EQ(0, aio_error(&rcb)); + ATF_REQUIRE_EQ((ssize_t)blksz, aio_return(&rcb)); + ATF_REQUIRE_EQ(0, memcmp(wbuf, rbuf, blksz)); + + rv = close(fd); + ATF_REQUIRE_EQ(0, rv); + rv = unlink(path); + ATF_REQUIRE_EQ(0, rv); + + free(wbuf); + free(rbuf); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, write_then_read_back); + return atf_no_error(); +} diff --git a/lib/libc/sys/t_aio_suspend.c b/lib/libc/sys/t_aio_suspend.c new file mode 100644 index 000000000000..96b0dc2cf6d0 --- /dev/null +++ b/lib/libc/sys/t_aio_suspend.c @@ -0,0 +1,170 @@ +/* $NetBSD: t_aio_suspend.c,v 1.1 2025/10/10 15:53:55 christos Exp $ */ + +/* + * Copyright (c) 2025 The NetBSD Foundation, Inc. + * All rights reserved. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 <aio.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <time.h> +#include <unistd.h> + +static int mktemp_file(char *, size_t); +static void fill_pattern(uint8_t *, size_t, uint8_t); +static void wait_cb(struct aiocb *); + +static int +mktemp_file(char *path, size_t pathlen) +{ + int fd, n; + + n = snprintf(path, pathlen, "t_aio_suspend.XXXXXX"); + ATF_REQUIRE(n > 0 && (size_t)n < pathlen); + + fd = mkstemp(path); + ATF_REQUIRE(fd >= 0); + + return fd; +} + +static void +fill_pattern(uint8_t *buf, size_t len, uint8_t seed) +{ + size_t i; + + for (i = 0; i < len; i++) { + buf[i] = (uint8_t)(seed + (i & 0xff)); + } +} + +static void +wait_cb(struct aiocb *cb) +{ + const struct aiocb *one[1]; + int rv; + + one[0] = cb; + while (aio_error(cb) == EINPROGRESS) { + rv = aio_suspend(one, 1, NULL); + ATF_REQUIRE_EQ(0, rv); + } + if (aio_error(cb) == 0) { + aio_return(cb); + } +} + +ATF_TC_WITHOUT_HEAD(suspend_any); +ATF_TC_BODY(suspend_any, tc) +{ + char path[64]; + int fd, rv; + const size_t blksz = 4096; + uint8_t *buf0, *buf1; + struct aiocb cb0, cb1; + const struct aiocb *list[2]; + int done; + + fd = mktemp_file(path, sizeof(path)); + + buf0 = malloc(blksz); + buf1 = malloc(blksz); + ATF_REQUIRE(buf0 != NULL && buf1 != NULL); + fill_pattern(buf0, blksz, 0x20); + fill_pattern(buf1, blksz, 0x40); + + memset(&cb0, 0, sizeof(cb0)); + cb0.aio_fildes = fd; + cb0.aio_buf = buf0; + cb0.aio_nbytes = blksz; + cb0.aio_offset = 0; + + memset(&cb1, 0, sizeof(cb1)); + cb1.aio_fildes = fd; + cb1.aio_buf = buf1; + cb1.aio_nbytes = blksz; + cb1.aio_offset = blksz; + + ATF_REQUIRE_EQ(0, aio_write(&cb0)); + ATF_REQUIRE_EQ(0, aio_write(&cb1)); + + list[0] = &cb0; + list[1] = &cb1; + + rv = aio_suspend(list, 2, NULL); + ATF_REQUIRE_EQ(0, rv); + + done = 0; + if (aio_error(&cb0) != EINPROGRESS) { + done++; + if (aio_error(&cb0) == 0) { + ATF_REQUIRE_EQ((ssize_t)blksz, aio_return(&cb0)); + } else { + ATF_REQUIRE_EQ(ECANCELED, aio_error(&cb0)); + ATF_REQUIRE_EQ(-1, aio_return(&cb0)); + } + } + if (aio_error(&cb1) != EINPROGRESS) { + done++; + if (aio_error(&cb1) == 0) { + ATF_REQUIRE_EQ((ssize_t)blksz, aio_return(&cb1)); + } else { + ATF_REQUIRE_EQ(ECANCELED, aio_error(&cb1)); + ATF_REQUIRE_EQ(-1, aio_return(&cb1)); + } + } + ATF_REQUIRE(done >= 1); + + if (aio_error(&cb0) == EINPROGRESS) { + wait_cb(&cb0); + } + if (aio_error(&cb1) == EINPROGRESS) { + wait_cb(&cb1); + } + + rv = close(fd); + ATF_REQUIRE_EQ(0, rv); + rv = unlink(path); + ATF_REQUIRE_EQ(0, rv); + + free(buf0); + free(buf1); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, suspend_any); + return atf_no_error(); +} diff --git a/lib/libc/sys/t_ptrace_kill.c b/lib/libc/sys/t_ptrace_kill.c new file mode 100644 index 000000000000..fdd6298c2a8a --- /dev/null +++ b/lib/libc/sys/t_ptrace_kill.c @@ -0,0 +1,131 @@ +/* $NetBSD: t_ptrace_kill.c,v 1.2 2025/05/02 02:24:32 riastradh Exp $ */ + +/*- + * Copyright (c) 2024 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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> +__RCSID("$NetBSD: t_ptrace_kill.c,v 1.2 2025/05/02 02:24:32 riastradh Exp $"); + +#include <sys/types.h> +#include <sys/ptrace.h> +#include <sys/wait.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <signal.h> +#include <unistd.h> +#include <errno.h> +#include <err.h> +#include <atf-c.h> + +#define SYSCALL(a, b) ATF_REQUIRE_EQ_MSG(a, b, "%s got %s", #a, strerror(errno)) + +ATF_TC(pt_kill); +ATF_TC_HEAD(pt_kill, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test PT_KILL of a PT_STOP'ed process"); +} + +static void +child(int *fdto, int *fdfrom) +{ + char p = '2', q; + printf("%d: born\n", getpid()); + write(fdfrom[1], &p, 1); + read(fdto[0], &q, 1); + printf("%d: seppuku %c\n", getpid(), q); + write(fdfrom[1], &p, 1); + read(fdto[0], &q, 1); +// *(int *)1 = 0; +// kill(getpid(), SIGSEGV); +// kill(getpid(), SIGSTOP); + for (;;) + sleep(1); + +} + +static void * +waitthread(void *pidp) +{ + int status = 0; + pid_t rpid, pid; + + pid = *(pid_t *)pidp; + printf("waiting for %d\n", pid); + while ((rpid = waitpid(pid, &status, 0)) != pid) { + printf("waitpid %d = %d status = %#x", pid, rpid, status); + } + printf("done waitpid %d = %d status = %#x", pid, rpid, status); + return NULL; +} + +ATF_TC_BODY(pt_kill, tc) +{ + pid_t pid; + int fdto[2], fdfrom[2]; + char p = '1', q; + int status; + pthread_t thread; + + SYSCALL(pipe(fdto), 0); + SYSCALL(pipe(fdfrom), 0); + switch (pid = fork()) { + case 0: + child(fdto, fdfrom); + break; + case -1: + err(EXIT_FAILURE, "fork failed"); + default: + SYSCALL(pthread_create(&thread, NULL, waitthread, &pid), 0); + sleep(1); // XXX: too lazy to sync properly + SYSCALL(read(fdfrom[0], &q, 1), 1); + printf("%d: read %c\n", pid, q); + SYSCALL(ptrace(PT_ATTACH, pid, NULL, 0), 0); + printf("%d: attached\n", pid); + SYSCALL(write(fdto[1], &p, 1), 1); + waitpid(pid, NULL, WNOHANG); + printf("%d: sent\n", pid); + SYSCALL(ptrace(PT_CONTINUE, pid, (void *)1, 0), 0); + SYSCALL(read(fdfrom[0], &p, 1), 1); + printf("%d: received\n", pid); + SYSCALL(ptrace(PT_STOP, pid, NULL, 0), 0); + SYSCALL(ptrace(PT_KILL, pid, NULL, 0), 0); + SYSCALL(waitpid(pid, &status, 0), pid); + ATF_REQUIRE(status == 9); + break; + } +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, pt_kill); + + return atf_no_error(); +} |
