public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] enhance -Warray-bounds to detect out-of-bounds offsets (PR 82455)
@ 2017-10-29 16:15 Martin Sebor
  2017-10-30 11:55 ` Richard Biener
  2017-10-30 22:16 ` Jeff Law
  0 siblings, 2 replies; 22+ messages in thread
From: Martin Sebor @ 2017-10-29 16:15 UTC (permalink / raw)
  To: Gcc Patch List, Jeff Law, Richard Biener

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

In my work on -Wrestrict, to issue meaningful warnings, I found
it important to detect both out of bounds array indices as well
as offsets in calls to restrict-qualified functions like strcpy.
GCC already detects some of these cases but my tests for
the enhanced warning exposed a few gaps.

The attached patch enhances -Warray-bounds to detect more instances
out-of-bounds indices and offsets to member arrays and non-array
members.  For example, it detects the out-of-bounds offset in the
call to strcpy below.

The patch is meant to be applied on top posted here but not yet
committed:
   https://gcc.gnu.org/ml/gcc-patches/2017-10/msg01304.html

Richard, since this also touches tree-vrp.c I look for your comments.

Jeff, this is the enhancement you were interested in when we spoke
last week.

Thanks
Martin

$ cat a.c && gcc -O2 -S -Wall a.c
   struct A { char a[4]; void (*pf)(void); };

   void f (struct A *p)
   {
     p->a[5] = 'x';            // existing -Warray-bounds

     strcpy (p->a + 6, "y");   // enhanced -Warray-bounds
   }

   a.c: In function ‘f’:
   a.c:7:3: warning: offset 6 is out of bounds of ‘char[4]’ [-Warray-bounds]
    strcpy (p->a + 6, "y");
    ^~~~~~~~~~~~~~~~~~~~~~
   a.c:1:17: note: member declared here
    struct A { char a[4]; void (*pf)(void); };
                    ^
   a.c:5:7: warning: array subscript 5 is above array bounds of 
‘char[4]’ [-Warray-bounds]
      p->a[5] = 'x';
      ~~~~^~~

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

PR tree-optimization/82455 - missing -Warray-bounds on strcpy offset in an out-of-bounds range

gcc/ChangeLog:

	PR tree-optimization/82455
	* tree-ssa-forwprop.c (maybe_warn_offset_out_of_bounds): New function.
	(forward_propagate_addr_expr_1): Call it.
	* tree-vrp.c (check_array_ref): Return bool.  Handle flexible array
	members, string literals, and inner indices.
	(search_for_addr_array): Return bool.  Add an argument.  Add detail
	to diagnostics.
	(check_array_bounds): New function.
	* tree-vrp.h (check_array_bounds): Declare it.
	* wide-int.h (sign_mask): Assert a precondition.

gcc/testsuite/ChangeLog:

	PR tree-optimization/82455
	* gcc.dg/Warray-bounds-23.c: New test.
	* gcc.dg/Warray-bounds-24.c: New test.

diff --git a/gcc/tree-ssa-forwprop.c b/gcc/tree-ssa-forwprop.c
index 5569b98..364a3a2 100644
--- a/gcc/tree-ssa-forwprop.c
+++ b/gcc/tree-ssa-forwprop.c
@@ -46,6 +46,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-cfgcleanup.h"
 #include "cfganal.h"
 #include "optabs-tree.h"
