diff --git a/gcc/ChangeLog b/gcc/ChangeLog index a60c7426334..d0c53d066ea 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,13 @@ +2017-01-10 Martin Sebor + + PR tree-optimization/78775 + * builtins.c (get_size_range): Move... + * calls.c: ...to here. + (alloc_max_size): Accept zero argument. + (operand_signed_p): Remove. + (maybe_warn_alloc_args_overflow): Call get_size_range. + * calls.h (get_size_range): Declare. + 2017-01-10 Joe Seymour * config/msp430/driver-msp430.c (msp430_mcu_data): Sync with data diff --git a/gcc/builtins.c b/gcc/builtins.c index 5b76dfd9b2f..bf68e317124 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -3031,42 +3031,6 @@ expand_builtin_memcpy_args (tree dest, tree src, tree len, rtx target, tree exp) return dest_addr; } -/* Fill the 2-element RANGE array with the minimum and maximum values - EXP is known to have and return true, otherwise null and return - false. */ - -static bool -get_size_range (tree exp, tree range[2]) -{ - if (tree_fits_uhwi_p (exp)) - { - range[0] = range[1] = exp; - return true; - } - - if (TREE_CODE (exp) == SSA_NAME) - { - wide_int min, max; - enum value_range_type range_type = get_range_info (exp, &min, &max); - - if (range_type == VR_RANGE) - { - /* Interpret the bound in the variable's type. */ - range[0] = wide_int_to_tree (TREE_TYPE (exp), min); - range[1] = wide_int_to_tree (TREE_TYPE (exp), max); - return true; - } - else if (range_type == VR_ANTI_RANGE) - { - /* FIXME: Handle anti-ranges. */ - } - } - - range[0] = NULL_TREE; - range[1] = NULL_TREE; - return false; -} - /* Try to verify that the sizes and lengths of the arguments to a string manipulation function given by EXP are within valid bounds and that the operation does not lead to buffer overflow. Arguments other than diff --git a/gcc/calls.c b/gcc/calls.c index b7bbec53ede..7b45b9a111d 100644 --- a/gcc/calls.c +++ b/gcc/calls.c @@ -1197,92 +1197,144 @@ alloc_max_size (void) { alloc_object_size_limit = TYPE_MAX_VALUE (ssizetype); - unsigned HOST_WIDE_INT unit = 1; - - char *end; - errno = 0; - unsigned HOST_WIDE_INT limit - = warn_alloc_size_limit ? strtoull (warn_alloc_size_limit, &end, 10) : 0; - - if (limit && !errno) + if (warn_alloc_size_limit) { - if (end && *end) - { - /* Numeric option arguments are at most INT_MAX. Make it - possible to specify a larger value by accepting common - suffixes. */ - if (!strcmp (end, "kB")) - unit = 1000; - else if (!strcasecmp (end, "KiB") || strcmp (end, "KB")) - unit = 1024; - else if (!strcmp (end, "MB")) - unit = 1000LU * 1000; - else if (!strcasecmp (end, "MiB")) - unit = 1024LU * 1024; - else if (!strcasecmp (end, "GB")) - unit = 1000LU * 1000 * 1000; - else if (!strcasecmp (end, "GiB")) - unit = 1024LU * 1024 * 1024; - else if (!strcasecmp (end, "TB")) - unit = 1000LU * 1000 * 1000 * 1000; - else if (!strcasecmp (end, "TiB")) - unit = 1024LU * 1024 * 1024 * 1024; - else if (!strcasecmp (end, "PB")) - unit = 1000LU * 1000 * 1000 * 1000 * 1000; - else if (!strcasecmp (end, "PiB")) - unit = 1024LU * 1024 * 1024 * 1024 * 1024; - else if (!strcasecmp (end, "EB")) - unit = 1000LU * 1000 * 1000 * 1000 * 1000 * 1000; - else if (!strcasecmp (end, "EiB")) - unit = 1024LU * 1024 * 1024 * 1024 * 1024 * 1024; - else - unit = 0; - } + char *end = NULL; + errno = 0; + unsigned HOST_WIDE_INT unit = 1; + unsigned HOST_WIDE_INT limit + = strtoull (warn_alloc_size_limit, &end, 10); - if (unit) - alloc_object_size_limit = build_int_cst (ssizetype, limit * unit); + if (!errno) + { + if (end && *end) + { + /* Numeric option arguments are at most INT_MAX. Make it + possible to specify a larger value by accepting common + suffixes. */ + if (!strcmp (end, "kB")) + unit = 1000; + else if (!strcasecmp (end, "KiB") || strcmp (end, "KB")) + unit = 1024; + else if (!strcmp (end, "MB")) + unit = 1000LU * 1000; + else if (!strcasecmp (end, "MiB")) + unit = 1024LU * 1024; + else if (!strcasecmp (end, "GB")) + unit = 1000LU * 1000 * 1000; + else if (!strcasecmp (end, "GiB")) + unit = 1024LU * 1024 * 1024; + else if (!strcasecmp (end, "TB")) + unit = 1000LU * 1000 * 1000 * 1000; + else if (!strcasecmp (end, "TiB")) + unit = 1024LU * 1024 * 1024 * 1024; + else if (!strcasecmp (end, "PB")) + unit = 1000LU * 1000 * 1000 * 1000 * 1000; + else if (!strcasecmp (end, "PiB")) + unit = 1024LU * 1024 * 1024 * 1024 * 1024; + else if (!strcasecmp (end, "EB")) + unit = 1000LU * 1000 * 1000 * 1000 * 1000 * 1000; + else if (!strcasecmp (end, "EiB")) + unit = 1024LU * 1024 * 1024 * 1024 * 1024 * 1024; + else + unit = 0; + } + + if (unit) + alloc_object_size_limit + = build_int_cst (ssizetype, limit * unit); + } } } return alloc_object_size_limit; } -/* Return true if the type of OP is signed, looking through any casts - to an unsigned type. */ +/* Return true when EXP's range can be determined and set RANGE[] to it + after adjusting it if necessary to make EXP a valid size argument to + an allocation function declared with attribute alloc_size (whose + argument may be signed), or to a string manipulation function like + memset. */ -static bool -operand_signed_p (tree op) +bool +get_size_range (tree exp, tree range[2]) { - if (TREE_CODE (op) == SSA_NAME) + if (tree_fits_uhwi_p (exp)) { - gimple *def = SSA_NAME_DEF_STMT (op); - if (is_gimple_assign (def)) - { - /* In an assignment involving a cast, ignore the type - of the cast and consider the type of its operand. */ - tree_code code = gimple_assign_rhs_code (def); - if (code == NOP_EXPR) - op = gimple_assign_rhs1 (def); - } - else if (gimple_code (def) == GIMPLE_PHI) - { - /* In a phi, a constant argument may be unsigned even - if in the source it's signed and negative. Ignore - those and consider the result of a phi signed if - all its non-constant operands are. */ - unsigned nargs = gimple_phi_num_args (def); - for (unsigned i = 0; i != nargs; ++i) - { - tree op = gimple_phi_arg_def (def, i); - if (TREE_CODE (op) != INTEGER_CST - && !operand_signed_p (op)) - return false; - } + /* EXP is a constant. */ + range[0] = range[1] = exp; + return true; + } - return true; + wide_int min, max; + enum value_range_type range_type + = (TREE_CODE (exp) == SSA_NAME + ? get_range_info (exp, &min, &max) : VR_VARYING); + + if (range_type == VR_VARYING) + { + /* No range information available. */ + range[0] = NULL_TREE; + range[1] = NULL_TREE; + return false; + } + + tree exptype = TREE_TYPE (exp); + unsigned expprec = TYPE_PRECISION (exptype); + wide_int wzero = wi::zero (expprec); + wide_int wmaxval = wide_int (TYPE_MAX_VALUE (exptype)); + + bool signed_p = !TYPE_UNSIGNED (exptype); + + if (range_type == VR_ANTI_RANGE) + { + if (signed_p) + { + if (wi::les_p (max, wzero)) + { + /* EXP is not in a strictly negative range. That means + it must be in some (not necessarily strictly) positive + range which includes zero. Since in signed to unsigned + conversions negative values end up converted to large + positive values, and otherwise they are not valid sizes, + the resulting range is in both cases [0, TYPE_MAX]. */ + min = wzero; + max = wmaxval; + } + else if (wi::les_p (min - 1, wzero)) + { + /* EXP is not in a negative-positive range. That means EXP + is either negative, or greater than max. Since negative + sizes are invalid make the range [MAX + 1, TYPE_MAX]. */ + min = max + 1; + max = wmaxval; + } + else + { + max = min - 1; + min = wzero; + } + } + else if (wi::eq_p (wzero, min - 1)) + { + /* EXP is unsigned and not in the range [1, MAX]. That means + it's either zero or greater than MAX. Even though 0 would + normally be detected by -Walloc-zero set the range to + [MAX, TYPE_MAX] so that when MAX is greater than the limit + the whole range is diagnosed. */ + min = max + 1; + max = wmaxval; + } + else + { + max = min - 1; + min = wzero; } } - return !TYPE_UNSIGNED (TREE_TYPE (op)); + range[0] = wide_int_to_tree (exptype, min); + range[1] = wide_int_to_tree (exptype, max); + + return true; } /* Diagnose a call EXP to function FN decorated with attribute alloc_size @@ -1316,8 +1368,8 @@ maybe_warn_alloc_args_overflow (tree fn, tree exp, tree args[2], int idx[2]) if (tree_int_cst_lt (args[i], integer_zero_node)) { warned = warning_at (loc, OPT_Walloc_size_larger_than_, - "argument %i value %qE is negative", - idx[i] + 1, args[i]); + "%Kargument %i value %qE is negative", + exp, idx[i] + 1, args[i]); } else if (integer_zerop (args[i])) { @@ -1334,8 +1386,8 @@ maybe_warn_alloc_args_overflow (tree fn, tree exp, tree args[2], int idx[2]) && !lookup_attribute ("returns_nonnull", TYPE_ATTRIBUTES (TREE_TYPE (fn))))) warned = warning_at (loc, OPT_Walloc_zero, - "argument %i value is zero", - idx[i] + 1); + "%Kargument %i value is zero", + exp, idx[i] + 1); } else if (tree_int_cst_lt (maxobjsize, args[i])) { @@ -1351,79 +1403,31 @@ maybe_warn_alloc_args_overflow (tree fn, tree exp, tree args[2], int idx[2]) continue; warned = warning_at (loc, OPT_Walloc_size_larger_than_, - "argument %i value %qE exceeds " + "%Kargument %i value %qE exceeds " "maximum object size %E", - idx[i] + 1, args[i], maxobjsize); + exp, idx[i] + 1, args[i], maxobjsize); } } - else if (TREE_CODE (args[i]) == SSA_NAME) + else if (TREE_CODE (args[i]) == SSA_NAME + && get_size_range (args[i], argrange[i])) { - tree type = TREE_TYPE (args[i]); - - wide_int min, max; - value_range_type range_type = get_range_info (args[i], &min, &max); - if (range_type == VR_RANGE) - { - argrange[i][0] = wide_int_to_tree (type, min); - argrange[i][1] = wide_int_to_tree (type, max); - } - else if (range_type == VR_ANTI_RANGE) - { - /* For an anti-range, if the type of the formal argument - is unsigned and the bounds of the range are of opposite - signs when interpreted as signed, check to see if the - type of the actual argument is signed. If so, the lower - bound must be taken to be zero (rather than a large - positive value corresonding to the actual lower bound - interpreted as unsigned) and there is nothing else that - can be inferred from it. */ - --min; - ++max; - wide_int zero = wi::uhwi (0, TYPE_PRECISION (type)); - if (TYPE_UNSIGNED (type) - && wi::lts_p (zero, min) && wi::lts_p (max, zero) - && operand_signed_p (args[i])) - continue; - - argrange[i][0] = wide_int_to_tree (type, max); - argrange[i][1] = wide_int_to_tree (type, min); - - /* Verify that the anti-range doesn't make all arguments - invalid (treat the anti-range ~[0, 0] as invalid). */ - if (tree_int_cst_lt (maxobjsize, argrange[i][0]) - && tree_int_cst_le (argrange[i][1], integer_zero_node)) - { - warned - = warning_at (loc, OPT_Walloc_size_larger_than_, - (TYPE_UNSIGNED (type) - ? G_("argument %i range [%E, %E] exceeds " - "maximum object size %E") - : G_("argument %i range [%E, %E] is both " - "negative and exceeds maximum object " - "size %E")), - idx[i] + 1, argrange[i][0], - argrange[i][1], maxobjsize); - } - continue; - } - else - continue; - /* Verify that the argument's range is not negative (including upper bound of zero). */ if (tree_int_cst_lt (argrange[i][0], integer_zero_node) && tree_int_cst_le (argrange[i][1], integer_zero_node)) { warned = warning_at (loc, OPT_Walloc_size_larger_than_, - "argument %i range [%E, %E] is negative", - idx[i] + 1, argrange[i][0], argrange[i][1]); + "%Kargument %i range [%E, %E] is negative", + exp, idx[i] + 1, + argrange[i][0], argrange[i][1]); } else if (tree_int_cst_lt (maxobjsize, argrange[i][0])) { warned = warning_at (loc, OPT_Walloc_size_larger_than_, - "argument %i range [%E, %E] exceeds " + "%Kargument %i range [%E, %E] exceeds " "maximum object size %E", - idx[i] + 1, argrange[i][0], argrange[i][1], + exp, idx[i] + 1, + argrange[i][0], argrange[i][1], maxobjsize); } } @@ -1450,15 +1454,15 @@ maybe_warn_alloc_args_overflow (tree fn, tree exp, tree args[2], int idx[2]) if (vflow) warned = warning_at (loc, OPT_Walloc_size_larger_than_, - "product %<%E * %E%> of arguments %i and %i " + "%Kproduct %<%E * %E%> of arguments %i and %i " "exceeds %", - argrange[0][0], argrange[1][0], + exp, argrange[0][0], argrange[1][0], idx[0] + 1, idx[1] + 1); else if (wi::ltu_p (wi::to_wide (maxobjsize, szprec), prod)) warned = warning_at (loc, OPT_Walloc_size_larger_than_, - "product %<%E * %E%> of arguments %i and %i " + "%Kproduct %<%E * %E%> of arguments %i and %i " "exceeds maximum object size %E", - argrange[0][0], argrange[1][0], + exp, argrange[0][0], argrange[1][0], idx[0] + 1, idx[1] + 1, maxobjsize); diff --git a/gcc/calls.h b/gcc/calls.h index e87fbda1d14..df5817fe785 100644 --- a/gcc/calls.h +++ b/gcc/calls.h @@ -38,5 +38,6 @@ extern bool pass_by_reference (CUMULATIVE_ARGS *, machine_mode, extern bool reference_callee_copied (CUMULATIVE_ARGS *, machine_mode, tree, bool); extern void maybe_warn_alloc_args_overflow (tree, tree, tree[2], int[2]); +extern bool get_size_range (tree, tree[2]); #endif // GCC_CALLS_H diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 009d32d730a..f8cba4221ab 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,14 @@ +2017-01-10 Martin Sebor + + PR tree-optimization/78775 + * gcc.dg/attr-alloc_size-4.c: Add test cases. + * gcc.dg/attr-alloc_size-10.c: New test. + * gcc.dg/attr-alloc_size-11.c: New test. + * gcc.dg/builtin-stringop-chk-7.c: New test. + * gcc.dg/pr78775.c: New test. + * gcc.dg/pr78973-2.c: New test. + * gcc.dg/pr78973.c: New test. + 2017-01-10 Jeff Law PR tree-optimization/77766 diff --git a/gcc/testsuite/gcc.dg/attr-alloc_size-10.c b/gcc/testsuite/gcc.dg/attr-alloc_size-10.c new file mode 100644 index 00000000000..d7a0a987003 --- /dev/null +++ b/gcc/testsuite/gcc.dg/attr-alloc_size-10.c @@ -0,0 +1,142 @@ +/* Verify that -Walloc-size-greater-than doesn't cause false positives + for anti-ranges. Note that not all of the statements used to create + anti-ranges below result in the argument being represented as an anti + range. + + { dg-do compile } + { dg-options "-O2 -Walloc-size-larger-than=12" } */ + +#define SCHAR_MAX __SCHAR_MAX__ +#define SCHAR_MIN (-SCHAR_MAX - 1) +#define UCHAR_MAX (SCHAR_MAX * 2 + 1) + +#define SHRT_MAX __SHRT_MAX__ +#define SHRT_MIN (-SHRT_MAX - 1) +#define USHRT_MAX (SHRT_MAX * 2 + 1) + +#define INT_MAX __INT_MAX__ +#define INT_MIN (-INT_MAX - 1) +#define UINT_MAX (INT_MAX * 2U + 1) + +#define LONG_MAX __LONG_MAX__ +#define LONG_MIN (-LONG_MAX - 1) +#define ULONG_MAX (LONG_MAX * 2LU + 1) + +#define PTRDIFF_MAX __PTRDIFF_MAX__ +#define PTRDIFF_MIN (-PTRDIFF_MAX - 1) +#define SIZE_MAX __SIZE_MAX__ + +typedef __PTRDIFF_TYPE__ ptrdiff_t; +typedef __SIZE_TYPE__ size_t; + +#define CONCAT(a, b) a ## b +#define CAT(a, b) CONCAT (a, b) + +/* Macro to generate a unique function to test the anti-range + ~[MIN, MAX] for type T. */ +#define TEST(T, min, max) \ + void* CAT (test_anti_range_, __LINE__)(T n) \ + { \ + extern void* CAT (alloc_anti_range_, __LINE__)(T) \ + __attribute__ ((alloc_size (1))); \ + if (min <= n && n <= max) \ + n = min - 1; \ + return CAT (alloc_anti_range_, __LINE__)(n); \ + } typedef void dummy /* Require a semicolon. */ + + +/* Verify the anti-range ~[TYPE_MAX - 1, TYPE_MAX - 1]. */ +TEST (signed char, SCHAR_MAX - 1, SCHAR_MAX - 1); +TEST (unsigned char, UCHAR_MAX - 1, UCHAR_MAX - 1); +TEST (short, SHRT_MAX - 1, SHRT_MAX - 1); +TEST (unsigned short, USHRT_MAX - 1, USHRT_MAX - 1); +TEST (int, INT_MAX - 1, INT_MAX - 1); +TEST (unsigned, UINT_MAX - 1, UINT_MAX - 1); +TEST (long, LONG_MAX - 1, LONG_MAX - 1); +TEST (unsigned long, ULONG_MAX - 1, ULONG_MAX - 1); +TEST (ptrdiff_t, PTRDIFF_MAX - 1, PTRDIFF_MAX - 1); +TEST (size_t, SIZE_MAX - 1, SIZE_MAX - 1); + +/* Verify ~[0, 0]. */ +TEST (signed char, 0, 0); +TEST (unsigned char, 0, 0); +TEST (short, 0, 0); +TEST (unsigned short, 0, 0); +TEST (int, 0, 0); +TEST (unsigned, 0, 0); +TEST (long, 0, 0); +TEST (unsigned long, 0, 0); +TEST (ptrdiff_t, 0, 0); +TEST (size_t, 0, 0); + +/* Verify ~[1, 1]. */ +TEST (signed char, 1, 1); +TEST (unsigned char, 1, 1); +TEST (short, 1, 1); +TEST (unsigned short, 1, 1); +TEST (int, 1, 1); +TEST (unsigned, 1, 1); +TEST (long, 1, 1); +TEST (unsigned long, 1, 1); +TEST (ptrdiff_t, 1, 1); +TEST (size_t, 1, 1); + + +/* Verify ~[TYPE_MAX - 2, TYPE_MAX - 1]. */ +TEST (signed char, SCHAR_MAX - 2, SCHAR_MAX - 1); +TEST (unsigned char, UCHAR_MAX - 2, UCHAR_MAX - 1); +TEST (short, SHRT_MAX - 2, SHRT_MAX - 1); +TEST (unsigned short, USHRT_MAX - 2, USHRT_MAX - 1); +TEST (int, INT_MAX - 2, INT_MAX - 1); +TEST (unsigned, UINT_MAX - 2, UINT_MAX - 1); +TEST (long, LONG_MAX - 2, LONG_MAX - 1); +TEST (unsigned long, ULONG_MAX - 2, ULONG_MAX - 1); +TEST (ptrdiff_t, PTRDIFF_MAX - 2, PTRDIFF_MAX - 1); +TEST (size_t, SIZE_MAX - 2, SIZE_MAX - 1); + +/* Verify ~[0, 2]. */ +TEST (signed char, 0, 2); +TEST (unsigned char, 0, 2); +TEST (short, 0, 2); +TEST (unsigned short, 0, 2); +TEST (int, 0, 2); +TEST (unsigned int, 0, 2); +TEST (long, 0, 2); +TEST (unsigned long, 0, 2); +TEST (ptrdiff_t, 0, 2); +TEST (size_t, 0, 2); + +/* Verify the signed anti-range ~[TYPE_MIN - 2, -1]. */ +TEST (signed char, SCHAR_MIN + 2, -1); +TEST (short, SHRT_MIN + 2, -1); +TEST (int, INT_MIN + 2, -1); +TEST (long, LONG_MIN + 2, -1); +TEST (ptrdiff_t, PTRDIFF_MIN + 2, -1); + +/* Verify the signed anti-range ~[TYPE_MIN - 2, 0]. */ +TEST (signed char, SCHAR_MIN + 2, 0); +TEST (short, SHRT_MIN + 2, 0); +TEST (int, INT_MIN + 2, 0); +TEST (long, LONG_MIN + 2, 0); +TEST (ptrdiff_t, PTRDIFF_MIN + 2, 0); + +/* Verify the signed anti-range ~[TYPE_MIN - 2, 1]. */ +TEST (signed char, SCHAR_MIN + 2, 1); +TEST (short, SHRT_MIN + 2, 1); +TEST (int, INT_MIN + 2, 1); +TEST (long, LONG_MIN + 2, 1); +TEST (ptrdiff_t, PTRDIFF_MIN + 2, 1); + +/* Verify the signed anti-range ~[TYPE_MIN - 2, 2]. */ +TEST (signed char, SCHAR_MIN + 2, 2); +TEST (short, SHRT_MIN + 2, 2); +TEST (int, INT_MIN + 2, 2); +TEST (long, LONG_MIN + 2, 2); +TEST (ptrdiff_t, PTRDIFF_MIN + 2, 2); + +/* Verify the signed anti-range ~[-1, 2]. */ +TEST (signed char, -1, 2); +TEST (short, -1, 2); +TEST (int, -1, 2); +TEST (long, -1, 2); +TEST (ptrdiff_t, 01, 2); diff --git a/gcc/testsuite/gcc.dg/attr-alloc_size-11.c b/gcc/testsuite/gcc.dg/attr-alloc_size-11.c new file mode 100644 index 00000000000..fac8b18de61 --- /dev/null +++ b/gcc/testsuite/gcc.dg/attr-alloc_size-11.c @@ -0,0 +1,69 @@ +/* Verify that -Walloc-size-greater-than doesn't cause false positives + for anti-ranges. Note that not all of the statements below result + in the argument being represented as an anti-range. + + { dg-do compile } + { dg-options "-O2 -Walloc-size-larger-than=12 -ftrack-macro-expansion=0" } */ + +#define SCHAR_MAX __SCHAR_MAX__ +#define SCHAR_MIN (-SCHAR_MAX - 1) +#define UCHAR_MAX (SCHAR_MAX * 2 + 1) + +#define SHRT_MAX __SHRT_MAX__ +#define SHRT_MIN (-SHRT_MAX - 1) +#define USHRT_MAX (SHRT_MAX * 2 + 1) + +#define INT_MAX __INT_MAX__ +#define INT_MIN (-INT_MAX - 1) +#define UINT_MAX (INT_MAX * 2U + 1) + +#define LONG_MAX __LONG_MAX__ +#define LONG_MIN (-LONG_MAX - 1) +#define ULONG_MAX (LONG_MAX * 2LU + 1) + +#define PTRDIFF_MAX __PTRDIFF_MAX__ +#define PTRDIFF_MIN (-PTRDIFF_MAX - 1) +#define SIZE_MAX __SIZE_MAX__ + +#define ALLOC_MAX 12 + +typedef __PTRDIFF_TYPE__ ptrdiff_t; +typedef __SIZE_TYPE__ size_t; + +#define CONCAT(a, b) a ## b +#define CAT(a, b) CONCAT (a, b) + +/* Macro to generate a unique function to test the anti-range + ~[MIN, MAX] for type T. */ +#define TEST(T, min, max) \ + void* CAT (test_anti_range_, __LINE__)(T n) \ + { \ + extern void* CAT (alloc_anti_range_, __LINE__)(T) \ + __attribute__ ((alloc_size (1))); \ + if (min <= n && n <= max) \ + n = min - 1; \ + return CAT (alloc_anti_range_, __LINE__)(n); \ + } typedef void dummy /* Require a semicolon. */ + +/* The following tests fail because of missing range information. */ +TEST (signed char, SCHAR_MIN + 2, ALLOC_MAX); /* { dg-warning "argument 1 range \\\[13, \[0-9\]+\\\] exceeds maximum object size 12" "missing range info for signed char" { xfail *-*-* } } */ +TEST (short, SHRT_MIN + 2, ALLOC_MAX); /* { dg-warning "argument 1 range \\\[13, \[0-9\]+\\\] exceeds maximum object size 12" "missing range info for short" { xfail *-*-* } } */ + +TEST (int, INT_MIN + 2, ALLOC_MAX); /* { dg-warning "argument 1 range \\\[13, \[0-9\]+\\\] exceeds maximum object size 12" } */ +TEST (int, -3, ALLOC_MAX); /* { dg-warning "argument 1 range \\\[13, \[0-9\]+\\\] exceeds maximum object size 12" } */ +TEST (int, -2, ALLOC_MAX); /* { dg-warning "argument 1 range \\\[13, \[0-9\]+\\\] exceeds maximum object size 12" } */ +TEST (int, -1, ALLOC_MAX); /* { dg-warning "argument 1 range \\\[13, \[0-9\]+\\\] exceeds maximum object size 12" } */ +TEST (int, 0, ALLOC_MAX); /* { dg-warning "argument 1 range \\\[13, \[0-9\]+\\\] exceeds maximum object size 12" } */ +TEST (int, 1, ALLOC_MAX); /* { dg-warning "argument 1 range \\\[13, \[0-9\]+\\\] exceeds maximum object size 12" } */ +TEST (int, 1, INT_MAX - 1); /* { dg-warning "argument 1 range \\\[\[0-9\]+, \[0-9\]+\\\] exceeds maximum object size 12" } */ + +/* The following two aren't necessarily anti-ranges. */ +TEST (int, 1, INT_MAX); /* { dg-warning "argument 1 range \\\[-\[0-9\]+, 0\\\] is negative" } */ +TEST (int, 0, INT_MAX); /* { dg-warning "argument 1 range \\\[-\[0-9\]+, -1\\\] is negative" } */ + +TEST (long, LONG_MIN + 2, ALLOC_MAX); /* { dg-warning "argument 1 range \\\[13, \[0-9\]+\\\] exceeds maximum object size 12" } */ +TEST (ptrdiff_t, PTRDIFF_MIN + 2, ALLOC_MAX); /* { dg-warning "argument 1 range \\\[13, \[0-9\]+\\\] exceeds maximum object size 12" } */ + +TEST (unsigned, 0, ALLOC_MAX); /* { dg-warning "argument 1 range \\\[13, \[0-9\]+\\\] exceeds maximum object size 12" } */ +TEST (unsigned long, 0, ALLOC_MAX); /* { dg-warning "argument 1 range \\\[13, \[0-9\]+\\\] exceeds maximum object size 12" } */ +TEST (size_t, 0, ALLOC_MAX); /* { dg-warning "argument 1 range \\\[13, \[0-9\]+\\\] exceeds maximum object size 12" } */ diff --git a/gcc/testsuite/gcc.dg/attr-alloc_size-4.c b/gcc/testsuite/gcc.dg/attr-alloc_size-4.c index 6b70a85835c..5ce593e0692 100644 --- a/gcc/testsuite/gcc.dg/attr-alloc_size-4.c +++ b/gcc/testsuite/gcc.dg/attr-alloc_size-4.c @@ -128,15 +128,22 @@ test_int_range (int n) sink (f_int_1 (SR (min, 1234))); sink (f_int_1 (SR (-2, -1))); /* { dg-warning "argument 1 range \\\[-2, -1\\\] is negative" } */ + sink (f_int_1 (SR (1235, 2345))); /* { dg-warning "argument 1 range \\\[1235, 2345\\\] exceeds maximum object size 1234" } */ sink (f_int_1 (SR (max - 1, max))); /* { dg-warning "argument 1 range \\\[\[0-9\]+, \[0-9\]+\\\] exceeds maximum object size 1234" } */ sink (f_int_1 (SAR (-1, 1))); sink (f_int_1 (SAR (-2, 12))); sink (f_int_1 (SAR (-3, 123))); - sink (f_int_1 (SAR (-4, 1234))); /* { dg-warning "argument 1 range \\\[1235, -5\\\] is both negative and exceeds maximum object size 1234" } */ + sink (f_int_1 (SAR (-4, 1234))); /* { dg-warning "argument 1 range \\\[1235, \[0-9\]+\\\] exceeds maximum object size 1234" } */ sink (f_int_1 (SAR (min + 1, 1233))); - sink (f_int_1 (SAR (min + 2, 1235))); /* { dg-warning "argument 1 range \\\[1236, -\[0-9\]+\\\] is both negative and exceeds maximum object size 1234" } */ + sink (f_int_1 (SAR (min + 2, 1235))); /* { dg-warning "argument 1 range \\\[1236, \[0-9\]+\\\] exceeds maximum object size 1234" } */ + sink (f_int_1 (SAR (0, max))); /* { dg-warning "argument 1 range \\\[-\[0-9\]*, -1\\\] is negative" } */ + /* The range below includes zero which would be diagnosed by + -Walloc-size-zero but since all other values are negative it + is diagnosed by -Walloc-size-larger-than. */ + sink (f_int_1 (SAR (1, max))); /* { dg-warning "argument 1 range \\\[-\[0-9\]*, 0\\\] is negative" } */ + sink (f_int_1 (SAR (2, max))); } void diff --git a/gcc/testsuite/gcc.dg/builtin-stringop-chk-7.c b/gcc/testsuite/gcc.dg/builtin-stringop-chk-7.c new file mode 100644 index 00000000000..173e4ff7e2a --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-stringop-chk-7.c @@ -0,0 +1,72 @@ +/* Verify that -Wstringop-overflow doesn't cause false positives for + anti-ranges. Note that not all of the statements below result in + the memset argument being represented as an anti-range. + + { dg-do compile } + { dg-options "-O2 -Wstringop-overflow" } */ + +#define SCHAR_MAX __SCHAR_MAX__ +#define UCHAR_MAX (SCHAR_MAX * 2 + 1) + +#define SHRT_MAX __SHRT_MAX__ +#define USHRT_MAX (SHRT_MAX * 2 + 1) + +#define INT_MAX __INT_MAX__ +#define UINT_MAX (INT_MAX * 2U + 1) + +#define LONG_MAX __LONG_MAX__ +#define ULONG_MAX (LONG_MAX * 2LU + 1) + +#define PTRDIFF_MAX __PTRDIFF_MAX__ +#define SIZE_MAX __SIZE_MAX__ + +typedef __PTRDIFF_TYPE__ ptrdiff_t; +typedef __SIZE_TYPE__ size_t; + +#define TEST_AR_1(T, prefix) \ + void test_ar_1_ ## prefix (void *d, T n) \ + { \ + if (n == prefix ## _MAX - 1) \ + n = prefix ## _MAX - 2; \ + __builtin_memset (d, 0, n); \ + } typedef void dummy + +#define TEST_AR_2(T, prefix) \ + void test_ar_2_ ## prefix (void *d, T n) \ + { \ + if (prefix ## _MAX - 2 <= n && n <= prefix ## _MAX - 1) \ + n = prefix ## _MAX - 3; \ + __builtin_memset (d, 0, n); \ + } typedef void dummy + +/* Verify antirange where MIN == MAX. */ +TEST_AR_1 (signed char, SCHAR); +TEST_AR_1 (unsigned char, UCHAR); + +TEST_AR_1 (short, SHRT); +TEST_AR_1 (unsigned short, USHRT); + +TEST_AR_1 (int, INT); +TEST_AR_1 (unsigned, UINT); + +TEST_AR_1 (long, LONG); +TEST_AR_1 (unsigned long, ULONG); + +TEST_AR_1 (ptrdiff_t, PTRDIFF); +TEST_AR_1 (size_t, SIZE); + +/* Verify antirange where MIN < MAX. */ +TEST_AR_2 (signed char, SCHAR); +TEST_AR_2 (unsigned char, UCHAR); + +TEST_AR_2 (short, SHRT); +TEST_AR_2 (unsigned short, USHRT); + +TEST_AR_2 (int, INT); +TEST_AR_2 (unsigned, UINT); + +TEST_AR_2 (long, LONG); +TEST_AR_2 (unsigned long, ULONG); + +TEST_AR_2 (ptrdiff_t, PTRDIFF); +TEST_AR_2 (size_t, SIZE); diff --git a/gcc/testsuite/gcc.dg/pr78775.c b/gcc/testsuite/gcc.dg/pr78775.c new file mode 100644 index 00000000000..120c252c99e --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr78775.c @@ -0,0 +1,19 @@ +/* PR c/78775 - [7 Regression] ICE in maybe_warn_alloc_args_overflow + { dg-do compile } + { dg-options "-O2" } */ + +int a, b, *c; + +int main (void) +{ + unsigned long d = 0; + while (1) + { + switch (b) + case 'S': + d = a; + c = __builtin_malloc (d); + } + + return 0; +} diff --git a/gcc/testsuite/gcc.dg/pr78973-2.c b/gcc/testsuite/gcc.dg/pr78973-2.c new file mode 100644 index 00000000000..cc3cfc5b157 --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr78973-2.c @@ -0,0 +1,25 @@ +/* PR c/78973 - warning: ‘memcpy’: specified size exceeds maximum object + size [-Wstringop-overflow=] + + This is a companion test for the bug above that verifies that the correct + range of the int variable is detected. + + { dg-do compile } + { dg-require-effective-target int32plus } + { dg-options "-O2 -Walloc-size-larger-than=4" } */ + +void *p; + +void f (int n) +{ + if (n <= 4) + p = __builtin_malloc (n); + /* { dg-warning "argument 1 range \\\[\[0-9\]+, \[0-9\]+\\\] exceeds maximum object size 4" "ilp32" { xfail { ! lp64 } } .-1 } */ +} + +void g (unsigned n) +{ + if (n < 5) + n = 5; + f (n); +} diff --git a/gcc/testsuite/gcc.dg/pr78973.c b/gcc/testsuite/gcc.dg/pr78973.c new file mode 100644 index 00000000000..a6195f051d9 --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr78973.c @@ -0,0 +1,20 @@ +/* PR c/78973 - warning: ‘memcpy’: specified size exceeds maximum object size + + Test case for what was initially thought to be a false positive but after + deeper investigation turned out to be a true positive. + + { dg-do compile } + { dg-options "-O2 -Wall" } */ + +void f (void *p, int n) +{ + if (n <= 4) + __builtin_memset (p, 0, n); /* { dg-warning "exceeds maximum object size" } */ +} + +void g (void *d, unsigned n) +{ + if (n < 5) + n = 5; + f (d, n); +}