Current array bound checker only instruments ARRAY_REF, and the INDEX
information is the 2nd operand of the ARRAY_REF.

When extending the array bound checker to pointer references with
counted_by attributes, the hardest part is to get the INDEX of the
corresponding array ref from the offset computation expression of
the pointer ref.  I.e.

Given an OFFSET expression, and the ELEMENT_SIZE,
get the index expression from the OFFSET.
For example:
  OFFSET:
   ((long unsigned int) m * (long unsigned int) SAVE_EXPR <n>) * 4
  ELEMENT_SIZE:
   (sizetype) SAVE_EXPR <n> * 4
get the index as (long unsigned int) m.

gcc/c-family/ChangeLog:

        * c-gimplify.cc (ubsan_walk_array_refs_r): Instrument INDIRECT_REF
        with .ACCESS_WITH_SIZE in its address computation.
        * c-ubsan.cc (ubsan_instrument_bounds): Format change.
        (ubsan_instrument_bounds_pointer): New function.
        (get_factors_from_mul_expr): New function.
        (get_index_from_offset): New function.
        (get_index_from_pointer_addr_expr): New function.
        (is_instrumentable_pointer_array): New function.
        (ubsan_array_ref_instrumented_p): Handle INDIRECT_REF.
        (ubsan_maybe_instrument_array_ref): Handle INDIRECT_REF.

gcc/testsuite/ChangeLog:

        * gcc.dg/ubsan/pointer-counted-by-bounds-2.c: New test.
        * gcc.dg/ubsan/pointer-counted-by-bounds-3.c: New test.
        * gcc.dg/ubsan/pointer-counted-by-bounds-4.c: New test.
        * gcc.dg/ubsan/pointer-counted-by-bounds-5.c: New test.
        * gcc.dg/ubsan/pointer-counted-by-bounds-6.c: New test.
        * gcc.dg/ubsan/pointer-counted-by-bounds.c: New test.
---
 gcc/c-family/c-gimplify.cc                    |   7 +
 gcc/c-family/c-ubsan.cc                       | 264 ++++++++++++++++--
 .../ubsan/pointer-counted-by-bounds-2.c       |  47 ++++
 .../ubsan/pointer-counted-by-bounds-3.c       |  35 +++
 .../ubsan/pointer-counted-by-bounds-4.c       |  35 +++
 .../ubsan/pointer-counted-by-bounds-5.c       |  46 +++
 .../ubsan/pointer-counted-by-bounds-6.c       |  33 +++
 .../gcc.dg/ubsan/pointer-counted-by-bounds.c  |  46 +++
 8 files changed, 496 insertions(+), 17 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/ubsan/pointer-counted-by-bounds-2.c
 create mode 100644 gcc/testsuite/gcc.dg/ubsan/pointer-counted-by-bounds-3.c
 create mode 100644 gcc/testsuite/gcc.dg/ubsan/pointer-counted-by-bounds-4.c
 create mode 100644 gcc/testsuite/gcc.dg/ubsan/pointer-counted-by-bounds-5.c
 create mode 100644 gcc/testsuite/gcc.dg/ubsan/pointer-counted-by-bounds-6.c
 create mode 100644 gcc/testsuite/gcc.dg/ubsan/pointer-counted-by-bounds.c

diff --git a/gcc/c-family/c-gimplify.cc b/gcc/c-family/c-gimplify.cc
index 89a1f5c1e80..1ca54911249 100644
--- a/gcc/c-family/c-gimplify.cc
+++ b/gcc/c-family/c-gimplify.cc
@@ -120,6 +120,13 @@ ubsan_walk_array_refs_r (tree *tp, int *walk_subtrees, 
void *data)
       walk_tree (&TREE_OPERAND (*tp, 1), ubsan_walk_array_refs_r, pset, pset);
       walk_tree (&TREE_OPERAND (*tp, 0), ubsan_walk_array_refs_r, pset, pset);
     }
