Module Name:    src
Committed By:   rillig
Date:           Wed Sep 13 20:31:58 UTC 2023

Modified Files:
        src/usr.bin/xlint/common: lint.h
        src/usr.bin/xlint/lint1: ckctype.c debug.c emit1.c lint1.h op.h oper.c
            ops.def tree.c

Log Message:
lint: prevent invalid memory access when checking an expression

In check_expr_misc, the left and right operands of an expression were
accessed even in the case of CON (constant), STRING (string literal) and
NAME (identifier), which led to invalid values in pointer variables.
These invalid values were not used though, but technically they invoked
undefined behavior.

Precede each access to the operands with a check that the expression
indeed has operands, except in those cases where the operand is known to
have operands by only looking at the code of the current function.


To generate a diff of this commit:
cvs rdiff -u -r1.43 -r1.44 src/usr.bin/xlint/common/lint.h
cvs rdiff -u -r1.6 -r1.7 src/usr.bin/xlint/lint1/ckctype.c
cvs rdiff -u -r1.61 -r1.62 src/usr.bin/xlint/lint1/debug.c
cvs rdiff -u -r1.76 -r1.77 src/usr.bin/xlint/lint1/emit1.c
cvs rdiff -u -r1.200 -r1.201 src/usr.bin/xlint/lint1/lint1.h
cvs rdiff -u -r1.21 -r1.22 src/usr.bin/xlint/lint1/op.h
cvs rdiff -u -r1.11 -r1.12 src/usr.bin/xlint/lint1/oper.c
cvs rdiff -u -r1.30 -r1.31 src/usr.bin/xlint/lint1/ops.def
cvs rdiff -u -r1.580 -r1.581 src/usr.bin/xlint/lint1/tree.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/usr.bin/xlint/common/lint.h
diff -u src/usr.bin/xlint/common/lint.h:1.43 src/usr.bin/xlint/common/lint.h:1.44
--- src/usr.bin/xlint/common/lint.h:1.43	Sat Aug 12 18:05:51 2023
+++ src/usr.bin/xlint/common/lint.h	Wed Sep 13 20:31:58 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: lint.h,v 1.43 2023/08/12 18:05:51 rillig Exp $	*/
+/*	$NetBSD: lint.h,v 1.44 2023/09/13 20:31:58 rillig Exp $	*/
 
 /*
  * Copyright (c) 1994, 1995 Jochen Pohl
@@ -134,6 +134,8 @@ type_properties(tspec_t t) {
 #define is_complex(t)		(type_properties(t)->tt_is_complex)
 #define is_scalar(t)		(type_properties(t)->tt_is_scalar)
 
+#define has_operands(tn)	(modtab[(tn)->tn_op].m_has_operands)
+
 
 typedef	enum {
 	NODECL,			/* not declared until now */