+#include "diagnostic-core.h"
+#include "tree-vrp.h"
 
 /* This pass propagates the RHS of assignment statements into use
    sites of the LHS of the assignment.  It's basically a specialized
@@ -641,6 +643,210 @@ tidy_after_forward_propagate_addr (gimple *stmt)
      recompute_tree_invariant_for_addr_expr (gimple_assign_rhs1 (stmt));
 }
 
+/* Check to see if the expression (char*)PTR + OFF is valid (i.e., it
+   doesn't overflow and the result is within the bounds of the object
+   pointed to by PTR.  OFF is an offset in bytes.  If it isn't valid,
+   issue a -Warray-bounds warning.  */
+
+static bool
+maybe_warn_offset_out_of_bounds (location_t loc, tree ptr, tree off)
+{
+  offset_int cstoff = 0;
+
+  bool ignore_off_by_1 = false;
+
+  if (TREE_CODE (ptr) == SSA_NAME)
+    {
+      gimple *stmt = SSA_NAME_DEF_STMT (ptr);
+      if (is_gimple_assign (stmt))
+	{
+	  tree_code code = gimple_assign_rhs_code (stmt);
+	  if (code == ADDR_EXPR)
+	    {
+	      ptr = gimple_assign_rhs1 (stmt);
+	      ignore_off_by_1 = true;
+	    }
+	  else if (code == POINTER_PLUS_EXPR)
+	    {
+	      ptr = gimple_assign_rhs1 (stmt);
+	      tree offset = gimple_assign_rhs2 (stmt);
+	      if (maybe_warn_offset_out_of_bounds (loc, ptr, offset))
+		return true;
+
+	      /* To do: handle ranges.  */
+	      if (TREE_CODE (offset) != INTEGER_CST)
+		return false;
+
+	      /* Convert the offset from sizetype to ptrdiff_t.  */
+	      offset = fold_convert (ptrdiff_type_node, offset);
+	      cstoff = wi::to_offset (offset);
+	    }
+	}
+    }
+
+  if (TREE_CODE (ptr) != ADDR_EXPR
+      || TREE_CODE (TREE_OPERAND (ptr, 0)) != ARRAY_REF)
+    ignore_off_by_1 = false;
+
+  if (check_array_bounds (ptr, ignore_off_by_1))
+    return true;
+
+  bool addr_expr = false;
+
+  if (TREE_CODE (ptr) == ADDR_EXPR)
+    {
+      ptr = TREE_OPERAND (ptr, 0);
+      addr_expr = true;
+    }
+
+  /* The type of the array to whichj the offset applies and the type
+     of the array element.  For offsets to non-arrays (i.e., pointers)
+     the array is considered to have a single element of ELTYPE.   */
+  tree atype = TREE_TYPE (ptr);
+  tree eltype = atype;
+
+  if (TREE_CODE (ptr) == ARRAY_REF)
+    {
+      tree idx = TREE_OPERAND (ptr, 1);
+
+      /* To do: handle ranges.  */
+      if (TREE_CODE (idx) != INTEGER_CST)
+	return false;
+
+      ptr = TREE_OPERAND (ptr, 0);
+      eltype = TREE_TYPE (TREE_TYPE (ptr));
+      tree eltsize = TYPE_SIZE_UNIT (eltype);
+
+      idx = fold_convert (ptrdiff_type_node, idx);
+
+      if (TREE_CODE (eltsize) == INTEGER_CST)
+	cstoff += wi::to_offset (idx) * wi::to_offset (eltsize);
+      else
+	cstoff += wi::to_offset (idx);
+
+      atype = TREE_TYPE (ptr);
+      if (TREE_CODE (atype) == POINTER_TYPE)
+	atype = TREE_TYPE (atype);
+    }
+  else if (TREE_CODE (ptr) == MEM_REF)
+    addr_expr = false;
+
+  bool zero_low_bound;
+  tree ubound = NULL_TREE;
+
+  if (TREE_CODE (atype) == ARRAY_TYPE)
+    {
+      eltype = TREE_TYPE (atype);
+      zero_low_bound = true;
+
+      if (warn_array_bounds > 1
+	  || !array_at_struct_end_p (ptr))
+	{
+	  /* Determine the upper bound of the array and the element type
+	     if it's known.  Otherwise, use the maximum object size to
+	     compute the most conservative upper bound.  */
+	  if (tree dom = TYPE_DOMAIN (atype))
+	    ubound = TYPE_MAX_VALUE (dom);
+	}
+    }
+  else if (addr_expr)
+    {
+      /* Treat a singleton (non-array) variable as an array of a single
+	 element.  */
+      eltype = atype;
+      atype = build_array_type_nelts (eltype, 1);
+      ubound = size_zero_node;
+      zero_low_bound = true;
+    }
+  else
+    {
+      eltype = atype;
+      zero_low_bound = false;
+    }
+
+  offset_int elsize;
+  if (TREE_CODE (TYPE_SIZE_UNIT (eltype)) == INTEGER_CST)
+    elsize = wi::to_offset (TYPE_SIZE_UNIT (eltype));
+  else
+    elsize = 1;
+
+  /* Compute the maximum and minimum valid byte offsets. */
+  offset_int maxoff = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
+  offset_int minoff = wi::to_offset (TYPE_MIN_VALUE (ptrdiff_type_node));
+
+  if (TREE_CODE (off) == SSA_NAME)
+    {
+      wide_int min, max;
+      value_range_type rng = get_range_info (off, &min, &max);
+      if (rng == VR_VARYING)
+	return false;
+
+      if (rng == VR_RANGE)
+	{
+	  if (wi::lts_p (min, wi::zero (min.get_precision ())))
+	    min = max;
+	}
+      else
+	min = max + 1;
+
+      off = wide_int_to_tree (TREE_TYPE (off), min);
+    }
+
+  /* Convert the unsigned byte offset into ptrdiff_t.  */
+  off = fold_convert (ptrdiff_type_node, off);
+  offset_int wioff = wi::to_offset (off) + cstoff;
+
+  /* Determine the byte offset of the (member) reference and subtract
+     it from the upper bound on the offset.  The lower bound stays
+     the same because its value doesn't contribute to the result.  */
+  HOST_WIDE_INT byteoff;
+  tree base = get_addr_base_and_unit_offset (ptr, &byteoff);
+  if (base)
+    maxoff -= byteoff;
+
+  bool warned = false;
+
+  if (wi::lts_p (maxoff, wioff) || wi::lts_p (wioff, minoff))
+    {
+      warned = warning_at (loc, OPT_Warray_bounds,
+			   "pointer overflow computing offset %lli from %qT",
+			   (long long) wioff.to_shwi (), atype);
+    }
+  else
+    {
+      if (ubound && TREE_CODE (ubound) == INTEGER_CST)
+	maxoff = (wi::to_offset (ubound) + 1) * elsize;
+
+      if (zero_low_bound)
+	minoff = 0;
+
+      if (wi::lts_p (maxoff, wioff) || wi::lts_p (wioff, minoff))
+	warned = warning_at (loc, OPT_Warray_bounds,
+			     "offset %lli is out of bounds of %qT",
+			     (long long) wioff.to_shwi (), atype);
+    }
+
+  if (warned)
+    {
+      if (base && TREE_CODE (base) == SSA_NAME)
+	{
+	  gimple *stmt = SSA_NAME_DEF_STMT (ptr);
+	  if (is_gimple_assign (stmt)
+	      && gimple_assign_rhs_code (stmt) == ADDR_EXPR)
+	    ptr = TREE_OPERAND (gimple_assign_rhs1 (stmt), 0);
+	}
+
+      if (TREE_CODE (ptr) == COMPONENT_REF)
+	{
+	  ptr = TREE_OPERAND (ptr, 1);
+	  inform (DECL_SOURCE_LOCATION (ptr), "member declared here");
+	}
+      return true;
+    }
+
+  return false;
+}
+
 /* NAME is a SSA_NAME representing DEF_RHS which is of the form
    ADDR_EXPR <whatever>.
 
@@ -717,10 +923,13 @@ forward_propagate_addr_expr_1 (tree name, tree def_rhs,
          to make sure we can build a valid constant offsetted address
 	 for further propagation.  Simply rely on fold building that
 	 and check after the fact.  */
