# HG changeset patch # User Taylor R Campbell # Date 1757631465 0 # Thu Sep 11 22:57:45 2025 +0000 # Branch trunk # Node ID 0c4de22d24a4ffebc9c0b616b4d7e307197feb91 # Parent 84d93e4dac07509dd1eb2f240eda797025d702bb # EXP-Topic riastradh-pr58208-runtimectypeabusedetection ctype(3): New environment variable LIBC_ALLOWCTYPEABUSE. If set, this does not force the ctype(3) functions to crash when passed invalid inputs -- instead, they will return nonsense results, and possibly print warnings to stderr, as is their right in implementing undefined behaviour. The nature of the nonsense results is unspecified. Currently, is*() will always return true (even if that leads to mutually contradictory conclusions, like isalpha and isdigit, or isgraph and isblank), and tolower/toupper() will always return EOF. But perhaps in the future the results may be randomized. This way, if an application like firefox crashes on ctype abuse, you can opt to accept the consequences of nonsense results instead by running `env LIBC_ALLOWCTYPEABUSE= firefox' until the application is fixed. PR lib/58208: ctype(3) provides poor runtime feedback of abuse diff -r 84d93e4dac07 -r 0c4de22d24a4 distrib/sets/lists/debug/mi --- a/distrib/sets/lists/debug/mi Sat Sep 06 07:16:28 2025 +0000 +++ b/distrib/sets/lists/debug/mi Thu Sep 11 22:57:45 2025 +0000 @@ -2026,6 +2026,7 @@ ./usr/libdata/debug/usr/tests/lib/libc/db/t_db_hash_seq.debug tests-lib-debug debug,atf,compattestfile ./usr/libdata/debug/usr/tests/lib/libc/gen/exect/t_exect.debug tests-kernel-obsolete obsolete ./usr/libdata/debug/usr/tests/lib/libc/gen/execve/t_execve.debug tests-kernel-tests debug,atf,compattestfile +./usr/libdata/debug/usr/tests/lib/libc/gen/h_ctype_abuse.debug tests-kernel-tests debug,atf,compattestfile ./usr/libdata/debug/usr/tests/lib/libc/gen/h_execsig.debug tests-kernel-tests debug,atf,compattestfile ./usr/libdata/debug/usr/tests/lib/libc/gen/posix_spawn/h_fileactions.debug tests-kernel-tests debug,atf,compattestfile ./usr/libdata/debug/usr/tests/lib/libc/gen/posix_spawn/h_spawn.debug tests-kernel-tests debug,atf,compattestfile diff -r 84d93e4dac07 -r 0c4de22d24a4 distrib/sets/lists/tests/mi --- a/distrib/sets/lists/tests/mi Sat Sep 06 07:16:28 2025 +0000 +++ b/distrib/sets/lists/tests/mi Thu Sep 11 22:57:45 2025 +0000 @@ -3082,6 +3082,7 @@ ./usr/tests/lib/libc/gen/execve/Atffile tests-kernel-tests compattestfile,atf ./usr/tests/lib/libc/gen/execve/Kyuafile tests-kernel-tests compattestfile,atf,kyua ./usr/tests/lib/libc/gen/execve/t_execve tests-kernel-tests compattestfile,atf +./usr/tests/lib/libc/gen/h_ctype_abuse tests-kernel-tests compattestfile,atf ./usr/tests/lib/libc/gen/h_execsig tests-kernel-tests compattestfile,atf ./usr/tests/lib/libc/gen/posix_spawn tests-kernel-tests compattestfile,atf ./usr/tests/lib/libc/gen/posix_spawn/Atffile tests-kernel-tests compattestfile,atf diff -r 84d93e4dac07 -r 0c4de22d24a4 lib/libc/gen/ctype_.c --- a/lib/libc/gen/ctype_.c Sat Sep 06 07:16:28 2025 +0000 +++ b/lib/libc/gen/ctype_.c Thu Sep 11 22:57:45 2025 +0000 @@ -47,6 +47,7 @@ #include #include +#include #include "ctype_guard.h" #include "ctype_local.h" @@ -69,6 +70,7 @@ static const unsigned char _C_compat_bsdctype_guarded[_C_COMPAT_BSDCTYPE_GUARD + 1 + _CTYPE_NUM_CHARS] = { + _CTYPE_GUARD_INIT(_C_COMPAT_BSDCTYPE_GUARD, -1) [_C_COMPAT_BSDCTYPE_GUARD] = 0, _C, _C, _C, _C, _C, _C, _C, _C, _C, _C|_S, _C|_S, _C|_S, _C|_S, _C|_S, _C, _C, @@ -121,6 +123,7 @@ const unsigned char *_ctype_ = &_C_compa __ctype_table static const unsigned short _C_ctype_tab_guarded_[_C_CTYPE_TAB_GUARD + 1 + _CTYPE_NUM_CHARS] = { + _CTYPE_GUARD_INIT(_C_CTYPE_TAB_GUARD, -1) [_C_CTYPE_TAB_GUARD] = 0, _C, _C, _C, _C, _C, _C, _C, _C, @@ -172,12 +175,95 @@ static const unsigned short _C_ctype_tab const unsigned short *_ctype_tab_ = &_C_ctype_tab_[0]; +/* + * _allow_ctype_abuse() + * + * Internal subroutine to interpret environ and return true if + * LIBC_ALLOWCTYPEABUSE is defined, false if not. + * + * We use environ and strcmp directly to make sure this works + * inside constructors and to avoid pulling in all the + * stdlib/_env.c machinery if unnecessary. We cache it so we only + * add one traversal of environ to each process startup for the + * ctype, toupper, and tolower tables (not ideal -- rtld already + * does another traversal -- but better than adding multiple + * traversals). + * + * This query is also used by the out-of-line ctype logic in + * isctype.c outside a constructor, where it could use getenv, and + * perhaps support finer-grained distinctions -- like + * LIBC_ALLOWCTYPEABUSE=silent vs LIBC_ALLOWCTYPEABUSE=noisy. But + * until we feel the need to implement such finer distinctions, + * let's keep the logic of interpreting LIBC_ALLOWCTYPEABUSE + * together in one place. + */ +#ifndef RTLD_LOADER +#include "extern.h" /* environ */ +static bool +_allow_ctype_abuse(void) +{ + const char vareq[] = "LIBC_ALLOWCTYPEABUSE="; + char *const *envp; + + for (envp = environ; *envp != NULL; envp++) { + if (strncmp(*envp, vareq, strlen(vareq)) == 0) { + return true; + } + } + return false; +} +#endif + #if _CTYPE_GUARD_PAGE + +static signed char allow_ctype_abuse_cache; + +/* + * allow_ctype_abuse() + * + * True if LIBC_ALLOWCTYPEABUSE is defined in the environment, + * false if not. May reflect the environment any time between + * process startup and now. + */ +__dso_hidden +bool +allow_ctype_abuse(void) +{ + + if (allow_ctype_abuse_cache) + return allow_ctype_abuse_cache == 1; + return _allow_ctype_abuse(); +} + +/* + * constructor_allow_ctype_abuse() + * + * True if LIBC_ALLOWCTYPEABUSE is defined in the environment, + * false if not. May be used only in an ELF constructor. + */ +__dso_hidden +bool +constructor_allow_ctype_abuse(void) +{ + + if (allow_ctype_abuse_cache) + return allow_ctype_abuse_cache == 1; + if (_allow_ctype_abuse()) { + allow_ctype_abuse_cache = 1; + return true; + } else { + allow_ctype_abuse_cache = -1; + return false; + } +} + __attribute__((constructor)) static void _C_ctype_tab_guard_init(void) { + if (constructor_allow_ctype_abuse()) + return; #ifdef __BUILD_LEGACY (void)mprotect(__UNCONST(_C_compat_bsdctype_guarded), _CTYPE_GUARD_SIZE, PROT_NONE); @@ -185,4 +271,17 @@ static void (void)mprotect(__UNCONST(_C_ctype_tab_guarded_), _CTYPE_GUARD_SIZE, PROT_NONE); } + +#else + +#ifndef RTLD_LOADER +__dso_hidden +bool +allow_ctype_abuse(void) +{ + + return _allow_ctype_abuse(); +} +#endif + #endif /* _CTYPE_GUARD_PAGE */ diff -r 84d93e4dac07 -r 0c4de22d24a4 lib/libc/gen/ctype_guard.h --- a/lib/libc/gen/ctype_guard.h Sat Sep 06 07:16:28 2025 +0000 +++ b/lib/libc/gen/ctype_guard.h Thu Sep 11 22:57:45 2025 +0000 @@ -31,6 +31,8 @@ #include +#include + #include "ctype_local.h" /* @@ -116,10 +118,14 @@ __asm(".size " _C_LABEL_STRING(#name) "," \ ___STRING((nelem) * (elemsize))) +__dso_hidden bool allow_ctype_abuse(void); + #if _CTYPE_GUARD_PAGE # include +__dso_hidden bool constructor_allow_ctype_abuse(void); + /* * _CTYPE_GUARD_SIZE must be a macro so it will work through ___STRING * to produce a string for symbol arithmetic in __asm. @@ -130,6 +136,8 @@ # define _CTYPE_GUARD_SIZE PAGE_SIZE # endif +# define _CTYPE_GUARD_INIT(n, x) [0 ... (n) - 1] = (x), + enum { _C_CTYPE_TAB_GUARD = _CTYPE_GUARD_SIZE/sizeof(_C_ctype_tab_[0]), # ifdef __BUILD_LEGACY @@ -152,6 +160,8 @@ enum { # define _CTYPE_GUARD_SIZE 0 +# define _CTYPE_GUARD_INIT(n, x) /* empty */ + enum { _C_CTYPE_TAB_GUARD = 0, # ifdef __BUILD_LEGACY diff -r 84d93e4dac07 -r 0c4de22d24a4 lib/libc/gen/isctype.c --- a/lib/libc/gen/isctype.c Sat Sep 06 07:16:28 2025 +0000 +++ b/lib/libc/gen/isctype.c Thu Sep 11 22:57:45 2025 +0000 @@ -48,13 +48,14 @@ #error "EOF != -1" #endif +#include "ctype_guard.h" #include "runetype_local.h" #include "setlocale_local.h" #define _RUNE_LOCALE(loc) \ ((_RuneLocale *)((loc)->part_impl[LC_CTYPE])) -static void __noinline __dead +static int __noinline ctype_nasaldemon(const char *func, int c) { char buf[128]; @@ -62,18 +63,16 @@ ctype_nasaldemon(const char *func, int c snprintf_ss(buf, sizeof(buf), "ctype(3) %s: invalid input: %d\n", func, c); (void)write(STDERR_FILENO, buf, strlen(buf)); + if (allow_ctype_abuse()) + return -1; abort(); } -static inline void -ctype_check(const char *func, int c) -{ - - if (__predict_false((c != EOF && c < 0) || c > UCHAR_MAX)) - ctype_nasaldemon(func, c); -} - -#define CTYPE_CHECK(c) ctype_check(__func__, c) +#define CTYPE_CHECK(c) do \ +{ \ + if (__predict_false(((c) != EOF && (c) < 0) || (c) > UCHAR_MAX)) \ + return ctype_nasaldemon(__func__, c); \ +} while (0) #define _ISCTYPE_FUNC(name, bit) \ int \ diff -r 84d93e4dac07 -r 0c4de22d24a4 lib/libc/gen/tolower_.c --- a/lib/libc/gen/tolower_.c Sat Sep 06 07:16:28 2025 +0000 +++ b/lib/libc/gen/tolower_.c Thu Sep 11 22:57:45 2025 +0000 @@ -25,6 +25,7 @@ __ctype_table static const short _C_tolower_tab_guarded_[_C_TOLOWER_TAB_GUARD + 1 + _CTYPE_NUM_CHARS] = { + _CTYPE_GUARD_INIT(_C_TOLOWER_TAB_GUARD, EOF) [_C_TOLOWER_TAB_GUARD] = EOF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, @@ -76,6 +77,8 @@ static void _C_tolower_tab_guard_init(void) { + if (constructor_allow_ctype_abuse()) + return; (void)mprotect(__UNCONST(_C_tolower_tab_guarded_), _CTYPE_GUARD_SIZE, PROT_NONE); } diff -r 84d93e4dac07 -r 0c4de22d24a4 lib/libc/gen/toupper_.c --- a/lib/libc/gen/toupper_.c Sat Sep 06 07:16:28 2025 +0000 +++ b/lib/libc/gen/toupper_.c Thu Sep 11 22:57:45 2025 +0000 @@ -25,6 +25,7 @@ __ctype_table static const short _C_toupper_tab_guarded_[_C_TOUPPER_TAB_GUARD + 1 + _CTYPE_NUM_CHARS] = { + _CTYPE_GUARD_INIT(_C_TOUPPER_TAB_GUARD, EOF) [_C_TOUPPER_TAB_GUARD] = EOF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, @@ -76,6 +77,8 @@ static void _C_toupper_tab_guard_init(void) { + if (constructor_allow_ctype_abuse()) + return; (void)mprotect(__UNCONST(_C_toupper_tab_guarded_), _CTYPE_GUARD_SIZE, PROT_NONE); } diff -r 84d93e4dac07 -r 0c4de22d24a4 lib/libc/locale/Makefile.inc --- a/lib/libc/locale/Makefile.inc Sat Sep 06 07:16:28 2025 +0000 +++ b/lib/libc/locale/Makefile.inc Thu Sep 11 22:57:45 2025 +0000 @@ -26,6 +26,7 @@ CPPFLAGS+= -DWITH_RUNE -I${.CURDIR} SRCS+= _wctrans.c _wctype.c rune.c runetable.c \ multibyte_c90.c multibyte_amd1.c iswctype_mb.c CPPFLAGS.rune.c+= -I${LIBCDIR}/citrus +CPPFLAGS.rune.c+= -I${LIBCDIR}/gen # ctype_guard.h CPPFLAGS.runetable.c+= -I${LIBCDIR}/citrus CPPFLAGS.multibyte_c90.c+= -I${LIBCDIR}/citrus CPPFLAGS.multibyte_amd1.c+= -I${LIBCDIR}/citrus diff -r 84d93e4dac07 -r 0c4de22d24a4 lib/libc/locale/rune.c --- a/lib/libc/locale/rune.c Sat Sep 06 07:16:28 2025 +0000 +++ b/lib/libc/locale/rune.c Thu Sep 11 22:57:45 2025 +0000 @@ -40,6 +40,8 @@ #include #include +#include "ctype_guard.h" + #include "setlocale_local.h" #include "citrus_module.h" @@ -89,7 +91,9 @@ alloc_guarded(size_t elemsize, size_t ne /*fd*/-1, /*offset*/0); if (p == MAP_FAILED) goto fail; - if (mprotect(p, page_size, PROT_NONE) == -1) + if (allow_ctype_abuse()) + memset(p, 0xff, page_size); + else if (mprotect(p, page_size, PROT_NONE) == -1) goto fail; q = (char *)p + page_size; return q; diff -r 84d93e4dac07 -r 0c4de22d24a4 tests/lib/libc/gen/Makefile --- a/tests/lib/libc/gen/Makefile Sat Sep 06 07:16:28 2025 +0000 +++ b/tests/lib/libc/gen/Makefile Thu Sep 11 22:57:45 2025 +0000 @@ -46,6 +46,7 @@ TESTS_C+= t_timespec_get TESTS_C+= t_ttyname TESTS_C+= t_vis +PROGS+= h_ctype_abuse PROGS+= h_execsig .if ${MKSANITIZER:Uno} != "yes" && ${MKLIBCSANITIZER:Uno} != "yes" diff -r 84d93e4dac07 -r 0c4de22d24a4 tests/lib/libc/gen/h_ctype_abuse.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/lib/libc/gen/h_ctype_abuse.c Thu Sep 11 22:57:45 2025 +0000 @@ -0,0 +1,124 @@ +/* $NetBSD$ */ + +/*- + * 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 +__RCSID("$NetBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#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; + + fprintf(stderr, "hello world\n"); + + setprogname(argv[0]); + if (argc != 3 && argc != 4) { + errx(1, "Usage: %s []", + 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) { + fprintf(stderr, "setlocale(LC_CTYPE, \"%s\")\n", argv[3]); + if (setlocale(LC_CTYPE, argv[3]) == NULL) + err(1, "setlocale"); + fprintf(stderr, "setlocale(LC_CTYPE, \"%s\") ok\n", argv[3]); + } + + for (ch = 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 -r 84d93e4dac07 -r 0c4de22d24a4 tests/lib/libc/gen/t_ctype.c --- a/tests/lib/libc/gen/t_ctype.c Sat Sep 06 07:16:28 2025 +0000 +++ b/tests/lib/libc/gen/t_ctype.c Thu Sep 11 22:57:45 2025 +0000 @@ -29,6 +29,8 @@ #include __RCSID("$NetBSD: t_ctype.c,v 1.11 2025/03/30 15:38:38 riastradh Exp $"); +#include + #include #include #include @@ -36,6 +38,7 @@ #include #include #include +#include #include "h_macros.h" @@ -782,6 +785,61 @@ test_tolower_c(int (*ctypefn)(int)) } } +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); \ @@ -852,6 +910,56 @@ ATF_TC_BODY(abuse_##FN##_function_locale 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); \ @@ -923,6 +1031,21 @@ 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) @@ -1064,6 +1187,21 @@ ATF_TP_ADD_TCS(tp) 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);