#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
#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 (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 (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:
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:
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:
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) {
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, 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, 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;
int ch;
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;
int hard = 0xdf;
int ch;
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;
int Ya = 0xdf;
int ch;
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();
}