restore -Wstringop-overflow for checked strcpy/strcat (PR 85259)

Message ID 9a8db8a9-acd7-665a-1300-a295e1945bf3@gmail.com
State New
Headers show
Series
  • restore -Wstringop-overflow for checked strcpy/strcat (PR 85259)
Related show

Commit Message

Martin Sebor May 14, 2018, 10:41 p.m.
r256683 committed to GCC 8 to avoiding duplicate instances of
-Wstringop-overflow warnings on some targets has the unintended
side-effect of suppressing even singleton instances of the warning
in cases such as 'strcat (strcpy (buf, "hello "), "world!")' when
_FORTIFY_SOURCE is defined.

The attached patch restores the warning for the trunk.

Since this is a regression in a security feature and the warning
isn't prone to false positives (I don't think I've seen any when
_FORTIFY_SOURCE is defined), I'd also like the fix considered for
the 8 branch.

Thanks
Martin

Comments

Martin Sebor June 5, 2018, 7:58 p.m. | #1
Ping: https://gcc.gnu.org/ml/gcc-patches/2018-05/msg00652.html

On 05/14/2018 04:41 PM, Martin Sebor wrote:
> r256683 committed to GCC 8 to avoiding duplicate instances of
> -Wstringop-overflow warnings on some targets has the unintended
> side-effect of suppressing even singleton instances of the warning
> in cases such as 'strcat (strcpy (buf, "hello "), "world!")' when
> _FORTIFY_SOURCE is defined.
>
> The attached patch restores the warning for the trunk.
>
> Since this is a regression in a security feature and the warning
> isn't prone to false positives (I don't think I've seen any when
> _FORTIFY_SOURCE is defined), I'd also like the fix considered for
> the 8 branch.
>
> Thanks
> Martin
Martin Sebor June 11, 2018, 5:36 p.m. | #2
Ping: https://gcc.gnu.org/ml/gcc-patches/2018-05/msg00652.html

On 06/05/2018 01:58 PM, Martin Sebor wrote:
> Ping: https://gcc.gnu.org/ml/gcc-patches/2018-05/msg00652.html
>
> On 05/14/2018 04:41 PM, Martin Sebor wrote:
>> r256683 committed to GCC 8 to avoiding duplicate instances of
>> -Wstringop-overflow warnings on some targets has the unintended
>> side-effect of suppressing even singleton instances of the warning
>> in cases such as 'strcat (strcpy (buf, "hello "), "world!")' when
>> _FORTIFY_SOURCE is defined.
>>
>> The attached patch restores the warning for the trunk.
>>
>> Since this is a regression in a security feature and the warning
>> isn't prone to false positives (I don't think I've seen any when
>> _FORTIFY_SOURCE is defined), I'd also like the fix considered for
>> the 8 branch.
>>
>> Thanks
>> Martin
>
Jeff Law June 11, 2018, 9:25 p.m. | #3
On 05/14/2018 04:41 PM, Martin Sebor wrote:
> r256683 committed to GCC 8 to avoiding duplicate instances of
> -Wstringop-overflow warnings on some targets has the unintended
> side-effect of suppressing even singleton instances of the warning
> in cases such as 'strcat (strcpy (buf, "hello "), "world!")' when
> _FORTIFY_SOURCE is defined.
> 
> The attached patch restores the warning for the trunk.
> 
> Since this is a regression in a security feature and the warning
> isn't prone to false positives (I don't think I've seen any when
> _FORTIFY_SOURCE is defined), I'd also like the fix considered for
> the 8 branch.
> 
> Thanks
> Martin
> 
> gcc-85259.diff
> 
> 
> PR tree-optimization/85259 - Missing -Wstringop-overflow= since r256683
> 
> gcc/ChangeLog:
> 
> 	PR tree-optimization/85259
> 	* builtins.c (compute_objsize): Handle constant offsets.
> 	* gimple-ssa-warn-restrict.c (maybe_diag_offset_bounds): Return
> 	true iff a warning has been issued.
> 	* gimple.h (gimple_nonartificial_location): New function.
> 	* tree-ssa-strlen.c (maybe_diag_stxncpy_trunc): Call
> 	gimple_nonartificial_location and handle -Wno-system-headers.
> 	(handle_builtin_stxncpy): Same.
> 
> gcc/testsuite/ChangeLog:
> 
> 	PR tree-optimization/85259
> 	* gcc.dg/Wstringop-overflow-5.c: New test.
> 	* gcc.dg/Wstringop-overflow-6.c: New test.
OK for the trunk.