+      tree off = gimple_assign_rhs2 (use_stmt);
+      maybe_warn_offset_out_of_bounds (gimple_location (use_stmt),
+				       rhs, off);
+
       new_def_rhs = fold_build2 (MEM_REF, TREE_TYPE (TREE_TYPE (rhs)),
 				 def_rhs,
-				 fold_convert (ptr_type_node,
-					       gimple_assign_rhs2 (use_stmt)));
+				 fold_convert (ptr_type_node, off));
       if (TREE_CODE (new_def_rhs) == MEM_REF
 	  && !is_gimple_mem_ref_addr (TREE_OPERAND (new_def_rhs, 0)))
 	return false;
diff --git a/gcc/tree-vrp.c b/gcc/tree-vrp.c
index 3e93685..8097ea5 100644
--- a/gcc/tree-vrp.c
+++ b/gcc/tree-vrp.c
@@ -6664,7 +6664,7 @@ insert_range_assertions (void)
    non-overlapping with valid range.
    IGNORE_OFF_BY_ONE is true if the ARRAY_REF is inside a ADDR_EXPR.  */
 
-static void
+static bool
 check_array_ref (location_t location, tree ref, bool ignore_off_by_one)
 {
   value_range *vr = NULL;
@@ -6672,7 +6672,7 @@ check_array_ref (location_t location, tree ref, bool ignore_off_by_one)
   tree low_bound, up_bound, up_bound_p1;
 
   if (TREE_NO_WARNING (ref))
-    return;
+    return false;
 
   low_sub = up_sub = TREE_OPERAND (ref, 1);
   up_bound = array_ref_up_bound (ref);
@@ -6691,7 +6691,7 @@ check_array_ref (location_t location, tree ref, bool ignore_off_by_one)
 
       /* FIXME: Handle VLAs.  */
       if (TREE_CODE (eltsize) != INTEGER_CST)
-	return;
+	return false;
 
       tree maxbound = TYPE_MAX_VALUE (ptrdiff_type_node);
 
@@ -6702,11 +6702,12 @@ check_array_ref (location_t location, tree ref, bool ignore_off_by_one)
       if (code == COMPONENT_REF)
 	{
 	  HOST_WIDE_INT off;
-	  if (tree base = get_addr_base_and_unit_offset (arg, &off))
+	  if (get_addr_base_and_unit_offset (arg, &off))
 	    {
-	      tree size = TYPE_SIZE_UNIT (TREE_TYPE (base));
-	      if (TREE_CODE (size) == INTEGER_CST)
-		up_bound_p1 = int_const_binop (MINUS_EXPR, up_bound_p1, size);
+	      off /= tree_to_uhwi (eltsize);
+	      up_bound_p1 = int_const_binop (MINUS_EXPR, up_bound_p1,
+					     build_int_cst (ptrdiff_type_node,
+							    off));
 	    }
 	}
 
@@ -6719,21 +6720,28 @@ check_array_ref (location_t location, tree ref, bool ignore_off_by_one)
 
   low_bound = array_ref_low_bound (ref);
 
+  if (location == UNKNOWN_LOCATION
+      && EXPR_HAS_LOCATION (ref))
+    location = EXPR_LOCATION (ref);
+
+  bool warned = false;
   tree artype = TREE_TYPE (TREE_OPERAND (ref, 0));
 
   /* Empty array.  */
   if (tree_int_cst_equal (low_bound, up_bound_p1))
     {
-      warning_at (location, OPT_Warray_bounds,
-		  "array subscript %E is above array bounds of %qT",
-		  low_bound, artype);
-      TREE_NO_WARNING (ref) = 1;
+      warned = warning_at (location, OPT_Warray_bounds,
+			   "array subscript %E is above array bounds of %qT",
+			   low_bound, artype);
+      if (warned)
+	TREE_NO_WARNING (ref) = 1;
+      return warned;
     }
 
   if (TREE_CODE (low_sub) == SSA_NAME)
     {
       vr = get_value_range (low_sub);
-      if (vr->type == VR_RANGE || vr->type == VR_ANTI_RANGE)
+      if (vr && (vr->type == VR_RANGE || vr->type == VR_ANTI_RANGE))
         {
           low_sub = vr->type == VR_RANGE ? vr->max : vr->min;
           up_sub = vr->type == VR_RANGE ? vr->min : vr->max;
@@ -6748,13 +6756,11 @@ check_array_ref (location_t location, tree ref, bool ignore_off_by_one)
 	      : tree_int_cst_le (up_bound, up_sub))
           && TREE_CODE (low_sub) == INTEGER_CST
           && tree_int_cst_le (low_sub, low_bound))
-        {
-          warning_at (location, OPT_Warray_bounds,
-		      "array subscript [%E, %E] is outside array bounds of %qT",
-		      low_sub, up_sub, artype);
-          TREE_NO_WARNING (ref) = 1;
-        }
-    }
+	warned = warning_at (location, OPT_Warray_bounds,
+			     "array subscript [%E, %E] is outside array "
+			     "bounds of %qT",
+			     low_sub, up_sub, artype);
+	}
   else if (TREE_CODE (up_sub) == INTEGER_CST
 	   && (ignore_off_by_one
 	       ? !tree_int_cst_le (up_sub, up_bound_p1)
@@ -6766,10 +6772,9 @@ check_array_ref (location_t location, tree ref, bool ignore_off_by_one)
 	  dump_generic_expr (MSG_NOTE, TDF_SLIM, ref);
 	  fprintf (dump_file, "\n");
 	}
-      warning_at (location, OPT_Warray_bounds,
-		  "array subscript %E is above array bounds of %qT",
-		  up_sub, artype);
-      TREE_NO_WARNING (ref) = 1;
+      warned = warning_at (location, OPT_Warray_bounds,
+			   "array subscript %E is above array bounds of %qT",
+			   up_sub, artype);
     }
   else if (TREE_CODE (low_sub) == INTEGER_CST
            && tree_int_cst_lt (low_sub, low_bound))
