This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PATCH] avoid false positives due to signed to unsigned conversion (PR 78973)
- From: Martin Sebor <msebor at gmail dot com>
- To: Gcc Patch List <gcc-patches at gcc dot gnu dot org>
- Date: Thu, 5 Jan 2017 14:53:27 -0700
- Subject: [PATCH] avoid false positives due to signed to unsigned conversion (PR 78973)
- Authentication-results: sourceware.org; auth=none
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);
+}