The general guidance we've received is to not try and fix these on the
release branches.  However, I think this one deserves an exception to
the general guidance.  So OK for the gcc-8 branch.

jeff
Martin Sebor June 12, 2018, 6:14 p.m. | #4
On 06/11/2018 03:25 PM, Jeff Law wrote:
> On 05/14/2018 04:41 PM, Martin Sebor wrote:
>> r256683 committed to GCC 8 to avoiding duplicate instances of
>> -Wstringop-overflow warnings on some targets has the unintended
>> side-effect of suppressing even singleton instances of the warning
>> in cases such as 'strcat (strcpy (buf, "hello "), "world!")' when
>> _FORTIFY_SOURCE is defined.
>>
>> The attached patch restores the warning for the trunk.
>>
>> Since this is a regression in a security feature and the warning
>> isn't prone to false positives (I don't think I've seen any when
>> _FORTIFY_SOURCE is defined), I'd also like the fix considered for
>> the 8 branch.
>>
>> Thanks
>> Martin
>>
>> gcc-85259.diff
>>
>>
>> PR tree-optimization/85259 - Missing -Wstringop-overflow= since r256683
>>
>> gcc/ChangeLog:
>>
>> 	PR tree-optimization/85259
>> 	* builtins.c (compute_objsize): Handle constant offsets.
>> 	* gimple-ssa-warn-restrict.c (maybe_diag_offset_bounds): Return
>> 	true iff a warning has been issued.
>> 	* gimple.h (gimple_nonartificial_location): New function.
>> 	* tree-ssa-strlen.c (maybe_diag_stxncpy_trunc): Call
>> 	gimple_nonartificial_location and handle -Wno-system-headers.
>> 	(handle_builtin_stxncpy): Same.
>>
>> gcc/testsuite/ChangeLog:
>>
>> 	PR tree-optimization/85259
>> 	* gcc.dg/Wstringop-overflow-5.c: New test.
>> 	* gcc.dg/Wstringop-overflow-6.c: New test.
> OK for the trunk.

On retesting the patch I noticed a failure in one of the test
cases it adds.  It turns out that a recent change made to improve
code generation (r261061) has resulted in transforming one of
the strcat calls in the test into a MEM_REF, which defeats
the -Wstringop-overflow checker (the checker only looks at
built-ins, not MEM_REF expressions).  It feels like one step
forward and one step back with these MEM_REFs.  I've xfailed
the test case until the -Wstringop-overflow checker is made
smarter and opened bug 86121 to track this regression.

(Though, IMO, GCC should check calls to make sure they're
valid before transforming them into MEM_REFs and only do
the transformation if it is within the bounds of the objects .)

>
> The general guidance we've received is to not try and fix these on the
> release branches.  However, I think this one deserves an exception to
> the general guidance.  So OK for the gcc-8 branch.

Okay.  (GCC 8 is not affected by the above.)

Martin
Joseph Myers June 13, 2018, 8:45 p.m. | #5
This patch (commit r261518) has broken the build of the glibc testsuite.  
I see, for example for aarch64-linux-gnu with build-many-glibcs.py:

In function 'test_strncat',
    inlined from 'main' at tester.c:1621:3:
tester.c:490:13: error: 'strncat' accessing 18446744073709551600 or more bytes at offsets 0 and 0 overlaps 9223372036854775793 bytes at offset -9223372036854775793 [-Werror=restrict]
      check (strncat (buf1 + n2, buf2 + n1, ~((size_t) 0) - n4)
             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors

The code in question already has -Wstringop-overflow= and -Warray-bounds 
warnings disabled as it's deliberately testing a large size.  But a 
-Wrestrict warning makes no sense to me at all here.  The arguments are 
based on two different variables, buf1 and buf2, and regardless of sizes 
and offsets I wouldn't expect a -Wrestrict warning when the arguments are 
based on different array variables like that.
Martin Sebor June 13, 2018, 10:09 p.m. | #6
On 06/13/2018 02:45 PM, Joseph Myers wrote:
> This patch (commit r261518) has broken the build of the glibc testsuite.
> I see, for example for aarch64-linux-gnu with build-many-glibcs.py:
>
> In function 'test_strncat',
>     inlined from 'main' at tester.c:1621:3:
> tester.c:490:13: error: 'strncat' accessing 18446744073709551600 or more bytes at offsets 0 and 0 overlaps 9223372036854775793 bytes at offset -9223372036854775793 [-Werror=restrict]
>       check (strncat (buf1 + n2, buf2 + n1, ~((size_t) 0) - n4)
>              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> cc1: all warnings being treated as errors
>
> The code in question already has -Wstringop-overflow= and -Warray-bounds
> warnings disabled as it's deliberately testing a large size.  But a
> -Wrestrict warning makes no sense to me at all here.  The arguments are
> based on two different variables, buf1 and buf2, and regardless of sizes
> and offsets I wouldn't expect a -Wrestrict warning when the arguments are
> based on different array variables like that.

I couldn't readily reproduce the warning with the Glibc test
but I think it triggers for the same reason as the one below:

   extern char a[16], b[16];

   void f (void)
   {
     __builtin_strncat (a, b, __SIZE_MAX__ - 16);
   }

   In function ‘f’:
   warning: ‘__builtin_strncat’ accessing 18446744073709551599 or more 
bytes at offsets 0 and 0 overlaps 9223372036854775792 bytes at offset 
-9223372036854775792 [-Wrestrict]

There's logic in -Wrestrict that tries to determine the sizes
even distinct objects would need to have in order for an access
to them of a given kind not to overlap.  (This code starts on
line 1209 in gimple-ssa-warn-restrict.c.)    This is probably
unnecessary when the objects sizes are known (and may even be
redundant when they aren't in some cases since other warnings
might catch that).  Then again, the test does some bad things
so it's not surprising (or necessarily bad) that they get
caught by multiple checkers.

What could stand to be improved is the diagnostic.  Some of
the offsets in it don't look very meaningful (they may not
be wrong, but they don't help the user make sense out of
the warning).

Martin

Patch

PR tree-optimization/85259 - Missing -Wstringop-overflow= since r256683

gcc/ChangeLog:

	PR tree-optimization/85259
	* builtins.c (compute_objsize): Handle constant offsets.
	* gimple-ssa-warn-restrict.c (maybe_diag_offset_bounds): Return
	true iff a warning has been issued.
	* gimple.h (gimple_nonartificial_location): New function.
	* tree-ssa-strlen.c (maybe_diag_stxncpy_trunc): Call
	gimple_nonartificial_location and handle -Wno-system-headers.
	(handle_builtin_stxncpy): Same.

gcc/testsuite/ChangeLog:

	PR tree-optimization/85259
	* gcc.dg/Wstringop-overflow-5.c: New test.
	* gcc.dg/Wstringop-overflow-6.c: New test.
Index: gcc/builtins.c
===================================================================
--- gcc/builtins.c	(revision 259298)
+++ gcc/builtins.c	(working copy)
@@ -3329,10 +3329,29 @@  compute_objsize (tree dest, int ostype)
 	{
 	  /* compute_builtin_object_size fails for addresses with
 	     non-constant offsets.  Try to determine the range of
-	     such an offset here and use it to adjus the constant
+	     such an offset here and use it to adjust the constant
 	     size.  */
 	  tree off = gimple_assign_rhs2 (stmt);
-	  if (TREE_CODE (off) == SSA_NAME
+	  if (TREE_CODE (off) == INTEGER_CST)
+	    {
+	      if (tree size = compute_objsize (dest, ostype))
+		{
+		  wide_int wioff = wi::to_wide (off);
+		  wide_int wisiz = wi::to_wide (size);
+
+		  /* Ignore negative offsets for now.  For others,
+		     use the lower bound as the most optimistic
+		     estimate of the (remaining) size.  */
+		  if (wi::sign_mask (wioff))
+		    ;
+		  else if (wi::ltu_p (wioff, wisiz))
+		    return wide_int_to_tree (TREE_TYPE (size),
+					     wi::sub (wisiz, wioff));
+		  else
+		    return size_zero_node;
+		}
+	    }
+	  else if (TREE_CODE (off) == SSA_NAME
 	      && INTEGRAL_TYPE_P (TREE_TYPE (off)))
 	    {
 	      wide_int min, max;
Index: gcc/gimple-ssa-warn-restrict.c
===================================================================
--- gcc/gimple-ssa-warn-restrict.c	(revision 259298)
+++ gcc/gimple-ssa-warn-restrict.c	(working copy)
@@ -1603,8 +1603,6 @@  maybe_diag_offset_bounds (location_t loc, gcall *c
 
   loc = expansion_point_location_if_in_system_header (loc);
 
-  tree type;
-
   char rangestr[2][64];
   if (ooboff[0] == ooboff[1]
       || (ooboff[0] != ref.offrange[0]
@@ -1615,6 +1613,8 @@  maybe_diag_offset_bounds (location_t loc, gcall *c
 	     (long long) ooboff[0].to_shwi (),
 	     (long long) ooboff[1].to_shwi ());
 
+  bool warned = false;
+
   if (oobref == error_mark_node)
     {
       if (ref.sizrange[0] == ref.sizrange[1])
@@ -1624,6 +1624,8 @@  maybe_diag_offset_bounds (location_t loc, gcall *c
 		 (long long) ref.sizrange[0].to_shwi (),
 		 (long long) ref.sizrange[1].to_shwi ());
 
+      tree type;
+
       if (DECL_P (ref.base)
 	  && TREE_CODE (type = TREE_TYPE (ref.base)) == ARRAY_TYPE)
 	{
@@ -1631,19 +1633,22 @@  maybe_diag_offset_bounds (location_t loc, gcall *c
 			  "%G%qD pointer overflow between offset %s "
 			  "and size %s accessing array %qD with type %qT",
 			  call, func, rangestr[0], rangestr[1], ref.base, type))
-	    inform (DECL_SOURCE_LOCATION (ref.base),
-		    "array %qD declared here", ref.base);
+	    {
+	      inform (DECL_SOURCE_LOCATION (ref.base),
+		      "array %qD declared here", ref.base);
+	      warned = true;
+	    }
 	  else
-	    warning_at (loc, OPT_Warray_bounds,
-			"%G%qD pointer overflow between offset %s "
-			"and size %s",
-			call, func, rangestr[0], rangestr[1]);
+	    warned = warning_at (loc, OPT_Warray_bounds,
+				 "%G%qD pointer overflow between offset %s "
+				 "and size %s",
+				 call, func, rangestr[0], rangestr[1]);
 	}
       else
-	warning_at (loc, OPT_Warray_bounds,
-		    "%G%qD pointer overflow between offset %s "
-		    "and size %s",
-		    call, func, rangestr[0], rangestr[1]);
+	warned = warning_at (loc, OPT_Warray_bounds,
+			     "%G%qD pointer overflow between offset %s "
+			     "and size %s",
+			     call, func, rangestr[0], rangestr[1]);
     }
   else if (oobref == ref.base)
     {
@@ -1674,22 +1679,26 @@  maybe_diag_offset_bounds (location_t loc, gcall *c
 				  "of object %qD with type %qT"),
 			     call, func, rangestr[0],
 			     ref.base, TREE_TYPE (ref.base)))
-	    inform (DECL_SOURCE_LOCATION (ref.base),
-		    "%qD declared here", ref.base);
+	    {
+	      inform (DECL_SOURCE_LOCATION (ref.base),
+		      "%qD declared here", ref.base);
+	      warned = true;
+	    }
 	}
       else if (ref.basesize < maxobjsize)
-	warning_at (loc, OPT_Warray_bounds,
-		    form
-		    ? G_("%G%qD forming offset %s is out of the bounds "
-			 "[0, %wu]")
-		    : G_("%G%qD offset %s is out of the bounds [0, %wu]"),
-		    call, func, rangestr[0], ref.basesize.to_uhwi ());
+	warned = warning_at (loc, OPT_Warray_bounds,
+			     form
+			     ? G_("%G%qD forming offset %s is out "
+				  "of the bounds [0, %wu]")
+			     : G_("%G%qD offset %s is out "
+				  "of the bounds [0, %wu]"),
+			     call, func, rangestr[0], ref.basesize.to_uhwi ());
       else
-	warning_at (loc, OPT_Warray_bounds,
-		    form
-		    ? G_("%G%qD forming offset %s is out of bounds")
-		    : G_("%G%qD offset %s is out of bounds"),
-		    call, func, rangestr[0]);
+	warned = warning_at (loc, OPT_Warray_bounds,
+			     form
+			     ? G_("%G%qD forming offset %s is out of bounds")
+			     : G_("%G%qD offset %s is out of bounds"),
+			     call, func, rangestr[0]);
     }
   else if (TREE_CODE (ref.ref) == MEM_REF)
     {
@@ -1698,24 +1707,25 @@  maybe_diag_offset_bounds (location_t loc, gcall *c
 	type = TREE_TYPE (type);
       type = TYPE_MAIN_VARIANT (type);
 
-      warning_at (loc, OPT_Warray_bounds,
-		  "%G%qD offset %s from the object at %qE is out "
-		  "of the bounds of %qT",
-		  call, func, rangestr[0], ref.base, type);
+      warned = warning_at (loc, OPT_Warray_bounds,
+			   "%G%qD offset %s from the object at %qE is out "
+			   "of the bounds of %qT",
+			   call, func, rangestr[0], ref.base, type);
     }
   else
     {
-      type = TYPE_MAIN_VARIANT (TREE_TYPE (ref.ref));
+      tree type = TYPE_MAIN_VARIANT (TREE_TYPE (ref.ref));
 
-      warning_at (loc, OPT_Warray_bounds,
-		"%G%qD offset %s from the object at %qE is out "
-		"of the bounds of referenced subobject %qD with type %qT "
-		"at offset %wu",
-		call, func, rangestr[0], ref.base, TREE_OPERAND (ref.ref, 1),
-		type, ref.refoff.to_uhwi ());
+      warned = warning_at (loc, OPT_Warray_bounds,
+			   "%G%qD offset %s from the object at %qE is out "
+			   "of the bounds of referenced subobject %qD with "
+			   "type %qT at offset %wu",
+			   call, func, rangestr[0], ref.base,
+			   TREE_OPERAND (ref.ref, 1), type,
+			   ref.refoff.to_uhwi ());
     }
 
-  return true;
+  return warned;
 }
 
 /* Check a CALL statement for restrict-violations and issue warnings
@@ -1840,13 +1850,8 @@  bool
 check_bounds_or_overlap (gcall *call, tree dst, tree src, tree dstsize,
 			 tree srcsize, bool bounds_only /* = false */)
 {
-  location_t loc = gimple_location (call);
-
-  if (tree block = gimple_block (call))
-    if (location_t *pbloc = block_nonartificial_location (block))
-      loc = *pbloc;
-
+  location_t loc = gimple_nonartificial_location (call);
   loc = expansion_point_location_if_in_system_header (loc);
 
   tree func = gimple_call_fndecl (call);
Index: gcc/gimple.h
===================================================================
--- gcc/gimple.h	(revision 260241)
+++ gcc/gimple.h	(working copy)
@@ -1796,7 +1796,20 @@  gimple_has_location (const gimple *g)
   return LOCATION_LOCUS (gimple_location (g)) != UNKNOWN_LOCATION;
 }
 
+/* Return non-artificial location information for statement G.  */
 
+static inline location_t
+gimple_nonartificial_location (const gimple *g)
+{
+  location_t *ploc = NULL;
+
+  if (tree block = gimple_block (g))
+    ploc = block_nonartificial_location (block);
+
+  return ploc ? *ploc : gimple_location (g);
+}
+
+
 /* Return the file name of the location of STMT.  */
 
 static inline const char *
Index: gcc/tree-ssa-strlen.c
===================================================================
--- gcc/tree-ssa-strlen.c	(revision 259298)
+++ gcc/tree-ssa-strlen.c	(working copy)
@@ -1947,7 +1947,9 @@  maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi
 	}
     }
 