+  else if (TREE_CODE (*tp) == INDIRECT_REF
+          && TREE_CODE (TREE_OPERAND (*tp, 0)) == POINTER_PLUS_EXPR
+          && TREE_CODE (TREE_OPERAND (TREE_OPERAND (*tp, 0), 0))
+               == INDIRECT_REF)
+    if (is_access_with_size_p
+       (TREE_OPERAND (TREE_OPERAND (TREE_OPERAND (*tp, 0), 0), 0)))
+    ubsan_maybe_instrument_array_ref (tp, false);
   return NULL_TREE;
 }
 
diff --git a/gcc/c-family/c-ubsan.cc b/gcc/c-family/c-ubsan.cc
index 78b78685469..21fb0e312f7 100644
--- a/gcc/c-family/c-ubsan.cc
+++ b/gcc/c-family/c-ubsan.cc
@@ -420,7 +420,6 @@ get_bound_from_access_with_size (tree call)
   return size;
 }
 
-
 /* Instrument array bounds for ARRAY_REFs.  We create special builtin,
    that gets expanded in the sanopt pass, and make an array dimension
    of it.  ARRAY is the array, *INDEX is an index to the array.
@@ -450,8 +449,7 @@ ubsan_instrument_bounds (location_t loc, tree array, tree 
*index,
               && is_access_with_size_p ((TREE_OPERAND (array, 0))))
        {
          bound = get_bound_from_access_with_size ((TREE_OPERAND (array, 0)));
-         bound = fold_build2 (MINUS_EXPR, TREE_TYPE (bound),
-                              bound,
+         bound = fold_build2 (MINUS_EXPR, TREE_TYPE (bound), bound,
                               build_int_cst (TREE_TYPE (bound), 1));
        }
       else
@@ -554,38 +552,270 @@ ubsan_instrument_bounds (location_t loc, tree array, 
tree *index,
                                       *index, bound);
 }
 
-/* Return true iff T is an array that was instrumented by SANITIZE_BOUNDS.  */
+
+/* Instrument array bounds for the pointer array whose base address
+   is a call to .ACCESS_WITH_SIZE.  We create special builtin, that
+   gets expanded in the sanopt pass, and make an array dimension of
+   it.  POINTER is the pointer array's base address, *INDEX is an
+   index to the array.
+   Return NULL_TREE if no instrumentation is emitted.  */
+
+tree
+ubsan_instrument_bounds_pointer (location_t loc, tree pointer, tree *index)
+{
+  if (!is_access_with_size_p (TREE_OPERAND (pointer, 0)))
+    return NULL_TREE;
+  tree bound = get_bound_from_access_with_size (TREE_OPERAND (pointer, 0));
+
+  /* Don't emit instrumentation in the most common cases.  */
+  tree idx = NULL_TREE;
+  if (TREE_CODE (*index) == INTEGER_CST)
+    idx = *index;
+  else if (TREE_CODE (*index) == BIT_AND_EXPR
+          && TREE_CODE (TREE_OPERAND (*index, 1)) == INTEGER_CST)
+    idx = TREE_OPERAND (*index, 1);
+  if (idx
+      && TREE_CODE (bound) == INTEGER_CST
+      && tree_int_cst_sgn (idx) >= 0
+      && tree_int_cst_lt (idx, bound))
+    return NULL_TREE;
+
+  *index = save_expr (*index);
+
+  /* Create an array_type for the corresponding pointer array.  */
+  tree itype = build_range_type (sizetype, size_zero_node, NULL_TREE);
+  tree array_type = build_array_type (TREE_TYPE (TREE_TYPE (pointer)), itype);
+  /* Create a "(T *) 0" tree node to describe the array type.  */
+  tree zero_with_type = build_int_cst (build_pointer_type (array_type), 0);
+  return build_call_expr_internal_loc (loc, IFN_UBSAN_BOUNDS,
+                                      void_type_node, 3, zero_with_type,
+                                      *index, bound);
+}
+
+/* for a multiply expression like:
+    ((long unsigned int) m * (long unsigned int) SAVE_EXPR <n>) * 4
+
+   locate all the factors, and put each of them to VEC_FACTORS.  */
+
+static void
+get_factors_from_mul_expr (tree mult_expr, auto_vec<tree> *vec_factors)
+{
+  mult_expr = STRIP_NOPS (mult_expr);
+  if (TREE_CODE (mult_expr) != MULT_EXPR)
+    vec_factors->safe_push (mult_expr);
+  else
+    {
+      get_factors_from_mul_expr (TREE_OPERAND (mult_expr, 0), vec_factors);
+      get_factors_from_mul_expr (TREE_OPERAND (mult_expr, 1), vec_factors);
+    }
+}
+
+/* Given an OFFSET expression, and the ELEMENT_SIZE,
+   get the index expression from OFFSET and return it.
+   For example:
+   OFFSET:
+    ((long unsigned int) m * (long unsigned int) SAVE_EXPR <n>) * 4
+   ELEMENT_SIZE:
+    (sizetype) SAVE_EXPR <n> * 4
+   get the index as (long unsigned int) m, and return it.  */
+
+static tree
+get_index_from_offset (tree offset, tree element_size)
+{
+  if (TREE_CODE (offset) != MULT_EXPR)
+    return NULL_TREE;
+
+  auto_vec<tree> e_factors, o_factors;
+  get_factors_from_mul_expr (element_size, &e_factors);
+  get_factors_from_mul_expr (offset, &o_factors);
+
+  if (e_factors.is_empty () || o_factors.is_empty ())
+    return NULL_TREE;
+
+  bool all_found = true;
+  for (unsigned i = 0; i < e_factors.length (); i++)
+    {
+      tree e_size_factor = e_factors[i];
+      bool found = false;
+      for (unsigned j = 0; j < o_factors.length ();)
+       {
+         tree o_exp_factor = o_factors[j];
+         if (operand_equal_p (e_size_factor, o_exp_factor))
+           {
+             o_factors.unordered_remove (j);
+             found = true;
+             break;
+           }
+         else
+           j++;
+       }
+      if (!found)
+       all_found = false;
+    }
+
+  if (!all_found)
+    return NULL_TREE;
+
+  if (o_factors.length () != 1)
+    return NULL_TREE;
+
+  return o_factors[0];
+}
+
+/* For an pointer + offset computation expression, such as,
+   *.ACCESS_WITH_SIZE (p->c, &p->b, 1, 0, -1, 0B)
+    + (sizetype) ((long unsigned int) index * 4
+   Return the index of this pointer array reference,
+   set the parent tree of INDEX to *INDEX_P.
+   set the operand position of the INDEX in the parent tree to *INDEX_N.
+   If failed, return NULL_TREE.  */
+
+static tree
+get_index_from_pointer_addr_expr (tree pointer, tree *index_p, int *index_n)
+{
+  *index_p = NULL_TREE;
+  *index_n = -1;
+  if (TREE_CODE (TREE_OPERAND (pointer, 0)) != INDIRECT_REF)
+    return NULL_TREE;
+  tree call = TREE_OPERAND (TREE_OPERAND (pointer, 0), 0);
+  if (!is_access_with_size_p (call))
+    return NULL_TREE;
+
+  tree offset = TREE_OPERAND (pointer, 1);
+
+  /* Get the pointee type of the call to .ACCESS_WITH_SIZE.
+     This should be the element type of the pointer array.  */
+  tree pointee_type = TREE_TYPE (TREE_TYPE (TREE_TYPE (call)));
+  tree pointee_size = TYPE_SIZE_UNIT (pointee_type);
+
+  while (CONVERT_EXPR_CODE_P (TREE_CODE (offset)))
+    offset = TREE_OPERAND (offset, 0);
+
+  if (TREE_CODE (offset) != MULT_EXPR)
+    return NULL_TREE;
+  tree index_exp = get_index_from_offset (offset, pointee_size);
+  if (!index_exp)
+    return NULL_TREE;
+
+  while (CONVERT_EXPR_CODE_P (TREE_CODE (index_exp)))
+    {
+      *index_p = index_exp;
+      *index_n = 0;
+      index_exp = TREE_OPERAND (index_exp, 0);
+    }
+
+  return index_exp;
+}
+
+/* Return TRUE when the EXPR is a pointer array that could be instrumented.
+   We only instrument an indirect ref similar as the following:
+      *(*.ACCESS_WITH_SIZE (p->c, &p->b, 1, 0, -1, 0B)
+       + (sizetype) ((long unsigned int) index * 4))
+   if the EXPR is instrumentable, return TRUE and
+   set the index to *INDEX.
+   set the *.ACCESS_WITH_SIZE to *BASE.
+   set the parent tree of INDEX to *INDEX_P.
+   set the operand position of the INDEX in the parent tree to INDEX_N.  */
+
+static bool
+is_instrumentable_pointer_array (const_tree expr, tree *base, tree *index,
+                                tree *index_p, int *index_n)
+{
+  if (TREE_CODE (expr) != INDIRECT_REF)
+    return false;
+
+  /* For an indirect ref as:
+       *(*.ACCESS_WITH_SIZE (p->c, &p->b, 1, 0, -1, 0B)
+         + (sizetype) ((long unsigned int) index * 4))
+     op0 is the call to *.ACCESS_WITH_SIZE;
+     op1 is the index.  */
+  tree pointer = TREE_OPERAND (expr, 0);
+  if (TREE_CODE (pointer) != POINTER_PLUS_EXPR)
+    return false;
+  tree op0 = TREE_OPERAND (pointer, 0);
+  if (TREE_CODE (op0) != INDIRECT_REF)
+    return false;
+  if (!is_access_with_size_p (TREE_OPERAND (op0, 0)))
+    return false;
+  tree op1 = get_index_from_pointer_addr_expr (pointer, index_p, index_n);
+  if (op1 != NULL_TREE)
+    {
+      *base = op0;
+      *index = op1;
+      return true;
+    }
+  return false;
+}
+
+/* Return true iff T is an array or an indirect reference that was
+   instrumented by SANITIZE_BOUNDS.  */
 
 bool
 ubsan_array_ref_instrumented_p (const_tree t)
 {
-  if (TREE_CODE (t) != ARRAY_REF)
+  if (TREE_CODE (t) != ARRAY_REF
+      && TREE_CODE (t) != MEM_REF)
     return false;
 
-  tree op1 = TREE_OPERAND (t, 1);
-  return TREE_CODE (op1) == COMPOUND_EXPR
-        && TREE_CODE (TREE_OPERAND (op1, 0)) == CALL_EXPR
-        && CALL_EXPR_FN (TREE_OPERAND (op1, 0)) == NULL_TREE
-        && CALL_EXPR_IFN (TREE_OPERAND (op1, 0)) == IFN_UBSAN_BOUNDS;
+  bool is_array = (TREE_CODE (t) == ARRAY_REF);
+  tree op0 = NULL_TREE;
+  tree op1 = NULL_TREE;
+  tree index_p = NULL_TREE;
+  int index_n = 0;
+  if (is_array)
+    {
+      op1 = TREE_OPERAND (t, 1);
+      return TREE_CODE (op1) == COMPOUND_EXPR
+            && TREE_CODE (TREE_OPERAND (op1, 0)) == CALL_EXPR
+            && CALL_EXPR_FN (TREE_OPERAND (op1, 0)) == NULL_TREE
+            && CALL_EXPR_IFN (TREE_OPERAND (op1, 0)) == IFN_UBSAN_BOUNDS;
+    }
+  else if (is_instrumentable_pointer_array (t, &op0, &op1, &index_p, &index_n))
+    {
+      return TREE_CODE (op1) == COMPOUND_EXPR
+            && TREE_CODE (TREE_OPERAND (op1, 0)) == CALL_EXPR
+            && CALL_EXPR_FN (TREE_OPERAND (op1, 0)) == NULL_TREE
+            && CALL_EXPR_IFN (TREE_OPERAND (op1, 0)) == IFN_UBSAN_BOUNDS;
+
+    }
+  return false;
 }
 
