This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] detect strcat/strcpy overflowing member arrays (PR 91631)


While testing some other buffer overflow improvements I was
reminded that GCC doesn't detect even some basic instances
of overflowing struct members involving strcpy calls with
non-constant strings, with or without _FORTIFY_SOURCE.  For
example:

  struct S { char a[4]; void (*pf)(void); };

  extern struct S a[];

  void f (void)
  {
    char s[] = "123456789";
    strcpy (a[0].a, s);
  }

This is because the strlen pass that computes the length of
non-constant strings like s above isn't yet integrated with
the -Wstringop-overflow/_FORTIFY_SOURCE checking.

But the strlen pass does call into the wrestrict pass to check
for overlapping copies (mainly to avoid issuing meaningless
-Wrestrict warnings or mentioning invalid offsets in then), and
the wrestrict pass checks for invalid offsets and ordinarily
diagnoses those.

However, the invalid offset detection isn't quite robust enough
to detect these cases (it gives up too early), and so the first
line of defense fails.

The attached patch improves the wrestrict pass to also detect
these invalid offsets.  The improvement relies on the recently
introduced component_size() function.  Testing the change in
turn exposed a bug in the function (relying on the size of
an array to determine if it was the member[1] kind rather than
considering its domain.  So the patch also corrects that bug
With the patch applied the out-of-bounds access above is
diagnosed:

warning: ‘strcpy’ offset [4, 9] from the object at ‘a’ is out of the bounds of referenced subobject ‘a’ with type ‘char[4]’ at offset 0 [-Warray-bounds]

-Warray-bounds isn't the ideal warning to issue and ultimately,
the strcpy member overflow detection will need to be done under
-Wstringop-overflow, and trigger aborts at runtime with
_FORTIFY_SOURCE.  But that's a project of its own (possibly
involving moving the -Wstringop-overflow checking code from
builtins.c into the strlen pass itself).

Tested on x86_64-linux.

Martin

PS I can think of little in terms of "infrastructure" in this
work that could be split out into a separate patch, except
perhaps the component_size change.  But given how small
the function is it doesn't seem necessary.
PR middle-end/91631 - buffer overflow into an array member of a declared object not detected

gcc/ChangeLog:

	PR middle-end/91631
	* builtins.c (component_size): Correct trailing array computation,
	rename to component_ref_size and move...
	(compute_objsize): Adjust.
	* gimple-ssa-warn-restrict.c (builtin_memref::refsize): New member.
	(builtin_access::strict): Do not consider mememmove.
	(builtin_access::write_off): New function.
	(builtin_memref::builtin_memref): Initialize refsize.
	(builtin_memref::set_base_and_offset): Adjust refoff and compute
	refsize.
	(builtin_memref::offset_out_of_bounds): Use ooboff input values.
	Handle refsize.
	(builtin_access::builtin_access): Intialize dstoff to destination
	refeence offset here instead of in maybe_diag_overlap.  Adjust
	referencess even to unrelated objects.	Adjust sizrange of bounded
	string functions to reflect bound.  For strcat, adjust destination
	sizrange by that of source.
	(builtin_access::strcat_overlap):  Adjust offsets and sizes
	to reflect the increase in destination sizrange above.
	(builtin_access::overlap): Do not set dstoff here but instead
	in builtin_access::builtin_access.
	(check_bounds_or_overlap): Use builtin_access::write_off.
	(maybe_diag_access_bounds): Add argument.  Add informational notes.
	(dump_builtin_memref, dump_builtin_access): New functions.
	* tree.c (component_ref_size): ...to here.
	* tree.h (component_ref_size): Declare.

gcc/testsuite/ChangeLog:

	PR middle-end/91631
	* /c-c++-common/Warray-bounds-3.c: Correct expected offsets.
	* /c-c++-common/Warray-bounds-4.c: Same.
	* gcc.dg/Warray-bounds-39.c: Remove xfails.
	* gcc.dg/Warray-bounds-44.c: New test.
	* gcc.dg/Warray-bounds-45.c: New test.

Index: gcc/builtins.c
===================================================================
--- gcc/builtins.c	(revision 275343)
+++ gcc/builtins.c	(working copy)
@@ -3562,54 +3562,6 @@ check_access (tree exp, tree, tree, tree dstwrite,
   return true;
 }
 
-/* Determines the size of the member referenced by the COMPONENT_REF
-   REF, using its initializer expression if necessary in order to
-   determine the size of an initialized flexible array member.
-   Returns the size (which might be zero for an object with
-   an uninitialized flexible array member) or null if the size
-   cannot be determined.  */
-
-static tree
-component_size (tree ref)
-{
-  gcc_assert (TREE_CODE (ref) == COMPONENT_REF);
-
-  tree member = TREE_OPERAND (ref, 1);
-
-  /* If the member is not last or has a size greater than one, return
-     it.  Otherwise it's either a flexible array member or a zero-length
-     array member, or an array of length one treated as such.  */
-  tree size = DECL_SIZE_UNIT (member);
-  if (size
-      && (!array_at_struct_end_p (ref)
-	  || (!integer_zerop (size)
-	      && !integer_onep (size))))
-    return size;
-
-  /* If the reference is to a declared object and the member a true
-     flexible array, try to determine its size from its initializer.  */
-  poly_int64 off = 0;
-  tree base = get_addr_base_and_unit_offset (ref, &off);
-  if (!base || !VAR_P (base))
-    return NULL_TREE;
-
-  /* The size of any member of a declared object other than a flexible
-     array member is that obtained above.  */
-  if (size)
-    return size;
-
-  if (tree init = DECL_INITIAL (base))
-    if (TREE_CODE (init) == CONSTRUCTOR)
-      {
-	off <<= LOG2_BITS_PER_UNIT;
-	init = fold_ctor_reference (NULL_TREE, init, off, 0, base);
-	if (init)
-	  return TYPE_SIZE_UNIT (TREE_TYPE (init));
-      }
-
-  return DECL_EXTERNAL (base) ? NULL_TREE : integer_zero_node;
-}
-
 /* Helper to compute the size of the object referenced by the DEST
    expression which must have pointer type, using Object Size type
    OSTYPE (only the least significant 2 bits are used).  Return
@@ -3744,7 +3696,7 @@ compute_objsize (tree dest, int ostype, tree *pdec
   if (TREE_CODE (dest) == COMPONENT_REF)
     {
       *pdecl = TREE_OPERAND (dest, 1);
-      return component_size (dest);
+      return component_ref_size (dest);
     }
 
   if (TREE_CODE (dest) != ADDR_EXPR)
Index: gcc/gimple-ssa-warn-restrict.c
===================================================================
--- gcc/gimple-ssa-warn-restrict.c	(revision 275343)
+++ gcc/gimple-ssa-warn-restrict.c	(working copy)
@@ -137,6 +137,8 @@ class builtin_memref
   /* The size of the BASE object, PTRDIFF_MAX if indeterminate,
      and negative until (possibly lazily) initialized.  */
   offset_int basesize;
+  /* Same for the subobject.  */
+  offset_int refsize;
 
   /* The non-negative offset of the referenced subobject.  Used to avoid
      warnings for (apparently) possibly but not definitively overlapping
@@ -160,7 +162,7 @@ class builtin_memref
 
   builtin_memref (tree, tree);
 
-  tree offset_out_of_bounds (int, offset_int[2]) const;
+  tree offset_out_of_bounds (int, offset_int[3]) const;
 
 private:
 
@@ -192,7 +194,8 @@ class builtin_access
      and false for raw memory functions.  */
   bool strict () const
   {
-    return detect_overlap != &builtin_access::generic_overlap;
+    return (detect_overlap != &builtin_access::generic_overlap
+	    && detect_overlap != &builtin_access::no_overlap);
   }
 
   builtin_access (gimple *, builtin_memref &, builtin_memref &);
@@ -200,6 +203,10 @@ class builtin_access
   /* Entry point to determine overlap.  */
   bool overlap ();
 
+  offset_int write_off (tree) const;
+
+  void dump (FILE *) const;
+
  private:
   /* Implementation functions used to determine overlap.  */
   bool generic_overlap ();
@@ -234,6 +241,7 @@ builtin_memref::builtin_memref (tree expr, tree si
   ref (),
   base (),
   basesize (-1),
+  refsize (-1),
   refoff (HOST_WIDE_INT_MIN),
   offrange (),
   sizrange (),
@@ -298,6 +306,19 @@ builtin_memref::builtin_memref (tree expr, tree si
     }
 }
 
+/* Based on the initial length of the destination STARTLEN, returns
+   the offset of the first write access from the beginning of
+   the destination.  Nonzero only for strcat-type of calls.  */
+
+offset_int builtin_access::write_off (tree startlen) const
+{
+  if (detect_overlap != &builtin_access::strcat_overlap
+      || !startlen || TREE_CODE (startlen) != INTEGER_CST)
+    return 0;
+
+  return wi::to_offset (startlen);
+}
+
 /* Ctor helper to set or extend OFFRANGE based on the OFFSET argument.
    Pointer offsets are represented as unsigned sizetype but must be
    treated as signed.  */
@@ -486,30 +507,66 @@ builtin_memref::set_base_and_offset (tree expr)
       tree memrefoff = TREE_OPERAND (base, 1);
       extend_offset_range (memrefoff);
       base = TREE_OPERAND (base, 0);
+
+      if (refoff != HOST_WIDE_INT_MIN
+      	  && TREE_CODE (expr) == COMPONENT_REF)
+      	{
+	  /* Bump up the offset of the referenced subobject to reflect
+	     the offset to the enclosing object.  For example, so that
+	     in
+	       struct S { char a, b[3]; } s[2];
+	       strcpy (s[1].b, "1234");
+	     REFOFF is set to s[1].b - (char*)s.  */
+      	  tree basetype = TREE_TYPE (TREE_TYPE (base));
+	  if (tree basesize = TYPE_SIZE_UNIT (basetype))
+	    if (TREE_CODE (basesize) == INTEGER_CST)
+	      {
+		offset_int size = wi::to_offset (basesize);
+		offset_int off = (HOST_WIDE_INT)tree_to_uhwi (memrefoff);
+		refoff += size * (off / size);
+	      }
+      	}
+
+      if (!integer_zerop (memrefoff))
+	/* A non-zero offset into an array of struct with flexible array
+	   members implies that the array is empty because there is no
+	   way to initialize such a member when it belongs to an array.
+	   This must be some sort of a bug.  */
+	refsize = 0;
     }
 
