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)