@@ -6780,24 +6785,31 @@ check_array_ref (location_t location, tree ref, bool ignore_off_by_one)
 	  dump_generic_expr (MSG_NOTE, TDF_SLIM, ref);
 	  fprintf (dump_file, "\n");
 	}
-      warning_at (location, OPT_Warray_bounds,
-		  "array subscript %E is below array bounds of %qT",
-		  low_sub, artype);
-      TREE_NO_WARNING (ref) = 1;
+      warned = warning_at (location, OPT_Warray_bounds,
+			   "array subscript %E is below array bounds of %qT",
+			   low_sub, artype);
     }
+
+  if (warned)
+    TREE_NO_WARNING (ref) = 1;
+
+  return warned;
 }
 
-/* Searches if the expr T, located at LOCATION computes
-   address of an ARRAY_REF, and call check_array_ref on it.  */
+/* Search if the expr T, located at LOCATION, compute the address
+   of an ARRAY_REF, and call check_array_ref on it.
+   IGNORE_OFF_BY_ONE is true if the ARRAY_REF is inside a ADDR_EXPR.  */
 
-static void
-search_for_addr_array (tree t, location_t location)
+static bool
+search_for_addr_array (tree t, location_t location, bool ignore_off_by_one)
 {
+  bool warned = false;
+
   /* Check each ARRAY_REFs in the reference chain. */
   do
     {
       if (TREE_CODE (t) == ARRAY_REF)
-	check_array_ref (location, t, true /*ignore_off_by_one*/);
+	warned |= check_array_ref (location, t, ignore_off_by_one);
 
       t = TREE_OPERAND (t, 0);
     }
@@ -6813,7 +6825,7 @@ search_for_addr_array (tree t, location_t location)
       if (TREE_CODE (TREE_TYPE (tem)) != ARRAY_TYPE
 	  || TREE_CODE (TREE_TYPE (TREE_TYPE (tem))) == ARRAY_TYPE
 	  || !TYPE_DOMAIN (TREE_TYPE (tem)))
-	return;
+	return warned;
 
       low_bound = TYPE_MIN_VALUE (TYPE_DOMAIN (TREE_TYPE (tem)));
       up_bound = TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (tem)));
@@ -6824,7 +6836,7 @@ search_for_addr_array (tree t, location_t location)
 	  || TREE_CODE (up_bound) != INTEGER_CST
 	  || !el_sz
 	  || TREE_CODE (el_sz) != INTEGER_CST)
-	return;
+	return warned;
 
       idx = mem_ref_offset (t);
       idx = wi::sdiv_trunc (idx, wi::to_offset (el_sz));
@@ -6836,10 +6848,10 @@ search_for_addr_array (tree t, location_t location)
 	      dump_generic_expr (MSG_NOTE, TDF_SLIM, t);
 	      fprintf (dump_file, "\n");
 	    }
-	  warning_at (location, OPT_Warray_bounds,
-		      "array subscript %wi is below array bounds of %qT",
-		      idx.to_shwi (), TREE_TYPE (tem));
-	  TREE_NO_WARNING (t) = 1;
+	  warned |= warning_at (location, OPT_Warray_bounds,
+				"array subscript %wi is below array bounds "
+				"of %qT",
+				idx.to_shwi (), TREE_TYPE (tem));
 	}
       else if (idx > (wi::to_offset (up_bound)
 		      - wi::to_offset (low_bound) + 1))
@@ -6850,12 +6862,17 @@ search_for_addr_array (tree t, location_t location)
 	      dump_generic_expr (MSG_NOTE, TDF_SLIM, t);
 	      fprintf (dump_file, "\n");
 	    }
