diff --git a/distrib/sets/lists/tests/mi b/distrib/sets/lists/tests/mi index 0c983df9b816..3891febf9f0d 100644 --- a/distrib/sets/lists/tests/mi +++ b/distrib/sets/lists/tests/mi @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.1036 2021/04/04 13:20:52 rillig Exp $ +# $NetBSD: mi,v 1.1037 2021/04/05 02:05:47 rillig Exp $ # # Note: don't delete entries from here - mark them as "obsolete" instead. # @@ -6863,6 +6863,10 @@ ./usr/tests/usr.bin/xlint/lint1/msg_339.exp tests-usr.bin-tests compattestfile,atf ./usr/tests/usr.bin/xlint/lint1/msg_340.c tests-usr.bin-tests compattestfile,atf ./usr/tests/usr.bin/xlint/lint1/msg_340.exp tests-usr.bin-tests compattestfile,atf +./usr/tests/usr.bin/xlint/lint1/msg_341.c tests-usr.bin-tests compattestfile,atf +./usr/tests/usr.bin/xlint/lint1/msg_341.exp tests-usr.bin-tests compattestfile,atf +./usr/tests/usr.bin/xlint/lint1/msg_342.c tests-usr.bin-tests compattestfile,atf +./usr/tests/usr.bin/xlint/lint1/msg_342.exp tests-usr.bin-tests compattestfile,atf ./usr/tests/usr.bin/xlint/lint1/op_colon.c tests-usr.bin-tests compattestfile,atf ./usr/tests/usr.bin/xlint/lint1/op_colon.exp tests-usr.bin-tests compattestfile,atf ./usr/tests/usr.bin/xlint/lint1/t_integration tests-usr.bin-tests compattestfile,atf diff --git a/tests/usr.bin/xlint/lint1/Makefile b/tests/usr.bin/xlint/lint1/Makefile index 9d62461ed27d..e7ad0fbedbd0 100644 --- a/tests/usr.bin/xlint/lint1/Makefile +++ b/tests/usr.bin/xlint/lint1/Makefile @@ -1,6 +1,7 @@ -# $NetBSD: Makefile,v 1.38 2021/04/02 17:25:04 rillig Exp $ +# $NetBSD: Makefile,v 1.39 2021/04/05 02:05:47 rillig Exp $ NOMAN= # defined +MAX_MESSAGE= 342 # see lint1/err.c .include @@ -98,7 +99,7 @@ FILES+= d_type_question_colon.c FILES+= d_typefun.c FILES+= d_typename_as_var.c FILES+= d_zero_sized_arrays.c -FILES+= ${:U0 ${:U:range=340}:C,^.$,0&,:C,^..$,0&,:@msg@msg_${msg}.c msg_${msg}.exp@:Nmsg_176.exp} +FILES+= ${:U0 ${:U:${:Urange=${MAX_MESSAGE}}}:C,^.$,0&,:C,^..$,0&,:@i@msg_${i}.c msg_${i}.exp@:Nmsg_176.exp} FILES+= op_colon.c FILES+= op_colon.exp diff --git a/tests/usr.bin/xlint/lint1/msg_341.c b/tests/usr.bin/xlint/lint1/msg_341.c new file mode 100644 index 000000000000..bbd2a900a7ae --- /dev/null +++ b/tests/usr.bin/xlint/lint1/msg_341.c @@ -0,0 +1,77 @@ +/* $NetBSD: msg_341.c,v 1.1 2021/04/05 02:05:47 rillig Exp $ */ +# 3 "msg_341.c" + +// Test for message: argument to '%s' must be 'unsigned char' or EOF, not '%s' [341] + +/* + * Ensure that the functions from are called with the correct + * argument. + */ + +/* NetBSD 9.99.81, */ +extern const unsigned short *_ctype_tab_; +extern const short *_tolower_tab_; +extern const short *_toupper_tab_; +int isspace(int); + +void sink(int); + +void +function_call_char(char c) +{ + + /* expect+1: argument to 'isspace' must be 'unsigned char' or EOF, not 'char' */ + (isspace)(c); + + /* This is the only allowed form. */ + isspace((unsigned char)c); + + /* The cast to 'int' is redundant, it doesn't hurt though. */ + isspace((int)(unsigned char)c); + + /* expect+1: argument to 'isspace' must be cast to 'unsigned char', not to 'int' */ + isspace((int)c); + + /* expect+1: argument to 'isspace' must be cast to 'unsigned char', not to 'unsigned int' */ + isspace((unsigned int)c); +} + +/* + * If the expression starts with type 'unsigned char', it can be cast to any + * other type. Chances are low enough that the cast is to 'char', which would + * be the only bad type. + */ +void +function_call_unsigned_char(unsigned char c) +{ + + (isspace)(c); + isspace((unsigned char)c); + isspace((int)c); + isspace((unsigned int)c); +} + +/* When used in a loop of fgetc, the type is already 'int'. That's fine. */ +void +function_call_int(int c) +{ + + isspace(c); +} + +void +macro_invocation_NetBSD(char c) +{ + + /* expect+1: argument to 'function from ' must be 'unsigned char' or EOF, not 'char' */ + sink(((int)((_ctype_tab_ + 1)[(c)] & 0x0040))); + + /* This is the only allowed form. */ + sink(((int)((_ctype_tab_ + 1)[((unsigned char)c)] & 0x0040))); + + /* expect+1: argument to 'function from ' must be cast to 'unsigned char', not to 'int' */ + sink(((int)((_ctype_tab_ + 1)[((int)c)] & 0x0040))); + + /* expect+1: argument to 'function from ' must be cast to 'unsigned char', not to 'unsigned int' */ + sink(((int)((_ctype_tab_ + 1)[((unsigned int)c)] & 0x0040))); +} diff --git a/tests/usr.bin/xlint/lint1/msg_341.exp b/tests/usr.bin/xlint/lint1/msg_341.exp new file mode 100644 index 000000000000..b03ac9231075 --- /dev/null +++ b/tests/usr.bin/xlint/lint1/msg_341.exp @@ -0,0 +1,6 @@ +msg_341.c(24): warning: argument to 'isspace' must be 'unsigned char' or EOF, not 'char' [341] +msg_341.c(33): warning: argument to 'isspace' must be cast to 'unsigned char', not to 'int' [342] +msg_341.c(36): warning: argument to 'isspace' must be cast to 'unsigned char', not to 'unsigned int' [342] +msg_341.c(67): warning: argument to 'function from ' must be 'unsigned char' or EOF, not 'char' [341] +msg_341.c(73): warning: argument to 'function from ' must be cast to 'unsigned char', not to 'int' [342] +msg_341.c(76): warning: argument to 'function from ' must be cast to 'unsigned char', not to 'unsigned int' [342] diff --git a/tests/usr.bin/xlint/lint1/msg_342.c b/tests/usr.bin/xlint/lint1/msg_342.c new file mode 100644 index 000000000000..59ff8a104961 --- /dev/null +++ b/tests/usr.bin/xlint/lint1/msg_342.c @@ -0,0 +1,77 @@ +/* $NetBSD: msg_342.c,v 1.1 2021/04/05 02:05:47 rillig Exp $ */ +# 3 "msg_341.c" + +// Test for message: argument to '%s' must be cast to 'unsigned char', not to '%s' [342] + +/* + * Ensure that the functions from are called with the correct + * argument. + */ + +/* NetBSD 9.99.81, */ +extern const unsigned short *_ctype_tab_; +extern const short *_tolower_tab_; +extern const short *_toupper_tab_; +int isspace(int); + +void sink(int); + +void +function_call_char(char c) +{ + + /* expect+1: argument to 'isspace' must be 'unsigned char' or EOF, not 'char' */ + (isspace)(c); + + /* This is the only allowed form. */ + isspace((unsigned char)c); + + /* The cast to 'int' is redundant, it doesn't hurt though. */ + isspace((int)(unsigned char)c); + + /* expect+1: argument to 'isspace' must be cast to 'unsigned char', not to 'int' */ + isspace((int)c); + + /* expect+1: argument to 'isspace' must be cast to 'unsigned char', not to 'unsigned int' */ + isspace((unsigned int)c); +} + +/* + * If the expression starts with type 'unsigned char', it can be cast to any + * other type. Chances are low enough that the cast is to 'char', which would + * be the only bad type. + */ +void +function_call_unsigned_char(unsigned char c) +{ + + (isspace)(c); + isspace((unsigned char)c); + isspace((int)c); + isspace((unsigned int)c); +} + +/* When used in a loop of fgetc, the type is already 'int'. That's fine. */ +void +function_call_int(int c) +{ + + isspace(c); +} + +void +macro_invocation_NetBSD(char c) +{ + + /* expect+1: argument to 'function from ' must be 'unsigned char' or EOF, not 'char' */ + sink(((int)((_ctype_tab_ + 1)[(c)] & 0x0040))); + + /* This is the only allowed form. */ + sink(((int)((_ctype_tab_ + 1)[((unsigned char)c)] & 0x0040))); + + /* expect+1: argument to 'function from ' must be cast to 'unsigned char', not to 'int' */ + sink(((int)((_ctype_tab_ + 1)[((int)c)] & 0x0040))); + + /* expect+1: argument to 'function from ' must be cast to 'unsigned char', not to 'unsigned int' */ + sink(((int)((_ctype_tab_ + 1)[((unsigned int)c)] & 0x0040))); +} diff --git a/tests/usr.bin/xlint/lint1/msg_342.exp b/tests/usr.bin/xlint/lint1/msg_342.exp new file mode 100644 index 000000000000..b03ac9231075 --- /dev/null +++ b/tests/usr.bin/xlint/lint1/msg_342.exp @@ -0,0 +1,6 @@ +msg_341.c(24): warning: argument to 'isspace' must be 'unsigned char' or EOF, not 'char' [341] +msg_341.c(33): warning: argument to 'isspace' must be cast to 'unsigned char', not to 'int' [342] +msg_341.c(36): warning: argument to 'isspace' must be cast to 'unsigned char', not to 'unsigned int' [342] +msg_341.c(67): warning: argument to 'function from ' must be 'unsigned char' or EOF, not 'char' [341] +msg_341.c(73): warning: argument to 'function from ' must be cast to 'unsigned char', not to 'int' [342] +msg_341.c(76): warning: argument to 'function from ' must be cast to 'unsigned char', not to 'unsigned int' [342] diff --git a/tests/usr.bin/xlint/lint1/t_integration.sh b/tests/usr.bin/xlint/lint1/t_integration.sh index f4290028ba69..e610a2204474 100644 --- a/tests/usr.bin/xlint/lint1/t_integration.sh +++ b/tests/usr.bin/xlint/lint1/t_integration.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_integration.sh,v 1.38 2021/04/02 17:25:04 rillig Exp $ +# $NetBSD: t_integration.sh,v 1.39 2021/04/05 02:05:47 rillig Exp $ # # Copyright (c) 2008, 2010 The NetBSD Foundation, Inc. # All rights reserved. @@ -175,7 +175,7 @@ all_messages_body() failed="" - for msg in $(seq 0 340); do + for msg in $(seq 0 342); do name="$(printf 'msg_%03d.c' "${msg}")" check_lint1 "${name}" \ || failed="$failed${failed:+ }$name" diff --git a/usr.bin/xlint/lint1/Makefile b/usr.bin/xlint/lint1/Makefile index d2253bbf4435..6db00492db65 100644 --- a/usr.bin/xlint/lint1/Makefile +++ b/usr.bin/xlint/lint1/Makefile @@ -1,9 +1,9 @@ -# $NetBSD: Makefile,v 1.64 2021/03/21 13:10:58 rillig Exp $ +# $NetBSD: Makefile,v 1.65 2021/04/05 02:05:47 rillig Exp $ .include PROG= lint1 -SRCS= cgram.y ckgetopt.c decl.c emit.c emit1.c err.c \ +SRCS= cgram.y ckctype.c ckgetopt.c decl.c emit.c emit1.c err.c \ func.c init.c inittyp.c lex.c \ main1.c mem.c mem1.c oper.c print.c scan.l tree.c tyname.c diff --git a/usr.bin/xlint/lint1/ckctype.c b/usr.bin/xlint/lint1/ckctype.c new file mode 100644 index 000000000000..c63b1b02689e --- /dev/null +++ b/usr.bin/xlint/lint1/ckctype.c @@ -0,0 +1,151 @@ +/* $NetBSD: ckctype.c,v 1.1 2021/04/05 02:07:14 rillig Exp $ */ + +/*- + * Copyright (c) 2021 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Roland Illig . + * + * 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. + */ + +#if HAVE_NBTOOL_CONFIG_H + +#include "nbtool_config.h" + +#endif + +#include + +#if defined(__RCSID) && !defined(lint) +__RCSID("$NetBSD: ckctype.c,v 1.1 2021/04/05 02:07:14 rillig Exp $"); +#endif + +#include + +#include "lint1.h" + +/* + * Check that the functions from are used properly. They are + * difficult to use when their argument comes from an expression of type + * 'char'. In such a case, the argument must be converted to 'unsigned char', + * not directly to 'int'. + * + * https://stackoverflow.com/a/60696378 + */ + +#define NEED(cond) \ + do { \ + if (!(cond)) \ + return; \ + } while (false) + +static bool +is_ctype_function(const char *name) +{ + + if (name[0] != 'i' || name[1] != 's') + if (name[0] != 't' || name[1] != 'o') + return false; + + return strcmp(name, "isalnum") == 0 || + strcmp(name, "isalpha") == 0 || + strcmp(name, "isblank") == 0 || + strcmp(name, "iscntrl") == 0 || + strcmp(name, "isdigit") == 0 || + strcmp(name, "isgraph") == 0 || + strcmp(name, "islower") == 0 || + strcmp(name, "isprint") == 0 || + strcmp(name, "ispunct") == 0 || + strcmp(name, "isspace") == 0 || + strcmp(name, "isupper") == 0 || + strcmp(name, "isxdigit") == 0 || + strcmp(name, "tolower") == 0 || + strcmp(name, "toupper") == 0; +} + +static bool +is_ctype_table(const char *name) +{ + + /* NetBSD sys/ctype_bits.h 1.6 from 2016-01-22 */ + if (strcmp(name, "_ctype_tab_") == 0 || + strcmp(name, "_tolower_tab_") == 0 || + strcmp(name, "_toupper_tab_") == 0) + return true; + + /* NetBSD sys/ctype_bits.h 1.1 from 2010-06-01 */ + return strcmp(name, "_ctype_") == 0; +} + +static void +check_ctype_arg(const char *func, const tnode_t *arg) +{ + const tnode_t *on, *cn; + + for (on = arg; on->tn_op == CVT; on = on->tn_left) + if (on->tn_type->t_tspec == UCHAR) + return; + if (on->tn_type->t_tspec == UCHAR) + return; + + if (arg->tn_op == CVT && arg->tn_cast) { + /* argument to '%s' must be cast to 'unsigned char', not to '%s' */ + warning(342, func, type_name(arg->tn_type)); + return; + } + + cn = before_conversion(arg); + if (cn->tn_type->t_tspec == CHAR) { + /* argument to '%s' must be 'unsigned char' or EOF, not '%s' */ + warning(341, func, type_name(cn->tn_type)); + return; + } +} + +void +check_ctype_function_call(const tnode_t *func, const tnode_t *args) +{ + + NEED(func->tn_op == NAME); + NEED(is_ctype_function(func->tn_sym->s_name)); + NEED(args != NULL); + NEED(args->tn_left != NULL); + NEED(args->tn_right == NULL); + + check_ctype_arg(func->tn_sym->s_name, args->tn_left); +} + +void +check_ctype_macro_invocation(const tnode_t *ln, const tnode_t *rn) +{ + + NEED(ln->tn_op == PLUS); + NEED(ln->tn_left != NULL); + NEED(ln->tn_left->tn_op == LOAD); + NEED(ln->tn_left->tn_left != NULL); + NEED(ln->tn_left->tn_left->tn_op == NAME); + NEED(is_ctype_table(ln->tn_left->tn_left->tn_sym->s_name)); + + check_ctype_arg("function from ", rn); +} diff --git a/usr.bin/xlint/lint1/err.c b/usr.bin/xlint/lint1/err.c index ca21f89a160c..38be84637394 100644 --- a/usr.bin/xlint/lint1/err.c +++ b/usr.bin/xlint/lint1/err.c @@ -1,4 +1,4 @@ -/* $NetBSD: err.c,v 1.101 2021/04/02 22:41:53 rillig Exp $ */ +/* $NetBSD: err.c,v 1.102 2021/04/05 02:05:47 rillig Exp $ */ /* * Copyright (c) 1994, 1995 Jochen Pohl @@ -37,7 +37,7 @@ #include #if defined(__RCSID) && !defined(lint) -__RCSID("$NetBSD: err.c,v 1.101 2021/04/02 22:41:53 rillig Exp $"); +__RCSID("$NetBSD: err.c,v 1.102 2021/04/05 02:05:47 rillig Exp $"); #endif #include @@ -395,6 +395,8 @@ const char *msgs[] = { "option '%c' should be handled in the switch", /* 338 */ "option '%c' should be listed in the options string", /* 339 */ "initialization with '[a...b]' is a GNU extension", /* 340 */ + "argument to '%s' must be 'unsigned char' or EOF, not '%s'", /* 341 */ + "argument to '%s' must be cast to 'unsigned char', not to '%s'", /* 342 */ }; /* diff --git a/usr.bin/xlint/lint1/externs1.h b/usr.bin/xlint/lint1/externs1.h index 1af8f43fb35f..0e2b1bf8dc25 100644 --- a/usr.bin/xlint/lint1/externs1.h +++ b/usr.bin/xlint/lint1/externs1.h @@ -1,4 +1,4 @@ -/* $NetBSD: externs1.h,v 1.99 2021/04/02 11:53:25 rillig Exp $ */ +/* $NetBSD: externs1.h,v 1.100 2021/04/05 02:05:47 rillig Exp $ */ /* * Copyright (c) 1994, 1995 Jochen Pohl @@ -196,6 +196,7 @@ extern int to_int_constant(tnode_t *, bool); /* * tree.c */ +extern const tnode_t *before_conversion(const tnode_t *); extern type_t *derive_type(type_t *, tspec_t); extern type_t *expr_derive_type(type_t *, tspec_t); extern tnode_t *expr_new_constant(type_t *, val_t *); @@ -334,6 +335,12 @@ extern int lex_input(void); */ extern char *print_tnode(char *, size_t, const tnode_t *); +/* + * ckctype.c + */ +extern void check_ctype_function_call(const tnode_t *, const tnode_t *); +extern void check_ctype_macro_invocation(const tnode_t *, const tnode_t *); + /* * ckgetopt.c */ diff --git a/usr.bin/xlint/lint1/tree.c b/usr.bin/xlint/lint1/tree.c index 1ec53fbc007e..df419df1f27a 100644 --- a/usr.bin/xlint/lint1/tree.c +++ b/usr.bin/xlint/lint1/tree.c @@ -1,4 +1,4 @@ -/* $NetBSD: tree.c,v 1.265 2021/04/02 22:41:53 rillig Exp $ */ +/* $NetBSD: tree.c,v 1.266 2021/04/05 02:05:47 rillig Exp $ */ /* * Copyright (c) 1994, 1995 Jochen Pohl @@ -37,7 +37,7 @@ #include #if defined(__RCSID) && !defined(lint) -__RCSID("$NetBSD: tree.c,v 1.265 2021/04/02 22:41:53 rillig Exp $"); +__RCSID("$NetBSD: tree.c,v 1.266 2021/04/05 02:05:47 rillig Exp $"); #endif #include @@ -718,7 +718,7 @@ cconv(tnode_t *tn) return tn; } -static const tnode_t * +const tnode_t * before_conversion(const tnode_t *tn) { while (tn->tn_op == CVT && !tn->tn_cast) @@ -2800,8 +2800,11 @@ build_plus_minus(op_t op, tnode_t *ln, tnode_t *rn) if (ln->tn_type->t_tspec == PTR && rn->tn_type->t_tspec != PTR) { + /* XXX: this assertion should be easy to trigger */ lint_assert(is_integer(rn->tn_type->t_tspec)); + check_ctype_macro_invocation(ln, rn); + ctn = plength(ln->tn_type); if (rn->tn_type->t_tspec != ctn->tn_type->t_tspec) rn = convert(NOOP, 0, ctn->tn_type, rn); @@ -3585,6 +3588,8 @@ new_function_call_node(tnode_t *func, tnode_t *args) fcop = ICALL; } + check_ctype_function_call(func, args); + /* * after cconv() func will always be a pointer to a function * if it is a valid function designator.