public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] avoid issuing -Warray-bounds during folding (PR 88800)
@ 2019-01-15  0:07 Martin Sebor
  2019-01-15 11:08 ` Richard Biener
  0 siblings, 1 reply; 11+ messages in thread
From: Martin Sebor @ 2019-01-15  0:07 UTC (permalink / raw)
  To: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 1648 bytes --]

The gimple_fold_builtin_memory_op() function folds calls to memcpy
and similar to MEM_REF when the size of the copy is a small power
of 2, but it does so without considering whether the copy might
write (or read) past the end of one of the objects.  To detect
these kinds of errors (and help distinguish them from -Westrict)
the folder calls into the wrestrict pass and lets it diagnose them.
Unfortunately, that can lead to false positives for even some fairly
straightforward code that is ultimately found to be unreachable.
PR 88800 is a report of one such problem.

To avoid these false positives the attached patch adjusts
the function to avoid issuing -Warray-bounds for out-of-bounds
calls to memcpy et al.  Instead, the patch disables the folding
of such invalid calls (and only those).  Those that are not
eliminated during DCE or other subsequent passes are eventually
diagnosed by the wrestrict pass.

Since this change required removing the dependency of the detection
on the warning options (originally done as a micro-optimization to
avoid spending compile-time cycles on something that wasn't needed)
the patch also adds tests to verify that code generation is not
affected as a result of warnings being enabled or disabled.  With
the patch as is, the invalid memcpy calls end up emitted (currently
they are folded into equally invalid MEM_REFs).  At some point,
I'd like us to consider whether they should be replaced with traps
(possibly under the control of  as has been proposed a number of
times in the past.  If/when that's done, these tests will need to
be adjusted to look for traps instead.

Tested on x86_64-linux.

Martin

[-- Attachment #2: gcc-88800.diff --]
[-- Type: text/x-patch, Size: 15207 bytes --]

PR tree-optimization/88800 - Spurious -Werror=array-bounds for non-taken branch

gcc/ChangeLog:

	PR tree-optimization/88800
	* gimple-fold.c (gimple_fold_builtin_memory_op): Avoid checking
	NO_WARNING bit here.  Avoid folding out-of-bounds calls.
	* gimple-ssa-warn-restrict.c (maybe_diag_offset_bounds): Remove
	redundant argument.  Add new argument and issue diagnostics under
	its control.  Detect out-of-bounds access even with warnings
	disabled.
	(check_bounds_or_overlap): Change return type.  Add argument.
	(wrestrict_dom_walker::check_call): Adjust.
	* gimple-ssa-warn-restrict.h (check_bounds_or_overlap): Add argument.
	* tree-ssa-strlen.c (handle_builtin_strcpy): Adjust to change in
	check_bounds_or_overlap's return value.
	(handle_builtin_stxncpy): Same.
	(handle_builtin_strcat): Same.

gcc/testsuite/ChangeLog:

	PR tree-optimization/88800
	* c-c++-common/Wrestrict.c: Adjust.
	* gcc.dg/Warray-bounds-37.c: New test.
	* gcc.dg/builtin-memcpy-2.c: New test.
	* gcc.dg/builtin-memcpy.c: New test.

Index: gcc/gimple-fold.c
===================================================================
--- gcc/gimple-fold.c	(revision 267925)
+++ gcc/gimple-fold.c	(working copy)
@@ -697,8 +697,6 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterato
   tree destvar, srcvar;
   location_t loc = gimple_location (stmt);
 
-  bool nowarn = gimple_no_warning_p (stmt);
-
   /* If the LEN parameter is a constant zero or in range where
      the only valid value is zero, return DEST.  */
   if (size_must_be_zero_p (len))
@@ -766,12 +764,16 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterato
 	  unsigned ilen = tree_to_uhwi (len);
 	  if (pow2p_hwi (ilen))
 	    {
-	      /* Detect invalid bounds and overlapping copies and issue
-		 either -Warray-bounds or -Wrestrict.  */
-	      if (!nowarn
-		  && check_bounds_or_overlap (as_a <gcall *>(stmt),
-					      dest, src, len, len))
-	      	gimple_set_no_warning (stmt, true);
+	      /* Detect out-of-bounds accesses without issuing warnings.
+		 Avoid folding out-of-bounds copies but to avoid false
+		 positives for unreachable code defer warning until after
+		 DCE has worked its magic.
+		 -Wrestrict is still diagnosed.  */
+	      if (int warning = check_bounds_or_overlap (as_a <gcall *>(stmt),
+							 dest, src, len, len,
+							 false, false))
+		if (warning != OPT_Wrestrict)
+		  return false;
 
 	      scalar_int_mode mode;
 	      tree type = lang_hooks.types.type_for_size (ilen * 8, 1);
@@ -1038,10 +1040,16 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterato
 	    }
 	}
 
-      /* Detect invalid bounds and overlapping copies and issue either
-	 -Warray-bounds or -Wrestrict.  */
-      if (!nowarn)
-	check_bounds_or_overlap (as_a <gcall *>(stmt), dest, src, len, len);
+      /* Same as above, detect out-of-bounds accesses without issuing
+	 warnings.  Avoid folding out-of-bounds copies but to avoid
+	 false positives for unreachable code defer warning until
+	 after DCE has worked its magic.
+	 -Wrestrict is still diagnosed.  */
+      if (int warning = check_bounds_or_overlap (as_a <gcall *>(stmt),
+						 dest, src, len, len,
+						 false, false))
+	if (warning != OPT_Wrestrict)
+	  return false;
 
       gimple *new_stmt;
       if (is_gimple_reg_type (TREE_TYPE (srcvar)))
Index: gcc/gimple-ssa-warn-restrict.c
===================================================================
--- gcc/gimple-ssa-warn-restrict.c	(revision 267925)
+++ gcc/gimple-ssa-warn-restrict.c	(working copy)
@@ -1329,6 +1329,9 @@ maybe_diag_overlap (location_t loc, gimple *call,
   if (!acs.overlap ())
     return false;
 
+  if (gimple_no_warning_p (call))
+    return true;
+
   /* For convenience.  */
   const builtin_memref &dstref = *acs.dstref;
   const builtin_memref &srcref = *acs.srcref;
@@ -1568,7 +1571,7 @@ maybe_diag_overlap (location_t loc, gimple *call,
   return true;
 }
 
-/* Validate REF offsets in an EXPRession passed as an argument to a CALL
+/* Validate REF offsets in an expression passed as an argument to a CALL
    to a built-in function FUNC to make sure they are within the bounds
    of the referenced object if its size is known, or PTRDIFF_MAX otherwise.
    Both initial values of the offsets and their final value computed by
@@ -1578,8 +1581,19 @@ maybe_diag_overlap (location_t loc, gimple *call,
 
 static bool
 maybe_diag_offset_bounds (location_t loc, gimple *call, tree func, int strict,
-			  tree expr, const builtin_memref &ref)
+			  const builtin_memref &ref, bool do_warn)
 {
+  /* Check for out-bounds pointers regardless of warning options since
+     the result is used to make codegen decisions.  */
+  offset_int ooboff[] = { ref.offrange[0], ref.offrange[1] };
+  tree oobref = ref.offset_out_of_bounds (strict, ooboff);
+  if (!oobref)
+    return false;
+
+  /* Return true without issuing a warning.  */
+  if (!do_warn)
+    return true;
+
   if (!warn_array_bounds)
     return false;
 
@@ -1586,14 +1600,9 @@ maybe_diag_offset_bounds (location_t loc, gimple *
   if (ref.ref && TREE_NO_WARNING (ref.ref))
     return false;
 
-  offset_int ooboff[] = { ref.offrange[0], ref.offrange[1] };
-  tree oobref = ref.offset_out_of_bounds (strict, ooboff);
-  if (!oobref)
-    return false;
+  if (EXPR_HAS_LOCATION (ref.ptr))
+    loc = EXPR_LOCATION (ref.ptr);
 
-  if (EXPR_HAS_LOCATION (expr))
-    loc = EXPR_LOCATION (expr);
-
   loc = expansion_point_location_if_in_system_header (loc);
 
   char rangestr[2][64];
@@ -1811,7 +1820,7 @@ wrestrict_dom_walker::check_call (gimple *call)
       || (dstwr && !INTEGRAL_TYPE_P (TREE_TYPE (dstwr))))
     return;
 
-  if (check_bounds_or_overlap (call, dst, src, dstwr, NULL_TREE))
+  if (!check_bounds_or_overlap (call, dst, src, dstwr, NULL_TREE))
     return;
 
   /* Avoid diagnosing the call again.  */
@@ -1823,12 +1832,14 @@ wrestrict_dom_walker::check_call (gimple *call)
 /* Attempt to detect and diagnose invalid offset bounds and (except for
    memmove) overlapping copy in a call expression EXPR from SRC to DST
    and DSTSIZE and SRCSIZE bytes, respectively.  Both DSTSIZE and
-   SRCSIZE may be NULL.  Return false when one or the other has been
-   detected and diagnosed, true otherwise.  */
+   SRCSIZE may be NULL.  DO_WARN is false to detect either problem
+   without issue a warning.  Return the OPT_Wxxx constant corresponding
+   to the warning if one has been detected and zero otherwise.  */
 
-bool
+int
 check_bounds_or_overlap (gimple *call, tree dst, tree src, tree dstsize,
-			 tree srcsize, bool bounds_only /* = false */)
+			 tree srcsize, bool bounds_only /* = false */,
+			 bool do_warn /* = true */)
 {
   location_t loc = gimple_nonartificial_location (call);
   loc = expansion_point_location_if_in_system_header (loc);
@@ -1847,11 +1858,12 @@ check_bounds_or_overlap (gimple *call, tree dst, t
   /* Validate offsets first to make sure they are within the bounds
      of the destination object if its size is known, or PTRDIFF_MAX
      otherwise.  */
-  if (maybe_diag_offset_bounds (loc, call, func, strict, dst, dstref)
-      || maybe_diag_offset_bounds (loc, call, func, strict, src, srcref))
+  if (maybe_diag_offset_bounds (loc, call, func, strict, dstref, do_warn)
+      || maybe_diag_offset_bounds (loc, call, func, strict, srcref, do_warn))
     {
-      gimple_set_no_warning (call, true);
-      return false;
+      if (do_warn)
+	gimple_set_no_warning (call, true);
+      return OPT_Warray_bounds;
     }
 
   bool check_overlap
@@ -1861,7 +1873,7 @@ check_bounds_or_overlap (gimple *call, tree dst, t
 	       && DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE_CHK)));
 
   if (!check_overlap)
-    return true;
+    return 0;
 
   if (operand_equal_p (dst, src, 0))
     {
@@ -1875,10 +1887,10 @@ check_bounds_or_overlap (gimple *call, tree dst, t
 		      "%G%qD source argument is the same as destination",
 		      call, func);
 	  gimple_set_no_warning (call, true);
-	  return false;
+	  return OPT_Wrestrict;
 	}
 
-      return true;
+      return 0;
     }
 
   /* Return false when overlap has been detected.  */
@@ -1885,10 +1897,10 @@ check_bounds_or_overlap (gimple *call, tree dst, t
   if (maybe_diag_overlap (loc, call, acs))
     {
       gimple_set_no_warning (call, true);
-      return false;
+      return OPT_Wrestrict;
     }
 
-  return true;
+  return 0;
 }
 
 gimple_opt_pass *
Index: gcc/gimple-ssa-warn-restrict.h
===================================================================
--- gcc/gimple-ssa-warn-restrict.h	(revision 267925)
+++ gcc/gimple-ssa-warn-restrict.h	(working copy)
@@ -20,7 +20,7 @@
 
 #ifndef GIMPLE_SSA_WARN_RESTRICT_H
 
-extern bool check_bounds_or_overlap (gimple *, tree, tree, tree, tree,
-				     bool = false);
+extern int check_bounds_or_overlap (gimple *, tree, tree, tree, tree,
+				    bool = false, bool = true);
 
 #endif /* GIMPLE_SSA_WARN_RESTRICT_H */
Index: gcc/testsuite/c-c++-common/Wrestrict.c
===================================================================
--- gcc/testsuite/c-c++-common/Wrestrict.c	(revision 267925)
+++ gcc/testsuite/c-c++-common/Wrestrict.c	(working copy)
@@ -636,7 +636,7 @@ void test_strcpy_cst (ptrdiff_t i)
   T ("012", a, a + 3);
   /* The following doesn't overlap but it should trigger -Wstringop-overflow
      for reading past the end.  */
-  T ("012", a, a + sizeof a);
+  T ("012", a, a + sizeof a);     /* { dg-warning "\\\[-Wstringop-overflow" "pr81437" { xfail *-*-* } } */
 
   /* The terminating nul written to d[2] overwrites s[0].  */
   T ("0123", a, a + 2);           /* { dg-warning "accessing 3 bytes at offsets 0 and 2 overlaps 1 byte at offset 2" } */
@@ -651,9 +651,9 @@ void test_strcpy_cst (ptrdiff_t i)
   T ("012", a + 2, a);            /* { dg-warning "accessing 4 bytes at offsets 2 and 0 overlaps 2 bytes at offset 2" "strcpy" } */
   T ("012", a + 3, a);            /* { dg-warning "accessing 4 bytes at offsets 3 and 0 overlaps 1 byte at offset 3" "strcpy" } */
   T ("012", a + 4, a);
-  /* The following doesn't overlap but it should trigger -Wstrinop-ovewrflow
+  /* The following doesn't overlap but it triggers -Wstringop-overflow
      for writing past the end.  */
-  T ("012", a + sizeof a, a);
+  T ("012", a + sizeof a, a);     /* { dg-warning "\\\[-Wstringop-overflow" } */
 }
 
 /* Exercise strcpy with constant or known arguments offset by a range.
Index: gcc/testsuite/gcc.dg/Warray-bounds-37.c
===================================================================
--- gcc/testsuite/gcc.dg/Warray-bounds-37.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/Warray-bounds-37.c	(working copy)
@@ -0,0 +1,47 @@
+/* PR tree-optimization/88800 - Spurious -Werror=array-bounds for non-taken
+   branch
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+extern void* memmove (void*, const void*, __SIZE_TYPE__);
+
+struct A
+{
+  const char *s;
+  int n;
+};
+
+void f (void*);
+
+struct B
+{
+  char d[5];
+  int n;
+};
+
+__attribute__ ((always_inline)) inline void
+g (struct B *p, struct A a)
+{
+  int i = a.n;
+  if (i <= 5)
+    p->n = i;
+  else {
+    p->n = -1;
+    f (p);
+  }
+
+  if (p->n >= 0)
+    memmove (p->d, a.s, a.n);   /* { dg-bogus "\\\[-Warray-bounds" } */
+}
+
+void h (void)
+{
+  char c[8] = "";
+
+  struct A a;
+  a.s = c;
+  a.n = 8;
+
+  struct B b;
+  g (&b, a);
+}
Index: gcc/testsuite/gcc.dg/builtin-memcpy-2.c
===================================================================
--- gcc/testsuite/gcc.dg/builtin-memcpy-2.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/builtin-memcpy-2.c	(working copy)
@@ -0,0 +1,40 @@
+/* PR tree-optimization/88800 - Spurious -Werror=array-bounds for non-taken
+   branch
+   Verify that out-of-bounds memcpy calls are not folded even when
+   warnings are disabled.
+   { dg-do compile }
+   { dg-options "-O2 -w" } */
+
+extern void* memcpy (void*, const void*, __SIZE_TYPE__);
+
+char a1[1], a2[2], a4[4], a8[8], a16[16], a32[32];
+
+void f1 (const void *p)
+{
+  __builtin_memcpy (a1, p, sizeof a1 * 2);
+}
+
+void f2 (const void *p)
+{
+  __builtin_memcpy (a2, p, sizeof a2 * 2);
+}
+
+void f4 (const void *p)
+{
+  __builtin_memcpy (a4, p, sizeof a4 * 2);
+}
+
+void f8 (const void *p)
+{
+  __builtin_memcpy (a8, p, sizeof a8 * 2);
+}
+
+void f16 (const void *p)
+{
+  __builtin_memcpy (a16, p, sizeof a16 * 2);
+}
+
+void f32 (const void *p)
+{
+  __builtin_memcpy (a32, p, sizeof a32 * 2);
+}
Index: gcc/testsuite/gcc.dg/builtin-memcpy.c
===================================================================
--- gcc/testsuite/gcc.dg/builtin-memcpy.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/builtin-memcpy.c	(working copy)
@@ -0,0 +1,41 @@
+/* PR tree-optimization/88800 - Spurious -Werror=array-bounds for non-taken
+   branch
+   Verify that out-of-bounds memcpy calls are not folded when warnings are
+   enabled (builtin-memcpy-2.c verifies they're not folded with warnings
+   disabled).
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+extern void* memcpy (void*, const void*, __SIZE_TYPE__);
+
+char a1[1], a2[2], a4[4], a8[8], a16[16], a32[32];
+
+void f1 (const void *p)
+{
+  memcpy (a1, p, sizeof a1 * 2);    /* { dg-warning "\\\[-Warray-bounds" } */
+}
+
+void f2 (const void *p)
+{
+  memcpy (a2, p, sizeof a2 * 2);    /* { dg-warning "\\\[-Warray-bounds" } */
+}
+
+void f4 (const void *p)
+{
+  memcpy (a4, p, sizeof a4 * 2);    /* { dg-warning "\\\[-Warray-bounds" } */
+}
+
+void f8 (const void *p)
+{
+  memcpy (a8, p, sizeof a8 * 2);    /* { dg-warning "\\\[-Warray-bounds" } */
+}
+
+void f16 (const void *p)
+{
+  memcpy (a16, p, sizeof a16 * 2);  /* { dg-warning "\\\[-Warray-bounds" } */
+}
+
+void f32 (const void *p)
+{
+  memcpy (a32, p, sizeof a32 * 2);  /* { dg-warning "\\\[-Warray-bounds" } */
+}
Index: gcc/tree-ssa-strlen.c
===================================================================
--- gcc/tree-ssa-strlen.c	(revision 267925)
+++ gcc/tree-ssa-strlen.c	(working copy)
@@ -1742,7 +1742,7 @@ handle_builtin_strcpy (enum built_in_function bcod
 
   if (const strinfo *chksi = olddsi ? olddsi : dsi)
     if (si
-	&& !check_bounds_or_overlap (stmt, chksi->ptr, si->ptr, NULL_TREE, len))
+	&& check_bounds_or_overlap (stmt, chksi->ptr, si->ptr, NULL_TREE, len))
       {
 	gimple_set_no_warning (stmt, true);
 	set_no_warning = true;
@@ -2214,7 +2214,7 @@ handle_builtin_stxncpy (built_in_function, gimple_
   else
     srcsize = NULL_TREE;
 
-  if (!check_bounds_or_overlap (stmt, dst, src, dstsize, srcsize))
+  if (check_bounds_or_overlap (stmt, dst, src, dstsize, srcsize))
     {
       gimple_set_no_warning (stmt, true);
       return;
@@ -2512,7 +2512,7 @@ handle_builtin_strcat (enum built_in_function bcod
 
 	tree sptr = si && si->ptr ? si->ptr : src;
 
-	if (!check_bounds_or_overlap (stmt, dst, sptr, NULL_TREE, slen))
+	if (check_bounds_or_overlap (stmt, dst, sptr, NULL_TREE, slen))
 	  {
 	    gimple_set_no_warning (stmt, true);
 	    set_no_warning = true;
@@ -2622,7 +2622,7 @@ handle_builtin_strcat (enum built_in_function bcod
 
       tree sptr = si && si->ptr ? si->ptr : src;
 
-      if (!check_bounds_or_overlap (stmt, dst, sptr, dstlen, srcsize))
+      if (check_bounds_or_overlap (stmt, dst, sptr, dstlen, srcsize))
 	{
 	  gimple_set_no_warning (stmt, true);
 	  set_no_warning = true;

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2019-01-19 16:46 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-01-15  0:07 [PATCH] avoid issuing -Warray-bounds during folding (PR 88800) Martin Sebor
2019-01-15 11:08 ` Richard Biener
2019-01-15 15:21   ` Martin Sebor
2019-01-17  1:14     ` Jeff Law
2019-01-17  1:51       ` Martin Sebor
2019-01-17  7:48         ` Richard Biener
2019-01-18  9:35         ` Christophe Lyon
2019-01-18 12:25           ` Rainer Orth
2019-01-19  0:03             ` Martin Sebor
2019-01-18 23:43           ` Martin Sebor
2019-01-19 16:46             ` Jeff Law

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).