Index: src/usr.bin/xlint/lint1/ckctype.c
diff -u src/usr.bin/xlint/lint1/ckctype.c:1.6 src/usr.bin/xlint/lint1/ckctype.c:1.7
--- src/usr.bin/xlint/lint1/ckctype.c:1.6	Wed Aug  2 18:51:25 2023
+++ src/usr.bin/xlint/lint1/ckctype.c	Wed Sep 13 20:31:58 2023
@@ -1,4 +1,4 @@
-/* $NetBSD: ckctype.c,v 1.6 2023/08/02 18:51:25 rillig Exp $ */
+/* $NetBSD: ckctype.c,v 1.7 2023/09/13 20:31:58 rillig Exp $ */
 
 /*-
  * Copyright (c) 2021 The NetBSD Foundation, Inc.
@@ -36,7 +36,7 @@
 #include <sys/cdefs.h>
 
 #if defined(__RCSID)
-__RCSID("$NetBSD: ckctype.c,v 1.6 2023/08/02 18:51:25 rillig Exp $");
+__RCSID("$NetBSD: ckctype.c,v 1.7 2023/09/13 20:31:58 rillig Exp $");
 #endif
 
 #include <string.h>
@@ -126,7 +126,7 @@ check_ctype_function_call(const tnode_t 
 	if (func->tn_op == NAME &&
 	    is_ctype_function(func->tn_sym->s_name) &&
 	    args != NULL &&
-	    args->tn_left != NULL &&
+	    tn_ck_left(args) != NULL &&
 	    args->tn_right == NULL)
 		check_ctype_arg(func->tn_sym->s_name, args->tn_left);
 }

Index: src/usr.bin/xlint/lint1/debug.c
diff -u src/usr.bin/xlint/lint1/debug.c:1.61 src/usr.bin/xlint/lint1/debug.c:1.62
--- src/usr.bin/xlint/lint1/debug.c:1.61	Wed Aug  2 21:58:11 2023
+++ src/usr.bin/xlint/lint1/debug.c	Wed Sep 13 20:31:58 2023
@@ -1,4 +1,4 @@
-/* $NetBSD: debug.c,v 1.61 2023/08/02 21:58:11 rillig Exp $ */
+/* $NetBSD: debug.c,v 1.62 2023/09/13 20:31:58 rillig Exp $ */
 
 /*-
  * Copyright (c) 2021 The NetBSD Foundation, Inc.
@@ -35,7 +35,7 @@
 
 #include <sys/cdefs.h>
 #if defined(__RCSID)
-__RCSID("$NetBSD: debug.c,v 1.61 2023/08/02 21:58:11 rillig Exp $");
+__RCSID("$NetBSD: debug.c,v 1.62 2023/09/13 20:31:58 rillig Exp $");
 #endif
 
 #include <stdlib.h>
@@ -253,6 +253,7 @@ debug_node(const tnode_t *tn) // NOLINT(
 		debug_printf("\n");
 
 		debug_indent_inc();
+		lint_assert(has_operands(tn));
 		lint_assert(tn->tn_left != NULL);
 		debug_node(tn->tn_left);
 		if (op != INCBEF && op != INCAFT

Index: src/usr.bin/xlint/lint1/emit1.c
diff -u src/usr.bin/xlint/lint1/emit1.c:1.76 src/usr.bin/xlint/lint1/emit1.c:1.77
--- src/usr.bin/xlint/lint1/emit1.c:1.76	Sat Aug 12 21:32:16 2023
+++ src/usr.bin/xlint/lint1/emit1.c	Wed Sep 13 20:31:58 2023
@@ -1,4 +1,4 @@
-/* $NetBSD: emit1.c,v 1.76 2023/08/12 21:32:16 rillig Exp $ */
+/* $NetBSD: emit1.c,v 1.77 2023/09/13 20:31:58 rillig Exp $ */
 
 /*
  * Copyright (c) 1996 Christopher G. Demetriou.  All Rights Reserved.
@@ -38,7 +38,7 @@
 
 #include <sys/cdefs.h>
 #if defined(__RCSID)
-__RCSID("$NetBSD: emit1.c,v 1.76 2023/08/12 21:32:16 rillig Exp $");
+__RCSID("$NetBSD: emit1.c,v 1.77 2023/09/13 20:31:58 rillig Exp $");
 #endif
 
 #include "lint1.h"
@@ -338,8 +338,8 @@ outcall(const tnode_t *tn, bool retval_u
 	 * the function
 	 */
 	narg = 0;