-	  warning_at (location, OPT_Warray_bounds,
-		      "array subscript %wu is above array bounds of %qT",
-		      idx.to_uhwi (), TREE_TYPE (tem));
-	  TREE_NO_WARNING (t) = 1;
+	  warned = warning_at (location, OPT_Warray_bounds,
+			       "array subscript %wu is above array bounds "
+			       "of %qT",
+			       idx.to_uhwi (), TREE_TYPE (tem));
 	}
     }
+
+  if (warned)
+    TREE_NO_WARNING (t) = 1;
+
+  return warned;
 }
 
 /* walk_tree() callback that checks if *TP is
@@ -6874,7 +6891,10 @@ check_array_bounds (tree *tp, int *walk_subtree, void *data)
   if (EXPR_HAS_LOCATION (t))
     location = EXPR_LOCATION (t);
   else
-    location = gimple_location (wi->stmt);
+    {
+      location_t *locp = (location_t *) wi->info;
+      location = *locp;
+    }
 
   *walk_subtree = TRUE;
 
@@ -6883,13 +6903,31 @@ check_array_bounds (tree *tp, int *walk_subtree, void *data)
 
   else if (TREE_CODE (t) == ADDR_EXPR)
     {
-      search_for_addr_array (t, location);
+      search_for_addr_array (t, location, true /*ignore_off_by_one*/);
       *walk_subtree = FALSE;
     }
 
   return NULL_TREE;
 }
 
+/* Check array indices in EXPR against the bounds of arrays they refer
+   to and issue warnings for any that are outside those bounds.  When
+   IGNORE_OFF_BY_ONE is true, ignore indices that refer to one past
+   the last element of an array (used in &A[N] when N equals A's upper
+   bound.  Return true if any warnings have been issued.  */
+
+bool
+check_array_bounds (tree expr, bool ignore_off_by_one)
+{
+  if (TREE_CODE (expr) == ARRAY_REF)
+    return check_array_ref (UNKNOWN_LOCATION, expr, ignore_off_by_one);
+
+  if (TREE_CODE (expr) == ADDR_EXPR)
+    return search_for_addr_array (expr, UNKNOWN_LOCATION, ignore_off_by_one);
+
+  return false;
+}
+
 /* Walk over all statements of all reachable BBs and call check_array_bounds
    on them.  */
 
@@ -6921,6 +6959,9 @@ check_all_array_refs (void)
 
 	  memset (&wi, 0, sizeof (wi));
 
+	  location_t loc = gimple_location (stmt);
+	  wi.info = &loc;
+
 	  walk_gimple_op (gsi_stmt (si),
 			  check_array_bounds,
 			  &wi);
diff --git a/gcc/tree-vrp.h b/gcc/tree-vrp.h
index f84403a..6587b57 100644
--- a/gcc/tree-vrp.h
+++ b/gcc/tree-vrp.h
@@ -59,5 +59,6 @@ extern void extract_range_from_unary_expr (value_range *vr,
 					   tree type,
 					   value_range *vr0_,
 					   tree op0_type);
+extern bool check_array_bounds (tree, bool);
 
 #endif /* GCC_TREE_VRP_H */
