/* $NetBSD: h_ctype_abuse.c,v 1.1 2025/09/15 00:11:54 riastradh Exp $ */ /*- * Copyright (c) 2025 The NetBSD Foundation, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Helper program to verify effects of ctype(3) abuse. * * NOTE: This program intentionally triggers undefined behaviour by * passing int values to the ctype(3) functions which are neither EOF * nor representable by unsigned char. The purpose is to verify that * NetBSD's ctype(3) _does not_ trap the undefined behaviour when the * environment variable LIBC_ALLOWCTYPEABUSE. (It does not check * anything about the results, which are perforce nonsense -- just that * it gets a result without crashing.) */ #include __RCSID("$NetBSD: h_ctype_abuse.c,v 1.1 2025/09/15 00:11:54 riastradh Exp $"); #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; 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) { if (setlocale(LC_CTYPE, argv[3]) == NULL) err(1, "setlocale"); } /* * Make sure we cover EOF as well. */ __CTASSERT(CHAR_MIN <= EOF); __CTASSERT(EOF <= UCHAR_MAX); 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; }