-	args = tn->tn_right;
-	for (arg = args; arg != NULL; arg = arg->tn_right)
+	args = tn_ck_right(tn);
+	for (arg = args; arg != NULL; arg = tn_ck_right(arg))
 		narg++;
 	/* information about arguments */
 	for (n = 1; n <= narg; n++) {
@@ -379,7 +379,7 @@ outcall(const tnode_t *tn, bool retval_u
 	outchar((char)(retval_discarded ? 'd' : retval_used ? 'u' : 'i'));
 
 	/* name of the called function */
-	outname(tn->tn_left->tn_left->tn_sym->s_name);
+	outname(tn_ck_left(tn->tn_left)->tn_sym->s_name);
 
 	/* types of arguments */
 	outchar('f');

Index: src/usr.bin/xlint/lint1/lint1.h
diff -u src/usr.bin/xlint/lint1/lint1.h:1.200 src/usr.bin/xlint/lint1/lint1.h:1.201
--- src/usr.bin/xlint/lint1/lint1.h:1.200	Sat Aug 26 10:43:53 2023
+++ src/usr.bin/xlint/lint1/lint1.h	Wed Sep 13 20:31:58 2023
@@ -1,4 +1,4 @@
-/* $NetBSD: lint1.h,v 1.200 2023/08/26 10:43:53 rillig Exp $ */
+/* $NetBSD: lint1.h,v 1.201 2023/09/13 20:31:58 rillig Exp $ */
 
 /*
  * Copyright (c) 1996 Christopher G. Demetriou.  All Rights Reserved.
@@ -502,6 +502,20 @@ typedef enum {
 			assert_failed(__FILE__, __LINE__, __func__, #cond); \
 	} while (false)
 
+static inline tnode_t *
+tn_ck_left(const tnode_t *tn)
+{
+	lint_assert(has_operands(tn));
+	return tn->tn_left;
+}
+
+static inline tnode_t *
+tn_ck_right(const tnode_t *tn)
+{
+	lint_assert(has_operands(tn));
+	return tn->tn_right;
+}
+
 #ifdef DEBUG
 #  include "err-msgs.h"
 

Index: src/usr.bin/xlint/lint1/op.h
diff -u src/usr.bin/xlint/lint1/op.h:1.21 src/usr.bin/xlint/lint1/op.h:1.22
--- src/usr.bin/xlint/lint1/op.h:1.21	Thu Jul 13 08:40:38 2023
+++ src/usr.bin/xlint/lint1/op.h	Wed Sep 13 20:31:58 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: op.h,v 1.21 2023/07/13 08:40:38 rillig Exp $	*/
+/*	$NetBSD: op.h,v 1.22 2023/09/13 20:31:58 rillig Exp $	*/
 
 /*
  * Copyright (c) 1994, 1995 Jochen Pohl
@@ -56,6 +56,7 @@ typedef	struct {
 	bool	m_valid_on_enum: 1;
 	bool	m_bad_on_enum: 1;
 	bool	m_warn_if_operand_eq: 1;
+	bool	m_has_operands: 1;
 	const char *m_name;
 } mod_t;
 
@@ -68,7 +69,8 @@ extern const mod_t modtab[];
 		can_fold, is_value, unused, balances_operands, \
 		side_effects, left_unsigned, right_unsigned, \
 		precedence_confusion, is_comparison, \
-		valid_on_enum, bad_on_enum, warn_if_eq) \
+		valid_on_enum, bad_on_enum, warn_if_eq, \
+		has_operands) \
 	name,
 #define end_ops() } op_t;
 #include "ops.def"

Index: src/usr.bin/xlint/lint1/oper.c
diff -u src/usr.bin/xlint/lint1/oper.c:1.11 src/usr.bin/xlint/lint1/oper.c:1.12
--- src/usr.bin/xlint/lint1/oper.c:1.11	Sat Apr 16 22:21:10 2022
+++ src/usr.bin/xlint/lint1/oper.c	Wed Sep 13 20:31:58 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: oper.c,v 1.11 2022/04/16 22:21:10 rillig Exp $	*/
+/*	$NetBSD: oper.c,v 1.12 2023/09/13 20:31:58 rillig Exp $	*/
 
 /*-
  * Copyright (c) 2021 The NetBSD Foundation, Inc.
@@ -41,7 +41,7 @@ const mod_t modtab[NOPS] =
 		can_fold, is_value, unused, balances_operands, \
 		side_effects, left_unsigned, right_unsigned, \
 		precedence_confusion, is_comparison, \
-		valid_on_enum, bad_on_enum, warn_if_eq) \
+		valid_on_enum, bad_on_enum, warn_if_eq, has_operands) \
 	{ \
 		is_binary	+ 0 > 0, is_logical		+ 0 > 0, \
 		takes_bool	+ 0 > 0, requires_bool		+ 0 > 0, \
@@ -53,6 +53,7 @@ const mod_t modtab[NOPS] =
 		right_unsigned	+ 0 > 0, precedence_confusion	+ 0 > 0, \
 		is_comparison	+ 0 > 0, valid_on_enum		+ 0 > 0, \
 		bad_on_enum	+ 0 > 0, warn_if_eq		+ 0 > 0, \
+		has_operands	+ 0 > 0, \
 		repr, \
 	},
 #define end_ops(n) };

Index: src/usr.bin/xlint/lint1/ops.def
diff -u src/usr.bin/xlint/lint1/ops.def:1.30 src/usr.bin/xlint/lint1/ops.def:1.31
--- src/usr.bin/xlint/lint1/ops.def:1.30	Sat Jun  3 20:58:00 2023
+++ src/usr.bin/xlint/lint1/ops.def	Wed Sep 13 20:31:58 2023
@@ -1,123 +1,124 @@
-/*	$NetBSD: ops.def,v 1.30 2023/06/03 20:58:00 rillig Exp $ */
+/*	$NetBSD: ops.def,v 1.31 2023/09/13 20:31:58 rillig Exp $ */
 
 begin_ops()
 
 /*
  * Operator properties; see mod_t in op.h.
  *
- *	warn if operand '='					      x
- *	bad on enum						    x
- *	valid on enum						  x
- *	comparison		- - - - - - - - - - - - - - - - x
- *	possible precedence confusion			      x	|
- *	warn if right operand unsigned			    x	|
- *	warn if left operand unsigned			  x	|
- *	has side effects	- - - - - - - - - - - - x	|
- *	balance operands			      x	|	|
- *	(unused)				    x	|	|
- *	value context				  x	|	|
- *	fold constant operands	- - - - - - - - x	|	|
- *	requires scalar			      x	|	|	|
- *	requires arithmetic		    x	|	|	|
- *	requires integer or complex	  x	|	|	|
- *	requires integer	- - - - x	|	|	|
- *	compares with zero	      x	|	|	|	|
- *	takes _Bool		    x	|	|	|	|
- *	logical			  x	|	|	|	|
- *	binary			x	|	|	|	|
+ *	has operands		- - - - - - - - - - - - - - - - - - - - x
+ *	warn if operand '='					      x	|
+ *	bad on enum						    x	|
+ *	valid on enum						  x	|
+ *	comparison		- - - - - - - - - - - - - - - - x	|
+ *	possible precedence confusion			      x	|	|
+ *	warn if right operand unsigned			    x	|	|
+ *	warn if left operand unsigned			  x	|	|
+ *	has side effects	- - - - - - - - - - - - x	|	|
+ *	balance operands			      x	|	|	|
+ *	(unused)				    x	|	|	|
+ *	value context				  x	|	|	|
+ *	fold constant operands	- - - - - - - - x	|	|	|
+ *	requires scalar			      x	|	|	|	|
+ *	requires arithmetic		    x	|	|	|	|
+ *	requires integer or complex	  x	|	|	|	|
+ *	requires integer	- - - - x	|	|	|	|
+ *	compares with zero	      x	|	|	|	|	|
+ *	takes _Bool		    x	|	|	|	|	|
+ *	logical			  x	|	|	|	|	|
+ *	binary			x	|	|	|	|	|
  */
-/*	name	repr		b l b z i c a s f v - b s l r p c e e =	*/
-op(	NOOP,	"no-op",	 , , , , , , , , , , , , , , , , , , , )
-op(	ARROW,	"->",		1, ,1, , , , , , ,1, , , , , , , , , , )
-op(	POINT,	".",		1, ,1, , , , , , , , , , , , , , , , , )
-op(	NOT,	"!",		 ,1,1,1, , , ,1,1, , , , , , , , , ,1, )
-op(	COMPL,	"~",		 , , , , ,1, , ,1,1, , , , , , , , ,1,1)
-op(	INC,	"++",		 , , , , , , , , , , , , , , , , , , , )
-op(	DEC,	"--",		 , , , , , , , , , , , , , , , , , , , )
+/*	name	repr		b l b z i c a s f v - b s l r p c e e =	o */
+op(	NOOP,	"no-op",	 , , , , , , , , , , , , , , , , , , , , )
+op(	ARROW,	"->",		1, ,1, , , , , , ,1, , , , , , , , , , ,1)
+op(	POINT,	".",		1, ,1, , , , , , , , , , , , , , , , , ,1)
+op(	NOT,	"!",		 ,1,1,1, , , ,1,1, , , , , , , , , ,1, ,1)
+op(	COMPL,	"~",		 , , , , ,1, , ,1,1, , , , , , , , ,1,1,1)
+op(	INC,	"++",		 , , , , , , , , , , , , , , , , , , , ,1)
+op(	DEC,	"--",		 , , , , , , , , , , , , , , , , , , , ,1)
 /*
  * The '++' and '--' operators are conceptually unary operators, but lint
  * implements them as binary operators due to the pre-multiplied pointer
  * arithmetics, see build_prepost_incdec and build_plus_minus.
  */
-op(	INCBEF,	"++x",		 , , , , , , ,1, , , , ,1, , , , , ,1, )
-op(	DECBEF,	"--x",		 , , , , , , ,1, , , , ,1, , , , , ,1, )
-op(	INCAFT,	"x++",		 , , , , , , ,1, , , , ,1, , , , , ,1, )
-op(	DECAFT,	"x--",		 , , , , , , ,1, , , , ,1, , , , , ,1, )
-op(	UPLUS,	"+",		 , , , , , ,1, ,1,1, , , , , , , , ,1,1)
-op(	UMINUS,	"-",		 , , , , , ,1, ,1,1, , , ,1, , , , ,1,1)
-op(	INDIR,	"*",		 , , , , , , , , ,1, , , , , , , , , , )
-op(	ADDR,	"&",		 , ,1, , , , , , , , , , , , , , , , , )
+op(	INCBEF,	"++x",		 , , , , , , ,1, , , , ,1, , , , , ,1, ,1)
+op(	DECBEF,	"--x",		 , , , , , , ,1, , , , ,1, , , , , ,1, ,1)
+op(	INCAFT,	"x++",		 , , , , , , ,1, , , , ,1, , , , , ,1, ,1)
+op(	DECAFT,	"x--",		 , , , , , , ,1, , , , ,1, , , , , ,1, ,1)
+op(	UPLUS,	"+",		 , , , , , ,1, ,1,1, , , , , , , , ,1,1,1)
+op(	UMINUS,	"-",		 , , , , , ,1, ,1,1, , , ,1, , , , ,1,1,1)
+op(	INDIR,	"*",		 , , , , , , , , ,1, , , , , , , , , , ,1)
+op(	ADDR,	"&",		 , ,1, , , , , , , , , , , , , , , , , ,1)
 /* the operator 'arr[ind]' is translated to '*(arr + ind)' during parsing. */
 
-/*	name	repr		b l b z i c a s f v - b s l r p c e e =	*/
-op(	MULT,	"*",		1, , , , , ,1, ,1,1, ,1, , ,1, , , ,1,1)
-op(	DIV,	"/",		1, , , , , ,1, ,1,1, ,1, ,1,1, , , ,1,1)
-op(	MOD,	"%",		1, , , ,1, , , ,1,1, ,1, ,1,1, , , ,1,1)
-op(	PLUS,	"+",		1, , , , , , ,1,1,1, ,1, , , , , , ,1, )
-op(	MINUS,	"-",		1, , , , , , ,1,1,1, ,1, , , , , , ,1, )
-op(	SHL,	"<<",		1, , , ,1, , , ,1,1, , , , , ,1, , ,1,1)
-op(	SHR,	">>",		1, , , ,1, , , ,1,1, , , ,1, ,1, , ,1,1)
-
-/*	name	repr		b l b z i c a s f v - b s l r p c e e =	*/
-op(	LT,	"<",		1,1, , , , , ,1,1,1, ,1, ,1,1, ,1,1, ,1)
-op(	LE,	"<=",		1,1, , , , , ,1,1,1, ,1, ,1,1, ,1,1, ,1)
-op(	GT,	">",		1,1, , , , , ,1,1,1, ,1, ,1,1, ,1,1, ,1)
-op(	GE,	">=",		1,1, , , , , ,1,1,1, ,1, ,1,1, ,1,1, ,1)
-op(	EQ,	"==",		1,1,1, , , , ,1,1,1, ,1, , , , ,1,1, ,1)
-op(	NE,	"!=",		1,1,1, , , , ,1,1,1, ,1, , , , ,1,1, ,1)
-
-/*	name	repr		b l b z i c a s f v - b s l r p c e e =	*/
-op(	BITAND,	"&",		1, ,1, ,1, , , ,1,1, ,1, , , ,1, , ,1, )
-op(	BITXOR,	"^",		1, ,1, ,1, , , ,1,1, ,1, , , ,1, , ,1, )
-op(	BITOR,	"|",		1, ,1, ,1, , , ,1,1, ,1, , , ,1, , ,1, )
-op(	LOGAND,	"&&",		1,1,1,1, , , ,1,1, , , , , , , , , ,1, )
-op(	LOGOR,	"||",		1,1,1,1, , , ,1,1, , , , , , ,1, , ,1, )
-op(	QUEST,	"?",		1, , ,1, , , , ,1, , , , , , , , , , , )
-op(	COLON,	":",		1, ,1, , , , , , ,1, ,1, , , , , ,1, , )
-
-/*	name	repr		b l b z i c a s f v - b s l r p c e e =	*/
-op(	ASSIGN,	"=",		1, ,1, , , , , , , , , ,1, , , , ,1, , )
-op(	MULASS,	"*=",		1, , , , , ,1, , , , , ,1, , , , , ,1, )
-op(	DIVASS,	"/=",		1, , , , , ,1, , , , , ,1, ,1, , , ,1, )
-op(	MODASS,	"%=",		1, , , ,1, , , , , , , ,1, ,1, , , ,1, )
-op(	ADDASS,	"+=",		1, , , , , , ,1, , , , ,1, , , , , ,1, )
-op(	SUBASS,	"-=",		1, , , , , , ,1, , , , ,1, , , , , ,1, )
-op(	SHLASS,	"<<=",		1, , , ,1, , , , , , , ,1, , , , , ,1, )
-op(	SHRASS,	">>=",		1, , , ,1, , , , , , , ,1, , , , , ,1, )
-op(	ANDASS,	"&=",		1, ,1, ,1, , , , , , , ,1, , , , , ,1, )
-op(	XORASS,	"^=",		1, ,1, ,1, , , , , , , ,1, , , , , ,1, )
-op(	ORASS,	"|=",		1, ,1, ,1, , , , , , , ,1, , , , , ,1, )
-
-/*	name	repr		b l b z i c a s f v - b s l r p c e e =	*/
-op(	NAME,	"name",		 , , , , , , , , , , , , , , , , , , , )
-op(	CON,	"constant",	 , , , , , , , , , , , , , , , , , , , )
-op(	STRING,	"string",	 , , , , , , , , , , , , , , , , , , , )
-op(	FSEL,	"fsel",		 , , , , , , , , , , , , , , , , , , , )
-op(	CALL,	"call",		1, , , , , , , , , , , ,1, , , , , , , )
-op(	COMMA,	",",		1, ,1, , , , , , , , , , , , , , , , ,1)
-op(	CVT,	"convert",	 , , , , , , , , ,1, , , , , , , , , , )
-op(	ICALL,	"icall",	1, , , , , , , , , , , ,1, , , , , , , )
-op(	LOAD,	"load",		 , , , , , , , , , , , , , , , , , , , )
+/*	name	repr		b l b z i c a s f v - b s l r p c e e =	o */
+op(	MULT,	"*",		1, , , , , ,1, ,1,1, ,1, , ,1, , , ,1,1,1)
+op(	DIV,	"/",		1, , , , , ,1, ,1,1, ,1, ,1,1, , , ,1,1,1)
+op(	MOD,	"%",		1, , , ,1, , , ,1,1, ,1, ,1,1, , , ,1,1,1)
+op(	PLUS,	"+",		1, , , , , , ,1,1,1, ,1, , , , , , ,1, ,1)
+op(	MINUS,	"-",		1, , , , , , ,1,1,1, ,1, , , , , , ,1, ,1)
+op(	SHL,	"<<",		1, , , ,1, , , ,1,1, , , , , ,1, , ,1,1,1)
+op(	SHR,	">>",		1, , , ,1, , , ,1,1, , , ,1, ,1, , ,1,1,1)
+
+/*	name	repr		b l b z i c a s f v - b s l r p c e e =	o */
+op(	LT,	"<",		1,1, , , , , ,1,1,1, ,1, ,1,1, ,1,1, ,1,1)
+op(	LE,	"<=",		1,1, , , , , ,1,1,1, ,1, ,1,1, ,1,1, ,1,1)
+op(	GT,	">",		1,1, , , , , ,1,1,1, ,1, ,1,1, ,1,1, ,1,1)
+op(	GE,	">=",		1,1, , , , , ,1,1,1, ,1, ,1,1, ,1,1, ,1,1)
+op(	EQ,	"==",		1,1,1, , , , ,1,1,1, ,1, , , , ,1,1, ,1,1)
+op(	NE,	"!=",		1,1,1, , , , ,1,1,1, ,1, , , , ,1,1, ,1,1)
+
+/*	name	repr		b l b z i c a s f v - b s l r p c e e =	o */
+op(	BITAND,	"&",		1, ,1, ,1, , , ,1,1, ,1, , , ,1, , ,1, ,1)
+op(	BITXOR,	"^",		1, ,1, ,1, , , ,1,1, ,1, , , ,1, , ,1, ,1)
+op(	BITOR,	"|",		1, ,1, ,1, , , ,1,1, ,1, , , ,1, , ,1, ,1)
+op(	LOGAND,	"&&",		1,1,1,1, , , ,1,1, , , , , , , , , ,1, ,1)
+op(	LOGOR,	"||",		1,1,1,1, , , ,1,1, , , , , , ,1, , ,1, ,1)
+op(	QUEST,	"?",		1, , ,1, , , , ,1, , , , , , , , , , , ,1)
+op(	COLON,	":",		1, ,1, , , , , , ,1, ,1, , , , , ,1, , ,1)
+
+/*	name	repr		b l b z i c a s f v - b s l r p c e e =	o */
+op(	ASSIGN,	"=",		1, ,1, , , , , , , , , ,1, , , , ,1, , ,1)
+op(	MULASS,	"*=",		1, , , , , ,1, , , , , ,1, , , , , ,1, ,1)
+op(	DIVASS,	"/=",		1, , , , , ,1, , , , , ,1, ,1, , , ,1, ,1)
+op(	MODASS,	"%=",		1, , , ,1, , , , , , , ,1, ,1, , , ,1, ,1)
+op(	ADDASS,	"+=",		1, , , , , , ,1, , , , ,1, , , , , ,1, ,1)
+op(	SUBASS,	"-=",		1, , , , , , ,1, , , , ,1, , , , , ,1, ,1)
+op(	SHLASS,	"<<=",		1, , , ,1, , , , , , , ,1, , , , , ,1, ,1)
+op(	SHRASS,	">>=",		1, , , ,1, , , , , , , ,1, , , , , ,1, ,1)
+op(	ANDASS,	"&=",		1, ,1, ,1, , , , , , , ,1, , , , , ,1, ,1)
+op(	XORASS,	"^=",		1, ,1, ,1, , , , , , , ,1, , , , , ,1, ,1)
+op(	ORASS,	"|=",		1, ,1, ,1, , , , , , , ,1, , , , , ,1, ,1)
+
+/*	name	repr		b l b z i c a s f v - b s l r p c e e =	o */
+op(	NAME,	"name",		 , , , , , , , , , , , , , , , , , , , , )
+op(	CON,	"constant",	 , , , , , , , , , , , , , , , , , , , , )
+op(	STRING,	"string",	 , , , , , , , , , , , , , , , , , , , , )
+op(	FSEL,	"fsel",		 , , , , , , , , , , , , , , , , , , , ,1)
+op(	CALL,	"call",		1, , , , , , , , , , , ,1, , , , , , , ,1)
+op(	COMMA,	",",		1, ,1, , , , , , , , , , , , , , , , ,1,1)
+op(	CVT,	"convert",	 , , , , , , , , ,1, , , , , , , , , , ,1)
+op(	ICALL,	"icall",	1, , , , , , , , , , , ,1, , , , , , , ,1)
+op(	LOAD,	"load",		 , , , , , , , , , , , , , , , , , , , ,1)
 /*
  * PUSH is a virtual node that is used to concatenate arguments in a function
  * call expression.  The PUSH nodes are ordered from right to left.  For
  * example, the function call f(17, 23) is represented as
  * CALL(f, PUSH(23, PUSH(17, NULL))).
  */
-op(	PUSH,	"push",		 , , , , , , , , ,1, , , , , , , , , , )
-op(	RETURN,	"return",	1, ,1, , , , , , , , , ,1, , , , ,1, , )
-op(	REAL,	"real",		 , , , , , , , , , , , , , , , , , , , )
-op(	IMAG,	"imag",		 , , , , , , , , , , , , , , , , , , , )
+op(	PUSH,	"push",		 , , , , , , , , ,1, , , , , , , , , , ,1)
+op(	RETURN,	"return",	1, ,1, , , , , , , , , ,1, , , , ,1, , ,1)
+op(	REAL,	"real",		 , , , , , , , , , , , , , , , , , , , ,1)
+op(	IMAG,	"imag",		 , , , , , , , , , , , , , , , , , , , ,1)
 
 /* INIT, CASE and FARG are pseudo operators that don't appear in the tree. */
-/*	name	repr		b l b z i c a s f v - b s l r p c e e =	*/
-op(	INIT,	"init",		1, ,1, , , , , , , , , ,1, , , , ,1, , )
-op(	CASE,	"case",		 , , , , , , , , , , , , , , , , , , , )
+/*	name	repr		b l b z i c a s f v - b s l r p c e e =	o */
+op(	INIT,	"init",		1, ,1, , , , , , , , , ,1, , , , ,1, , ,1)
+op(	CASE,	"case",		 , , , , , , , , , , , , , , , , , , , , )
 /*
  * FARG is only used temporarily in check_prototype_argument to check type
  * compatibility and conversion for function arguments.
  */
-op(	FARG,	"farg",		1, ,1, , , , , , , , , , , , , , ,1, , )
+op(	FARG,	"farg",		1, ,1, , , , , , , , , , , , , , ,1, , ,1)
 
 end_ops()
 

Index: src/usr.bin/xlint/lint1/tree.c
diff -u src/usr.bin/xlint/lint1/tree.c:1.580 src/usr.bin/xlint/lint1/tree.c:1.581
--- src/usr.bin/xlint/lint1/tree.c:1.580	Tue Sep 12 22:01:05 2023
+++ src/usr.bin/xlint/lint1/tree.c	Wed Sep 13 20:31:58 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: tree.c,v 1.580 2023/09/12 22:01:05 rillig Exp $	*/
+/*	$NetBSD: tree.c,v 1.581 2023/09/13 20:31:58 rillig Exp $	*/
 
 /*
  * Copyright (c) 1994, 1995 Jochen Pohl
@@ -37,7 +37,7 @@
 
 #include <sys/cdefs.h>
 #if defined(__RCSID)
-__RCSID("$NetBSD: tree.c,v 1.580 2023/09/12 22:01:05 rillig Exp $");
+__RCSID("$NetBSD: tree.c,v 1.581 2023/09/13 20:31:58 rillig Exp $");
 #endif
 
 #include <float.h>
@@ -794,6 +794,7 @@ fold(tnode_t *tn)
 	val_t *v = xcalloc(1, sizeof(*v));
 	v->v_tspec = tn->tn_type->t_tspec;
 
+	lint_assert(has_operands(tn));
 	tspec_t t = tn->tn_left->tn_type->t_tspec;
 	bool utyp = !is_integer(t) || is_uinteger(t);
 	int64_t sl = tn->tn_left->tn_val.u.integer, sr = 0;
@@ -1250,7 +1251,7 @@ build_colon(bool sys, tnode_t *ln, tnode
 static bool
 is_cast_redundant(const tnode_t *tn)
 {
-	const type_t *ntp = tn->tn_type, *otp = tn->tn_left->tn_type;
+	const type_t *ntp = tn->tn_type, *otp = tn_ck_left(tn)->tn_type;
 	tspec_t nt = ntp->t_tspec, ot = otp->t_tspec;
 
 	if (nt == BOOL || ot == BOOL)
@@ -1485,6 +1486,7 @@ fold_bool(tnode_t *tn)
 	v->v_tspec = tn->tn_type->t_tspec;
 	lint_assert(v->v_tspec == INT || (Tflag && v->v_tspec == BOOL));
 
+	lint_assert(has_operands(tn));
 	bool l = constant_is_nonzero(tn->tn_left);
 	bool r = is_binary(tn) && constant_is_nonzero(tn->tn_right);
 
@@ -1564,6 +1566,7 @@ fold_float(tnode_t *tn)
 	v->v_tspec = t;
 
 	lint_assert(is_floating(t));
+	lint_assert(has_operands(tn));
 	lint_assert(t == tn->tn_left->tn_type->t_tspec);
 	lint_assert(!is_binary(tn) || t == tn->tn_right->tn_type->t_tspec);
 
@@ -1648,6 +1651,7 @@ use(const tnode_t *tn)
 	case STRING:
 		break;
 	default:
+		lint_assert(has_operands(tn));
 		use(tn->tn_left);
 		if (is_binary(tn) || tn->tn_op == PUSH)
 			use(tn->tn_right);
@@ -1828,6 +1832,7 @@ build_binary(tnode_t *ln, op_t op, bool 
 				ntn = fold(ntn);
 			}
 		} else if (op == QUEST && ln->tn_op == CON) {
+			lint_assert(has_operands(rn));
 			use(ln->tn_val.u.integer != 0
 			    ? rn->tn_right : rn->tn_left);
 			ntn = ln->tn_val.u.integer != 0
@@ -2702,11 +2707,12 @@ is_const_char_pointer(const tnode_t *tn)
 static bool
 is_first_arg_const_char_pointer(const tnode_t *tn)
 {
+	lint_assert(has_operands(tn));
 	const tnode_t *an = tn->tn_right;
 	if (an == NULL)
 		return false;
 
-	while (an->tn_right != NULL)
+	while (tn_ck_right(an) != NULL)
 		an = an->tn_right;
 	return is_const_char_pointer(an->tn_left);
 }
@@ -2721,11 +2727,11 @@ is_const_pointer(const tnode_t *tn)
 static bool
 is_second_arg_const_pointer(const tnode_t *tn)
 {
-	const tnode_t *an = tn->tn_right;
-	if (an == NULL || an->tn_right == NULL)
+	const tnode_t *an = tn_ck_right(tn);
+	if (an == NULL || tn_ck_right(an) == NULL)
 		return false;
 
-	while (an->tn_right->tn_right != NULL)
+	while (tn_ck_right(an->tn_right) != NULL)
 		an = an->tn_right;
 	return is_const_pointer(an->tn_left);
 }
@@ -4260,7 +4266,7 @@ check_function_arguments(type_t *ftp, tn
 
 	/* get # of arguments in the function call */
 	int narg = 0;
-	for (const tnode_t *arg = args; arg != NULL; arg = arg->tn_right)
+	for (const tnode_t *arg = args; arg != NULL; arg = tn_ck_right(arg))
 		narg++;
 
 	const sym_t *param = ftp->t_params;
@@ -4462,6 +4468,7 @@ expr(tnode_t *tn, bool vctx, bool cond, 
 static void
 check_array_index(tnode_t *tn, bool amper)
 {
+	lint_assert(has_operands(tn));
 	const tnode_t *ln = tn->tn_left;
 	const tnode_t *rn = tn->tn_right;
 
@@ -4582,7 +4589,7 @@ check_expr_call(const tnode_t *tn, const
 		outcall(tn, vctx || cond, retval_discarded);
 }
 
-static bool
+static void
 check_expr_op(const tnode_t *tn, op_t op, const tnode_t *ln,
 	      bool szof, bool fcall, bool vctx, bool cond,
 	      bool retval_discarded, bool eqwarn)
@@ -4624,14 +4631,9 @@ check_expr_op(const tnode_t *tn, op_t op
 			/* operator '==' found where '=' was expected */
 			warning(160);
 		break;
-	case CON:
-	case NAME:
-	case STRING:
-		return false;
 	default:
 		break;
 	}
-	return true;
 }
 
 /*
@@ -4655,16 +4657,17 @@ check_expr_misc(const tnode_t *tn, bool 
 
 	if (tn == NULL)
 		return;
+	op_t op = tn->tn_op;
+	if (op == NAME || op == CON || op == STRING)
+		return;
 
+	lint_assert(has_operands(tn));
 	tnode_t *ln = tn->tn_left;
 	tnode_t *rn = tn->tn_right;
-	op_t op = tn->tn_op;
-	const mod_t *mp = &modtab[op];
-
-	if (!check_expr_op(tn, op, ln,
-	    szof, fcall, vctx, cond, retval_discarded, eqwarn))
-		return;
+	check_expr_op(tn, op, ln,
+	    szof, fcall, vctx, cond, retval_discarded, eqwarn);
 
+	const mod_t *mp = &modtab[op];
 	bool cvctx = mp->m_value_context;
 	bool ccond = mp->m_compares_with_zero;
 	bool eq = mp->m_warn_if_operand_eq &&

Reply via email to