When the size passed to a call to a function like memcpy is a signed integer whose range has a negative lower bound and a positive upper bound the lower bound of the range of the argument after conversion to size_t may be in excess of the maximum object size (PTRDIFF_MAX by default). This results in -Wstringop-overflow false positives.
The attached patch detects this case and avoids the problem. Btw., I had a heck of a time creating a test case for this. The large translation unit submitted with the bug is from a file in the Linux kernel of some complexity. There, the range of the int variable (before conversion to size_t) is [INT_MIN, INT_MAX]. It seems very difficult to create a VR_RANGE for a signed int that matches it. The test case I came up with that still reproduces the false positive crates an anti-range for the signed int argument by converting an unsigned int in one range to a signed int and constraining it to another range. The false positive is avoided because the code doesn't (yet) handle anti-ranges. Martin PS This seems lie a bug or gotcha in the get_range_info() function. In the regression test added by the patch the VRP dump shows the following: n.0_1: ~[0, 4] _6: [18446744071562067968, +INF] ... _6 = (long unsigned int) n.0_1; __builtin_memset (d_5(D), 0, _6); but get_range_info(_6) returns the VR_RANGE: [ 0xffffffff:80000000, 0xffffffff:ffffffff ] That doesn't seem right. Is there a better/more appropriate way to determine the "correct" range? If not, perhaps get_range_info should be enhanced to make it possible to call it to detect this conversion and report a more correct result (e.g., based on a new, optional argument). Martin
PR c/78973 - [7.0 regression] warning: âmemcpyâ: specified size between 18446744071562067968 and 18446744073709551615 exceeds maximum object size 9223372036854775807 [-Wstringop-overflow=] gcc/ChangeLog: PR c/78973 * builtins.c (get_size_range): Handle signed to unsigned conversion. gcc/testsuite/ChangeLog: PR c/78973 * gcc.dg/pr78973.c: New test. diff --git a/gcc/builtins.c b/gcc/builtins.c index 5b76dfd..883d25c 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -3051,9 +3051,26 @@ get_size_range (tree exp, tree range[2]) if (range_type == VR_RANGE) { + /* The range can be the result of a conversion of a signed + variable to size_t with the lower bound after conversion + corresponding to a negative lower bound of the original + variable. Avoid the false positive for this case. */ + gimple *def = SSA_NAME_DEF_STMT (exp); + if (is_gimple_assign (def)) + { + tree_code code = gimple_assign_rhs_code (def); + if (code == NOP_EXPR) + { + tree rhs = gimple_assign_rhs1 (def); + if (!TYPE_UNSIGNED (TREE_TYPE (rhs))) + return get_size_range (rhs, 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) diff --git a/gcc/testsuite/gcc.dg/pr78973.c b/gcc/testsuite/gcc.dg/pr78973.c new file mode 100644 index 0000000..ef212cf --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr78973.c @@ -0,0 +1,18 @@ +/* PR c/78973 - [7.0 regression] warning: âmemcpyâ: specified size between + 18446744071562067968 and 18446744073709551615 exceeds maximum object + size 9223372036854775807 [-Wstringop-overflow=] + { dg-do compile } + { dg-options "-O2 -Wall" } */ + +void f (void *p, int n) +{ + if (n <= 4) + __builtin_memset (p, 0, n); /* { dg-bogus "exceeds maximum object size" } */ +} + +void g (void *d, unsigned n) +{ + if (n < 5) + n = 5; + f (d, n); +}