The test case submitted in bug 79095 - [7 regression] spurious
stringop-overflow warning shows that GCC optimizes some loops
into calls to memset with size arguments in excess of the object
size limit.  Since such calls will unavoidably lead to a buffer
overflow and memory corruption the attached patch detects them
and replaces them with a trap.  That both prevents the buffer
overflow and eliminates the warning.

Martin
PR c++/79095 - [7 regression] spurious stringop-overflow warning

gcc/ChangeLog:

	PR c++/79095
	* tree-loop-distribution.c (maybe_emit_trap): New function.
	(generate_memset_builtin): Call it.
	(generate_memcpy_builtin): Same.

gcc/testsuite/ChangeLog:

	PR c++/79095
	* g++.dg/pr79095.C: New test.

Index: gcc/testsuite/g++.dg/pr79095.C
===================================================================
--- gcc/testsuite/g++.dg/pr79095.C	(revision 0)
+++ gcc/testsuite/g++.dg/pr79095.C	(working copy)
@@ -0,0 +1,41 @@
+/* PR c++/79095 - spurious stringop-overflow warning
+   { dg-do compile }
+   { dg-options "-O3 -Wall -fdump-tree-optimized" } */
+
+typedef long unsigned int size_t;
+
+inline void
+fill (int *p, size_t n, int)
+{
+  while (n--)
+    *p++ = 0;
+}
+
+struct B
+{
+  int* p0, *p1, *p2;
+
+  size_t size () const {
+    return size_t (p1 - p0);
+  }
+
+  void resize (size_t n) {
+    if (n > size())
+      append (n - size());
+  }
+
+  void append (size_t n)
+  {
+    if (size_t (p2 - p1) >= n) {
+      fill (p1, n, 0);
+    }
+  }
+};
+
+void foo (B &b)
+{
+  b.resize (b.size () - 1);
+}
+
+
+/* { dg-final { scan-tree-dump-not "memset" "optimized" } } */
Index: gcc/tree-loop-distribution.c
===================================================================
--- gcc/tree-loop-distribution.c	(revision 244508)
+++ gcc/tree-loop-distribution.c	(working copy)
@@ -796,6 +796,49 @@ const_with_all_bytes_same (tree val)
   return buf[0];
 }
 
+/* If NBYTES is greater than SSIZE_MAX emit a trap and return true.
+   Otherwise return false.  FNCODE identifies the built-in function
+   being generated.  */
+
+static bool
+maybe_emit_trap (gimple_stmt_iterator gsi, tree nbytes,
+		 built_in_function fncode)
+{
+  if (TREE_CODE (nbytes) != INTEGER_CST
+      || tree_int_cst_le (nbytes, TYPE_MAX_VALUE (ssizetype)))
+    return false;
+
+  tree fn = builtin_decl_implicit (BUILT_IN_TRAP);
+  gimple *fn_call = gimple_build_call (fn, 0);
+  gsi_insert_after (&gsi, fn_call, GSI_CONTINUE_LINKING);
+  split_block (gimple_bb (fn_call), fn_call);
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    {
+      const char *fname = 0;
+
+      switch (fncode)
+	{
+	case BUILT_IN_MEMCPY:
+	  fname = "memcpy";
+	  break;
+	case BUILT_IN_MEMMOVE:
+	  fname = "memove";
+	  break;
+	case BUILT_IN_MEMSET:
+	  fname = "memset";
+	  break;
+	default:
+	  gcc_unreachable ();
+	}
+
+      fprintf (dump_file, "generated trap for an out-of-bounds %s "
+	       "with %wu size", fname, tree_to_uhwi (nbytes));
+    }
+
+  return true;
+}
+
 /* Generate a call to memset for PARTITION in LOOP.  */
 
 static void
@@ -817,6 +860,13 @@ generate_memset_builtin (struct loop *loop, partit
 				 partition->plus_one);
   nb_bytes = force_gimple_operand_gsi (&gsi, nb_bytes, true, NULL_TREE,
 				       false, GSI_CONTINUE_LINKING);
+
+  /* If the number of bytes is in excess of SSIZE_MAX avoid generating
+     the memset call that would certainly overflow and emit a trap
+     instead.  */
+  if (maybe_emit_trap (gsi, nb_bytes, BUILT_IN_MEMSET))
+    return;
+
   mem = build_addr_arg_loc (loc, partition->main_dr, nb_bytes);
   mem = force_gimple_operand_gsi (&gsi, mem, true, NULL_TREE,
 				  false, GSI_CONTINUE_LINKING);
@@ -873,6 +923,7 @@ generate_memcpy_builtin (struct loop *loop, partit
 				 partition->plus_one);
   nb_bytes = force_gimple_operand_gsi (&gsi, nb_bytes, true, NULL_TREE,
 				       false, GSI_CONTINUE_LINKING);
+
   dest = build_addr_arg_loc (loc, partition->main_dr, nb_bytes);
   src = build_addr_arg_loc (loc, partition->secondary_dr, nb_bytes);
   if (partition->kind == PKIND_MEMCPY
@@ -881,6 +932,12 @@ generate_memcpy_builtin (struct loop *loop, partit
   else
     kind = BUILT_IN_MEMMOVE;
 
+  /* If the number of bytes is in excess of SSIZE_MAX avoid generating
+     the mem(cpy|move) call that would certainly overflow and instead
+     emit a trap.  */
+  if (maybe_emit_trap (gsi, nb_bytes, kind))
+    return;
+
   dest = force_gimple_operand_gsi (&gsi, dest, true, NULL_TREE,
 				   false, GSI_CONTINUE_LINKING);
   src = force_gimple_operand_gsi (&gsi, src, true, NULL_TREE,

Reply via email to