/* $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 __RCSID("$NetBSD: t_ctype.c,v 1.12 2025/09/15 00:11:55 riastradh Exp $"); #include #include #include #include #include #include #include #include #include #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(); }