-/* Instrument an ARRAY_REF, if it hasn't already been instrumented.
+/* Instrument an ARRAY_REF or an INDRECT_REF whose first argument is a call
+   to .ACCESS_WITH_SIZE, if it hasn't already been instrumented.
    IGNORE_OFF_BY_ONE is true if the ARRAY_REF is inside a ADDR_EXPR.  */
 
 void
 ubsan_maybe_instrument_array_ref (tree *expr_p, bool ignore_off_by_one)
 {
+  tree e = NULL_TREE;
+  tree op0 = NULL_TREE;
+  tree op1 = NULL_TREE;
+  tree index_p = NULL_TREE;  /* the parent tree of INDEX.  */
+  int index_n = 0;  /* the operand position of INDEX in the parent tree.  */
+
   if (!ubsan_array_ref_instrumented_p (*expr_p)
       && sanitize_flags_p (SANITIZE_BOUNDS | SANITIZE_BOUNDS_STRICT)
       && current_function_decl != NULL_TREE)
     {
-      tree op0 = TREE_OPERAND (*expr_p, 0);
-      tree op1 = TREE_OPERAND (*expr_p, 1);
-      tree e = ubsan_instrument_bounds (EXPR_LOCATION (*expr_p), op0, &op1,
-                                       ignore_off_by_one);
+      if (TREE_CODE (*expr_p) == ARRAY_REF)
+       {
+         op0 = TREE_OPERAND (*expr_p, 0);
+         op1 = TREE_OPERAND (*expr_p, 1);
+         index_p = *expr_p;
+         index_n = 1;
+         e = ubsan_instrument_bounds (EXPR_LOCATION (*expr_p), op0,
+                                      &op1, ignore_off_by_one);
+       }
+      else if (is_instrumentable_pointer_array (*expr_p, &op0, &op1,
+                                               &index_p, &index_n))
+       e = ubsan_instrument_bounds_pointer (EXPR_LOCATION (*expr_p),
+                                            op0, &op1);
+
+      /* Replace the original INDEX with the instrumented INDEX.  */
       if (e != NULL_TREE)
-       TREE_OPERAND (*expr_p, 1) = build2 (COMPOUND_EXPR, TREE_TYPE (op1),
-                                           e, op1);
+       TREE_OPERAND (index_p, index_n)
+         = build2 (COMPOUND_EXPR, TREE_TYPE (op1), e, op1);
     }
 }
 