+  if (TREE_CODE (ref) == COMPONENT_REF)
+    if (tree size = component_ref_size (ref))
+      if (TREE_CODE (size) == INTEGER_CST)
+	refsize = wi::to_offset (size);
+
   if (TREE_CODE (base) == SSA_NAME)
     set_base_and_offset (base);
 }
 
 /* Return error_mark_node if the signed offset exceeds the bounds
-   of the address space (PTRDIFF_MAX).  Otherwise, return either
-   BASE or REF when the offset exceeds the bounds of the BASE or
-   REF object, and set OOBOFF to the past-the-end offset formed
-   by the reference, including its size.  When STRICT is non-zero
-   use REF size, when available, otherwise use BASE size.  When
-   STRICT is greater than 1, use the size of the last array member
-   as the bound, otherwise treat such a member as a flexible array
-   member.  Return NULL when the offset is in bounds.  */
+   of the address space (PTRDIFF_MAX).  Otherwise, return either BASE
+   or REF when the offset exceeds the bounds of the BASE or REF object,
+   and set OOBOFF to the past-the-end offset formed by the reference,
+   including its size.  OOBOFF is initially setto the range of offsets,
+   and OOBOFF[2] to the offset of the first write access (nonzero for
+   the strcat family).  When STRICT is nonzero use REF size, when
+   available, otherwise use BASE size.  When STRICT is greater than 1,
+   use the size of the last array member as the bound, otherwise treat
+   such a member as a flexible array member.  Return NULL when the offset
+   is in bounds.  */
 
 tree
-builtin_memref::offset_out_of_bounds (int strict, offset_int ooboff[2]) const
+builtin_memref::offset_out_of_bounds (int strict, offset_int ooboff[3]) const
 {
   if (!ptr)
     return NULL_TREE;
 
+  /* The offset of the first write access or zero.  */
+  offset_int wroff = ooboff[2];
+
   /* A temporary, possibly adjusted, copy of the offset range.  */
-  offset_int offrng[2] = { offrange[0], offrange[1] };
+  offset_int offrng[2] = { ooboff[0], ooboff[1] };
 
   if (DECL_P (base) && TREE_CODE (TREE_TYPE (base)) == ARRAY_TYPE)
     {
@@ -527,9 +584,19 @@ tree
   bool hib = wi::les_p (offrng[0], offrng[1]);
   bool lob = !hib;
 
+  /* Set to the size remaining in the object object after subtracting
+     REFOFF.  It may become negative as a result of negative indices
+     into the enclosing object, such as in:
+       extern struct S { char a[4], b[3], c[1]; } *p;
+       strcpy (p[-3].b, "123");  */
+  offset_int size = basesize;
+  tree obj = base;
+
+  const bool decl_p = DECL_P (obj);
+
   if (basesize < 0)
     {
-      endoff = offrng[lob] + sizrange[0];
+      endoff = offrng[lob] + (sizrange[0] - wroff);
 
       /* For a reference through a pointer to an object of unknown size
 	 all initial offsets are considered valid, positive as well as
@@ -539,41 +606,45 @@ tree
       if (endoff > maxobjsize)
 	return error_mark_node;
 
-      return NULL_TREE;
+      /* When the referenced subobject is known, the end offset must be
+	 within its bounds.  Otherwise there is nothing to do.  */
+      if (strict
+	  && !decl_p
+	  && ref
+	  && refsize >= 0
+	  && TREE_CODE (ref) == COMPONENT_REF)
+	{
+	  /* If REFOFF is negative, SIZE will become negative here.  */
+	  size = refoff + refsize;
+	  obj = ref;
+	}
+      else
+	return NULL_TREE;
     }
 
   /* A reference to an object of known size must be within the bounds
-     of the base object.  */
-  if (offrng[hib] < 0 || offrng[lob] > basesize)
-    return base;
+     of either the base object or the subobject (see above for when
+     a subobject can be used).  */
+  if ((decl_p && offrng[hib] < 0) || offrng[lob] > size)
+    return obj;
 
   /* The extent of the reference must also be within the bounds of
-     the base object (if known) or the maximum object size otherwise.  */
-  endoff = wi::smax (offrng[lob], 0) + sizrange[0];
+     the base object (if known) or the subobject or the maximum object
+     size otherwise.  */
+  endoff = offrng[lob] + sizrange[0];
   if (endoff > maxobjsize)
     return error_mark_node;
 
-  offset_int size = basesize;
-  tree obj = base;
-
   if (strict
-      && DECL_P (obj)
+      && decl_p
       && ref
-      && refoff >= 0
-      && TREE_CODE (ref) == COMPONENT_REF
-      && (strict > 1
-	  || !array_at_struct_end_p (ref)))
+      && refsize >= 0
+      && TREE_CODE (ref) == COMPONENT_REF)
     {
-      /* If the reference is to a member subobject, the offset must
-	 be within the bounds of the subobject.  */
-      tree field = TREE_OPERAND (ref, 1);
-      tree type = TREE_TYPE (field);
-      if (tree sz = TYPE_SIZE_UNIT (type))
-	if (TREE_CODE (sz) == INTEGER_CST)
-	  {
-	    size = refoff + wi::to_offset (sz);
-	    obj = ref;
-	  }
+      /* If the reference is to a member subobject of a declared object,
+	 the offset must be within the bounds of the subobject.  */
+      size = refoff + refsize;
+      obj = ref;
     }
 
   if (endoff <= size)
@@ -581,12 +652,12 @@ tree
 
   /* Set the out-of-bounds offset range to be one greater than
      that delimited by the reference including its size.  */
-  ooboff[lob] = size + 1;
+  ooboff[lob] = size;
 
   if (endoff > ooboff[lob])
-    ooboff[hib] = endoff;
+    ooboff[hib] = endoff - 1;
   else
-    ooboff[hib] = wi::smax (offrng[lob], 0) + sizrange[1];
+    ooboff[hib] = offrng[lob] + sizrange[1];
 
   return obj;
 }
