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,