Module Name: src
Committed By: riastradh
Date: Fri Mar 28 18:41:55 UTC 2025
Modified Files:
src/distrib/sets/lists/debug: mi
src/distrib/sets/lists/tests: mi
src/tests/lib/libc/gen: Makefile
Added Files:
src/tests/lib/libc/gen: t_ctype.c
Log Message:
t_ctype: New test for ctype(3) functions/macros.
PR lib/58208: ctype(3) provides poor runtime feedback of abuse
To generate a diff of this commit:
cvs rdiff -u -r1.469 -r1.470 src/distrib/sets/lists/debug/mi
cvs rdiff -u -r1.1361 -r1.1362 src/distrib/sets/lists/tests/mi
cvs rdiff -u -r1.57 -r1.58 src/tests/lib/libc/gen/Makefile
cvs rdiff -u -r0 -r1.1 src/tests/lib/libc/gen/t_ctype.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/distrib/sets/lists/debug/mi
diff -u src/distrib/sets/lists/debug/mi:1.469 src/distrib/sets/lists/debug/mi:1.470
--- src/distrib/sets/lists/debug/mi:1.469 Thu Mar 13 01:27:27 2025
+++ src/distrib/sets/lists/debug/mi Fri Mar 28 18:41:55 2025
@@ -1,4 +1,4 @@
-# $NetBSD: mi,v 1.469 2025/03/13 01:27:27 riastradh Exp $
+# $NetBSD: mi,v 1.470 2025/03/28 18:41:55 riastradh Exp $
#
./etc/mtree/set.debug comp-sys-root
./usr/lib comp-sys-usr compatdir
@@ -2029,6 +2029,7 @@
./usr/libdata/debug/usr/tests/lib/libc/gen/t_basedirname.debug tests-lib-debug debug,atf,compattestfile
./usr/libdata/debug/usr/tests/lib/libc/gen/t_closefrom.debug tests-lib-debug debug,atf,compattestfile
./usr/libdata/debug/usr/tests/lib/libc/gen/t_cpuset.debug tests-lib-debug debug,atf,compattestfile
+./usr/libdata/debug/usr/tests/lib/libc/gen/t_ctype.debug tests-lib-debug debug,atf,compattestfile
./usr/libdata/debug/usr/tests/lib/libc/gen/t_dir.debug tests-lib-debug debug,atf,compattestfile
./usr/libdata/debug/usr/tests/lib/libc/gen/t_floatunditf.debug tests-lib-debug debug,atf,compattestfile
./usr/libdata/debug/usr/tests/lib/libc/gen/t_fmtcheck.debug tests-lib-debug debug,atf,compattestfile
Index: src/distrib/sets/lists/tests/mi
diff -u src/distrib/sets/lists/tests/mi:1.1361 src/distrib/sets/lists/tests/mi:1.1362
--- src/distrib/sets/lists/tests/mi:1.1361 Thu Mar 13 01:27:27 2025
+++ src/distrib/sets/lists/tests/mi Fri Mar 28 18:41:55 2025
@@ -1,4 +1,4 @@
-# $NetBSD: mi,v 1.1361 2025/03/13 01:27:27 riastradh Exp $
+# $NetBSD: mi,v 1.1362 2025/03/28 18:41:55 riastradh Exp $
#
# Note: don't delete entries from here - mark them as "obsolete" instead.
#
@@ -3009,6 +3009,7 @@
./usr/tests/lib/libc/gen/t_basedirname tests-lib-tests compattestfile,atf
./usr/tests/lib/libc/gen/t_closefrom tests-lib-tests compattestfile,atf
./usr/tests/lib/libc/gen/t_cpuset tests-lib-tests compattestfile,atf
+./usr/tests/lib/libc/gen/t_ctype tests-lib-tests compattestfile,atf
./usr/tests/lib/libc/gen/t_dir tests-lib-tests compattestfile,atf
./usr/tests/lib/libc/gen/t_floatunditf tests-lib-tests compattestfile,atf
./usr/tests/lib/libc/gen/t_fmtcheck tests-lib-tests compattestfile,atf
Index: src/tests/lib/libc/gen/Makefile
diff -u src/tests/lib/libc/gen/Makefile:1.57 src/tests/lib/libc/gen/Makefile:1.58
--- src/tests/lib/libc/gen/Makefile:1.57 Thu Mar 13 01:27:27 2025
+++ src/tests/lib/libc/gen/Makefile Fri Mar 28 18:41:55 2025
@@ -1,4 +1,4 @@
-# $NetBSD: Makefile,v 1.57 2025/03/13 01:27:27 riastradh Exp $
+# $NetBSD: Makefile,v 1.58 2025/03/28 18:41:55 riastradh Exp $
NOMAN= # defined
@@ -16,6 +16,7 @@ TESTS_C+= t_assert
TESTS_C+= t_basedirname
TESTS_C+= t_closefrom
TESTS_C+= t_cpuset
+TESTS_C+= t_ctype
TESTS_C+= t_dir
TESTS_C+= t_floatunditf
TESTS_C+= t_fmtcheck
Added files:
Index: src/tests/lib/libc/gen/t_ctype.c
diff -u /dev/null src/tests/lib/libc/gen/t_ctype.c:1.1
--- /dev/null Fri Mar 28 18:41:55 2025
+++ src/tests/lib/libc/gen/t_ctype.c Fri Mar 28 18:41:55 2025
@@ -0,0 +1,944 @@
+/* $NetBSD: t_ctype.c,v 1.1 2025/03/28 18:41: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.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_ctype.c,v 1.1 2025/03/28 18:41:55 riastradh Exp $");
+
+#include <atf-c.h>
+#include <ctype.h>
+#include <locale.h>
+#include <limits.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdio.h>
+
+#include "h_macros.h"
+
+static const char *locales[] = { "C.UTF-8", "fr_FR.ISO8859-1" };
+
+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); }
+
+#ifndef __CHAR_UNSIGNED__
+jmp_buf env;
+
+static void
+handle_sigsegv(int signo)
+{
+
+ longjmp(env, 1);
+}
+#endif
+
+static void
+test_abuse(const char *name, int (*ctypefn)(int))
+{
+#ifdef __CHAR_UNSIGNED__
+ atf_tc_skip("runtime ctype(3) abuse is impossible with unsigned char");
+#else
+ volatile int ch; /* for longjmp */
+
+ for (ch = CHAR_MIN; ch < 0; ch++) {
+ void (*h)(int) = SIG_DFL;
+ volatile int result;
+
+ if (ch == EOF)
+ continue;
+ ATF_REQUIRE_MSG(ch != (int)(unsigned char)ch, "ch=%d", ch);
+ if (setjmp(env) == 0) {
+ REQUIRE_LIBC(h = signal(SIGSEGV, &handle_sigsegv),
+ SIG_ERR);
+ result = (*ctypefn)(ch);
+ REQUIRE_LIBC(signal(SIGSEGV, h), SIG_ERR);
+ atf_tc_fail_nonfatal("%s failed to detect invalid %d,"
+ " returned %d",
+ name, ch, result);
+ } else {
+ REQUIRE_LIBC(signal(SIGSEGV, h), SIG_ERR);
+ }
+ }
+
+ for (; ch <= CHAR_MAX; ch++)
+ ATF_REQUIRE_MSG(ch == (int)(unsigned char)ch, "ch=%d", ch);
+#endif
+}
+
+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)) {
+ void (*h)(int) = SIG_DFL;
+ volatile int result;
+
+ if (setjmp(env) == 0) {
+ REQUIRE_LIBC(h = signal(SIGSEGV, &handle_sigsegv),
+ SIG_ERR);
+ result = (*ctypefn)(ch);
+ REQUIRE_LIBC(signal(SIGSEGV, h), SIG_ERR);
+ (void)result;
+ } else {
+ atf_tc_fail_nonfatal("%s(%d) raised SIGSEGV",
+ name, ch);
+ REQUIRE_LIBC(signal(SIGSEGV, h), SIG_ERR);
+ }
+ }
+}
+
+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);
+ }
+}
+
+#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) \
+{ \
+ atf_tc_expect_fail("PR lib/58208:" \
+ " ctype(3) provides poor runtime feedback of abuse"); \
+ 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) \
+{ \
+ atf_tc_expect_fail("PR lib/58208:" \
+ " ctype(3) provides poor runtime feedback of abuse"); \
+ 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) \
+{ \
+ size_t i; \
+ \
+ atf_tc_expect_fail("PR lib/58208:" \
+ " ctype(3) provides poor runtime feedback of abuse"); \
+ for (i = 0; i < __arraycount(locales); i++) { \
+ char buf[128]; \
+ \
+ ATF_REQUIRE_MSG(setlocale(LC_CTYPE, locales[i]) != NULL, \
+ "locales[i]=%s", locales[i]); \
+ snprintf(buf, sizeof(buf), "[%s]%s", locales[i], #FN); \
+ test_abuse(buf, &FN##_wrapper); \
+ } \
+} \
+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) \
+{ \
+ size_t i; \
+ \
+ atf_tc_expect_fail("PR lib/58208:" \
+ " ctype(3) provides poor runtime feedback of abuse"); \
+ for (i = 0; i < __arraycount(locales); i++) { \
+ char buf[128]; \
+ \
+ ATF_REQUIRE_MSG(setlocale(LC_CTYPE, locales[i]) != NULL, \
+ "locales[i]=%s", locales[i]); \
+ snprintf(buf, sizeof(buf), "[%s]%s", locales[i], #FN); \
+ test_abuse(buf, &FN); \
+ } \
+}
+
+#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_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_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_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);
+
+ return atf_no_error();
+}