Module Name:    src
Committed By:   rillig
Date:           Sun Mar 10 15:49:12 UTC 2024

Modified Files:
        src/tests/usr.bin/xlint/lint1: msg_141.c
        src/usr.bin/xlint/lint1: tree.c

Log Message:
lint: fix integer overflow detection

Previously, an unsigned operation that had a negative result went
undetected in a few cases. Now, all results that are not representable
by their type are considered overflows.

The implementation of signed shift-right had been wrong for a few
commits.


To generate a diff of this commit:
cvs rdiff -u -r1.14 -r1.15 src/tests/usr.bin/xlint/lint1/msg_141.c
cvs rdiff -u -r1.620 -r1.621 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/tests/usr.bin/xlint/lint1/msg_141.c
diff -u src/tests/usr.bin/xlint/lint1/msg_141.c:1.14 src/tests/usr.bin/xlint/lint1/msg_141.c:1.15
--- src/tests/usr.bin/xlint/lint1/msg_141.c:1.14	Sun Mar 10 14:32:30 2024
+++ src/tests/usr.bin/xlint/lint1/msg_141.c	Sun Mar 10 15:49:12 2024
@@ -1,4 +1,4 @@
-/*	$NetBSD: msg_141.c,v 1.14 2024/03/10 14:32:30 rillig Exp $	*/
+/*	$NetBSD: msg_141.c,v 1.15 2024/03/10 15:49:12 rillig Exp $	*/
 # 3 "msg_141.c"
 
 // Test for message: operator '%s' produces integer overflow [141]
@@ -412,7 +412,7 @@ minus_u64(void)
 void
 shl_s32(void)
 {
-	/* TODO: expect+1: warning: operator '<<' produces integer overflow [141] */
+	/* expect+1: warning: operator '<<' produces integer overflow [141] */
 	s32 = 0x0100 << 23;
 	/* expect+1: warning: operator '<<' produces integer overflow [141] */
 	s32 = 0x0100 << 24;
@@ -457,6 +457,15 @@ shr_s32(void)
 	s32 = +9 >> 1;
 	s32 = +10 >> 1;
 	s32 = 0x7fffffff >> 1;
+
+	/* expect+1: error: negative array dimension (-16) [20] */
+	typedef int minus_32_shr_1[-32 >> 1];
+	/* expect+1: error: negative array dimension (-16) [20] */
+	typedef int minus_31_shr_1[-31 >> 1];
+	/* expect+1: error: negative array dimension (-15) [20] */
+	typedef int minus_30_shr_1[-30 >> 1];
+	/* expect+1: error: negative array dimension (-1) [20] */
+	typedef int minus_1_shr_1[-1 >> 31];
 }
 
 void
@@ -472,6 +481,15 @@ void
 shr_s64(void)
 {
 	// TODO
+
+	/* expect+1: error: negative array dimension (-16) [20] */
+	typedef int shr_minus_1_shr_0[-16LL >> 0];
+	/* expect+1: error: negative array dimension (-8) [20] */
+	typedef int shr_minus_1_shr_1[-16LL >> 1];
+	/* expect+1: error: negative array dimension (-1) [20] */
+	typedef int shr_minus_1_shr_16[-16LL >> 16];
+	/* expect+1: error: negative array dimension (-1) [20] */
+	typedef int shr_minus_1_shr_40[-16LL >> 40];
 }
 
 void

Index: src/usr.bin/xlint/lint1/tree.c
diff -u src/usr.bin/xlint/lint1/tree.c:1.620 src/usr.bin/xlint/lint1/tree.c:1.621
--- src/usr.bin/xlint/lint1/tree.c:1.620	Sun Mar 10 14:42:04 2024
+++ src/usr.bin/xlint/lint1/tree.c	Sun Mar 10 15:49:12 2024
@@ -1,4 +1,4 @@
-/*	$NetBSD: tree.c,v 1.620 2024/03/10 14:42:04 rillig Exp $	*/
+/*	$NetBSD: tree.c,v 1.621 2024/03/10 15:49:12 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.620 2024/03/10 14:42:04 rillig Exp $");
+__RCSID("$NetBSD: tree.c,v 1.621 2024/03/10 15:49:12 rillig Exp $");
 #endif
 
 #include <float.h>
@@ -793,11 +793,11 @@ fold_unsigned_integer(op_t op, uint64_t 
 {
 	switch (op) {
 	case COMPL:
-		return ~l;
+		return ~l & max_value;
 	case UPLUS:
 		return +l;
 	case UMINUS:
-		return -l;
+		return -l & max_value;
 	case MULT:
 		*overflow = r > 0 && l > max_value / r;
 		return l * r;
@@ -920,12 +920,11 @@ fold_signed_integer(op_t op, int64_t l, 
 		/* TODO: warn about out-of-bounds 'sr'. */
 		/* TODO: warn about overflow. */
 		return l << (r & 63);