-  location_t callloc = gimple_location (stmt);
+  location_t callloc = gimple_nonartificial_location (stmt);
+  callloc = expansion_point_location_if_in_system_header (callloc);
+
   tree func = gimple_call_fndecl (stmt);
 
   if (lenrange[0] != 0 || !wi::neg_p (lenrange[1]))
@@ -2132,7 +2134,8 @@  handle_builtin_stxncpy (built_in_function, gimple_
      to strlen(S)).  */
   strinfo *silen = get_strinfo (pss->first);
 
-  location_t callloc = gimple_location (stmt);
+  location_t callloc = gimple_nonartificial_location (stmt);
+  callloc = expansion_point_location_if_in_system_header (callloc);
 
   tree func = gimple_call_fndecl (stmt);
 
Index: gcc/testsuite/gcc.dg/Wstringop-overflow-5.c
===================================================================
--- gcc/testsuite/gcc.dg/Wstringop-overflow-5.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/Wstringop-overflow-5.c	(working copy)
@@ -0,0 +1,58 @@ 
+/* PR tree-optimization/85259 - Missing -Wstringop-overflow= since r256683
+   { dg-do compile }
+   { dg-options "-O2 -Wstringop-overflow" } */
+
+extern char* strcpy (char*, const char*);
+extern char* strcat (char*, const char*);
+
+char a1[1];
+char a2[2];
+char a3[3];
+char a4[4];
+char a5[5];
+char a6[6];
+char a7[7];
+char a8[8];
+
+/* Verify that at least one instance of -Wstringop-overflow is issued
+   for each pair of strcpy/strcat calls.  */
+
+void test_strcpy_strcat_1 (void)
+{
+  strcpy (a1, "1"), strcat (a1, "2");   /* { dg-warning "\\\[-Wstringop-overflow=]" } */
+}
+
+void test_strcpy_strcat_2 (void)
+{
+  strcpy (a2, "12"), strcat (a2, "3");   /* { dg-warning "\\\[-Wstringop-overflow=]" } */
+}
+
+void test_strcpy_strcat_3 (void)
+{
+  strcpy (a3, "123"), strcat (a3, "4");   /* { dg-warning "\\\[-Wstringop-overflow=]" } */
+}
+
+void test_strcpy_strcat_4 (void)
+{
+  strcpy (a4, "1234"), strcat (a4, "5");   /* { dg-warning "\\\[-Wstringop-overflow=]" } */
+}
+
+void test_strcpy_strcat_5 (void)
+{
+  strcpy (a5, "12345"), strcat (a5, "6");   /* { dg-warning "\\\[-Wstringop-overflow=]" } */
+}
+
+void test_strcpy_strcat_6 (void)
+{
+  strcpy (a6, "123456"), strcat (a6, "7");   /* { dg-warning "\\\[-Wstringop-overflow=]" } */
+}
+
+void test_strcpy_strcat_7 (void)
+{
+  strcpy (a7, "1234567"), strcat (a7, "8");   /* { dg-warning "\\\[-Wstringop-overflow=]" } */
+}
+
+void test_strcpy_strcat_8 (void)
+{
+  strcpy (a8, "12345678"), strcat (a8, "9");   /* { dg-warning "\\\[-Wstringop-overflow=]" } */
+}
Index: gcc/testsuite/gcc.dg/Wstringop-overflow-6.c
===================================================================
--- gcc/testsuite/gcc.dg/Wstringop-overflow-6.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/Wstringop-overflow-6.c	(working copy)
@@ -0,0 +1,59 @@ 
+/* PR tree-optimization/85259 - Missing -Wstringop-overflow= since r256683
+   { dg-do compile }
+   { dg-options "-O2 -Wstringop-overflow -ftrack-macro-expansion=0" } */
+
+#define bos1(p) __builtin_object_size (p, 1)
+#define strcat(d, s) __builtin___strcat_chk (d, s, bos1 (d))
+#define strcpy(d, s) __builtin___strcpy_chk (d, s, bos1 (d))
+
+char a1[1];
+char a2[2];
+char a3[3];
+char a4[4];
+char a5[5];
+char a6[6];
+char a7[7];
+char a8[8];
+
+/* Verify that at least one instance of -Wstringop-overflow is issued
+   for each pair of strcpy/strcat calls.  */
+
+void test_strcpy_strcat_1 (void)
+{
+  strcpy (a1, "1"), strcat (a1, "2");   /* { dg-warning "\\\[-Wstringop-overflow=]" } */
+}
+
+void test_strcpy_strcat_2 (void)
+{
+  strcpy (a2, "12"), strcat (a2, "3");   /* { dg-warning "\\\[-Wstringop-overflow=]" } */
+}
+
+void test_strcpy_strcat_3 (void)
+{
+  strcpy (a3, "123"), strcat (a3, "4");   /* { dg-warning "\\\[-Wstringop-overflow=]" } */
+}
+
+void test_strcpy_strcat_4 (void)
+{
+  strcpy (a4, "1234"), strcat (a4, "5");   /* { dg-warning "\\\[-Wstringop-overflow=]" } */
+}
+
+void test_strcpy_strcat_5 (void)
+{
+  strcpy (a5, "12345"), strcat (a5, "6");   /* { dg-warning "\\\[-Wstringop-overflow=]" } */
+}
+
+void test_strcpy_strcat_6 (void)
+{
+  strcpy (a6, "123456"), strcat (a6, "7");   /* { dg-warning "\\\[-Wstringop-overflow=]" } */
+}
+
+void test_strcpy_strcat_7 (void)
+{
+  strcpy (a7, "1234567"), strcat (a7, "8");   /* { dg-warning "\\\[-Wstringop-overflow=]" } */
+}
+
+void test_strcpy_strcat_8 (void)
+{
+  strcpy (a8, "12345678"), strcat (a8, "9");   /* { dg-warning "\\\[-Wstringop-overflow=]" } */
+}