@@ -599,8 +670,10 @@ builtin_access::builtin_access (gimple *call, buil
 : dstref (&dst), srcref (&src), sizrange (), ovloff (), ovlsiz (),
   dstoff (), srcoff (), dstsiz (), srcsiz ()
 {
+  dstoff[0] = dst.offrange[0];
+  dstoff[1] = dst.offrange[1];
+
   /* Zero out since the offset_int ctors invoked above are no-op.  */
-  dstoff[0] = dstoff[1] = 0;
   srcoff[0] = srcoff[1] = 0;
   dstsiz[0] = dstsiz[1] = 0;
   srcsiz[0] = srcsiz[1] = 0;
@@ -716,16 +789,10 @@ builtin_access::builtin_access (gimple *call, buil
 	src.basesize = maxobjsize;
     }
 
-  /* If there is no dependency between the references or the base
-     objects of the two references aren't the same there's nothing
-     else to do.  */
-  if (depends_p && dstref->base != srcref->base)
-    return;
+  /* Make adjustments for references to the same object by string
+     built-in functions to reflect the constraints imposed by
+     the function.  */
 
-  /* ...otherwise, make adjustments for references to the same object
-     by string built-in functions to reflect the constraints imposed
-     by the function.  */
-
   /* For bounded string functions determine the range of the bound
      on the access.  For others, the range stays unbounded.  */
   offset_int bounds[2] = { maxobjsize, maxobjsize };
@@ -755,6 +822,7 @@ builtin_access::builtin_access (gimple *call, buil
 	}
     }
 
+  bool dstsize_set = false;
   /* The size range of one reference involving the same base object
      can be determined from the size range of the other reference.
      This makes it possible to compute accurate offsets for warnings
@@ -766,6 +834,7 @@ builtin_access::builtin_access (gimple *call, buil
 	 the source.  */
       dstref->sizrange[0] = srcref->sizrange[0];
       dstref->sizrange[1] = srcref->sizrange[1];
+      dstsize_set = true;
     }
   else if (srcref->sizrange[0] == 0 && srcref->sizrange[1] == maxobjsize)
     {
@@ -778,11 +847,15 @@ builtin_access::builtin_access (gimple *call, buil
 	{
 	  if (dstref->strbounded_p)
 	    {
-	      /* Read access by strncpy is bounded.  */
-	      if (bounds[0] < srcref->sizrange[0])
-		srcref->sizrange[0] = bounds[0];
-	      if (bounds[1] < srcref->sizrange[1])
-		srcref->sizrange[1] = bounds[1];
+	      /* Read access by strncpy is constrained by the third
+		 argument but except for a zero bound is at least one.  */
+	      offset_int size = wi::umax (srcref->basesize, 1);
+	      offset_int bound = wi::umin (size, bounds[0]);
+	      if (bound < srcref->sizrange[0])
+		srcref->sizrange[0] = bound;
+	      bound = wi::umin (srcref->basesize, bounds[1]);
+	      if (bound < srcref->sizrange[1])
+		srcref->sizrange[1] = bound;
 	    }
 
 	  /* For string functions, adjust the size range of the source
@@ -834,6 +907,11 @@ builtin_access::builtin_access (gimple *call, buil
 	    }
 	}
     }
+  else if (!dstsize_set && detect_overlap == &builtin_access::strcat_overlap)
+    {
+      dstref->sizrange[0] += srcref->sizrange[0] - 1;
+      dstref->sizrange[1] += srcref->sizrange[1] - 1;
+    }
 
   if (dstref->strbounded_p)
     {
@@ -1108,10 +1186,11 @@ builtin_access::strcat_overlap ()
   /* Adjust for strcat-like accesses.  */
 
   /* As a special case for strcat, set the DSTREF offsets to the length
-     of the source string since the function starts writing at the first
-     nul, and set the size to 1 for the length of the nul.  */
-  acs.dstoff[0] += acs.dstsiz[0];
-  acs.dstoff[1] += acs.dstsiz[1];
+     of the destination string since the function starts writing over
+     its terminating nul, and set the destination size to 1 for the length
+     of the nul.  */
+  acs.dstoff[0] += dstsiz[0] - srcref->sizrange[0];
+  acs.dstoff[1] += dstsiz[1] - srcref->sizrange[1];
 
   bool strfunc_unknown_args = acs.dstsiz[0] == 0 && acs.dstsiz[1] != 0;
 
@@ -1189,7 +1268,8 @@ builtin_access::strcat_overlap ()
   acs.ovlsiz[0] = dstref->sizrange[0] == dstref->sizrange[1] ? 1 : 0;
   acs.ovlsiz[1] = 1;
 
-  offset_int endoff = dstref->offrange[0] + dstref->sizrange[0];
+  offset_int endoff
+    = dstref->offrange[0] + (dstref->sizrange[0] - srcref->sizrange[0]);
   if (endoff <= srcref->offrange[0])
     acs.ovloff[0] = wi::smin (maxobjsize, srcref->offrange[0]).to_shwi ();
   else
@@ -1261,10 +1341,6 @@ builtin_access::overlap ()
   if (!dstref->base || !srcref->base)
     return false;
 
-  /* Set the access offsets.  */
-  acs.dstoff[0] = dstref->offrange[0];
-  acs.dstoff[1] = dstref->offrange[1];
-
   /* If the base object is an array adjust the bounds of the offset
      to be non-negative and within the bounds of the array if possible.  */
   if (dstref->base
@@ -1626,7 +1702,8 @@ maybe_diag_overlap (location_t loc, gimple *call,
 
 static bool
 maybe_diag_access_bounds (location_t loc, gimple *call, tree func, int strict,
-			  const builtin_memref &ref, bool do_warn)
+			  const builtin_memref &ref, offset_int wroff,
+			  bool do_warn)
 {
   const offset_int maxobjsize = ref.maxobjsize;
 
@@ -1665,8 +1742,12 @@ maybe_diag_access_bounds (location_t loc, gimple *
     }
 
   /* 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] };
+     the result is used to make codegen decisions.  An excessive WROFF
+     can only come up as a result of an invalid strncat bound and is
+     diagnosed separately using a more meaningful warning.  */
+  if (maxobjsize < wroff)
+    wroff = 0;
+  offset_int ooboff[] = { ref.offrange[0], ref.offrange[1], wroff };
   tree oobref = ref.offset_out_of_bounds (strict, ooboff);
   if (!oobref)
     return false;
@@ -1787,27 +1868,45 @@ maybe_diag_access_bounds (location_t loc, gimple *
     }
   else if (TREE_CODE (ref.ref) == MEM_REF)
     {
-      tree type = TREE_TYPE (TREE_OPERAND (ref.ref, 0));
+      tree refop = TREE_OPERAND (ref.ref, 0);
+      tree type = TREE_TYPE (refop);
       if (POINTER_TYPE_P (type))
 	type = TREE_TYPE (type);
       type = TYPE_MAIN_VARIANT (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);
+      if (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))
+	{
+	  if (TREE_CODE (ref.ref) == COMPONENT_REF)
+	    refop = TREE_OPERAND (ref.ref, 1);
+	  if (DECL_P (refop))
+	    inform (DECL_SOURCE_LOCATION (refop),
+		    "subobject %qD declared here", refop);
+	  warned = true;
+	}
     }
   else
     {
+      tree refop = TREE_OPERAND (ref.ref, 0);
       tree type = TYPE_MAIN_VARIANT (TREE_TYPE (ref.ref));
 
-      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 ());
+      if (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 %wi",
+		      call, func, rangestr[0], ref.base,
+		      TREE_OPERAND (ref.ref, 1), type,
+		      ref.refoff.to_shwi ()))
+	{
+	  if (TREE_CODE (ref.ref) == COMPONENT_REF)
+	    refop = TREE_OPERAND (ref.ref, 1);
+	  if (DECL_P (refop))
+	    inform (DECL_SOURCE_LOCATION (refop),
+		    "subobject %qD declared here", refop);
+	  warned = true;
+	}
     }
 
   return warned;
@@ -1936,6 +2035,8 @@ check_bounds_or_overlap (gimple *call, tree dst, t
   builtin_memref dstref (dst, dstsize);
   builtin_memref srcref (src, srcsize);
 
+  /* Create a descriptor of the access.  This may adjust both DSTREF
+     and SRCREF based on one another and the kind of the access.  */
   builtin_access acs (call, dstref, srcref);
 
   /* Set STRICT to the value of the -Warray-bounds=N argument for
@@ -1942,11 +2043,15 @@ check_bounds_or_overlap (gimple *call, tree dst, t
      string functions or when N > 1.  */
   int strict = (acs.strict () || warn_array_bounds > 1 ? warn_array_bounds : 0);
 
-  /* 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_access_bounds (loc, call, func, strict, dstref, do_warn)
-      || maybe_diag_access_bounds (loc, call, func, strict, srcref, do_warn))
+  /* The starting offset of the destination write access.  Nonzero only
+     for the strcat family of functions.  */
+  offset_int wroff = acs.write_off (dstsize);
+
+  /* Validate offsets to each reference before the access 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_access_bounds (loc, call, func, strict, dstref, wroff, do_warn)
+      || maybe_diag_access_bounds (loc, call, func, strict, srcref, 0, do_warn))
     {
       if (do_warn)
 	gimple_set_no_warning (call, true);
@@ -2003,3 +2108,76 @@ make_pass_warn_restrict (gcc::context *ctxt)
 {
   return new pass_wrestrict (ctxt);
 }
+
+DEBUG_FUNCTION void
+dump_builtin_memref (FILE *fp, const builtin_memref &ref)
+{
+  fprintf (fp, "\n    ptr = ");
+  print_generic_expr (fp, ref.ptr, TDF_LINENO);
+  fprintf (fp, "\n    ref = ");
+  if (ref.ref)
+    print_generic_expr (fp, ref.ref, TDF_LINENO);
+  else
+    fputs ("null", fp);
+  fprintf (fp, "\n    base = ");
+  print_generic_expr (fp, ref.base, TDF_LINENO);
+  fprintf (fp,
+	   "\n    basesize = %lli"
+	   "\n    refsize = %lli"
+	   "\n    refoff = %lli"
+	   "\n    offrange = [%lli, %lli]"
+	   "\n    sizrange = [%lli, %lli]"
+	   "\n    strbounded_p = %s\n",
+	   (long long)ref.basesize.to_shwi (),
+	   (long long)ref.refsize.to_shwi (),
+	   (long long)ref.refoff.to_shwi (),
+	   (long long)ref.offrange[0].to_shwi (),
+	   (long long)ref.offrange[1].to_shwi (),
+	   (long long)ref.sizrange[0].to_shwi (),
+	   (long long)ref.sizrange[1].to_shwi (),
+	   ref.strbounded_p ? "true" : "false");
+}
+
+void
+builtin_access::dump (FILE *fp) const
+{
+  fprintf (fp, "  dstref:");
+  dump_builtin_memref (fp, *dstref);
+  fprintf (fp, "\n  srcref:");
+  dump_builtin_memref (fp, *srcref);
+
+  fprintf (fp,
+	   "  sizrange = [%lli, %lli]\n"
+	   "  ovloff = [%lli, %lli]\n"
+	   "  ovlsiz = [%lli, %lli]\n"
+	   "  dstoff = [%lli, %lli]\n"
+	   "  dstsiz = [%lli, %lli]\n"
+	   "  srcoff = [%lli, %lli]\n"
+	   "  srcsiz = [%lli, %lli]\n",
+	   (long long)sizrange[0], (long long)sizrange[1],
+	   (long long)ovloff[0], (long long)ovloff[1],
+	   (long long)ovlsiz[0], (long long)ovlsiz[1],
+	   (long long)dstoff[0].to_shwi (), (long long)dstoff[1].to_shwi (),
+	   (long long)dstsiz[0].to_shwi (), (long long)dstsiz[1].to_shwi (),
+	   (long long)srcoff[0].to_shwi (), (long long)srcoff[1].to_shwi (),
+	   (long long)srcsiz[0].to_shwi (), (long long)srcsiz[1].to_shwi ());
+}
+
+DEBUG_FUNCTION void
+dump_builtin_access (FILE *fp, gimple *stmt, const builtin_access &acs)
+{
+  if (stmt)
+    {
+      fprintf (fp, "\nDumping builtin_access for ");
+      print_gimple_expr (fp, stmt, TDF_LINENO);
+      fputs (":\n", fp);
+    }
+
+  acs.dump (fp);
+}
+
+DEBUG_FUNCTION void
+debug (gimple *stmt, const builtin_access &acs)
+{
+  dump_builtin_access (stdout, stmt, acs);
+}
Index: gcc/testsuite/c-c++-common/Warray-bounds-3.c
===================================================================
--- gcc/testsuite/c-c++-common/Warray-bounds-3.c	(revision 275343)
+++ gcc/testsuite/c-c++-common/Warray-bounds-3.c	(working copy)
@@ -115,7 +115,7 @@ void test_memcpy_bounds_anti_range (char *d, const
      offset, i.e., 7 + 3.  Including the whole final range because would be
      confusing (the upper bound would either be negative or a very large
      positive number) so only the lower bound is included.  */
-  T (char, 9, a, a + SAR ( 0,  6), 3);   /* { dg-warning "forming offset 10 is out of the bounds \\\[0, 9] of object " "memcpy" } */
+  T (char, 9, a, a + SAR ( 0,  6), 3);   /* { dg-warning "forming offset 9 is out of the bounds \\\[0, 9] of object " "memcpy" } */
 
   /* This fails because the offset isn't represented as an SSA_NAME
      but rather as a GIMPLE_PHI (offset, 0).  With some effort it is
@@ -129,9 +129,9 @@ void test_memcpy_bounds_anti_range (char *d, const
   T (char, 9, a, a + SAR ( 2,  6), 3);
   T (char, 9, a, a + SAR ( 3,  6), 3);
 
-  T (char, 9, a, a + SAR (-1,  7), 3);   /* { dg-warning "forming offset \\\[10, 11] is out of the bounds \\\[0, 9] of object " "memcpy" } */
-  T (char, 9, a, a + SAR (-2,  8), 3);   /* { dg-warning "forming offset \\\[10, 12] is out of the bounds \\\[0, 9] of object " "memcpy" } */
-  T (char, 9, a, a + SAR (-3,  7), 5);   /* { dg-warning "forming offset \\\[10, 13] is out of the bounds \\\[0, 9] of object " "memcpy" } */
+  T (char, 9, a, a + SAR (-1,  7), 3);   /* { dg-warning "forming offset \\\[9, 10] is out of the bounds \\\[0, 9] of object " "memcpy" } */
+  T (char, 9, a, a + SAR (-2,  8), 3);   /* { dg-warning "offset \\\[9, 11] is out of the bounds \\\[0, 9] of object " "memcpy" } */
+  T (char, 9, a, a + SAR (-3,  7), 5);   /* { dg-warning "forming offset \\\[9, 12] is out of the bounds \\\[0, 9] of object " "memcpy" } */
 
   T (char, 9, a + SAR (-2, -1), a, 3);
   T (char, 9, a + SAR (-1,  1), a, 3);
@@ -138,9 +138,9 @@ void test_memcpy_bounds_anti_range (char *d, const
   T (char, 9, a + SAR ( 0,  1), a, 3);
   T (char, 9, a + SAR ( 0,  2), a, 3);
   T (char, 9, a + SAR ( 0,  3), a, 3);
-  T (char, 9, a + SAR ( 0,  6), a, 3);   /* { dg-warning "forming offset 10 is out of the bounds \\\[0, 9] of object " "memcpy" } */
-  T (char, 9, a + SAR (-1,  7), a, 3);   /* { dg-warning "forming offset \\\[10, 11] is out of the bounds \\\[0, 9] of object " "memcpy" } */
-  T (char, 9, a + SAR (-2,  8), a, 3);   /* { dg-warning "forming offset \\\[10, 12] is out of the bounds \\\[0, 9] of object " "memcpy" } */
+  T (char, 9, a + SAR ( 0,  6), a, 3);   /* { dg-warning "forming offset 9 is out of the bounds \\\[0, 9] of object " "memcpy" } */
+  T (char, 9, a + SAR (-1,  7), a, 3);   /* { dg-warning "forming offset \\\[9, 10] is out of the bounds \\\[0, 9] of object " "memcpy" } */
+  T (char, 9, a + SAR (-2,  8), a, 3);   /* { dg-warning "offset \\\[9, 11] is out of the bounds \\\[0, 9] of object " "memcpy" } */
 
   ptrdiff_t i = SAR (DIFF_MIN + 1, DIFF_MAX - 4);
   T (char, 1, d, d + SAR (DIFF_MIN + 3, DIFF_MAX - 1), 3);
@@ -312,13 +312,13 @@ void test_strcpy_bounds (char *d, const char *s)
      it out of bounds (it isn't) but because the final source offset
      after the access has completed, is.  It would be clearer if
      the warning mentioned the final offset.  */
-  TI (char, 2, "", a + SR (2, DIFF_MAX - 1), s);   /* { dg-warning "forming offset 3 is out of the bounds \\\[0, 2] of object \[^\n\r\]+ with type .char ?\\\[2\\\]."  "strcpy" } */
+  TI (char, 2, "", a + SR (2, DIFF_MAX - 1), s);   /* { dg-warning "offset 2 is out of the bounds \\\[0, 2] of object \[^\n\r\]+ with type .char ?\\\[2\\\]."  "strcpy" } */
   TI (char, 2, "", a + SR (3, DIFF_MAX - 1), s);   /* { dg-warning "offset \\\[3, \[0-9\]+] is out of the bounds \\\[0, 2] of object \[^\n\r\]+ with type .char ?\\\[2\\\]."  "strcpy" } */
 
   TI (char, 3, "", a + SR (0, DIFF_MAX - 1), s);
   TI (char, 3, "", a + SR (1, DIFF_MAX - 1), s);
   TI (char, 3, "", a + SR (2, DIFF_MAX - 1), s);
-  TI (char, 3, "", a + SR (3, DIFF_MAX - 1), s);   /* { dg-warning "forming offset 4 is out of the bounds \\\[0, 3] of object \[^\n\r\]+ with type .char ?\\\[3\\\]."  "strcpy" } */
+  TI (char, 3, "", a + SR (3, DIFF_MAX - 1), s);   /* { dg-warning "offset 3 is out of the bounds \\\[0, 3] of object \[^\n\r\]+ with type .char ?\\\[3\\\]."  "strcpy" } */
   TI (char, 3, "", a + SR (4, DIFF_MAX - 1), s);   /* { dg-warning "offset \\\[4, \[0-9\]+] is out of the bounds \\\[0, 3] of object \[^\n\r\]+ with type .char ?\\\[3\\\]."  "strcpy" } */
 
   TI (char, 4, "", a + SR (DIFF_MAX - 2, DIFF_MAX - 1), s);   /* { dg-warning "offset \\\[\[0-9\]+, \[0-9\]+] is out of the bounds \\\[0, 4] of object \[^\n\r\]+ with type .char ?\\\[4\\\]."  "strcpy" } */
@@ -364,15 +364,15 @@ void test_strcpy_bounds_memarray_range (void)
   TM (a5, "0",    ma.a5 + i, ma.a5);
   TM (a5, "01",   ma.a5 + i, ma.a5);
   TM (a5, "012",  ma.a5 + i, ma.a5);
-  TM (a5, "0123", ma.a5 + i, ma.a5);     /* { dg-warning "offset 10 from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a5. with type .char ?\\\[5]. at offset 4" "strcpy" } */
+  TM (a5, "0123", ma.a5 + i, ma.a5);     /* { dg-warning "offset 9 from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a5. with type .char ?\\\[5]. at offset 4" "strcpy" } */
 
   TM (a11, "0",       ma.a5, ma.a11);
   TM (a11, "01",      ma.a5, ma.a11);
   TM (a11, "012",     ma.a5, ma.a11);
   TM (a11, "0123",    ma.a5, ma.a11);
-  TM (a11, "01234",   ma.a5, ma.a11);    /* { dg-warning "offset 10 from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a5. with type .char ?\\\[5]' at offset 4" } */
-  TM (a11, "012345",  ma.a5, ma.a11);    /* { dg-warning "offset \\\[10, 11] from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a5. with type .char ?\\\[5]' at offset 4" } */
-  TM (a11, "0123456", ma.a5, ma.a11);    /* { dg-warning "offset \\\[10, 12] from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a5. with type .char ?\\\[5]' at offset 4" } */
+  TM (a11, "01234",   ma.a5, ma.a11);    /* { dg-warning "offset 9 from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a5. with type .char ?\\\[5]' at offset 4" } */
+  TM (a11, "012345",  ma.a5, ma.a11);    /* { dg-warning "offset \\\[9, 10] from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a5. with type .char ?\\\[5]' at offset 4" } */
+  TM (a11, "0123456", ma.a5, ma.a11);    /* { dg-warning "offset \\\[9, 11] from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a5. with type .char ?\\\[5]' at offset 4" } */
 
   TM (a11, "0123456", ma.a11 + i, "789abcd");
 }
Index: gcc/testsuite/c-c++-common/Warray-bounds-4.c
===================================================================
--- gcc/testsuite/c-c++-common/Warray-bounds-4.c	(revision 275343)
+++ gcc/testsuite/c-c++-common/Warray-bounds-4.c	(working copy)
@@ -44,8 +44,8 @@ void test_memcpy_bounds_memarray_range (void)
   TM (ma.a5, ma.a5 + j, ma.a5, 1);
   TM (ma.a5, ma.a5 + j, ma.a5, 3);
   TM (ma.a5, ma.a5 + j, ma.a5, 5);
-  TM (ma.a5, ma.a5 + j, ma.a5, 7);        /* { dg-warning "offset \\\[6, 8] from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a5. with type .char ?\\\[5]. at offset 0" } */
-  TM (ma.a5, ma.a5 + j, ma.a5, 9);        /* { dg-warning "offset \\\[6, 10] from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a5. with type .char ?\\\[5]. at offset 0" } */
+  TM (ma.a5, ma.a5 + j, ma.a5, 7);        /* { dg-warning "offset \\\[5, 7] from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a5. with type .char ?\\\[5]. at offset 0" } */
+  TM (ma.a5, ma.a5 + j, ma.a5, 9);        /* { dg-warning "offset \\\[5, 9] from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a5. with type .char ?\\\[5]. at offset 0" } */
 }
 
 void test_strcpy_bounds_memarray_range (void)
@@ -67,7 +67,7 @@ void test_strcpy_bounds_memarray_range (void)
 
 #if __i386__ || __x86_64__
   /* Disabled for non-x86 targets due to bug 83462.  */
-  TM ("", "012345", ma.a7 + i, ma.a7);    /* { dg-warning "offset 13 from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a7. with type .char ?\\\[7]. at offset 5" "strcpy" { xfail { ! { i?86-*-* x86_64-*-* } } } } */
+  TM ("", "012345", ma.a7 + i, ma.a7);    /* { dg-warning "offset 12 from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a7. with type .char ?\\\[7]. at offset 5" "strcpy" { xfail { ! { i?86-*-* x86_64-*-* } } } } */
 #endif
 
 }
Index: gcc/testsuite/gcc.dg/Warray-bounds-39.c
===================================================================
--- gcc/testsuite/gcc.dg/Warray-bounds-39.c	(revision 275343)
+++ gcc/testsuite/gcc.dg/Warray-bounds-39.c	(working copy)
@@ -123,7 +123,7 @@ char* test_strcpy_s0 (char *d)
 
 char* test_strcpy_s0_0 (char *d)
 {
-  return strcpy (d, s0_0[0]);     /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr88991" { xfail *-*-* } } */
+  return strcpy (d, s0_0[0]);     /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
 }
 
 
@@ -139,10 +139,10 @@ char* test_strncpy_s0_2 (char *d)
 
 char* test_strncpy_s0_0_1 (char *d)
 {
-  return strncpy (d, s0_0[0], 1); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr88991" { xfail *-*-* } } */
+  return strncpy (d, s0_0[0], 1); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
 }
 
 char* test_strncpy_s0_0_2 (char *d)
 {
-  return strncpy (d, s0_0[0], 2); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr88991" { xfail *-*-* } } */
+  return strncpy (d, s0_0[0], 2); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
 }
Index: gcc/testsuite/gcc.dg/Warray-bounds-44.c
===================================================================
--- gcc/testsuite/gcc.dg/Warray-bounds-44.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/Warray-bounds-44.c	(working copy)
@@ -0,0 +1,249 @@
+/* PR middle-end/91631 - buffer overflow into an array member of a declared
+   object not detected
+   Test to verify that past-the-end accesses by string functions to member
+   arrays by-reference objects are diagnosed.
+   { dg-do compile }
+   { dg-options "-O2 -Wall -Wno-unused-local-typedefs -ftrack-macro-expansion=0" }  */
+
+#define SA(expr) typedef int StaticAssert [2 * !!(expr) - 1]
+
+typedef __SIZE_TYPE__ size_t;
+
+extern char* strcpy (char*, const char*);
+extern char* strncpy (char*, const char*, size_t);
+
+void sink (void*);
+
+struct MA17
+{
+  char pad[4];
+  char a1[1], a2[2], a3[3], a4[4], a5[5], a6[6], a7[7], a8[8], a9[9], a10[10];
+  char a11[11], a12[12], a13[13], a14[14], a15[15], a16[16], a17[17], ax[];
+};
+
+extern struct MA17 gma;
+extern struct MA17 gma2[2];
+
+struct MA17 igma_3 = { .ax = { 1, 2, 3 } };
+struct MA17 igma2_[2];
+
+#define S36 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+
+#define S(N)   (S36 + sizeof (S36) - N - 1)
+
+/* In the test macro, prevent the strcpy to memcpy transformation
+   by using a local array initialized with the string literal.  Without
+   it, GCC transforms the strcpy call with memcpy which (unfortunately)
+   permits accesses that cross subobject boundaries.  */
+#define T(dst, n)				\
+  do {						\
+    const char a[] = S36;			\
+    strcpy (dst, a + sizeof a - n - 1);		\
+    sink (dst);					\
+  } while (0)
+
+void strcpy_global (void)
+{
+  T (gma.a1, 0);
+  T (gma.a1, 1);          // { dg-warning "'strcpy' offset 5 from the object at 'gma' is out of the bounds of referenced subobject 'a1' with type 'char\\\[1]' at offset 4" }
+  T (gma.a1, 4);          // { dg-warning "'strcpy' offset \\\[5, 8] from the object at 'gma' is out of the bounds of referenced subobject 'a1' with type 'char\\\[1]' at offset 4" }
+
+  T (gma.a2, 1);
+  T (gma.a2, 2);          // { dg-warning "'strcpy' offset 7 from the object at 'gma' is out of the bounds of referenced subobject 'a2' with type 'char\\\[2]' at offset 5" }
+
+  T (gma.a3, 2);
+  T (gma.a3, 3);          // { dg-warning "'strcpy' offset 10 from the object at 'gma' is out of the bounds of referenced subobject 'a3' with type 'char\\\[3]' at offset 7" }
+
+  T (gma.a4, 3);
+  T (gma.a4, 4);          // { dg-warning "'strcpy' offset 14 from the object at 'gma' is out of the bounds of referenced subobject 'a4' with type 'char\\\[4]' at offset 10" }
+
+  T (gma.a5, 4);
+  T (gma.a5, 5);          // { dg-warning "'strcpy' offset 19 from the object at 'gma' is out of the bounds of referenced subobject 'a5' with type 'char\\\[5]' at offset 14" }
+
+  SA (__builtin_offsetof (struct MA17, a17) == 140);
+
+  T (gma.a17, 16);
+  T (gma.a17, 17);        // { dg-warning "'strcpy' offset 157 from the object at 'gma' is out of the bounds of referenced subobject 'a17' with type 'char\\\[17]' at offset 140" }
+
+  SA (__builtin_offsetof (struct MA17, ax) == 157);
+
+  T (gma.ax, 0);          // { dg-warning "'strcpy' offset 157 is out of the bounds \\\[0, 157] of object 'gma' with type 'struct MA17'" }
+}
+
+
+void strcpy_global_array (void)
+{
+  T (gma2[0].a1, 0);
+  T (gma2[0].a1, 1);      // { dg-warning "'strcpy' offset 5 from the object at 'gma2' is out of the bounds of referenced subobject 'a1' with type 'char\\\[1]' at offset 4" }
+  T (gma2[0].a1, 4);      // { dg-warning "'strcpy' offset \\\[5, 8] from the object at 'gma2' is out of the bounds of referenced subobject 'a1' with type 'char\\\[1]' at offset 4" }
+
+  T (gma2[0].a2, 1);
+  T (gma2[0].a2, 2);      // { dg-warning "'strcpy' offset 7 from the object at 'gma2' is out of the bounds of referenced subobject 'a2' with type 'char\\\[2]' at offset 5" }
+
+  T (gma2[0].a3, 2);
+  T (gma2[0].a3, 3);      // { dg-warning "'strcpy' offset 10 from the object at 'gma2' is out of the bounds of referenced subobject 'a3' with type 'char\\\[3]' at offset 7" }
+
+  T (gma2[0].a4, 3);
+  T (gma2[0].a4, 4);      // { dg-warning "'strcpy' offset 14 from the object at 'gma2' is out of the bounds of referenced subobject 'a4' with type 'char\\\[4]' at offset 10" }
+
+  T (gma2[0].a5, 4);
+  T (gma2[0].a5, 5);      // { dg-warning "'strcpy' offset 19 from the object at 'gma2' is out of the bounds of referenced subobject 'a5' with type 'char\\\[5]' at offset 14" }
+
+  T (gma2[0].a17, 16);
+  T (gma2[0].a17, 17);    // { dg-warning "'strcpy' offset 157 from the object at 'gma2' is out of the bounds of referenced subobject 'a17' with type 'char\\\[17]' at offset 140" }
+
+  /* GMA2 is external buts because it's an array its definition in another
+     translation unit may not provide an initializer for the flexible array
+     member.  Verify that a warning is issued for access to it.  */
+  T (gma2[0].ax, 1);      // { dg-warning "'strcpy' offset \\\[157, 158] from the object at 'gma2' is out of the bounds of referenced subobject 'ax' with type 'char\\\[]' at offset 157" }
+  T (gma2[0].ax, 7);      // { dg-warning "'strcpy' offset \\\[157, 164] from the object at 'gma2' is out of the bounds of referenced subobject 'ax' with type 'char\\\[]' at offset 157" }
+
+  /* IGMA_ is internal and provides on definition for the flexible array
+     member.  Verify that a warnin is issued for out-of-bounds accesses
+     to it.  */
+  T (igma2_[0].ax, 1);    // { dg-warning "'strcpy' offset \\\[157, 158] from the object at 'igma2_' is out of the bounds of referenced subobject 'ax' with type 'char\\\[]' at offset 157" }
+
+  T (igma_3.ax, 0);
+  T (igma_3.ax, 1);
+  T (igma_3.ax, 1);
+  T (igma_3.ax, 3);       // { dg-warning " offset 160 " }
+  T (igma_3.ax, 9);       // { dg-warning " offset \\\[160, 166] " }
+}
+
+
+void strcpy_local (void)
+{
+  struct MA17 lma;
+
+  T (lma.a1, 0);
+  T (lma.a1, 1);          // { dg-warning "'strcpy' offset 5 from the object at 'lma' is out of the bounds of referenced subobject 'a1' with type 'char\\\[1]' at offset 4" }
+  T (lma.a1, 4);          // { dg-warning "'strcpy' offset \\\[5, 8] from the object at 'lma' is out of the bounds of referenced subobject 'a1' with type 'char\\\[1]' at offset 4" }
+
+  T (lma.a2, 1);
+  T (lma.a2, 2);          // { dg-warning "'strcpy' offset 7 from the object at 'lma' is out of the bounds of referenced subobject 'a2' with type 'char\\\[2]' at offset 5" }
+
+  T (lma.a3, 2);
+  T (lma.a3, 3);          // { dg-warning "'strcpy' offset 10 from the object at 'lma' is out of the bounds of referenced subobject 'a3' with type 'char\\\[3]' at offset 7" }
+
+  T (lma.a4, 3);
+  T (lma.a4, 4);          // { dg-warning "'strcpy' offset 14 from the object at 'lma' is out of the bounds of referenced subobject 'a4' with type 'char\\\[4]' at offset 10" }
+
+  T (lma.a5, 4);
+  T (lma.a5, 5);          // { dg-warning "'strcpy' offset 19 from the object at 'lma' is out of the bounds of referenced subobject 'a5' with type 'char\\\[5]' at offset 14" }
+
+  T (lma.a17, 16);
+  T (lma.a17, 17);        // { dg-warning "'strcpy' offset 157 from the object at 'lma' is out of the bounds of referenced subobject 'a17' with type 'char\\\[17]' at offset 140" }
+
+  T (lma.ax, 0);          // { dg-warning "'strcpy' offset 157 from the object at 'lma' is out of the bounds of referenced subobject 'ax' with type 'char\\\[]' at offset 157" }
+}
+
+
+void strcpy_ref (struct MA17 *pma)
+{
+  T (pma->a1, 0);
+  T (pma->a1, 1);         // { dg-warning "'strcpy' offset 5 from the object at 'pma' is out of the bounds of referenced subobject 'a1' with type 'char\\\[1]' at offset 4" }
+  T (pma->a1, 4);         // { dg-warning "'strcpy' offset \\\[5, 8] from the object at 'pma' is out of the bounds of referenced subobject 'a1' with type 'char\\\[1]' at offset 4" }
+
+  T (pma->a2, 1);
+  T (pma->a2, 2);         // { dg-warning "'strcpy' offset 7 from the object at 'pma' is out of the bounds of referenced subobject 'a2' with type 'char\\\[2]' at offset 5" }
+
+  T (pma->a3, 2);
+  T (pma->a3, 3);         // { dg-warning "'strcpy' offset 10 from the object at 'pma' is out of the bounds of referenced subobject 'a3' with type 'char\\\[3]' at offset 7" }
+
+  T (pma->a4, 3);
+  T (pma->a4, 4);         // { dg-warning "'strcpy' offset 14 from the object at 'pma' is out of the bounds of referenced subobject 'a4' with type 'char\\\[4]' at offset 10" }
+
+  T (pma->a5, 4);
+  T (pma->a5, 5);         // { dg-warning "'strcpy' offset 19 from the object at 'pma' is out of the bounds of referenced subobject 'a5' with type 'char\\\[5]' at offset 14" }
+
+  T (pma->a17, 16);
+  T (pma->a17, 17);       // { dg-warning "'strcpy' offset 157 from the object at 'pma' is out of the bounds of referenced subobject 'a17' with type 'char\\\[17]' at offset 140" }
+
+  T (pma->ax, 0);
+  T ((*pma).ax, 8);
+  T (pma[0].ax, 9);
+
+  SA (__builtin_offsetof (struct MA17, a1) == 4
+      && sizeof (struct MA17) == 157);
+
+  T (pma[1].a1, 0);
+  T (pma[1].a1, 1);       // { dg-warning "'strcpy' offset 162 from the object at 'pma' is out of the bounds of referenced subobject 'a1' with type 'char\\\[1]' at offset 161" }
+  T (pma[1].a1, 4);       // { dg-warning "'strcpy' offset \\\[162, 165] from the object at 'pma' is out of the bounds of referenced subobject 'a1' with type 'char\\\[1]' at offset 161" }
+
+  T (pma[1].a2, 1);
+  T (pma[1].a2, 2);       // { dg-warning "'strcpy' offset 164 from the object at 'pma' is out of the bounds of referenced subobject 'a2' with type 'char\\\[2]' at offset 162" }
+
+  T (pma[1].a3, 2);
+  T (pma[1].a3, 3);       // { dg-warning "'strcpy' offset 167 from the object at 'pma' is out of the bounds of referenced subobject 'a3' with type 'char\\\[3]' at offset 164" }
+
+  T (pma[1].a4, 3);
+  T (pma[1].a4, 4);       // { dg-warning "'strcpy' offset 171 from the object at 'pma' is out of the bounds of referenced subobject 'a4' with type 'char\\\[4]' at offset 167" }
+
+  T (pma[1].a5, 4);
+  T (pma[1].a5, 5);       // { dg-warning "'strcpy' offset 176 from the object at 'pma' is out of the bounds of referenced subobject 'a5' with type 'char\\\[5]' at offset 171" }
+
+  T (pma[1].a17, 16);
+  T (pma[1].a17, 17);     // { dg-warning "'strcpy' offset 314 from the object at 'pma' is out of the bounds of referenced subobject 'a17' with type 'char\\\[17]' at offset 297" }
+
+  /* Since PMA points to an array of structs, accessing the flexible
+     member of any of the elements of the array except for the last one
+     would necessarily access a part of the next element of the enclosing
+     array.  The warning assumes that PMA doesn't point to the last element
+     of the array which could in theory have nonzero elements without
+     overlapping other objects.  */
+  T (pma[1].ax, 0);       // { dg-warning "'strcpy' offset 314 from the object at 'pma' is out of the bounds of referenced subobject 'ax' with type 'char\\\[]' at offset 314" }
+  T ((pma + 1)->ax, 1);   // { dg-warning "'strcpy' offset \\\[314, 315] from the object at 'pma' is out of the bounds of referenced subobject 'ax' with type 'char\\\[]' at offset 314" }
+  T ((pma + 1)[1].ax, 2); // { dg-warning "'strcpy' offset \\\[471, 473] from the object at 'pma' is out of the bounds of referenced subobject 'ax' with type 'char\\\[]' at offset 471" }
+  T ((*(pma + 2)).ax, 2); // { dg-warning "'strcpy' offset \\\[471, 473] from the object at 'pma' is out of the bounds of referenced subobject 'ax' with type 'char\\\[]' at offset 471" }
+  T (pma[3].ax, 9);       // { dg-warning "'strcpy' offset \\\[628, 637] from the object at 'pma' is out of the bounds of referenced subobject 'ax' with type 'char\\\[]' at offset 628" }
+
+  T (pma[-1].a1, 0);
+  T (pma[-1].a1, 1);      // { dg-warning "'strcpy' offset -152 from the object at 'pma' is out of the bounds of referenced subobject 'a1' with type 'char\\\[1]' at offset -153" }
+  T (pma[-1].a1, 4);      // { dg-warning "'strcpy' offset \\\[-152, -149] from the object at 'pma' is out of the bounds of referenced subobject 'a1' with type 'char\\\[1]' at offset -153" }
+}
+
+struct MA3
+{
+  char a4[4];             // { dg-message "'a4' declared here" }
+  char a3[3];             // { dg-message "'a3' declared here" }
+  char c;
+};
+
+void strcpy_ref_note (struct MA17 *pma, struct MA3 *pma3)
+{
+  T (pma3[-1].a4, 0);
+  T (pma3[-1].a4, 1);
+  T (pma3[-1].a4, 2);
+  T (pma3[-1].a4, 3);
+  T (pma3[-1].a4, 4);     // { dg-warning "'strcpy' offset -4 from the object at 'pma3' is out of the bounds of referenced subobject 'a4' with type 'char\\\[4]' at offset -8" }
+  T (pma3[-1].a4, 5);     // { dg-warning "'strcpy' offset \\\[-4, -3] from the object at 'pma3' is out of the bounds of referenced subobject 'a4' with type 'char\\\[4]' at offset -8" }
+
+  T (pma3[-1].a3, 0);
+  T (pma3[-1].a3, 1);
+  T (pma3[-1].a3, 2);
+  T (pma3[-1].a3, 3);     // { dg-warning "'strcpy' offset -1 from the object at 'pma3' is out of the bounds of referenced subobject 'a3' with type 'char\\\[3]' at offset -4" }
+  T (pma3[-1].a3, 4);     // { dg-warning "'strcpy' offset \\\[-1, 0] from the object at 'pma3' is out of the bounds of referenced subobject 'a3' with type 'char\\\[3]' at offset -4" }
+}
+
+
+void strncpy_vla_member (unsigned n)
+{
+  struct VarLenStruct {
+    char a4[4], an[n], bn[n];
+  } x;
+
+  sink (&x);
+
+  strncpy (x.bn, x.a4, sizeof x.bn);
+  sink (&x);
+
+  strncpy (x.a4, x.bn, sizeof x.a4);
+  x.a4[sizeof x.a4 - 1] = '\0';
+  sink (&x);
+
+  strncpy (x.a4, x.bn, n);
+  sink (&x);
+
+  strncpy (x.an, x.bn, sizeof x.bn);    /* { dg-bogus "\\\[-Warray-bounds" } */
+  sink (&x);
+}
Index: gcc/testsuite/gcc.dg/Warray-bounds-45.c
===================================================================
--- gcc/testsuite/gcc.dg/Warray-bounds-45.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/Warray-bounds-45.c	(working copy)
@@ -0,0 +1,330 @@
+/* PR middle-end/91631 - buffer overflow into an array member of a declared
+   object not detected
+   Test to verify that past-the-end accesses by string functions to member
+   arrays by-reference objects are diagnosed.
+   { dg-do compile }
+   { dg-options "-O2 -Wall -Wno-unused-local-typedefs -ftrack-macro-expansion=0" }  */
+
+extern char* strcpy (char*, const char*);
+extern char* strcat (char*, const char*);
+
+void sink (void*, ...);
+
+#define S36 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+
+#define S(N)   (S36 + sizeof (S36) - N - 1)
+
+/* In the test macro, prevent the strcpy to memcpy transformation
+   by using a local array initialized with the string literal.  Without
+   it, GCC transforms the strcpy call with memcpy which (unfortunately)
+   permits accesses that cross subobject boundaries.  */
+#define T(dst, ncpy, ncat)			\
+  do {						\
+    const char a[] = S36;			\
+    strcpy (dst, a + sizeof a - ncpy - 1);	\
+    const char b[] = S36;			\
+    strcat (dst, b + sizeof b - ncat - 1);	\
+    sink (dst);					\
+  } while (0)
+
+
+struct MemArrays
+{
+  char a7[7];             // { dg-message "'a7' declared here" }
+  char a4[4];             // { dg-message "'a4' declared here" }
+  char a3[3];             // { dg-message "'a3' declared here" }
+};
+
+struct MemArrays gma;
+
+void strcat_value (void)
+{
+  T (gma.a7, 1, 1);
+  T (gma.a7, 1, 5);
+  T (gma.a7, 1, 6);       // { dg-warning "'strcat' offset 7 from the object at 'gma' is out of the bounds of referenced subobject 'a7' with type 'char\\\[7]' at offset 0" }
+  T (gma.a7, 1, 7);       // { dg-warning "'strcat' offset \\\[7, 8] from the object at 'gma' is out of the bounds of referenced subobject 'a7' with type 'char\\\[7]' at offset 0" }
+
+  T (gma.a7, 2, 1);
+  T (gma.a7, 2, 4);
+  T (gma.a7, 2, 5);       // { dg-warning "'strcat' offset 7 from the object at 'gma' is out of the bounds of referenced subobject 'a7' with type 'char\\\[7]' at offset 0" }
+  T (gma.a7, 2, 6);       // { dg-warning "'strcat' offset \\\[7, 8] from the object at 'gma' is out of the bounds of referenced subobject 'a7' with type 'char\\\[7]' at offset 0" }
+
+  T (gma.a7, 5, 1);
+  T (gma.a7, 5, 2);       // { dg-warning "'strcat' offset 7 from the object at 'gma' is out of the bounds of referenced subobject 'a7' with type 'char\\\[7]' at offset 0" }
+
+  T (gma.a4, 1, 1);
+  T (gma.a4, 1, 2);
+  T (gma.a4, 1, 3);       // { dg-warning "'strcat' offset 11 from the object at 'gma' is out of the bounds of referenced subobject 'a4' with type 'char\\\[4]' at offset 7" }
+   T (gma.a4, 1, 4);       // { dg-warning "'strcat' offset \\\[11, 12] from the object at 'gma' is out of the bounds of referenced subobject 'a4' with type 'char\\\[4]' at offset 7" }
+
+  T (gma.a4, 2, 3);       // { dg-warning "'strcat' offset \\\[11, 12] from the object at 'gma' is out of the bounds of referenced subobject 'a4' with type 'char\\\[4]' at offset 7" }
+
+  T (gma.a3, 1, 1);
+  T (gma.a3, 1, 2);       // { dg-warning "'strcat' offset 14 from the object at 'gma' is out of the bounds of referenced subobject 'a3' with type 'char\\\[3]' at offset 11" }
+}
+
+
+void strcat_ref (struct MemArrays *pma)
+{
+  T (pma->a7, 1, 1);
+  T (pma->a7, 1, 5);
+  T (pma->a7, 1, 6);      // { dg-warning "'strcat' offset 7 from the object at 'pma' is out of the bounds of referenced subobject 'a7' with type 'char\\\[7]' at offset 0" }
+  T (pma->a7, 1, 7);      // { dg-warning "'strcat' offset \\\[7, 8] from the object at 'pma' is out of the bounds of referenced subobject 'a7' with type 'char\\\[7]' at offset 0" }
+
+  T (pma->a7, 2, 1);
+  T (pma->a7, 2, 4);
+  T (pma->a7, 2, 5);      // { dg-warning "'strcat' offset 7 from the object at 'pma' is out of the bounds of referenced subobject 'a7' with type 'char\\\[7]' at offset 0" }
+  T (pma->a7, 2, 6);      // { dg-warning "'strcat' offset \\\[7, 8] from the object at 'pma' is out of the bounds of referenced subobject 'a7' with type 'char\\\[7]' at offset 0" }
+
+  T (pma->a4, 1, 1);
+  T (pma->a4, 1, 2);
+  T (pma->a4, 1, 3);      // { dg-warning "'strcat' offset 11 from the object at 'pma' is out of the bounds of referenced subobject 'a4' with type 'char\\\[4]' at offset 7" }
+   T (pma->a4, 1, 4);      // { dg-warning "'strcat' offset \\\[11, 12] from the object at 'pma' is out of the bounds of referenced subobject 'a4' with type 'char\\\[4]' at offset 7" }
+
+  T (pma->a4, 2, 3);      // { dg-warning "'strcat' offset \\\[11, 12] from the object at 'pma' is out of the bounds of referenced subobject 'a4' with type 'char\\\[4]' at offset 7" }
+
+  T (pma->a3, 1, 1);
+  T (pma->a3, 1, 2);      // { dg-warning "'strcat' offset 14 from the object at 'pma' is out of the bounds of referenced subobject 'a3' with type 'char\\\[3]' at offset 11" }
+}
+
+
+#define T2(dst1, dst2, ncpy, ncat)		\
+  do {						\
+    const char a[] = S36;			\
+    strcpy (dst1, a + sizeof a - ncpy - 1);	\
+    const char b[] = S36;			\
+    strcat (dst2, b + sizeof b - ncat - 1);	\
+    sink (dst1, dst2);				\
+  } while (0)
+
+struct ArraysOfMemArrays
+{
+  struct MemArrays ma3[3];
+} a3[3];
+
+void strcat_arrays_of_arrays_value (void)
+{
+  T2 (a3[0].ma3[0].a7, a3[0].ma3[0].a7, 6, 6);   // { dg-warning "\\\[-Warray-bounds" }
+  T2 (a3[0].ma3[0].a7, a3[0].ma3[1].a7, 6, 6);
+  T2 (a3[0].ma3[0].a7, a3[0].ma3[2].a7, 6, 6);
+
+  T2 (a3[0].ma3[1].a7, a3[0].ma3[0].a7, 6, 6);
+  T2 (a3[0].ma3[1].a7, a3[0].ma3[1].a7, 6, 6);   // { dg-warning "\\\[-Warray-bounds" }
+  T2 (a3[0].ma3[1].a7, a3[0].ma3[2].a7, 6, 6);
+
+  T2 (a3[0].ma3[2].a7, a3[0].ma3[0].a7, 6, 6);
+  T2 (a3[0].ma3[2].a7, a3[0].ma3[1].a7, 6, 6);
+  T2 (a3[0].ma3[2].a7, a3[0].ma3[2].a7, 6, 6);   // { dg-warning "\\\[-Warray-bounds" }
+
+  T2 (a3[0].ma3[0].a7, a3[1].ma3[0].a7, 6, 6);
+  T2 (a3[0].ma3[0].a7, a3[1].ma3[1].a7, 6, 6);
+  T2 (a3[0].ma3[0].a7, a3[1].ma3[2].a7, 6, 6);
+
+  T2 (a3[0].ma3[1].a7, a3[1].ma3[0].a7, 6, 6);
+  T2 (a3[0].ma3[1].a7, a3[1].ma3[1].a7, 6, 6);
+  T2 (a3[0].ma3[1].a7, a3[1].ma3[2].a7, 6, 6);
+
+  T2 (a3[0].ma3[2].a7, a3[1].ma3[0].a7, 6, 6);
+  T2 (a3[0].ma3[2].a7, a3[1].ma3[1].a7, 6, 6);
+  T2 (a3[0].ma3[2].a7, a3[1].ma3[2].a7, 6, 6);
+
+  T2 (a3[0].ma3[0].a7, a3[2].ma3[0].a7, 6, 6);
+  T2 (a3[0].ma3[0].a7, a3[2].ma3[1].a7, 6, 6);
+  T2 (a3[0].ma3[0].a7, a3[2].ma3[2].a7, 6, 6);
+
+  T2 (a3[0].ma3[1].a7, a3[2].ma3[0].a7, 6, 6);
+  T2 (a3[0].ma3[1].a7, a3[2].ma3[1].a7, 6, 6);
+  T2 (a3[0].ma3[1].a7, a3[2].ma3[2].a7, 6, 6);
+
+  T2 (a3[0].ma3[2].a7, a3[2].ma3[0].a7, 6, 6);
+  T2 (a3[0].ma3[2].a7, a3[2].ma3[1].a7, 6, 6);
+  T2 (a3[0].ma3[2].a7, a3[2].ma3[2].a7, 6, 6);
+
+
+  T2 (a3[1].ma3[0].a7, a3[0].ma3[0].a7, 6, 6);
+  T2 (a3[1].ma3[0].a7, a3[0].ma3[1].a7, 6, 6);
+  T2 (a3[1].ma3[0].a7, a3[0].ma3[2].a7, 6, 6);
+
+  T2 (a3[1].ma3[1].a7, a3[0].ma3[0].a7, 6, 6);
+  T2 (a3[1].ma3[1].a7, a3[0].ma3[1].a7, 6, 6);
+  T2 (a3[1].ma3[1].a7, a3[0].ma3[2].a7, 6, 6);
+
+  T2 (a3[1].ma3[2].a7, a3[0].ma3[0].a7, 6, 6);
+  T2 (a3[1].ma3[2].a7, a3[0].ma3[1].a7, 6, 6);
+  T2 (a3[1].ma3[2].a7, a3[0].ma3[2].a7, 6, 6);
+
+  T2 (a3[1].ma3[0].a7, a3[1].ma3[0].a7, 6, 6);   // { dg-warning "\\\[-Warray-bounds" }
+  T2 (a3[1].ma3[0].a7, a3[1].ma3[1].a7, 6, 6);
+  T2 (a3[1].ma3[0].a7, a3[1].ma3[2].a7, 6, 6);
+
+  T2 (a3[1].ma3[1].a7, a3[1].ma3[0].a7, 6, 6);
+  T2 (a3[1].ma3[1].a7, a3[1].ma3[1].a7, 6, 6);   // { dg-warning "\\\[-Warray-bounds" }
+  T2 (a3[1].ma3[1].a7, a3[1].ma3[2].a7, 6, 6);
+
+  T2 (a3[1].ma3[2].a7, a3[1].ma3[0].a7, 6, 6);
+  T2 (a3[1].ma3[2].a7, a3[1].ma3[1].a7, 6, 6);
+  T2 (a3[1].ma3[2].a7, a3[1].ma3[2].a7, 6, 6);   // { dg-warning "\\\[-Warray-bounds" }
+
+  T2 (a3[1].ma3[0].a7, a3[2].ma3[0].a7, 6, 6);
+  T2 (a3[1].ma3[0].a7, a3[2].ma3[1].a7, 6, 6);
+  T2 (a3[1].ma3[0].a7, a3[2].ma3[2].a7, 6, 6);
+
+  T2 (a3[1].ma3[1].a7, a3[2].ma3[0].a7, 6, 6);
+  T2 (a3[1].ma3[1].a7, a3[2].ma3[1].a7, 6, 6);
+  T2 (a3[1].ma3[1].a7, a3[2].ma3[2].a7, 6, 6);
+
+  T2 (a3[1].ma3[2].a7, a3[2].ma3[0].a7, 6, 6);
+  T2 (a3[1].ma3[2].a7, a3[2].ma3[1].a7, 6, 6);
+  T2 (a3[1].ma3[2].a7, a3[2].ma3[2].a7, 6, 6);
+
+
+  T2 (a3[2].ma3[0].a7, a3[0].ma3[0].a7, 6, 6);
+  T2 (a3[2].ma3[0].a7, a3[0].ma3[1].a7, 6, 6);
+  T2 (a3[2].ma3[0].a7, a3[0].ma3[2].a7, 6, 6);
+
+  T2 (a3[2].ma3[1].a7, a3[0].ma3[0].a7, 6, 6);
+  T2 (a3[2].ma3[1].a7, a3[0].ma3[1].a7, 6, 6);
+  T2 (a3[2].ma3[1].a7, a3[0].ma3[2].a7, 6, 6);
+
+  T2 (a3[2].ma3[2].a7, a3[0].ma3[0].a7, 6, 6);
+  T2 (a3[2].ma3[2].a7, a3[0].ma3[1].a7, 6, 6);
+  T2 (a3[2].ma3[2].a7, a3[0].ma3[2].a7, 6, 6);
+
+  T2 (a3[2].ma3[0].a7, a3[1].ma3[0].a7, 6, 6);
+  T2 (a3[2].ma3[0].a7, a3[1].ma3[1].a7, 6, 6);
+  T2 (a3[2].ma3[0].a7, a3[1].ma3[2].a7, 6, 6);
+
+  T2 (a3[2].ma3[1].a7, a3[1].ma3[0].a7, 6, 6);
+  T2 (a3[2].ma3[1].a7, a3[1].ma3[1].a7, 6, 6);
+  T2 (a3[2].ma3[1].a7, a3[1].ma3[2].a7, 6, 6);
+
+  T2 (a3[2].ma3[2].a7, a3[1].ma3[0].a7, 6, 6);
+  T2 (a3[2].ma3[2].a7, a3[1].ma3[1].a7, 6, 6);
+  T2 (a3[2].ma3[2].a7, a3[1].ma3[2].a7, 6, 6);
+
+  T2 (a3[2].ma3[0].a7, a3[2].ma3[0].a7, 6, 6);   // { dg-warning "\\\[-Warray-bounds" }
+  T2 (a3[2].ma3[0].a7, a3[2].ma3[1].a7, 6, 6);
+  T2 (a3[2].ma3[0].a7, a3[2].ma3[2].a7, 6, 6);
+
+  T2 (a3[2].ma3[1].a7, a3[2].ma3[0].a7, 6, 6);
+  T2 (a3[2].ma3[1].a7, a3[2].ma3[1].a7, 6, 6);   // { dg-warning "\\\[-Warray-bounds" }
+  T2 (a3[2].ma3[1].a7, a3[2].ma3[2].a7, 6, 6);
+
+  T2 (a3[2].ma3[2].a7, a3[2].ma3[0].a7, 6, 6);
+  T2 (a3[2].ma3[2].a7, a3[2].ma3[1].a7, 6, 6);
+  T2 (a3[2].ma3[2].a7, a3[2].ma3[2].a7, 6, 6);   // { dg-warning "\\\[-Warray-bounds" }
+}
+
+
+void strcat_arrays_of_arrays_ref (struct ArraysOfMemArrays *p)
+{
+  T2 (p[0].ma3[0].a7, p[0].ma3[0].a7, 6, 6);   // { dg-warning "\\\[-Warray-bounds" }
+  T2 (p[0].ma3[0].a7, p[0].ma3[1].a7, 6, 6);
+  T2 (p[0].ma3[0].a7, p[0].ma3[2].a7, 6, 6);
+
+  T2 (p[0].ma3[1].a7, p[0].ma3[0].a7, 6, 6);
+  T2 (p[0].ma3[1].a7, p[0].ma3[1].a7, 6, 6);   // { dg-warning "\\\[-Warray-bounds" }
+  T2 (p[0].ma3[1].a7, p[0].ma3[2].a7, 6, 6);
+
+  T2 (p[0].ma3[2].a7, p[0].ma3[0].a7, 6, 6);
+  T2 (p[0].ma3[2].a7, p[0].ma3[1].a7, 6, 6);
+  T2 (p[0].ma3[2].a7, p[0].ma3[2].a7, 6, 6);   // { dg-warning "\\\[-Warray-bounds" }
+
+  T2 (p[0].ma3[0].a7, p[1].ma3[0].a7, 6, 6);
+  T2 (p[0].ma3[0].a7, p[1].ma3[1].a7, 6, 6);
+  T2 (p[0].ma3[0].a7, p[1].ma3[2].a7, 6, 6);
+
+  T2 (p[0].ma3[1].a7, p[1].ma3[0].a7, 6, 6);
+  T2 (p[0].ma3[1].a7, p[1].ma3[1].a7, 6, 6);
+  T2 (p[0].ma3[1].a7, p[1].ma3[2].a7, 6, 6);
+
+  T2 (p[0].ma3[2].a7, p[1].ma3[0].a7, 6, 6);
+  T2 (p[0].ma3[2].a7, p[1].ma3[1].a7, 6, 6);
+  T2 (p[0].ma3[2].a7, p[1].ma3[2].a7, 6, 6);
+
+  T2 (p[0].ma3[0].a7, p[2].ma3[0].a7, 6, 6);
+  T2 (p[0].ma3[0].a7, p[2].ma3[1].a7, 6, 6);
+  T2 (p[0].ma3[0].a7, p[2].ma3[2].a7, 6, 6);
+
+  T2 (p[0].ma3[1].a7, p[2].ma3[0].a7, 6, 6);
+  T2 (p[0].ma3[1].a7, p[2].ma3[1].a7, 6, 6);
+  T2 (p[0].ma3[1].a7, p[2].ma3[2].a7, 6, 6);
+
+  T2 (p[0].ma3[2].a7, p[2].ma3[0].a7, 6, 6);
+  T2 (p[0].ma3[2].a7, p[2].ma3[1].a7, 6, 6);
+  T2 (p[0].ma3[2].a7, p[2].ma3[2].a7, 6, 6);
+
+
+  T2 (p[1].ma3[0].a7, p[0].ma3[0].a7, 6, 6);
+  T2 (p[1].ma3[0].a7, p[0].ma3[1].a7, 6, 6);
+  T2 (p[1].ma3[0].a7, p[0].ma3[2].a7, 6, 6);
+
+  T2 (p[1].ma3[1].a7, p[0].ma3[0].a7, 6, 6);
+  T2 (p[1].ma3[1].a7, p[0].ma3[1].a7, 6, 6);
+  T2 (p[1].ma3[1].a7, p[0].ma3[2].a7, 6, 6);
+
+  T2 (p[1].ma3[2].a7, p[0].ma3[0].a7, 6, 6);
+  T2 (p[1].ma3[2].a7, p[0].ma3[1].a7, 6, 6);
+  T2 (p[1].ma3[2].a7, p[0].ma3[2].a7, 6, 6);
+
+  T2 (p[1].ma3[0].a7, p[1].ma3[0].a7, 6, 6);   // { dg-warning "\\\[-Warray-bounds" }
+  T2 (p[1].ma3[0].a7, p[1].ma3[1].a7, 6, 6);
+  T2 (p[1].ma3[0].a7, p[1].ma3[2].a7, 6, 6);
+
+  T2 (p[1].ma3[1].a7, p[1].ma3[0].a7, 6, 6);
+  T2 (p[1].ma3[1].a7, p[1].ma3[1].a7, 6, 6);   // { dg-warning "\\\[-Warray-bounds" }
+  T2 (p[1].ma3[1].a7, p[1].ma3[2].a7, 6, 6);
+
+  T2 (p[1].ma3[2].a7, p[1].ma3[0].a7, 6, 6);
+  T2 (p[1].ma3[2].a7, p[1].ma3[1].a7, 6, 6);
+  T2 (p[1].ma3[2].a7, p[1].ma3[2].a7, 6, 6);   // { dg-warning "\\\[-Warray-bounds" }
+
+  T2 (p[1].ma3[0].a7, p[2].ma3[0].a7, 6, 6);
+  T2 (p[1].ma3[0].a7, p[2].ma3[1].a7, 6, 6);
+  T2 (p[1].ma3[0].a7, p[2].ma3[2].a7, 6, 6);
+
+  T2 (p[1].ma3[1].a7, p[2].ma3[0].a7, 6, 6);
+  T2 (p[1].ma3[1].a7, p[2].ma3[1].a7, 6, 6);
+  T2 (p[1].ma3[1].a7, p[2].ma3[2].a7, 6, 6);
+
+  T2 (p[1].ma3[2].a7, p[2].ma3[0].a7, 6, 6);
+  T2 (p[1].ma3[2].a7, p[2].ma3[1].a7, 6, 6);
+  T2 (p[1].ma3[2].a7, p[2].ma3[2].a7, 6, 6);
+
+
+  T2 (p[2].ma3[0].a7, p[0].ma3[0].a7, 6, 6);
+  T2 (p[2].ma3[0].a7, p[0].ma3[1].a7, 6, 6);
+  T2 (p[2].ma3[0].a7, p[0].ma3[2].a7, 6, 6);
+
+  T2 (p[2].ma3[1].a7, p[0].ma3[0].a7, 6, 6);
+  T2 (p[2].ma3[1].a7, p[0].ma3[1].a7, 6, 6);
+  T2 (p[2].ma3[1].a7, p[0].ma3[2].a7, 6, 6);
+
+  T2 (p[2].ma3[2].a7, p[0].ma3[0].a7, 6, 6);
+  T2 (p[2].ma3[2].a7, p[0].ma3[1].a7, 6, 6);
+  T2 (p[2].ma3[2].a7, p[0].ma3[2].a7, 6, 6);
+
+  T2 (p[2].ma3[0].a7, p[1].ma3[0].a7, 6, 6);
+  T2 (p[2].ma3[0].a7, p[1].ma3[1].a7, 6, 6);
+  T2 (p[2].ma3[0].a7, p[1].ma3[2].a7, 6, 6);
+
+  T2 (p[2].ma3[1].a7, p[1].ma3[0].a7, 6, 6);
+  T2 (p[2].ma3[1].a7, p[1].ma3[1].a7, 6, 6);
+  T2 (p[2].ma3[1].a7, p[1].ma3[2].a7, 6, 6);
+
+  T2 (p[2].ma3[2].a7, p[1].ma3[0].a7, 6, 6);
+  T2 (p[2].ma3[2].a7, p[1].ma3[1].a7, 6, 6);
+  T2 (p[2].ma3[2].a7, p[1].ma3[2].a7, 6, 6);
+
+  T2 (p[2].ma3[0].a7, p[2].ma3[0].a7, 6, 6);   // { dg-warning "\\\[-Warray-bounds" }
+  T2 (p[2].ma3[0].a7, p[2].ma3[1].a7, 6, 6);
+  T2 (p[2].ma3[0].a7, p[2].ma3[2].a7, 6, 6);
+
+  T2 (p[2].ma3[1].a7, p[2].ma3[0].a7, 6, 6);
+  T2 (p[2].ma3[1].a7, p[2].ma3[1].a7, 6, 6);   // { dg-warning "\\\[-Warray-bounds" }
+  T2 (p[2].ma3[1].a7, p[2].ma3[2].a7, 6, 6);
+
+  T2 (p[2].ma3[2].a7, p[2].ma3[0].a7, 6, 6);
+  T2 (p[2].ma3[2].a7, p[2].ma3[1].a7, 6, 6);
+  T2 (p[2].ma3[2].a7, p[2].ma3[2].a7, 6, 6);   // { dg-warning "\\\[-Warray-bounds" }
+}
Index: gcc/tree-ssa-strlen.c
===================================================================
--- gcc/tree-ssa-strlen.c	(revision 275343)
+++ gcc/tree-ssa-strlen.c	(working copy)
@@ -3057,11 +3057,12 @@ handle_builtin_strcat (enum built_in_function bcod
 
       /* Compute the size of the source sequence, including the nul.  */
       tree srcsize = srclen ? srclen : size_zero_node;
-      srcsize = fold_build2 (PLUS_EXPR, type, srcsize, build_int_cst (type, 1));
-
+      tree one = build_int_cst (type, 1);
+      srcsize = fold_build2 (PLUS_EXPR, type, srcsize, one);
+      tree dstsize = fold_build2 (PLUS_EXPR, type, dstlen, one);
       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, dstsize, srcsize))
 	{
 	  gimple_set_no_warning (stmt, true);
 	  set_no_warning = true;
Index: gcc/tree.c
===================================================================
--- gcc/tree.c	(revision 275343)
+++ gcc/tree.c	(working copy)
@@ -67,6 +67,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "rtl.h"
 #include "regs.h"
 #include "tree-vector-builder.h"
+#include "gimple-fold.h"
 
 /* Tree code classes.  */
 
@@ -13860,6 +13861,75 @@ component_ref_field_offset (tree exp)
     return SUBSTITUTE_PLACEHOLDER_IN_EXPR (DECL_FIELD_OFFSET (field), exp);
 }
 
+/* Determines the size of the member referenced by the COMPONENT_REF
+   REF, using its initializer expression if necessary in order to
+   determine the size of an initialized flexible array member.
+   Returns the size (which might be zero for an object with
+   an uninitialized flexible array member) or null if the size
+   cannot be determined.  */
+
+tree
+component_ref_size (tree ref)
+{
+  gcc_assert (TREE_CODE (ref) == COMPONENT_REF);
+
+  tree member = TREE_OPERAND (ref, 1);
+
+  /* If the member is not an array, or is not last, or is an array with
+     more than one element, return its size.  Otherwise it's either
+     a bona fide flexible array member, or a zero-length array member,
+     or an array of length one treated as such.  */
+  tree size = DECL_SIZE_UNIT (member);
+  if (size)
+    {
+      tree memtype = TREE_TYPE (member);
+      if (TREE_CODE (memtype) != ARRAY_TYPE
+	  || !array_at_struct_end_p (ref))
+	return size;
+
+      if (!integer_zerop (size))
+	if (tree dom = TYPE_DOMAIN (memtype))
+	  if (tree min = TYPE_MIN_VALUE (dom))
+	    if (tree max = TYPE_MAX_VALUE (dom))
+	      if (TREE_CODE (min) == INTEGER_CST
+		  && TREE_CODE (max) == INTEGER_CST)
+		{
+		  offset_int minidx = wi::to_offset (min);
+		  offset_int maxidx = wi::to_offset (max);
+		  if (maxidx - minidx > 1)
+		    return size;
+		}
+    }
+
+  /* If the reference is to a declared object and the member a true
+     flexible array, try to determine its size from its initializer.  */
+  poly_int64 off = 0;
+  tree base = get_addr_base_and_unit_offset (ref, &off);
+  if (!base || !VAR_P (base))
+    return NULL_TREE;
+
+  /* The size of any member of a declared object other than a flexible
+     array member is that obtained above.  */
+  if (size)
+    return size;
+
+  if (tree init = DECL_INITIAL (base))
+    if (TREE_CODE (init) == CONSTRUCTOR)
+      {
+	off <<= LOG2_BITS_PER_UNIT;
+	init = fold_ctor_reference (NULL_TREE, init, off, 0, base);
+	if (init)
+	  return TYPE_SIZE_UNIT (TREE_TYPE (init));
+      }
+
+  /* Return "don't know" for an external non-array object since its
+     flexible array member can be initialized to have any number of
+     elements.  Otherwise, return zero because the flexible array
+     member has no elements.  */
+  return (DECL_EXTERNAL (base) && TREE_CODE (TREE_TYPE (base)) != ARRAY_TYPE
+	  ? NULL_TREE : integer_zero_node);
+}
+
 /* Return the machine mode of T.  For vectors, returns the mode of the
    inner type.  The main use case is to feed the result to HONOR_NANS,
    avoiding the BLKmode that a direct TYPE_MODE (T) might return.  */
Index: gcc/tree.h
===================================================================
--- gcc/tree.h	(revision 275343)
+++ gcc/tree.h	(working copy)
@@ -5259,6 +5259,13 @@ extern bool array_at_struct_end_p (tree);
    by EXP.  This does not include any offset in DECL_FIELD_BIT_OFFSET.  */
 extern tree component_ref_field_offset (tree);
 
+/* Return the size of the member referenced by the COMPONENT_REF, using
+   its initializer expression if necessary in order to determine the size
+   of an initialized flexible array member.  The size might be zero for
+   an object with an uninitialized flexible array member or null if it
+   cannot be determined.  */
+extern tree component_ref_size (tree);
+
 extern int tree_map_base_eq (const void *, const void *);
 extern unsigned int tree_map_base_hash (const void *);
 extern int tree_map_base_marked_p (const void *);

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]