-	case SHR:;
+	case SHR:
 		/* TODO: warn about out-of-bounds 'sr'. */
-		uint64_t shr_result = (uint64_t)l >> (r & 63);
-		if (shr_result & min_value)
-			shr_result |= min_value;
-		return (int64_t)shr_result;
+		if (l < 0)
+			return (int64_t)~(~(uint64_t)l >> (r & 63));
+		return (int64_t)((uint64_t)l >> (r & 63));
 	case LT:
 		return l < r ? 1 : 0;
 	case LE:
@@ -950,45 +949,40 @@ fold_signed_integer(op_t op, int64_t l, 
 	}
 }
 
-/*
- * XXX
- * Note: There appear to be a number of bugs in detecting overflow in
- * this function. An audit and a set of proper regression tests are needed.
- *     --Perry Metzger, Nov. 16, 2001
- */
 static tnode_t *
 fold_constant_integer(tnode_t *tn)
 {
 
 	lint_assert(has_operands(tn));
 	tspec_t t = tn->u.ops.left->tn_type->t_tspec;
-	bool utyp = !is_integer(t) || is_uinteger(t);
-	int64_t sl = tn->u.ops.left->u.value.u.integer, sr = 0;
-	uint64_t ul = sl, ur = 0;
-	if (is_binary(tn))
-		ur = sr = tn->u.ops.right->u.value.u.integer;
-
+	int64_t l = tn->u.ops.left->u.value.u.integer;
+	int64_t r = is_binary(tn) ? tn->u.ops.right->u.value.u.integer : 0;
 	uint64_t mask = value_bits(size_in_bits(t));
-	int64_t max_value = (int64_t)(mask >> 1);
-	int64_t min_value = -max_value - 1;
-	bool ovfl = false;
-
-	int64_t si = utyp
-	    ? (int64_t)fold_unsigned_integer(tn->tn_op, ul, ur, mask, &ovfl)
-	    : fold_signed_integer(tn->tn_op, sl, sr, min_value, max_value,
-		&ovfl);
-
-	/* XXX: The overflow check does not work for 64-bit integers. */
-	if (ovfl ||
-	    ((si | mask) != ~(uint64_t)0 && (si & ~mask) != 0)) {
-		if (hflag)
-			/* operator '%s' produces integer overflow */
-			warning(141, op_name(tn->tn_op));
+
+	int64_t res;
+	bool overflow = false;
+	if (!is_integer(t) || is_uinteger(t)) {
+		uint64_t u_res = fold_unsigned_integer(tn->tn_op,
+		    (uint64_t)l, (uint64_t)r, mask, &overflow);
+		if (u_res > mask)
+			overflow = true;
+		res = (int64_t)u_res;
+	} else {
+		int64_t max_value = (int64_t)(mask >> 1);
+		int64_t min_value = -max_value - 1;
+		res = fold_signed_integer(tn->tn_op,
+		    l, r, min_value, max_value, &overflow);
+		if (res < min_value || res > max_value)
+			overflow = true;
 	}
 
+	if (overflow && hflag)
+		/* operator '%s' produces integer overflow */
+		warning(141, op_name(tn->tn_op));
+
 	val_t *v = xcalloc(1, sizeof(*v));
 	v->v_tspec = tn->tn_type->t_tspec;
-	v->u.integer = convert_integer(si, t, 0);
+	v->u.integer = convert_integer(res, t, 0);
 
 	tnode_t *cn = build_constant(tn->tn_type, v);
 	if (tn->u.ops.left->tn_system_dependent)

Reply via email to