diff --git a/gcc/wide-int.h b/gcc/wide-int.h
index e17b016..7f431db 100644
--- a/gcc/wide-int.h
+++ b/gcc/wide-int.h
@@ -822,6 +822,8 @@ inline HOST_WIDE_INT
 generic_wide_int <storage>::sign_mask () const
 {
   unsigned int len = this->get_len ();
+  gcc_assert (len != 0);
+
   unsigned HOST_WIDE_INT high = this->get_val ()[len - 1];
   if (!is_sign_extended)
     {
diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-23.c b/gcc/testsuite/gcc.dg/Warray-bounds-23.c
new file mode 100644
index 0000000..85071b0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Warray-bounds-23.c
@@ -0,0 +1,16 @@
+/* PR tree-optimization/82588 - missing -Warray-bounds on an excessively
+   large index
+   { dg-do compile }
+   { dg-options "-O2 -Warray-bounds -ftrack-macro-expansion=0" }  */
+
+#define SIZE_MAX  __SIZE_MAX__
+#define SSIZE_MAX __PTRDIFF_MAX__
+#define SSIZE_MIN (-SSIZE_MAX - 1)
+
+
+int g (int i)
+{
+  int (*p)[2] = (int (*)[2])&i;
+
+  return (*p)[2];
+}
diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-24.c b/gcc/testsuite/gcc.dg/Warray-bounds-24.c
new file mode 100644
index 0000000..3ab06ee
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Warray-bounds-24.c
@@ -0,0 +1,246 @@
+/* PR tree-optimization/82581 -	missing -Warray-bounds on writing past
+   the end of a member array
+   { dg-do compile }
+   { dg-options "-O2 -Warray-bounds -ftrack-macro-expansion=0" } */
+
+#define SMAX  __PTRDIFF_MAX__
+#define SMIN  (-SMAX -1)
+#define UMAX  __SIZE_MAX__
+
+#define STATIC_ASSERT(expr) ((void)sizeof (char[1 - 2 * !!(expr)]))
+
+typedef __PTRDIFF_TYPE__ ptrdiff_t;
+typedef __SIZE_TYPE__    size_t;
+
+struct A { char a3[3], b4[4]; };
+struct Ax { char a4[4]; char ax[]; };
+
+void sink (void*);
+#define T(expr)   sink (expr)
+
+
+void test_char_memarray_1 (struct A *a, struct Ax *ax)
+{
+  T (a->a3 + SMIN);           /* { dg-warning "offset -\[0-9\]+ is out of bounds of .char\\\[3]." } */
+  T (a->a3 - SMAX);           /* { dg-warning "offset -\[0-9\]+ is out of bounds" } */
+  T (a->a3 - 1);              /* { dg-warning "offset -1 is out of bounds" } */
+  T (a->a3 + 0);
+  T (a->a3 + 1);
+  T (a->a3 + 2);
+  T (a->a3 + 3);
+  T (a->a3 + 4);              /* { dg-warning "offset 4 is out of bounds" } */
+  T (a->a3 + SMAX);           /* { dg-warning "offset \[0-9\]+ is out of bounds" } */
+  T (a->a3 + SMAX + (size_t)1);   /* { dg-warning "offset -\[0-9\]+ is out of bounds" } */
+
+  T (a->b4 + SMIN);           /* { dg-warning "offset -\[0-9\]+ is out of bounds of .char\\\[4]." } */
+  T (a->b4 - SMAX);           /* { dg-warning "offset -\[0-9\]+ is out of bounds" } */
+  T (a->b4 - 1);              /* { dg-warning "offset -1 is out of bounds" } */
+  T (a->b4 + 0);
+  T (a->b4 + 1);
+  T (a->b4 + 2);
+  T (a->b4 + 3);
+  T (a->b4 + 4);
+  /* This is considered okay for an array at the end of a struct.  */
+  T (a->b4 + 5);
+  T (a->b4 + 99);
+  T (a->b4 + SMAX - 4);
+  T (a->b4 + SMAX);               /* { dg-warning "pointer overflow" } */
+  T (a->b4 + SMAX + (size_t)1);   /* { dg-warning "offset -\[0-9\]+ is out of bounds " } */
+
+  T (ax->a4 + SMIN);          /* { dg-warning "offset -\[0-9\]+ is out of bounds of .char\\\[4]." } */
+  T (ax->a4 - SMAX);          /* { dg-warning "offset -\[0-9\]+ is out of bounds" } */
+  T (ax->a4 - 1);             /* { dg-warning "offset -1 is out of bounds" } */
+  T (ax->a4 + 0);
+  T (ax->a4 + 1);
+  T (ax->a4 + 2);
+  T (ax->a4 + 3);
+  T (ax->a4 + 4);
+  T (ax->a4 + 5);             /* { dg-warning "offset 5 is out of bounds" } */
+  T (ax->a4 + SMAX);          /* { dg-warning "offset \[0-9\]+ is out of bounds" } */
+
+  T (ax->ax + SMIN);          /* { dg-warning "offset -\[0-9\]+ is out of bounds." } */
+  T (ax->ax - SMAX);          /* { dg-warning "offset -\[0-9\]+ is out of bounds" } */
+  T (ax->ax - 1);             /* { dg-warning "offset -1 is out of bounds" } */
+  T (ax->ax + 0);
+  T (ax->ax + 1);
+  T (ax->ax + 2);
+  T (ax->ax + 3);
+  T (ax->ax + 4);
+  T (ax->ax + 99);
+  T (ax->ax + SMAX - 4);
+  T (ax->ax + SMAX - 3);      /* { dg-warning "pointer overflow computing offset \[0-9\]+ from .char\\\[]." } */
+  T (ax->ax + SMAX);          /* { dg-warning "pointer overflow computing offset \[0-9\]+ from .char\\\[]." } */
+ }
+
+struct B { struct A a[2]; };
+struct Bx { struct A a[2]; struct A ax[]; };
+
+void test_memarray_2 (struct B *b, struct Bx *bx)
+{
+  T (b[0].a + 0);
+  T (b[0].a + 1);
+  T (b[0].a + 2);
+  /* The following are okay for an array at the end of a struct.  */
+  T (b[0].a + 3);
+  T (b[0].a + 99);
+  T (b[0].a + SMAX / sizeof b[0].a[0]);
+  /* The offset computation in following overflows too early to detect
+     it (with the current implementation) but because the offset gets
+     negative it is detected and diagnosed.  */
+  T (b[0].a + SMAX / sizeof b[0].a[0] + 1);   /* { dg-warning "offset -?\[0-9\]+ is out of bounds of .struct A\\\[2]." } */
+  T (b[0].a + SMAX / sizeof b[0].a[0] + 2);   /* { dg-warning "offset -?\[0-9\]+ is out of bounds of .struct A\\\[2]." } */
+  T (b[0].a + SMAX / sizeof b[0].a[0] + 3);   /* { dg-warning "offset -?\[0-9\]+ is out of bounds of .struct A\\\[2]." } */
+
+  T (b[0].a[0].b4 + 0);
+  T (b[0].a[0].b4 + 1);
+  T (b[0].a[0].b4 + 2);
+  T (b[0].a[0].b4 + 3);
+  T (b[0].a[0].b4 + 5);   /* { dg-warning "offset 5 is out of bounds of .char\\\[4]." } */
+
+  T (b[0].a[1].b4 + 0);
+  T (b[0].a[1].b4 + 1);
+  T (b[0].a[1].b4 + 2);
+  T (b[0].a[1].b4 + 3);
+  T (b[0].a[1].b4 + 5);   /* { dg-warning "offset 5 is out of bounds of .char\\\[4]." } */
+  T (b[0].a[1].b4 + 6);   /* { dg-warning "offset 6 is out of bounds" } */
+  T (b[0].a[1].b4 + 7);   /* { dg-warning "offset 7 is out of bounds" } */
+  T (b[0].a[1].b4 + 8);   /* { dg-warning "offset 8 is out of bounds" } */
+  T (b[0].a[1].b4 + 9);   /* { dg-warning "offset 9 is out of bounds" } */
+  T (b[0].a[1].b4 + 10);  /* { dg-warning "offset 10 is out of bounds" } */
+  T (b[0].a[1].b4 + 11);  /* { dg-warning "offset 11 is out of bounds" } */
+  T (b[0].a[1].b4 + 12);  /* { dg-warning "offset 12 is out of bounds" } */
+  T (b[0].a[1].b4 + 13);  /* { dg-warning "offset 13 is out of bounds" } */
+
+  T (b[1].a[0].b4 + 0);
+  T (b[1].a[0].b4 + 1);
+  T (b[1].a[0].b4 + 2);
+  T (b[1].a[0].b4 + 3);
+  T (b[1].a[0].b4 + 5);   /* { dg-warning "offset 5 is out of bounds" } */
+
+  T (b[1].a[1].b4 + 0);
+  T (b[1].a[1].b4 + 1);
+  T (b[1].a[1].b4 + 2);
+  T (b[1].a[1].b4 + 3);
+  T (b[1].a[1].b4 + 5);   /* { dg-warning "offset 5 is out of bounds" } */
+
+  STATIC_ASSERT (sizeof bx[0].a == 7);
+  T (bx[0].a + 0);
+  T (bx[0].a + 1);
+  T (bx[0].a + 2);
+  T (bx[0].a + 3);        /* { dg-warning "offset 21 is out of bounds of .struct A\\\[2]." } */
+  T (bx[0].a + 99);       /* { dg-warning "offset 693 is out of bounds of .struct A\\\[2]." } */
+  T (bx[0].a + SMAX);     /* { dg-warning "offset -?\[0-9\]+ is out of bounds of .struct A\\\[2]." } */
+
+  T (bx[0].a[0].b4);
+  T (bx[0].a[0].b4 + 1);
+  T (bx[0].a[0].b4 + 2);
+  T (bx[0].a[0].b4 + 3);
+  T (bx[0].a[0].b4 + 99);   /* { dg-warning "offset 99 is out of bounds of .char\\\[4]." } */
+  T (bx[0].a[0].b4 + SMAX); /* { dg-warning "pointer overflow" } */
+
+  T (bx[0].a[1].b4);
+  T (bx[0].a[1].b4 + 1);
+  T (bx[0].a[1].b4 + 2);
+  T (bx[0].a[1].b4 + 3);
+  T (bx[0].a[1].b4 + 99);   /* { dg-warning "offset 99 is out of bounds of .char\\\[4]." } */
+  T (bx[0].a[1].b4 + SMAX); /* { dg-warning "pointer overflow computing offset -?\[0-9\]+ from .char\\\[4]." } */
+
+  T (bx[0].a[-1].b4);        /* { dg-warning "array subscript -1 is below array bounds" } */
+  T (bx[0].a[2].b4);
+  T (bx[0].a[2].b4 + 1);    /* { dg-warning "array subscript 2 is above array bounds" } */
+  T (bx[0].a[2].b4 + 2);    /* { dg-warning "array subscript 2 is above array bounds" } */
+  T (bx[0].a[2].b4 + 3);    /* { dg-warning "array subscript 2 is above array bounds" } */
+  T (bx[0].a[2].b4 + 99);   /* { dg-warning "array subscript 2 is above array bounds" } */
+  T (bx[0].a[2].b4 + SMAX); /* { dg-warning "array subscript 2 is above array bounds" } */
+
+  T (bx[0].ax[0].b4 + 0);
+  T (bx[0].ax[0].b4 + 1);
+  T (bx[0].ax[0].b4 + 2);
+  T (bx[0].ax[0].b4 + 3);
+  T (bx[0].ax[0].b4 + 5);   /* { dg-warning "offset 5 is out of bounds of .char\\\[4]." } */
+
+  T (bx[-99].ax[0].b4 + 0);
+  T (bx[ -1].ax[0].b4 + 0);
+  T (bx[  0].ax[0].b4 + 1);
+  T (bx[  1].ax[0].b4 + 2);
+  T (bx[  2].ax[0].b4 + 3);
+  T (bx[ 99].ax[0].b4 + 5);   /* { dg-warning "offset 5 is out of bounds of .char\\\[4]." } */
+
+  T (bx[-99].ax[-9].b4 + 0);  /* { dg-warning "array subscript -9 is below array bounds" } */
+  T (bx[-99].ax[-1].b4 + 0);  /* { dg-warning "array subscript -1 is below array bounds" } */
+  T (bx[-99].ax[11].b4 + 0);
+  T (bx[ -1].ax[22].b4 + 0);
+  T (bx[  0].ax[33].b4 + 1);
+  T (bx[  1].ax[44].b4 + 2);
+  T (bx[  2].ax[55].b4 + 3);
+  T (bx[ 99].ax[99].b4 + 5);   /* { dg-warning "offset 5 is out of bounds of .char\\\[4]." } */
+}
+
+struct Cx { struct B b2_3[2][3]; struct B bx_3[][3]; };
+
+void test_memarray_3 (struct Cx *cx)
+{
+  STATIC_ASSERT (sizeof cx->b2_3 == sizeof (struct B[3]));
+  STATIC_ASSERT (sizeof cx->b2_3 == 42);
+
+  T (cx->b2_3 - 1);             /* { dg-warning "offset -42 is out of bounds of .struct B\\\[2]\\\[3]." } */
+  T (cx->b2_3 + 0);
+  T (cx->b2_3 + 1);
+  T (cx->b2_3 + 2);
+  T (cx->b2_3 + 3);             /* { dg-warning "offset 126 is out of bounds of .struct B\\\[2]\\\[3]." } */
+}
+
+struct Memarray
+{
+  struct __attribute__ ((packed))
+  S2 { char a, b; }
+    s, *ps, a2[2], ax[];
+};
+
+void test_int_memarray (struct Memarray *p)
+{
+  T (&p->s);
+  T (&p->s + 0);
+  T (&p->s + 1);
+  T (&p->s - 1);                /* { dg-warning "offset -2 is out of bounds of .struct S2\\\[1]." } */
+  T (&p->s + 2);                /* { dg-warning "offset 4 is out of bounds of .struct S2\\\[1]." } */
+
+  T (&(&p->s)[0]);
+  T (&(&p->s)[1]);
+  T (&(&p->s)[-1]);             /* { dg-warning "offset -2 is out of bounds of .struct S2\\\[1]." } */
+  T (&(&p->s)[2]);              /* { dg-warning "offset 4 is out of bounds of .struct S2\\\[1]." } */
+
+  T (p->ps - 99);
+  T (p->ps -  1);
+  T (p->ps);
+  T (p->ps +  0);
+  T (p->ps +  1);
+  T (p->ps + 99);
+
+  T (&p->ps - 1);                 /* { dg-warning "offset -\[48\] is out of bounds of .struct S2\*." } */
+  T (&p->ps);
+  T (&p->ps + 1);
+  T (&p->ps + 2);                 /* { dg-warning "offset \(8|16\) is out of bounds of .struct S2\*." } */
+
+  T (&p->a2);
+  T (&p->a2 + 0);
+  T (&p->a2 + 1);
+  T (&p->a2 - 1);                /* { dg-warning "offset -4 is out of bounds of .struct S2\\\[2]." } */
+  T (&p->a2 + 2);                /* { dg-warning "offset 8 is out of bounds of .struct S2\\\[2]." } */
+  T (&p->a2 + 3);                /* { dg-warning "offset 12 is out of bounds of .struct S2\\\[2]." } */
+
+  T (&p->a2[0] + 0);
+  T (&p->a2[0] + 1);
+  T (&p->a2[0] + 2);
+  T (&p->a2[0] + 3);             /* { dg-warning "offset 6 is out of bounds of .struct S2\\\[2]." } */
+
+  T (&p->a2[1] + 0);
+  T (&p->a2[1] + 1);
+  T (&p->a2[1] + 2);             /* { dg-warning "offset 6 is out of bounds of .struct S2\\\[2]." } */
+  T (&p->a2[1] + 3);             /* { dg-warning "offset 8 is out of bounds of .struct S2\\\[2]." } */
+
+  T (&p->a2[2] + 0);
+  T (&p->a2[2] + 1);             /* { dg-warning "offset 6 is out of bounds of .struct S2\\\[2]." } */
+  T (&p->a2[2] + 2);             /* { dg-warning "offset 8 is out of bounds of .struct S2\\\[2]." } */
+  T (&p->a2[2] + 3);             /* { dg-warning "offset 10 is out of bounds of .struct S2\\\[2]." } */
+}

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

end of thread, other threads:[~2017-11-15  1:06 UTC | newest]

Thread overview: 22+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-10-29 16:15 [PATCH] enhance -Warray-bounds to detect out-of-bounds offsets (PR 82455) Martin Sebor
2017-10-30 11:55 ` Richard Biener
2017-10-30 15:21   ` Martin Sebor
2017-10-30 19:59     ` Richard Biener
2017-10-30 20:40       ` Martin Sebor
2017-10-30 21:23         ` Richard Biener
2017-10-30 21:49           ` Martin Sebor
2017-11-02 11:48             ` Richard Biener
2017-11-10  1:12               ` Jeff Law
2017-11-10  8:25                 ` Richard Biener
2017-11-14  0:04                   ` Jeff Law
2017-11-14  9:11                     ` Richard Biener
2017-11-15  1:52                       ` Jeff Law
2017-11-14  5:22                   ` Martin Sebor
2017-11-14  9:13                     ` Richard Biener
2017-11-15  1:54                     ` Jeff Law
2017-10-30 22:16     ` Jeff Law
2017-10-30 23:30       ` Martin Sebor
2017-10-31  4:32         ` Jeff Law
2017-11-01 22:21           ` Martin Sebor
2017-11-02 11:27           ` Richard Biener
2017-10-30 22:16 ` Jeff Law

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