PR tree-optimization/78775 - [7 Regression] ICE in maybe_warn_alloc_args_overflow
gcc/ChangeLog:
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.
gcc/testsuite/ChangeLog:
PR tree-optimization/78775
* gcc.dg/attr-alloc_size-4.c: Add test cases.
* gcc.dg/pr78775.c: New test.
* gcc.dg/pr78973-2.c: New test.
* gcc.dg/pr78973.c: New test.
@@ -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
@@ -1197,92 +1197,189 @@ 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)
+ char *end = NULL;
+ errno = 0;
+ unsigned HOST_WIDE_INT unit = 1;
+ unsigned HOST_WIDE_INT limit
+ = strtoull (warn_alloc_size_limit, &end, 10);
+
+ if (!errno)
{
- /* 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 (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);
+ 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. SIGNED_P is initially set to -1 and is used internally by
+ the function and should not be explicitly passed in by callers. */
-static bool
-operand_signed_p (tree op)
+bool
+get_size_range (tree exp, tree range[2], int signed_p /* = -1 */)
{
- if (TREE_CODE (op) == SSA_NAME)
+ if (tree_fits_uhwi_p (exp))
+ {
+ /* EXP is a constant. */
+ range[0] = range[1] = exp;
+ return true;
+ }
+
+ if (TREE_CODE (exp) != SSA_NAME)
+ {
+ /* No range information available. */
+ range[0] = NULL_TREE;
+ range[1] = NULL_TREE;
+ return false;
+ }
+
+ wide_int min, max;
+ enum value_range_type range_type = get_range_info (exp, &min, &max);
+
+ tree exptype = TREE_TYPE (exp);
+ unsigned expprec = TYPE_PRECISION (exptype);
+ wide_int wzero = wi::zero (expprec);
+
+ /* Set SIGNED_P once (will be used by recursive calls). */
+ if (signed_p < 0)
+ signed_p = !TYPE_UNSIGNED (exptype);
+
+ if (range_type == VR_VARYING)
{
- gimple *def = SSA_NAME_DEF_STMT (op);
- if (is_gimple_assign (def))
+ /* No range information available. */
+ range[0] = NULL_TREE;
+ range[1] = NULL_TREE;
+ return false;
+ }
+
+ if (range_type == VR_ANTI_RANGE)
+ {
+ if (TYPE_UNSIGNED (exptype) || wi::les_p (wzero, min))
{
- /* 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);
+ /* EXP is either unsigned or strictly not in an unsigned
+ range. Set the result range using zero as the lower
+ bound if that's still a range or the upper bound if
+ using zero would result in [0, 0]. */
+ if (wi::lts_p (wzero, min - 1))
+ {
+ max = min - 1;
+ min = wzero;
+ }
+ else
+ {
+ min = max + 1;
+ max = wide_int (TYPE_MAX_VALUE (exptype));
+ }
}
- else if (gimple_code (def) == GIMPLE_PHI)
+ else if (wi::les_p (max, wzero))
{
- /* 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)
+ /* 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 = wide_int (TYPE_MAX_VALUE (exptype));
+ }
+ else if (signed_p)
+ {
+ /* EXP is not in a negative-positive range and no conversion
+ is being performed. 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 = wide_int (TYPE_MAX_VALUE (exptype));
+ }
+ else
+ {
+ /* EXP is not in negative-positive range and it's being
+ converted to an unsigned. That means the negative
+ lower bound is inverted and the resulting range is
+ either [~(MIN - 1), MAX + 1] or [MAX + 1, ~(MIN - 1)],
+ whichever results in the lower bound being less than
+ the upper bound. */
+ --min;
+ ++max;
+
+ /* For a signed to unsigned conversion invert
+ the minimum. */
+ min = ~min;
+
+ if (wi::ltu_p (max, min))
{
- tree op = gimple_phi_arg_def (def, i);
- if (TREE_CODE (op) != INTEGER_CST
- && !operand_signed_p (op))
- return false;
+ wide_int tmp = max;
+ max = min;
+ min = tmp;
}
-
- return true;
}
}
- return !TYPE_UNSIGNED (TREE_TYPE (op));
+ if (TYPE_UNSIGNED (exptype) || !wi::neg_p (min) || signed_p)
+ {
+ /* EXP is either unsigned or in a non-negative range,
+ or the result is not being converted to unsigned.
+ Use its range as is. */
+ range[0] = wide_int_to_tree (exptype, min);
+ range[1] = wide_int_to_tree (exptype, max);
+ }
+ else if (wi::neg_p (min) && wi::les_p (max, wzero))
+ {
+ /* EXP is in a strictly negative range (or zero) and is
+ being converted to unsigned. Also use its range as is. */
+ range[0] = wide_int_to_tree (exptype, min);
+ range[1] = wide_int_to_tree (exptype, max);
+ }
+ else
+ {
+ /* Otherwise, clear the lower bound if it's negative and
+ the upper bound is positive. */
+ range[0] = integer_zero_node;
+ range[1] = wide_int_to_tree (exptype, max);
+ }
+
+ return true;
}
/* Diagnose a call EXP to function FN decorated with attribute alloc_size
@@ -1316,8 +1413,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 +1431,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 +1448,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 +1499,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 %<SIZE_MAX%>",
- 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);
@@ -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], int = -1);
#endif // GCC_CALLS_H
@@ -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
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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 \\\[5, 2147483647\\\] exceeds maximum object size 4" "ilp32" { xfail { ! lp64 } } .-1 } */
+}
+
+void g (unsigned n)
+{
+ if (n < 5)
+ n = 5;
+ f (n);
+}
new file mode 100644
@@ -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);
+}