diff --git a/gcc/testsuite/gcc.dg/ubsan/pointer-counted-by-bounds-2.c 
b/gcc/testsuite/gcc.dg/ubsan/pointer-counted-by-bounds-2.c
new file mode 100644
index 00000000000..3b5aa993e37
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ubsan/pointer-counted-by-bounds-2.c
@@ -0,0 +1,47 @@
+/* Test the attribute counted_by for pointer fields and its usage in
+   bounds sanitizer combined with VLA.  */
+/* { dg-do run } */
+/* { dg-options "-fsanitize=bounds" } */
+/* { dg-output "index 11 out of bounds for type 'int 
\\\[\\\*\\\]\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 20 out of bounds for type 'int 
\\\[\\\*\\\]\\\[\\\*\\\]\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 11 out of bounds for type 'int 
\\\[\\\*\\\]\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 10 out of bounds for type 'int 
\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+
+
+#include <stdlib.h>
+
+void __attribute__((__noinline__)) setup_and_test_vla (int n, int m)
+{
+   struct foo {
+       int n;
+       int (*p)[n] __attribute__((counted_by(n)));
+   } *f;
+
+   f = (struct foo *) malloc (sizeof (struct foo));
+   f->p = (int (*)[n]) malloc (m * sizeof (int[n]));
+   f->n = m;
+   f->p[m][n-1]=1;
+   return;
+}
+
+void __attribute__((__noinline__)) setup_and_test_vla_1 (int n1, int n2, int m)
+{
+  struct foo {
+    int n;
+    int (*p)[n2][n1] __attribute__((counted_by(n)));
+  } *f;
+
+  f = (struct foo *) malloc (sizeof(struct foo));
+  f->p = (int (*)[n2][n1]) malloc (m * sizeof (int[n2][n1]));
+  f->n = m;
+  f->p[m][n2][n1]=1;
+  return;
+}
+
+int main(int argc, char *argv[])
+{
+  setup_and_test_vla (10, 11);
+  setup_and_test_vla_1 (10, 11, 20);
+  return 0;
+}
+
diff --git a/gcc/testsuite/gcc.dg/ubsan/pointer-counted-by-bounds-3.c 
b/gcc/testsuite/gcc.dg/ubsan/pointer-counted-by-bounds-3.c
new file mode 100644
index 00000000000..80c40e26396
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ubsan/pointer-counted-by-bounds-3.c
@@ -0,0 +1,35 @@
+/* Test the attribute counted_by for pointer fields and its usage in bounds
+   sanitizer. when counted_by field is negative value.  */
+/* { dg-do run } */
+/* { dg-options "-fsanitize=bounds" } */
+
+#include <stdlib.h>
+
+struct annotated {
+  int b;
+  int *c __attribute__ ((counted_by (b)));
+} *array_annotated;
+
+void __attribute__((__noinline__)) setup (int annotated_count)
+{
+  array_annotated
+    = (struct annotated *)malloc (sizeof (struct annotated));
+  array_annotated->c = (int *) malloc (sizeof (int) * 10);
+  array_annotated->b = annotated_count;
+
+  return;
+}
+
+void __attribute__((__noinline__)) test (int annotated_index)
+{
+  array_annotated->c[annotated_index] = 2;
+}
+
+int main(int argc, char *argv[])
+{
+  setup (-3);   
+  test (2);
+  return 0;
+}
+
+/* { dg-output "25:21: runtime error: index 2 out of bounds for type" } */
diff --git a/gcc/testsuite/gcc.dg/ubsan/pointer-counted-by-bounds-4.c 
b/gcc/testsuite/gcc.dg/ubsan/pointer-counted-by-bounds-4.c
new file mode 100644
index 00000000000..f387be54c65
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ubsan/pointer-counted-by-bounds-4.c
@@ -0,0 +1,35 @@
+/* Test the attribute counted_by for pointer fields and its usage in bounds
+   sanitizer. when counted_by field is zero value.  */
+/* { dg-do run } */
+/* { dg-options "-fsanitize=bounds" } */
+
+#include <stdlib.h>
+
+struct annotated {
+  int b;
+  int *c __attribute__ ((counted_by (b)));
+} *array_annotated;
+
+void __attribute__((__noinline__)) setup (int annotated_count)
+{
+  array_annotated
+    = (struct annotated *)malloc (sizeof (struct annotated));
+  array_annotated->c = (int *)malloc (sizeof (int) * 10);
+  array_annotated->b = annotated_count;
+
+  return;
+}
+
+void __attribute__((__noinline__)) test (int annotated_index)
+{
+  array_annotated->c[annotated_index] = 2;
+}
+
+int main(int argc, char *argv[])
+{
+  setup (0);   
+  test (1);
+  return 0;
+}
+
+/* { dg-output "25:21: runtime error: index 1 out of bounds for type" } */
diff --git a/gcc/testsuite/gcc.dg/ubsan/pointer-counted-by-bounds-5.c 
b/gcc/testsuite/gcc.dg/ubsan/pointer-counted-by-bounds-5.c
new file mode 100644
index 00000000000..a54ff55d6ed
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ubsan/pointer-counted-by-bounds-5.c
@@ -0,0 +1,46 @@
+/* Test the attribute counted_by and its usage in
+ * __builtin_dynamic_object_size: when the type of the flexible array member
+ * is casting to another type.  */
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+
+typedef unsigned short u16;
+
+struct info {
+       u16 data_len;
+       char *data __attribute__((counted_by(data_len))); 
+};
+
+struct foo {
+       int a;
+       int b;
+};
+
+static __attribute__((__noinline__))
+struct info *setup ()
+{
+ struct info *p;
+ int bytes = 3 * sizeof(struct foo);
+
+ p = (struct info *)__builtin_malloc (sizeof (struct info));
+ p->data = (char *)__builtin_malloc (sizeof (char) * bytes);
+ p->data_len = bytes;
+
+ return p;
+}
+
+static void
+__attribute__((__noinline__)) report (struct info *p)
+{
+ struct foo *bar = (struct foo *)p->data;
+ bar[10].a = 10;
+ p->data[30] = 10;
+}
+
+int main(int argc, char *argv[])
+{
+ struct info *p = setup();
+ report(p);
+ return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/ubsan/pointer-counted-by-bounds-6.c 
b/gcc/testsuite/gcc.dg/ubsan/pointer-counted-by-bounds-6.c
new file mode 100644
index 00000000000..9885ac630dc
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ubsan/pointer-counted-by-bounds-6.c
@@ -0,0 +1,33 @@
+/* Test the attribute counted_by for pointer fields and its usage in
+   bounds sanitizer.  */
+/* { dg-do run } */
+/* { dg-options "-fsanitize=bounds" } */
+
+#include <stdlib.h>
+
+struct annotated {
+  int b;
+  int *c __attribute__ ((counted_by (b)));
+} *p_array_annotated;
+
+void __attribute__((__noinline__)) setup (int annotated_count)
+{
+  p_array_annotated
+    = (struct annotated *)malloc (sizeof (struct annotated));
+  p_array_annotated->c = (int *) malloc (annotated_count *  sizeof (int));
+  p_array_annotated->b = annotated_count;
+
+  return;
+}
+
+int main(int argc, char *argv[])
+{
+ int i;
+ setup (10);
+ for (i = 0; i < 11; i++)
+   p_array_annotated->c[i] = 2; // goes boom at i == 10
+ return 0;
+}
+
+
+/* { dg-output "28:24: runtime error: index 10 out of bounds for type" } */
diff --git a/gcc/testsuite/gcc.dg/ubsan/pointer-counted-by-bounds.c 
b/gcc/testsuite/gcc.dg/ubsan/pointer-counted-by-bounds.c
new file mode 100644
index 00000000000..72feae4468d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ubsan/pointer-counted-by-bounds.c
@@ -0,0 +1,46 @@
+/* Test the attribute counted_by for pointer fields and its usage in
+   bounds sanitizer.  */
+/* { dg-do run } */
+/* { dg-options "-fsanitize=bounds" } */
+
+#include <stdlib.h>
+
+struct pointer_array {
+  int b;
+  int *c;
+} *p_array;
+
+struct annotated {
+  int b;
+  int *c __attribute__ ((counted_by (b)));
+} *p_array_annotated;
+
+void __attribute__((__noinline__)) setup (int normal_count, int 
annotated_count)
+{
+  p_array
+    = (struct pointer_array *) malloc (sizeof (struct pointer_array));
+  p_array->c = (int *) malloc (normal_count *  sizeof (int));
+  p_array->b = normal_count;
+
+  p_array_annotated
+    = (struct annotated *)malloc (sizeof (struct annotated));
+  p_array_annotated->c = (int *) malloc (annotated_count *  sizeof (int));
+  p_array_annotated->b = annotated_count;
+
+  return;
+}
+
+void __attribute__((__noinline__)) test (int normal_index, int annotated_index)
+{
+  p_array->c[normal_index] = 1;
+  p_array_annotated->c[annotated_index] = 2;
+}
+
+int main(int argc, char *argv[])
+{
+  setup (10, 10);   
+  test (10, 10);
+  return 0;
+}
+
+/* { dg-output "36:23: runtime error: index 10 out of bounds for type" } */
-- 
2.31.1

Reply via email to