public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] Implement -fsanitize=bounds and internal calls in FEs
@ 2014-06-16 10:39 Marek Polacek
  2014-06-16 11:23 ` Jakub Jelinek
  0 siblings, 1 reply; 17+ messages in thread
From: Marek Polacek @ 2014-06-16 10:39 UTC (permalink / raw)
  To: GCC Patches
  Cc: Jakub Jelinek, Jason Merrill, Joseph S. Myers, Richard Biener, Jeff Law

The following is quite large patch for something rather simple as
bounds checking.  The idea is to instrument ARRAY_REFs in such a way
that iff the array index is greater than the max value of the TYPE_DOMAIN
of the ARRAY_TYPE, call __ubsan builtin and report error. 
My first attempts failed, however - e.g. if we add the instrumentation (the
conditional, not an internal call) in some early pass, VRP spuriously warns
about array bounds - it can't figure out that some path in the jump threaded
code never executes.  Another attempt was adding internal calls in the
.ubsan pass right after .ssa - same thing that we do for NULL sanitization -
and expand those in the .sanopt pass.
But this doesn't work either, the issue is with VLAs, where after
gimplification TYPE_MAX_VALUE of TYPE_DOMAIN is not usable - so this is
dead in the water too.

So next thing to do was to generate internal calls in the front ends;
we can use TYPE_MAX_VALUE of TYPE_DOMAIN therein.  But we don't have
the infrastructure for that, so I had to implement it.  To avoid
creating a new tree code, I'm reusing CALL_EXPR whose CALL_EXPR_FN
is NULL_TREE, but it has new "ifn" field in tree_base.u, which says
which internal function it is.  The .ifn field is accessed by new macro
CALL_EXPR_IFN.
Now everything seems to work, even though it might not be up to snuff
yet.

Jason/Joseph, could you please look at the C++/C FE parts?
Jakub/Richi, could you please look at the gimplification part?  I'm
pretty sure I missed something in there (LHS of internal calls?  
TREE_SIDE_EFFECTs?).
Pretty printing of internal calls is lame - maybe add a string param
to DEF_INTERNAL_FN?  I just wanted to make sure we don't ICE on it.

Regtested, bootstraped, "boostrap-ubsan-ed" on x86_64-linux.

2014-06-16  Marek Polacek  <polacek@redhat.com>

	* asan.c (pass_sanopt::execute): Handle IFN_UBSAN_BOUNDS.
	* flag-types.h (enum sanitize_code): Add SANITIZE_BOUNDS and or it
	into SANITIZE_UNDEFINED.
	* gimplify.c (gimplify_call_expr): Add gimplification of internal
	functions created in the FEs.
	* internal-fn.c: Move "internal-fn.h" after "tree.h".
	(expand_UBSAN_BOUNDS): New function.
	* internal-fn.def (UBSAN_BOUNDS): New internal function.
	* internal-fn.h: Don't define internal functions here.
	* opts.c (common_handle_option): Add -fsanitize=bounds.
	* sanitizer.def (BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS,
	BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS_ABORT): Add.
	* tree-core.h: Define internal functions here.
	(struct tree_base): Add ifn field.
	* tree-pretty-print.c (print_call_name): Handle functions without
	CALL_EXPR_FN.
	* tree.c (get_callee_fndecl): Likewise.
	(build_call_expr_internal_loc): New function.
	* tree.def (CALL_EXPR): Update description.
	* tree.h (CALL_EXPR_IFN): Define.
	(build_call_expr_internal_loc): Declare.
	* ubsan.c (get_ubsan_type_info_for_type): Return 0 for non-arithmetic
	types.
	(ubsan_type_descriptor): Change bool parameter to enum
	ubsan_print_style.  Adjust the code.  Add handling of
	UBSAN_PRINT_ARRAY.
	(ubsan_expand_bounds_btn): New function.
	(ubsan_expand_null_ifn): Adjust ubsan_type_descriptor call.
	(ubsan_build_overflow_builtin): Likewise.
	(instrument_bool_enum_load): Likewise.
	(ubsan_instrument_float_cast): Likewise.
	* ubsan.h (enum ubsan_print_style): New enum.
	(ubsan_expand_bounds_btn): Declare.
	(ubsan_type_descriptor): Adjust declaration.  Use a default parameter.
c-family/
	* c-common.c: Inlcude "c-ubsan.h".
	(c_fully_fold_internal): Instrument array bounds.
	* c-ubsan.c: Include "internal-fn.h".
	(ubsan_instrument_division): Mark instrumented arrays as having
	side effects.  Adjust ubsan_type_descriptor call.
	(ubsan_instrument_shift): Likewise.
	(ubsan_instrument_vla): Adjust ubsan_type_descriptor call.
	(ubsan_instrument_bounds): New function.
	* c-ubsan.h (ubsan_instrument_bounds): Declare.
cp/
	* cp-gimplify.c (cp_genericize_r): Instrument array bounds.
testsuite/
	* c-c++-common/ubsan/bounds-1.c: New test.
	* c-c++-common/ubsan/bounds-2.c: New test.
	* c-c++-common/ubsan/bounds-3.c: New test.

diff --git gcc/asan.c gcc/asan.c
index 651cfed..e0ca376 100644
--- gcc/asan.c
+++ gcc/asan.c
@@ -2708,6 +2708,9 @@ pass_sanopt::execute (function *fun)
 	      case IFN_UBSAN_NULL:
 		ubsan_expand_null_ifn (gsi);
 		break;
+	      case IFN_UBSAN_BOUNDS:
+		ubsan_expand_bounds_btn (&gsi);
+		break;
 	      default:
 		break;
 	      }
@@ -2718,6 +2721,10 @@ pass_sanopt::execute (function *fun)
 	      print_gimple_stmt (dump_file, stmt, 0, dump_flags);
 	      fprintf (dump_file, "\n");
 	    }
+
+	  /* ubsan_expand_bounds_btn might move us to the end of the BB.  */
+	  if (gsi_end_p (gsi))
+	    break;
 	}
     }
   return 0;
diff --git gcc/c-family/c-common.c gcc/c-family/c-common.c
index 077263e..ccb4971 100644
--- gcc/c-family/c-common.c
+++ gcc/c-family/c-common.c
@@ -34,6 +34,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "c-pragma.h"
 #include "c-common.h"
 #include "c-objc.h"
+#include "c-ubsan.h"
 #include "tm_p.h"
 #include "obstack.h"
 #include "cpplib.h"
@@ -1228,6 +1229,19 @@ c_fully_fold_internal (tree expr, bool in_init, bool *maybe_const_operands,
 				   maybe_const_itself);
       STRIP_TYPE_NOPS (op1);
       op1 = decl_constant_value_for_optimization (op1);
+
+      if (flag_sanitize & SANITIZE_BOUNDS
+	  && current_function_decl != 0
+	  && !lookup_attribute ("no_sanitize_undefined",
+			    DECL_ATTRIBUTES (current_function_decl)))
+	{
+	  op1 = c_save_expr (op1);
+	  op1 = c_fully_fold (op1, false, NULL);
+	  tree e = ubsan_instrument_bounds (loc, op0, op1);
+	  if (e != NULL_TREE)
+	    op1 = build2 (COMPOUND_EXPR, TREE_TYPE (op1), e, op1);
+	}
+
       if (op0 != orig_op0 || op1 != orig_op1)
 	ret = build4 (ARRAY_REF, TREE_TYPE (expr), op0, op1, op2, op3);
       if (ret != expr)
diff --git gcc/c-family/c-ubsan.c gcc/c-family/c-ubsan.c
index 8d5b685..65b0f2a 100644
--- gcc/c-family/c-ubsan.c
+++ gcc/c-family/c-ubsan.c
@@ -30,6 +30,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "c-family/c-common.h"
 #include "c-family/c-ubsan.h"
 #include "asan.h"
+#include "internal-fn.h"
 
 /* Instrument division by zero and INT_MIN / -1.  If not instrumenting,
    return NULL_TREE.  */
@@ -77,15 +78,27 @@ ubsan_instrument_division (location_t loc, tree op0, tree op1)
     return NULL_TREE;
 
   /* In case we have a SAVE_EXPR in a conditional context, we need to
-     make sure it gets evaluated before the condition.  */
+     make sure it gets evaluated before the condition.  If the OP0 is
+     an instrumented array reference, mark it as having side effects so
+     it's not folded away.  */
+  if (flag_sanitize & SANITIZE_BOUNDS)
+    {
+      tree xop0 = op0;
+      while (CONVERT_EXPR_P (xop0))
+	xop0 = TREE_OPERAND (xop0, 0);
+      if (TREE_CODE (xop0) == ARRAY_REF)
+	{
+	  TREE_SIDE_EFFECTS (xop0) = 1;
+	  TREE_SIDE_EFFECTS (op0) = 1;
+	}
+    }
   t = fold_build2 (COMPOUND_EXPR, TREE_TYPE (t), op0, t);
   if (flag_sanitize_undefined_trap_on_error)
     tt = build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0);
   else
     {
       tree data = ubsan_create_data ("__ubsan_overflow_data", &loc, NULL,
-				     ubsan_type_descriptor (type, false),
-				     NULL_TREE);
+				     ubsan_type_descriptor (type), NULL_TREE);
       data = build_fold_addr_expr_loc (loc, data);
       enum built_in_function bcode
 	= flag_sanitize_recover
@@ -154,7 +167,20 @@ ubsan_instrument_shift (location_t loc, enum tree_code code,
     return NULL_TREE;
 
   /* In case we have a SAVE_EXPR in a conditional context, we need to
-     make sure it gets evaluated before the condition.  */
+     make sure it gets evaluated before the condition.  If the OP0 is
+     an instrumented array reference, mark it as having side effects so
+     it's not folded away.  */
+  if (flag_sanitize & SANITIZE_BOUNDS)
+    {
+      tree xop0 = op0;
+      while (CONVERT_EXPR_P (xop0))
+	xop0 = TREE_OPERAND (xop0, 0);
+      if (TREE_CODE (xop0) == ARRAY_REF)
+	{
+	  TREE_SIDE_EFFECTS (xop0) = 1;
+	  TREE_SIDE_EFFECTS (op0) = 1;
+	}
+    }
   t = fold_build2 (COMPOUND_EXPR, TREE_TYPE (t), op0, t);
   t = fold_build2 (TRUTH_OR_EXPR, boolean_type_node, t,
 		   tt ? tt : integer_zero_node);
@@ -164,10 +190,8 @@ ubsan_instrument_shift (location_t loc, enum tree_code code,
   else
     {
       tree data = ubsan_create_data ("__ubsan_shift_data", &loc, NULL,
-				     ubsan_type_descriptor (type0, false),
-				     ubsan_type_descriptor (type1, false),
-				     NULL_TREE);
-
+				     ubsan_type_descriptor (type0),
+				     ubsan_type_descriptor (type1), NULL_TREE);
       data = build_fold_addr_expr_loc (loc, data);
 
       enum built_in_function bcode
@@ -197,8 +221,7 @@ ubsan_instrument_vla (location_t loc, tree size)
   else
     {
       tree data = ubsan_create_data ("__ubsan_vla_data", &loc, NULL,
-				     ubsan_type_descriptor (type, false),
-				     NULL_TREE);
+				     ubsan_type_descriptor (type), NULL_TREE);
       data = build_fold_addr_expr_loc (loc, data);
       enum built_in_function bcode
 	= flag_sanitize_recover
@@ -228,3 +251,42 @@ ubsan_instrument_return (location_t loc)
   tree t = builtin_decl_explicit (BUILT_IN_UBSAN_HANDLE_MISSING_RETURN);
   return build_call_expr_loc (loc, t, 1, build_fold_addr_expr_loc (loc, data));
 }
+
+/* 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.
+   Return NULL_TREE if no instrumentation is emitted.  */
+
+tree
+ubsan_instrument_bounds (location_t loc, tree array, tree index)
+{
+  tree type = TREE_TYPE (array);
+  tree domain = TYPE_DOMAIN (type);
+
+  if (domain == NULL_TREE)
+    return NULL_TREE;
+
+  tree bound = TYPE_MAX_VALUE (domain);
+
+  /* Detect flexible array members and suchlike.  */
+  if (TREE_CODE (array) == COMPONENT_REF
+      /* ISO C99 flexible array members don't have a size.  */
+      && (TYPE_SIZE (type) == NULL_TREE
+	  || integer_zerop (TYPE_SIZE_UNIT (type))
+	  || integer_zerop (bound)
+	  || integer_onep (bound)))
+  return NULL_TREE;
+
+  /* If the index and bound are constants, we can check early.  */
+  if (TREE_CODE (index) == INTEGER_CST
+      && TREE_CODE (bound) == INTEGER_CST
+      && tree_int_cst_le (index, bound)
+      && tree_int_cst_sgn (index) == 1)
+    return NULL_TREE;
+
+  /* Create a "(T *) 0" tree node to describe the array type.  */
+  tree zero_with_type = build_int_cst (build_pointer_type (type), 0);
+  return build_call_expr_internal_loc (loc, IFN_UBSAN_BOUNDS,
+				       void_type_node, 3, zero_with_type,
+				       index, bound);
+}
diff --git gcc/c-family/c-ubsan.h gcc/c-family/c-ubsan.h
index e504b90..9197d78 100644
--- gcc/c-family/c-ubsan.h
+++ gcc/c-family/c-ubsan.h
@@ -25,5 +25,6 @@ extern tree ubsan_instrument_division (location_t, tree, tree);
 extern tree ubsan_instrument_shift (location_t, enum tree_code, tree, tree);
 extern tree ubsan_instrument_vla (location_t, tree);
 extern tree ubsan_instrument_return (location_t);
+extern tree ubsan_instrument_bounds (location_t, tree, tree);
 
 #endif  /* GCC_C_UBSAN_H  */
diff --git gcc/cp/cp-gimplify.c gcc/cp/cp-gimplify.c
index 2798358..3207d87 100644
--- gcc/cp/cp-gimplify.c
+++ gcc/cp/cp-gimplify.c
@@ -1197,7 +1197,22 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
       if (*stmt_p == error_mark_node)
 	*stmt_p = size_one_node;
       return NULL;
-    }    
+    }
+  else if (flag_sanitize & SANITIZE_BOUNDS
+	   && TREE_CODE (stmt) == ARRAY_REF
+	   && !processing_template_decl
+	   && current_function_decl != 0
+	   && !lookup_attribute ("no_sanitize_undefined",
+				 DECL_ATTRIBUTES (current_function_decl)))
+    {
+      tree op0 = TREE_OPERAND (stmt, 0);
+      tree op1 = TREE_OPERAND (stmt, 1);
+      op1 = cp_save_expr (op1);
+      tree e = ubsan_instrument_bounds (input_location, op0, op1);
+      if (e != NULL_TREE)
+	TREE_OPERAND (stmt, 1) = build2 (COMPOUND_EXPR, TREE_TYPE (op1),
+					 e, op1);
+    }
 
   pointer_set_insert (p_set, *stmt_p);
 
diff --git gcc/flag-types.h gcc/flag-types.h
index ed00046..4b8e338 100644
--- gcc/flag-types.h
+++ gcc/flag-types.h
@@ -230,9 +230,11 @@ enum sanitize_code {
   SANITIZE_ENUM = 1 << 11,
   SANITIZE_FLOAT_DIVIDE = 1 << 12,
   SANITIZE_FLOAT_CAST = 1 << 13,
+  SANITIZE_BOUNDS = 1 << 14,
   SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE
 		       | SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN
-		       | SANITIZE_SI_OVERFLOW | SANITIZE_BOOL | SANITIZE_ENUM,
+		       | SANITIZE_SI_OVERFLOW | SANITIZE_BOOL | SANITIZE_ENUM
+		       | SANITIZE_BOUNDS,
   SANITIZE_NONDEFAULT = SANITIZE_FLOAT_DIVIDE | SANITIZE_FLOAT_CAST
 };
 
diff --git gcc/gimplify.c gcc/gimplify.c
index 10f8ac6..475e749 100644
--- gcc/gimplify.c
+++ gcc/gimplify.c
@@ -2245,6 +2245,24 @@ gimplify_call_expr (tree *expr_p, gimple_seq *pre_p, bool want_value)
   if (! EXPR_HAS_LOCATION (*expr_p))
     SET_EXPR_LOCATION (*expr_p, input_location);
 
+  /* Gimplify internal functions created in the FEs.  */
+  if (CALL_EXPR_FN (*expr_p) == NULL_TREE)
+    {
+      nargs = call_expr_nargs (*expr_p);
+      enum internal_fn ifn = CALL_EXPR_IFN (*expr_p);
+      auto_vec<tree> vargs (nargs);
+
+      for (i = 0; i < nargs; i++)
+	{
+	  gimplify_arg (&CALL_EXPR_ARG (*expr_p, i), pre_p,
+			EXPR_LOCATION (*expr_p));
+	  vargs.quick_push (CALL_EXPR_ARG (*expr_p, i));
+	}
+      gimple call = gimple_build_call_internal_vec (ifn, vargs);
+      gimplify_seq_add_stmt (pre_p, call);
+      return GS_ALL_DONE;
+    }
+
   /* This may be a call to a builtin function.
 
      Builtin function calls may be transformed into different
diff --git gcc/internal-fn.c gcc/internal-fn.c
index 68b2b66..78f59d6 100644
--- gcc/internal-fn.c
+++ gcc/internal-fn.c
@@ -20,8 +20,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
-#include "internal-fn.h"
 #include "tree.h"
+#include "internal-fn.h"
 #include "stor-layout.h"
 #include "expr.h"
 #include "optabs.h"
@@ -159,6 +159,14 @@ expand_UBSAN_NULL (gimple stmt ATTRIBUTE_UNUSED)
   gcc_unreachable ();
 }
 
+/* This should get expanded in the sanopt pass.  */
+
+static void
+expand_UBSAN_BOUNDS (gimple stmt ATTRIBUTE_UNUSED)
+{
+  gcc_unreachable ();
+}
+
 /* Add sub/add overflow checking to the statement STMT.
    CODE says whether the operation is +, or -.  */
 
diff --git gcc/internal-fn.def gcc/internal-fn.def
index 31dc4c9..f0766bc 100644
--- gcc/internal-fn.def
+++ gcc/internal-fn.def
@@ -48,6 +48,7 @@ DEF_INTERNAL_FN (MASK_LOAD, ECF_PURE | ECF_LEAF)
 DEF_INTERNAL_FN (MASK_STORE, ECF_LEAF)
 DEF_INTERNAL_FN (ANNOTATE,  ECF_CONST | ECF_LEAF | ECF_NOTHROW)
 DEF_INTERNAL_FN (UBSAN_NULL, ECF_LEAF | ECF_NOTHROW)
+DEF_INTERNAL_FN (UBSAN_BOUNDS, ECF_LEAF | ECF_NOTHROW)
 DEF_INTERNAL_FN (UBSAN_CHECK_ADD, ECF_CONST | ECF_LEAF | ECF_NOTHROW)
 DEF_INTERNAL_FN (UBSAN_CHECK_SUB, ECF_CONST | ECF_LEAF | ECF_NOTHROW)
 DEF_INTERNAL_FN (UBSAN_CHECK_MUL, ECF_CONST | ECF_LEAF | ECF_NOTHROW)
diff --git gcc/internal-fn.h gcc/internal-fn.h
index 9c3215f..2dcf44e 100644
--- gcc/internal-fn.h
+++ gcc/internal-fn.h
@@ -20,13 +20,6 @@ along with GCC; see the file COPYING3.  If not see
 #ifndef GCC_INTERNAL_FN_H
 #define GCC_INTERNAL_FN_H
 
-enum internal_fn {
-#define DEF_INTERNAL_FN(CODE, FLAGS) IFN_##CODE,
-#include "internal-fn.def"
-#undef DEF_INTERNAL_FN
-  IFN_LAST
-};
-
 /* Return the name of internal function FN.  The name is only meaningful
    for dumps; it has no linkage.  */
 
diff --git gcc/opts.c gcc/opts.c
index 2b1280a..79650da 100644
--- gcc/opts.c
+++ gcc/opts.c
@@ -1466,6 +1466,7 @@ common_handle_option (struct gcc_options *opts,
 		sizeof "float-divide-by-zero" - 1 },
 	      { "float-cast-overflow", SANITIZE_FLOAT_CAST,
 		sizeof "float-cast-overflow" - 1 },
+	      { "bounds", SANITIZE_BOUNDS, sizeof "bounds" - 1 },
 	      { NULL, 0, 0 }
 	    };
 	    const char *comma;
diff --git gcc/sanitizer.def gcc/sanitizer.def
index 4016fc5..aaa8846 100644
--- gcc/sanitizer.def
+++ gcc/sanitizer.def
@@ -385,3 +385,11 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW_ABORT,
 		      "__ubsan_handle_float_cast_overflow_abort",
 		      BT_FN_VOID_PTR_PTR,
 		      ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS,
+		      "__ubsan_handle_out_of_bounds",
+		      BT_FN_VOID_PTR_PTR,
+		      ATTR_COLD_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS_ABORT,
+		      "__ubsan_handle_out_of_bounds_abort",
+		      BT_FN_VOID_PTR_PTR,
+		      ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)
diff --git gcc/testsuite/c-c++-common/ubsan/bounds-1.c gcc/testsuite/c-c++-common/ubsan/bounds-1.c
index e69de29..aa192d3 100644
--- gcc/testsuite/c-c++-common/ubsan/bounds-1.c
+++ gcc/testsuite/c-c++-common/ubsan/bounds-1.c
@@ -0,0 +1,75 @@
+/* { dg-do run } */
+/* { dg-options "-fsanitize=bounds -fno-sanitize-recover -Wall" } */
+
+/* Don't fail on valid uses.  */
+
+struct S { int a[10]; };
+struct T { int l; int a[]; };
+struct U { int l; int a[0]; };
+
+__attribute__ ((noinline, noclone))
+void
+fn_p (int p)
+{
+}
+
+__attribute__ ((noinline, noclone))
+void
+fn_a (volatile int a[])
+{
+  /* This is not instrumented.  */
+  a[4] = 5;
+}
+
+__attribute__ ((noinline, noclone))
+int
+foo_i (int i)
+{
+  int a[5] = { };
+  int k = i ? a[i] : i;
+  return k;
+}
+
+int
+main (void)
+{
+  volatile int a[5];
+  a[4] = 1;
+  a[2] = a[3];
+  fn_p (a[4]);
+  fn_a (a);
+
+  int i = 4;
+  a[i] = 1;
+  a[2] = a[i];
+  fn_p (a[i]);
+  foo_i (i);
+
+  const int n = 5;
+  volatile int b[n];
+  b[4] = 1;
+  b[2] = b[3];
+  fn_p (b[4]);
+  fn_a (b);
+
+  volatile int c[n][n][n];
+  c[2][2][2] = 2;
+  i = c[4][4][4];
+
+  volatile struct S s;
+  s.a[9] = 1;
+  i = s.a[9];
+
+  /* Don't instrument flexible array members.  */
+  struct T *t = (struct T *) __builtin_malloc (sizeof (struct T) + 10);
+  t->a[1] = 1;
+
+  struct U *u = (struct U *) __builtin_malloc (sizeof (struct U) + 10);
+  u->a[1] = 1;
+
+  long int *d[10][5];
+  d[9][0] = (long int *) 0;
+  d[8][3] = d[9][0];
+
+  return 0;
+}
diff --git gcc/testsuite/c-c++-common/ubsan/bounds-2.c gcc/testsuite/c-c++-common/ubsan/bounds-2.c
index e69de29..95f77c2 100644
--- gcc/testsuite/c-c++-common/ubsan/bounds-2.c
+++ gcc/testsuite/c-c++-common/ubsan/bounds-2.c
@@ -0,0 +1,168 @@
+/* { dg-do run } */
+/* { dg-options "-fsanitize=bounds -Wall -Wextra -Wno-unused -Wno-array-bounds" } */
+
+/* Test runtime errors.  */
+
+struct S { int a[10]; };
+
+int
+foo_5 (void)
+{
+  return 5;
+}
+
+__attribute__ ((noinline, noclone))
+void
+fn_p (int p)
+{
+  (void) p;
+}
+
+static void __attribute__ ((noinline, noclone))
+fn1 (void)
+{
+  volatile int a[5];
+  a[5] = 1;
+  a[2] = a[5];
+}
+
+static void __attribute__ ((noinline, noclone))
+fn2 (void)
+{
+  volatile int a[5];
+  int i = 5;
+  int *p = &i;
+  a[*p] = 1;
+}
+
+static void __attribute__ ((noinline, noclone))
+fn3 (void)
+{
+  volatile int a[5];
+  fn_p (a[5]);
+}
+
+static void __attribute__ ((noinline, noclone))
+fn4 (void)
+{
+  volatile int a[5];
+  a[foo_5 ()] = 1;
+}
+
+static void __attribute__ ((noinline, noclone))
+fn5 (void)
+{
+  int i = 5;
+  volatile int a[i];
+  a[i] = 1;
+  a[2] = a[i];
+}
+
+static void __attribute__ ((noinline, noclone))
+fn6 (void)
+{
+  int i = 5;
+  volatile int a[i];
+  fn_p (a[i]);
+  a[foo_5 ()] = 1;
+}
+
+static void __attribute__ ((noinline, noclone))
+fn7 (void)
+{
+  int n = 5, i;
+  volatile int c[n][n][n];
+  c[5][2][2] = 2;
+  c[2][5][2] = 2;
+  c[2][2][5] = 2;
+  i = c[5][2][2];
+  i = c[2][5][2];
+  i = c[2][2][5];
+}
+
+static void __attribute__ ((noinline, noclone))
+fn8 (void)
+{
+  int i = 5;
+  volatile struct S s;
+  s.a[10] = 1;
+  i = s.a[10];
+}
+
+static void __attribute__ ((noinline, noclone))
+fn9 (void)
+{
+  long int *volatile d[10][5];
+  d[10][0] = 0;
+  d[8][3] = d[10][0];
+}
+
+static void __attribute__ ((noinline, noclone))
+fn10 (void)
+{
+  /* Beware of side-effects.  */
+  volatile int x = 10;
+  volatile int e[20];
+  e[x++] = 3;
+  if (x != 11)
+    __builtin_abort ();
+  e[x--] = 3;
+  if (x != 10)
+    __builtin_abort ();
+}
+
+static void __attribute__ ((noinline, noclone))
+fn11 (void)
+{
+  char ***volatile f[5];
+  f[5] = 0;
+  f[2] = f[5];
+}
+
+static void __attribute__ ((noinline, noclone))
+fn12 (int i)
+{
+  volatile int a[5] = { };
+  int k = i ? a[i] : i;
+}
+
+int
+main (void)
+{
+  fn1 ();
+  fn2 ();
+  fn3 ();
+  fn4 ();
+  fn5 ();
+  fn6 ();
+  fn7 ();
+  fn8 ();
+  fn9 ();
+  fn10 ();
+  fn11 ();
+  fn12 (5);
+  return 0;
+}
+
+/* { dg-output "index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]\\\[\\\*\\\]\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]\\\[\\\*\\\]\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 10 out of bounds for type 'int \\\[10\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 10 out of bounds for type 'int \\\[10\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 10 out of bounds for type 'long int \\\*\\\[10\\\]\\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 10 out of bounds for type 'long int \\\*\\\[10\\\]\\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'char \\\*\\\*\\\*\\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'char \\\*\\\*\\\*\\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
diff --git gcc/testsuite/c-c++-common/ubsan/bounds-3.c gcc/testsuite/c-c++-common/ubsan/bounds-3.c
index e69de29..fcf71a3 100644
--- gcc/testsuite/c-c++-common/ubsan/bounds-3.c
+++ gcc/testsuite/c-c++-common/ubsan/bounds-3.c
@@ -0,0 +1,23 @@
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=bounds -Wall -Wextra" } */
+
+/* Do not generate invalid diagnostics.  */
+
+extern const int a[10];
+extern int bar (int);
+void
+foo (int i, int j)
+{
+  bar (a[i] >> j);
+  bar ((unsigned long) a[i] >> j);
+  bar ((short int) (unsigned long) a[i] >> j);
+  bar (j >> a[i]);
+  bar (j >> (unsigned long) a[i]);
+  bar (j >> (short int) (unsigned long) a[i]);
+  bar (a[i] / j);
+  bar ((unsigned long) a[i] / j);
+  bar ((short int) (unsigned long) a[i] / j);
+  bar (j / a[i]);
+  bar (j / (unsigned long) a[i]);
+  bar (j / (short int) (unsigned long) a[i]);
+}
diff --git gcc/tree-core.h gcc/tree-core.h
index aa7498b..bf5540d 100644
--- gcc/tree-core.h
+++ gcc/tree-core.h
@@ -665,6 +665,13 @@ enum annot_expr_kind {
   annot_expr_kind_last
 };
 
+/* Internal functions.  */
+enum internal_fn {
+#define DEF_INTERNAL_FN(CODE, FLAGS) IFN_##CODE,
+#include "internal-fn.def"
+#undef DEF_INTERNAL_FN
+  IFN_LAST
+};
 
 /*---------------------------------------------------------------------------
                                 Type definitions
@@ -787,6 +794,9 @@ struct GTY(()) tree_base {
 
     /* SSA version number.  This field is only used with SSA_NAME.  */
     unsigned int version;
+
+    /* Internal function code.  */
+    ENUM_BITFIELD(internal_fn) ifn : 5;
   } GTY((skip(""))) u;
 };
 
diff --git gcc/tree-pretty-print.c gcc/tree-pretty-print.c
index c5b4aee..523dc09 100644
--- gcc/tree-pretty-print.c
+++ gcc/tree-pretty-print.c
@@ -3214,6 +3214,13 @@ print_call_name (pretty_printer *buffer, tree node, int flags)
 {
   tree op0 = node;
 
+  if (node == NULL_TREE)
+    {
+      /* TODO Print builtin name.  */
+      pp_string (buffer, "<internal function call>");
+      return;
+    }
+
   if (TREE_CODE (op0) == NON_LVALUE_EXPR)
     op0 = TREE_OPERAND (op0, 0);
 
diff --git gcc/tree.c gcc/tree.c
index 559e758..0317e4e 100644
--- gcc/tree.c
+++ gcc/tree.c
@@ -8995,6 +8995,10 @@ get_callee_fndecl (const_tree call)
      called.  */
   addr = CALL_EXPR_FN (call);
 
+  /* If there is no function, return early.  */
+  if (addr == NULL_TREE)
+    return NULL_TREE;
+
   STRIP_NOPS (addr);
 
   /* If this is a readonly function pointer, extract its initial value.  */
@@ -10622,6 +10626,27 @@ build_call_expr (tree fndecl, int n, ...)
   return build_call_expr_loc_array (UNKNOWN_LOCATION, fndecl, n, argarray);
 }
 
+/* Build internal call expression.  This is just like CALL_EXPR, except
+   its CALL_EXPR_FN is NULL.  It will get gimplified later into ordinary
+   internal function.  */
+
+tree
+build_call_expr_internal_loc (location_t loc, enum internal_fn ifn,
+			      tree type, int n, ...)
+{
+  va_list ap;
+  int i;
+
+  tree fn = build_call_1 (type, NULL_TREE, n);
+  va_start (ap, n);
+  for (i = 0; i < n; i++)
+    CALL_EXPR_ARG (fn, i) = va_arg (ap, tree);
+  va_end (ap);
+  SET_EXPR_LOCATION (fn, loc);
+  CALL_EXPR_IFN (fn) = ifn;
+  return fn;
+}
+
 /* Create a new constant string literal and return a char* pointer to it.
    The STRING_CST value is the LEN characters at STR.  */
 tree
diff --git gcc/tree.def gcc/tree.def
index 464c8c7..299aacc 100644
--- gcc/tree.def
+++ gcc/tree.def
@@ -574,7 +574,7 @@ DEFTREECODE (BIND_EXPR, "bind_expr", tcc_expression, 3)
 /* Function call.  CALL_EXPRs are represented by variably-sized expression
    nodes.  There are at least three fixed operands.  Operand 0 is an
    INTEGER_CST node containing the total operand count, the number of
-   arguments plus 3.  Operand 1 is the function, while operand 2 is
+   arguments plus 3.  Operand 1 is the function or NULL, while operand 2 is
    is static chain argument, or NULL.  The remaining operands are the
    arguments to the call.  */
 DEFTREECODE (CALL_EXPR, "call_expr", tcc_vl_exp, 3)
diff --git gcc/tree.h gcc/tree.h
index 4a29aa2..c6fbb7e 100644
--- gcc/tree.h
+++ gcc/tree.h
@@ -1134,12 +1134,12 @@ extern void protected_set_expr_location (tree, location_t);
 #define ASSERT_EXPR_VAR(NODE)	TREE_OPERAND (ASSERT_EXPR_CHECK (NODE), 0)
 #define ASSERT_EXPR_COND(NODE)	TREE_OPERAND (ASSERT_EXPR_CHECK (NODE), 1)
 
-/* CALL_EXPR accessors.
- */
+/* CALL_EXPR accessors.  */
 #define CALL_EXPR_FN(NODE) TREE_OPERAND (CALL_EXPR_CHECK (NODE), 1)
 #define CALL_EXPR_STATIC_CHAIN(NODE) TREE_OPERAND (CALL_EXPR_CHECK (NODE), 2)
 #define CALL_EXPR_ARG(NODE, I) TREE_OPERAND (CALL_EXPR_CHECK (NODE), (I) + 3)
 #define call_expr_nargs(NODE) (VL_EXP_OPERAND_LENGTH (NODE) - 3)
+#define CALL_EXPR_IFN(NODE) (CALL_EXPR_CHECK (NODE)->base.u.ifn)
 
 /* CALL_EXPR_ARGP returns a pointer to the argument vector for NODE.
    We can't use &CALL_EXPR_ARG (NODE, 0) because that will complain if
@@ -3626,6 +3626,8 @@ extern tree build_call_expr_loc_array (location_t, tree, int, tree *);
 extern tree build_call_expr_loc_vec (location_t, tree, vec<tree, va_gc> *);
 extern tree build_call_expr_loc (location_t, tree, int, ...);
 extern tree build_call_expr (tree, int, ...);
+extern tree build_call_expr_internal_loc (location_t, enum internal_fn,
+					  tree, int, ...);
 extern tree build_string_literal (int, const char *);
 
 /* Construct various nodes representing data types.  */
diff --git gcc/ubsan.c gcc/ubsan.c
index 5a8a447..0422cbb 100644
--- gcc/ubsan.c
+++ gcc/ubsan.c
@@ -271,23 +271,24 @@ get_ubsan_type_info_for_type (tree type)
   gcc_assert (TYPE_SIZE (type) && tree_fits_uhwi_p (TYPE_SIZE (type)));
   if (TREE_CODE (type) == REAL_TYPE)
     return tree_to_uhwi (TYPE_SIZE (type));
-  else
+  else if (INTEGRAL_TYPE_P (type))
     {
       int prec = exact_log2 (tree_to_uhwi (TYPE_SIZE (type)));
       gcc_assert (prec != -1);
       return (prec << 1) | !TYPE_UNSIGNED (type);
     }
+  else
+    return 0;
 }
 
 /* Helper routine that returns ADDR_EXPR of a VAR_DECL of a type
    descriptor.  It first looks into the hash table; if not found,
    create the VAR_DECL, put it into the hash table and return the
-   ADDR_EXPR of it.  TYPE describes a particular type.  WANT_POINTER_TYPE_P
-   means whether we are interested in the pointer type and not the pointer
-   itself.  */
+   ADDR_EXPR of it.  TYPE describes a particular type.  PSTYLE is
+   an enum controlling how we want to print the type.  */
 
 tree
-ubsan_type_descriptor (tree type, bool want_pointer_type_p)
+ubsan_type_descriptor (tree type, enum ubsan_print_style pstyle)
 {
   /* See through any typedefs.  */
   type = TYPE_MAIN_VARIANT (type);
@@ -308,7 +309,7 @@ ubsan_type_descriptor (tree type, bool want_pointer_type_p)
   unsigned short tkind, tinfo;
 
   /* Get the name of the type, or the name of the pointer type.  */
-  if (want_pointer_type_p)
+  if (pstyle == UBSAN_PRINT_POINTER)
     {
       gcc_assert (POINTER_TYPE_P (type));
       type2 = TREE_TYPE (type);
@@ -324,6 +325,12 @@ ubsan_type_descriptor (tree type, bool want_pointer_type_p)
   /* If an array, get its type.  */
   type2 = strip_array_types (type2);
 
+  if (pstyle == UBSAN_PRINT_ARRAY)
+    {
+      while (POINTER_TYPE_P (type2))
+        deref_depth++, type2 = TREE_TYPE (type2);
+    }
+
   if (TYPE_NAME (type2) != NULL)
     {
       if (TREE_CODE (TYPE_NAME (type2)) == IDENTIFIER_NODE)
@@ -338,7 +345,7 @@ ubsan_type_descriptor (tree type, bool want_pointer_type_p)
 
   /* Decorate the type name with '', '*', "struct", or "union".  */
   pretty_name = (char *) alloca (strlen (tname) + 16 + deref_depth);
-  if (want_pointer_type_p)
+  if (pstyle == UBSAN_PRINT_POINTER)
     {
       int pos = sprintf (pretty_name, "'%s%s%s%s%s%s%s",
 			 TYPE_VOLATILE (type2) ? "volatile " : "",
@@ -355,6 +362,33 @@ ubsan_type_descriptor (tree type, bool want_pointer_type_p)
       pretty_name[pos++] = '\'';
       pretty_name[pos] = '\0';
     }
+  else if (pstyle == UBSAN_PRINT_ARRAY)
+    {
+      /* Pretty print the array dimensions.  */
+      gcc_assert (TREE_CODE (type) == ARRAY_TYPE);
+      tree t = type;
+      int pos = sprintf (pretty_name, "'%s ", tname);
+      while (deref_depth-- > 0)
+        pretty_name[pos++] = '*';
+      while (TREE_CODE (t) == ARRAY_TYPE)
+	{
+	  pretty_name[pos++] = '[';
+	  tree dom = TYPE_DOMAIN (t);
+	  if (dom && TREE_CODE (TYPE_MAX_VALUE (dom)) == INTEGER_CST)
+	    pos += sprintf (&pretty_name[pos], HOST_WIDE_INT_PRINT_DEC,
+			    tree_to_shwi (TYPE_MAX_VALUE (dom)) + 1);
+	  else
+	    /* ??? We can't determine the variable name; print VLA unspec.  */
+	    pretty_name[pos++] = '*';
+	  pretty_name[pos++] = ']';
+	  t = TREE_TYPE (t);
+	}
+      pretty_name[pos++] = '\'';
+      pretty_name[pos] = '\0';
+
+     /* Save the tree with stripped types.  */
+     type = t;
+    }
   else
     sprintf (pretty_name, "'%s'", tname);
 
@@ -550,6 +584,69 @@ is_ubsan_builtin_p (tree t)
 		     "__builtin___ubsan_", 18) == 0;
 }
 
+/* Expand the UBSAN_BOUNDS special builtin function.  */
+
+void
+ubsan_expand_bounds_btn (gimple_stmt_iterator *gsi)
+{
+  gimple stmt = gsi_stmt (*gsi);
+  location_t loc = gimple_location (stmt);
+  gcc_assert (gimple_call_num_args (stmt) == 3);
+
+  /* Pick up the arguments of the UBSAN_BOUNDS call.  */
+  tree type = TREE_TYPE (TREE_TYPE (gimple_call_arg (stmt, 0)));
+  tree index = gimple_call_arg (stmt, 1);
+  tree orig_index_type = TREE_TYPE (index);
+  tree bound = gimple_call_arg (stmt, 2);
+
+  gimple_stmt_iterator gsi_orig = *gsi;
+
+  /* Create condition "if (index > bound)".  */
+  basic_block then_bb, fallthru_bb;
+  gimple_stmt_iterator cond_insert_point
+    = create_cond_insert_point (gsi, 0/*before_p*/, false, true,
+				&then_bb, &fallthru_bb);
+  index = fold_convert (TREE_TYPE (bound), index);
+  index = force_gimple_operand_gsi (&cond_insert_point, index,
+				    true/*simple_p*/, NULL_TREE,
+				    false/*before*/, GSI_NEW_STMT);
+  gimple g = gimple_build_cond (GT_EXPR, index, bound, NULL_TREE, NULL_TREE);
+  gimple_set_location (g, loc);
+  gsi_insert_after (&cond_insert_point, g, GSI_NEW_STMT);
+
+  /* Generate __ubsan_handle_out_of_bounds call.  */
+  *gsi = gsi_after_labels (then_bb);
+  if (flag_sanitize_undefined_trap_on_error)
+    g = gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP), 0);
+  else
+    {
+      tree data
+	= ubsan_create_data ("__ubsan_out_of_bounds_data", &loc, NULL,
+			     ubsan_type_descriptor (type, UBSAN_PRINT_ARRAY),
+			     ubsan_type_descriptor (orig_index_type),
+			     NULL_TREE);
+      data = build_fold_addr_expr_loc (loc, data);
+      enum built_in_function bcode
+	= flag_sanitize_recover
+	  ? BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS
+	  : BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS_ABORT;
+      tree fn = builtin_decl_explicit (bcode);
+      tree val = force_gimple_operand_gsi (gsi, ubsan_encode_value (index),
+					   true, NULL_TREE, true,
+					   GSI_SAME_STMT);
+      g = gimple_build_call (fn, 2, data, val);
+    }
+  gimple_set_location (g, loc);
+  gsi_insert_before (gsi, g, GSI_SAME_STMT);
+
+  /* Get rid of the UBSAN_BOUNDS call from the IR.  */
+  unlink_stmt_vdef (stmt);
+  gsi_remove (&gsi_orig, true);
+
+  /* Point GSI to next logical statement.  */
+  *gsi = gsi_start_bb (fallthru_bb);
+}
+
 /* Expand UBSAN_NULL internal call.  */
 
 void
@@ -609,9 +706,11 @@ ubsan_expand_null_ifn (gimple_stmt_iterator gsi)
       tree fn = builtin_decl_implicit (bcode);
       const struct ubsan_mismatch_data m
 	= { build_zero_cst (pointer_sized_int_node), ckind };
-      tree data = ubsan_create_data ("__ubsan_null_data", &loc, &m,
-				     ubsan_type_descriptor (TREE_TYPE (ptr),
-							    true), NULL_TREE);
+      tree data
+	= ubsan_create_data ("__ubsan_null_data", &loc, &m,
+			     ubsan_type_descriptor (TREE_TYPE (ptr),
+						    UBSAN_PRINT_POINTER),
+			     NULL_TREE);
       data = build_fold_addr_expr_loc (loc, data);
       g = gimple_build_call (fn, 2, data,
 			     build_zero_cst (pointer_sized_int_node));
@@ -689,8 +788,7 @@ ubsan_build_overflow_builtin (tree_code code, location_t loc, tree lhstype,
     return build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0);
 
   tree data = ubsan_create_data ("__ubsan_overflow_data", &loc, NULL,
-				 ubsan_type_descriptor (lhstype, false),
-				 NULL_TREE);
+				 ubsan_type_descriptor (lhstype), NULL_TREE);
   enum built_in_function fn_code;
 
   switch (code)
@@ -884,8 +982,7 @@ instrument_bool_enum_load (gimple_stmt_iterator *gsi)
   else
     {
       tree data = ubsan_create_data ("__ubsan_invalid_value_data", &loc, NULL,
-				     ubsan_type_descriptor (type, false),
-				     NULL_TREE);
+				     ubsan_type_descriptor (type), NULL_TREE);
       data = build_fold_addr_expr_loc (loc, data);
       enum built_in_function bcode
 	= flag_sanitize_recover
@@ -1005,10 +1102,8 @@ ubsan_instrument_float_cast (location_t loc, tree type, tree expr)
     {
       /* Create the __ubsan_handle_float_cast_overflow fn call.  */
       tree data = ubsan_create_data ("__ubsan_float_cast_overflow_data", NULL,
-				     NULL,
-				     ubsan_type_descriptor (expr_type, false),
-				     ubsan_type_descriptor (type, false),
-				     NULL_TREE);
+				     NULL, ubsan_type_descriptor (expr_type),
+				     ubsan_type_descriptor (type), NULL_TREE);
       enum built_in_function bcode
 	= flag_sanitize_recover
 	  ? BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW
diff --git gcc/ubsan.h gcc/ubsan.h
index b008419..2a24a92 100644
--- gcc/ubsan.h
+++ gcc/ubsan.h
@@ -30,17 +30,25 @@ enum ubsan_null_ckind {
   UBSAN_MEMBER_CALL
 };
 
+/* This controls how ubsan prints types.  Used in ubsan_type_descriptor.  */
+enum ubsan_print_style {
+  UBSAN_PRINT_NORMAL,
+  UBSAN_PRINT_POINTER,
+  UBSAN_PRINT_ARRAY
+};
+
 /* An extra data used by ubsan pointer checking.  */
 struct ubsan_mismatch_data {
   tree align;
   tree ckind;
 };
 
+extern void ubsan_expand_bounds_btn (gimple_stmt_iterator *);
 extern void ubsan_expand_null_ifn (gimple_stmt_iterator);
 extern tree ubsan_instrument_unreachable (location_t);
 extern tree ubsan_create_data (const char *, const location_t *,
 			       const struct ubsan_mismatch_data *, ...);
-extern tree ubsan_type_descriptor (tree, bool);
+extern tree ubsan_type_descriptor (tree, enum ubsan_print_style = UBSAN_PRINT_NORMAL);
 extern tree ubsan_encode_value (tree, bool = false);
 extern bool is_ubsan_builtin_p (tree);
 extern tree ubsan_build_overflow_builtin (tree_code, location_t, tree, tree, tree);

	Marek

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

* Re: [PATCH] Implement -fsanitize=bounds and internal calls in FEs
  2014-06-16 10:39 [PATCH] Implement -fsanitize=bounds and internal calls in FEs Marek Polacek
@ 2014-06-16 11:23 ` Jakub Jelinek
  2014-06-19 15:00   ` Marek Polacek
  0 siblings, 1 reply; 17+ messages in thread
From: Jakub Jelinek @ 2014-06-16 11:23 UTC (permalink / raw)
  To: Marek Polacek
  Cc: GCC Patches, Jason Merrill, Joseph S. Myers, Richard Biener, Jeff Law

On Mon, Jun 16, 2014 at 12:39:07PM +0200, Marek Polacek wrote:
> Jason/Joseph, could you please look at the C++/C FE parts?

As mentioned on IRC, you need to differentiate between taking address
and not taking address.

struct S { int a; int b; } s[4], *t;
int *a, *b, *c;
void *d;
int e[4][4];

void
foo ()
{
  t = &s[4];  // Should be fine
  a = &s[4].a; // Error
  b = &s[4].b; // Error
  d = &e[4];  // Should be fine
  c = &e[4][0]; // Error
}

So, supposedly when e.g. in cp_genericize_r, for ADDR_EXPR <ARRAY_REF>
allow off-by-one, for all other ARRAY_REFs (e.g. those not appearing
inside of ADDR_EXPR, or not directly inside of ADDR_EXPR, e.g. with
COMPONENT_REF or another ARRAY_REF in between) disallow off-by-one.

	Jakub

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

* Re: [PATCH] Implement -fsanitize=bounds and internal calls in FEs
  2014-06-16 11:23 ` Jakub Jelinek
@ 2014-06-19 15:00   ` Marek Polacek
  2014-06-19 16:24     ` Marek Polacek
                       ` (2 more replies)
  0 siblings, 3 replies; 17+ messages in thread
From: Marek Polacek @ 2014-06-19 15:00 UTC (permalink / raw)
  To: Jakub Jelinek
  Cc: GCC Patches, Jason Merrill, Joseph S. Myers, Richard Biener, Jeff Law

On Mon, Jun 16, 2014 at 01:23:04PM +0200, Jakub Jelinek wrote:
> On Mon, Jun 16, 2014 at 12:39:07PM +0200, Marek Polacek wrote:
> > Jason/Joseph, could you please look at the C++/C FE parts?
> 
> As mentioned on IRC, you need to differentiate between taking address
> and not taking address.
> 
> struct S { int a; int b; } s[4], *t;
> int *a, *b, *c;
> void *d;
> int e[4][4];
> 
> void
> foo ()
> {
>   t = &s[4];  // Should be fine
>   a = &s[4].a; // Error
>   b = &s[4].b; // Error
>   d = &e[4];  // Should be fine
>   c = &e[4][0]; // Error
> }
> 
> So, supposedly when e.g. in cp_genericize_r, for ADDR_EXPR <ARRAY_REF>
> allow off-by-one, for all other ARRAY_REFs (e.g. those not appearing
> inside of ADDR_EXPR, or not directly inside of ADDR_EXPR, e.g. with
> COMPONENT_REF or another ARRAY_REF in between) disallow off-by-one.

Should be fixed in this new patch.  Another change is in handling
flexible array member-like arrays, what I had in last patch was wrong.
I use the non-strict mode by default, which means flexible array
member-like arrays are not instrumented.   We could have e.g.
-fsanitize=bounds-strict option that would instrument even those.
(Regular FMAs are never instrumented.)

I moved the code that instruments array to c_genericize (works for
both C and C++).  Also I fixed an ICE (we shouldn't instrument
initializers of TREE_STATIC, otherwise internal calls from FE
survive up to expansion).

Oh, I see I forgot to update docs, I'll write something up in
another iteration.

Regtested/bootstrapped on x86_64-linux.  How does this look?

2014-06-19  Marek Polacek  <polacek@redhat.com>

	* asan.c (pass_sanopt::execute): Handle IFN_UBSAN_BOUNDS.
	* flag-types.h (enum sanitize_code): Add SANITIZE_BOUNDS and or it
	into SANITIZE_UNDEFINED.
	* gimplify.c (gimplify_call_expr): Add gimplification of internal
	functions created in the FEs.
	* internal-fn.c: Move "internal-fn.h" after "tree.h".
	(expand_UBSAN_BOUNDS): New function.
	* internal-fn.def (UBSAN_BOUNDS): New internal function.
	* internal-fn.h: Don't define internal functions here.
	* opts.c (common_handle_option): Add -fsanitize=bounds.
	* sanitizer.def (BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS,
	BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS_ABORT): Add.
	* tree-core.h: Define internal functions here.
	(struct tree_base): Add ifn field.
	* tree-pretty-print.c (print_call_name): Handle functions without
	CALL_EXPR_FN.
	* tree.c (get_callee_fndecl): Likewise.
	(build_call_expr_internal_loc): New function.
	* tree.def (CALL_EXPR): Update description.
	* tree.h (CALL_EXPR_IFN): Define.
	(build_call_expr_internal_loc): Declare.
	* ubsan.c (get_ubsan_type_info_for_type): Return 0 for non-arithmetic
	types.
	(ubsan_type_descriptor): Change bool parameter to enum
	ubsan_print_style.  Adjust the code.  Add handling of
	UBSAN_PRINT_ARRAY.
	(ubsan_expand_bounds_btn): New function.
	(ubsan_expand_null_ifn): Adjust ubsan_type_descriptor call.
	(ubsan_build_overflow_builtin): Likewise.
	(instrument_bool_enum_load): Likewise.
	(ubsan_instrument_float_cast): Likewise.
	* ubsan.h (enum ubsan_print_style): New enum.
	(ubsan_expand_bounds_btn): Declare.
	(ubsan_type_descriptor): Adjust declaration.  Use a default parameter.
c-family/
	* c-gimplify.c: Include "c-ubsan.h" and "pointer-set.h".
	(ubsan_walk_array_refs_r): New function.
	(c_genericize): Instrument array bounds.
	* c-ubsan.c: Include "internal-fn.h".
	(ubsan_instrument_division): Mark instrumented arrays as having
	side effects.  Adjust ubsan_type_descriptor call.
	(ubsan_instrument_shift): Likewise.
	(ubsan_instrument_vla): Adjust ubsan_type_descriptor call.
	(ubsan_instrument_bounds): New function.
	(ubsan_array_ref_instrumented_p): New function.
	(ubsan_maybe_instrument_array_ref): New function.
	* c-ubsan.h (ubsan_instrument_bounds): Declare.
	(ubsan_array_ref_instrumented_p): Declare.
	(ubsan_maybe_instrument_array_ref): Declare.
testsuite/
	* c-c++-common/ubsan/bounds-1.c: New test.
	* c-c++-common/ubsan/bounds-2.c: New test.
	* c-c++-common/ubsan/bounds-3.c: New test.
	* c-c++-common/ubsan/bounds-4.c: New test.
	* c-c++-common/ubsan/bounds-5.c: New test.
	* c-c++-common/ubsan/bounds-6.c: New test.

diff --git gcc/asan.c gcc/asan.c
index 281a795..5f5dcaa 100644
--- gcc/asan.c
+++ gcc/asan.c
@@ -2761,6 +2761,9 @@ pass_sanopt::execute (function *fun)
 	      case IFN_UBSAN_NULL:
 		ubsan_expand_null_ifn (gsi);
 		break;
+	      case IFN_UBSAN_BOUNDS:
+		ubsan_expand_bounds_btn (&gsi);
+		break;
 	      default:
 		break;
 	      }
@@ -2771,6 +2774,10 @@ pass_sanopt::execute (function *fun)
 	      print_gimple_stmt (dump_file, stmt, 0, dump_flags);
 	      fprintf (dump_file, "\n");
 	    }
+
+	  /* ubsan_expand_bounds_btn might move us to the end of the BB.  */
+	  if (gsi_end_p (gsi))
+	    break;
 	}
     }
   return 0;
diff --git gcc/c-family/c-gimplify.c gcc/c-family/c-gimplify.c
index 737be4d..4e1ff4b 100644
--- gcc/c-family/c-gimplify.c
+++ gcc/c-family/c-gimplify.c
@@ -45,6 +45,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "c-pretty-print.h"
 #include "cgraph.h"
 #include "cilk.h"
+#include "c-ubsan.h"
+#include "pointer-set.h"
 
 /*  The gimplification pass converts the language-dependent trees
     (ld-trees) emitted by the parser into language-independent trees
@@ -67,6 +69,35 @@ along with GCC; see the file COPYING3.  If not see
     walk back up, we check that they fit our constraints, and copy them
     into temporaries if not.  */
 
+/* Callback for c_genericize.  */
+
+static tree
+ubsan_walk_array_refs_r (tree *tp, int *walk_subtrees, void *data)
+{
+  struct pointer_set_t *pset = (struct pointer_set_t *) data;
+
+  if (TREE_CODE (*tp) == BIND_EXPR)
+    {
+      for (tree decl = BIND_EXPR_VARS (*tp); decl; decl = DECL_CHAIN (decl))
+	{
+	  if (TREE_STATIC (decl))
+	    {
+	      *walk_subtrees = 0;
+	      continue;
+	    }
+	  walk_tree (&DECL_INITIAL (decl), ubsan_walk_array_refs_r, NULL, pset);
+	  walk_tree (&DECL_SIZE (decl), ubsan_walk_array_refs_r, NULL, pset);
+	  walk_tree (&DECL_SIZE_UNIT (decl), ubsan_walk_array_refs_r, NULL, pset);
+	}
+    }
+  else if (TREE_CODE (*tp) == ADDR_EXPR
+	   && TREE_CODE (TREE_OPERAND (*tp, 0)) == ARRAY_REF)
+    ubsan_maybe_instrument_array_ref (&TREE_OPERAND (*tp, 0), true);
+  else if (TREE_CODE (*tp) == ARRAY_REF)
+    ubsan_maybe_instrument_array_ref (tp, false);
+  return NULL_TREE;
+}
+
 /* Gimplification of statement trees.  */
 
 /* Convert the tree representation of FNDECL from C frontend trees to
@@ -79,6 +110,14 @@ c_genericize (tree fndecl)
   int local_dump_flags;
   struct cgraph_node *cgn;
 
+  if (flag_sanitize & SANITIZE_BOUNDS)
+    {
+      struct pointer_set_t *pset = pointer_set_create ();
+      walk_tree (&DECL_SAVED_TREE (fndecl), ubsan_walk_array_refs_r, pset,
+		 pset);
+      pointer_set_destroy (pset);
+    }
+
   /* Dump the C-specific tree IR.  */
   dump_orig = dump_begin (TDI_original, &local_dump_flags);
   if (dump_orig)
@@ -207,16 +246,16 @@ c_gimplify_expr (tree *expr_p, gimple_seq *pre_p ATTRIBUTE_UNUSED,
 	  }
 	break;
       }
-      
+
     case CILK_SPAWN_STMT:
-      gcc_assert 
-	(fn_contains_cilk_spawn_p (cfun) 
+      gcc_assert
+	(fn_contains_cilk_spawn_p (cfun)
 	 && cilk_detect_spawn_and_unwrap (expr_p));
-      
+
       /* If errors are seen, then just process it as a CALL_EXPR.  */
       if (!seen_error ())
 	return (enum gimplify_status) gimplify_cilk_spawn (expr_p);
-      
+
     case MODIFY_EXPR:
     case INIT_EXPR:
     case CALL_EXPR:
diff --git gcc/c-family/c-ubsan.c gcc/c-family/c-ubsan.c
index 8d5b685..80b3dbe 100644
--- gcc/c-family/c-ubsan.c
+++ gcc/c-family/c-ubsan.c
@@ -30,6 +30,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "c-family/c-common.h"
 #include "c-family/c-ubsan.h"
 #include "asan.h"
+#include "internal-fn.h"
 
 /* Instrument division by zero and INT_MIN / -1.  If not instrumenting,
    return NULL_TREE.  */
@@ -77,15 +78,27 @@ ubsan_instrument_division (location_t loc, tree op0, tree op1)
     return NULL_TREE;
 
   /* In case we have a SAVE_EXPR in a conditional context, we need to
-     make sure it gets evaluated before the condition.  */
+     make sure it gets evaluated before the condition.  If the OP0 is
+     an instrumented array reference, mark it as having side effects so
+     it's not folded away.  */
+  if (flag_sanitize & SANITIZE_BOUNDS)
+    {
+      tree xop0 = op0;
+      while (CONVERT_EXPR_P (xop0))
+	xop0 = TREE_OPERAND (xop0, 0);
+      if (TREE_CODE (xop0) == ARRAY_REF)
+	{
+	  TREE_SIDE_EFFECTS (xop0) = 1;
+	  TREE_SIDE_EFFECTS (op0) = 1;
+	}
+    }
   t = fold_build2 (COMPOUND_EXPR, TREE_TYPE (t), op0, t);
   if (flag_sanitize_undefined_trap_on_error)
     tt = build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0);
   else
     {
       tree data = ubsan_create_data ("__ubsan_overflow_data", &loc, NULL,
-				     ubsan_type_descriptor (type, false),
-				     NULL_TREE);
+				     ubsan_type_descriptor (type), NULL_TREE);
       data = build_fold_addr_expr_loc (loc, data);
       enum built_in_function bcode
 	= flag_sanitize_recover
@@ -154,7 +167,20 @@ ubsan_instrument_shift (location_t loc, enum tree_code code,
     return NULL_TREE;
 
   /* In case we have a SAVE_EXPR in a conditional context, we need to
-     make sure it gets evaluated before the condition.  */
+     make sure it gets evaluated before the condition.  If the OP0 is
+     an instrumented array reference, mark it as having side effects so
+     it's not folded away.  */
+  if (flag_sanitize & SANITIZE_BOUNDS)
+    {
+      tree xop0 = op0;
+      while (CONVERT_EXPR_P (xop0))
+	xop0 = TREE_OPERAND (xop0, 0);
+      if (TREE_CODE (xop0) == ARRAY_REF)
+	{
+	  TREE_SIDE_EFFECTS (xop0) = 1;
+	  TREE_SIDE_EFFECTS (op0) = 1;
+	}
+    }
   t = fold_build2 (COMPOUND_EXPR, TREE_TYPE (t), op0, t);
   t = fold_build2 (TRUTH_OR_EXPR, boolean_type_node, t,
 		   tt ? tt : integer_zero_node);
@@ -164,10 +190,8 @@ ubsan_instrument_shift (location_t loc, enum tree_code code,
   else
     {
       tree data = ubsan_create_data ("__ubsan_shift_data", &loc, NULL,
-				     ubsan_type_descriptor (type0, false),
-				     ubsan_type_descriptor (type1, false),
-				     NULL_TREE);
-
+				     ubsan_type_descriptor (type0),
+				     ubsan_type_descriptor (type1), NULL_TREE);
       data = build_fold_addr_expr_loc (loc, data);
 
       enum built_in_function bcode
@@ -197,8 +221,7 @@ ubsan_instrument_vla (location_t loc, tree size)
   else
     {
       tree data = ubsan_create_data ("__ubsan_vla_data", &loc, NULL,
-				     ubsan_type_descriptor (type, false),
-				     NULL_TREE);
+				     ubsan_type_descriptor (type), NULL_TREE);
       data = build_fold_addr_expr_loc (loc, data);
       enum built_in_function bcode
 	= flag_sanitize_recover
@@ -228,3 +251,100 @@ ubsan_instrument_return (location_t loc)
   tree t = builtin_decl_explicit (BUILT_IN_UBSAN_HANDLE_MISSING_RETURN);
   return build_call_expr_loc (loc, t, 1, build_fold_addr_expr_loc (loc, data));
 }
+
+/* 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.
+   Return NULL_TREE if no instrumentation is emitted.
+   IGNORE_OFF_BY_ONE is true if the ARRAY_REF is inside a ADDR_EXPR.  */
+
+tree
+ubsan_instrument_bounds (location_t loc, tree array, tree *index,
+			 bool ignore_off_by_one)
+{
+  tree type = TREE_TYPE (array);
+  tree domain = TYPE_DOMAIN (type);
+
+  if (domain == NULL_TREE)
+    return NULL_TREE;
+
+  tree bound = TYPE_MAX_VALUE (domain);
+  if (ignore_off_by_one)
+    bound = fold_build2 (PLUS_EXPR, TREE_TYPE (bound), bound,
+			 build_int_cst (TREE_TYPE (bound), 1));
+
+  /* Detect flexible array members and suchlike.  */
+  tree base = get_base_address (array);
+  if (base && TREE_CODE (base) == INDIRECT_REF)
+    {
+      tree next = NULL_TREE;
+      tree cref = array;
+
+      /* Walk all structs.  */
+      while (TREE_CODE (cref) == COMPONENT_REF)
+	{
+	  if (TREE_CODE (TREE_TYPE (TREE_OPERAND (cref, 0))) == RECORD_TYPE)
+	    for (next = DECL_CHAIN (TREE_OPERAND (cref, 1));
+		 next && TREE_CODE (next) != FIELD_DECL;
+		 next = DECL_CHAIN (next))
+	      ;
+	  if (next)
+	    /* Not a last element.  Instrument it.  */
+	    break;
+	  /* Ok, this is the last field of the structure.  But the struct
+	     containing it must be the last field too, recursively.  */
+	  cref = TREE_OPERAND (cref, 0);
+	}
+      if (!next)
+	/* Don't instrument this FMA-like array in non-strict
+	   -fsanitize=bounds mode.  */
+        return NULL_TREE;
+    }
+
+  *index = save_expr (*index);
+  /* Create a "(T *) 0" tree node to describe the array type.  */
+  tree zero_with_type = build_int_cst (build_pointer_type (type), 0);
+  return build_call_expr_internal_loc (loc, IFN_UBSAN_BOUNDS,
+				       void_type_node, 3, zero_with_type,
+				       *index, bound);
+}
+
+/* Return true iff T is an array that was instrumented by SANITIZE_BOUNDS.  */
+
+bool
+ubsan_array_ref_instrumented_p (const_tree t)
+{
+  if (TREE_CODE (t) != ARRAY_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;
+}
+
+/* Instrument an ARRAY_REF, 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)
+{
+  if (!ubsan_array_ref_instrumented_p (*expr_p)
+      && current_function_decl != 0
+      && !lookup_attribute ("no_sanitize_undefined",
+			    DECL_ATTRIBUTES (current_function_decl)))
+    {
+      tree t = copy_node (*expr_p);
+      tree op0 = TREE_OPERAND (t, 0);
+      tree op1 = TREE_OPERAND (t, 1);
+      tree e = ubsan_instrument_bounds (EXPR_LOCATION (t), op0, &op1,
+					ignore_off_by_one);
+      if (e != NULL_TREE)
+	{
+	  TREE_OPERAND (t, 1) = build2 (COMPOUND_EXPR, TREE_TYPE (op1),
+					e, op1);
+	  *expr_p = t;
+	}
+    }
+}
diff --git gcc/c-family/c-ubsan.h gcc/c-family/c-ubsan.h
index e504b90..edf5bc6 100644
--- gcc/c-family/c-ubsan.h
+++ gcc/c-family/c-ubsan.h
@@ -25,5 +25,8 @@ extern tree ubsan_instrument_division (location_t, tree, tree);
 extern tree ubsan_instrument_shift (location_t, enum tree_code, tree, tree);
 extern tree ubsan_instrument_vla (location_t, tree);
 extern tree ubsan_instrument_return (location_t);
+extern tree ubsan_instrument_bounds (location_t, tree, tree *, bool);
+extern bool ubsan_array_ref_instrumented_p (const_tree);
+extern void ubsan_maybe_instrument_array_ref (tree *, bool);
 
 #endif  /* GCC_C_UBSAN_H  */
diff --git gcc/flag-types.h gcc/flag-types.h
index ed00046..4b8e338 100644
--- gcc/flag-types.h
+++ gcc/flag-types.h
@@ -230,9 +230,11 @@ enum sanitize_code {
   SANITIZE_ENUM = 1 << 11,
   SANITIZE_FLOAT_DIVIDE = 1 << 12,
   SANITIZE_FLOAT_CAST = 1 << 13,
+  SANITIZE_BOUNDS = 1 << 14,
   SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE
 		       | SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN
-		       | SANITIZE_SI_OVERFLOW | SANITIZE_BOOL | SANITIZE_ENUM,
+		       | SANITIZE_SI_OVERFLOW | SANITIZE_BOOL | SANITIZE_ENUM
+		       | SANITIZE_BOUNDS,
   SANITIZE_NONDEFAULT = SANITIZE_FLOAT_DIVIDE | SANITIZE_FLOAT_CAST
 };
 
diff --git gcc/gimplify.c gcc/gimplify.c
index 3dcb4af..338c5c0 100644
--- gcc/gimplify.c
+++ gcc/gimplify.c
@@ -2264,6 +2264,24 @@ gimplify_call_expr (tree *expr_p, gimple_seq *pre_p, bool want_value)
   if (! EXPR_HAS_LOCATION (*expr_p))
     SET_EXPR_LOCATION (*expr_p, input_location);
 
+  /* Gimplify internal functions created in the FEs.  */
+  if (CALL_EXPR_FN (*expr_p) == NULL_TREE)
+    {
+      nargs = call_expr_nargs (*expr_p);
+      enum internal_fn ifn = CALL_EXPR_IFN (*expr_p);
+      auto_vec<tree> vargs (nargs);
+
+      for (i = 0; i < nargs; i++)
+	{
+	  gimplify_arg (&CALL_EXPR_ARG (*expr_p, i), pre_p,
+			EXPR_LOCATION (*expr_p));
+	  vargs.quick_push (CALL_EXPR_ARG (*expr_p, i));
+	}
+      gimple call = gimple_build_call_internal_vec (ifn, vargs);
+      gimplify_seq_add_stmt (pre_p, call);
+      return GS_ALL_DONE;
+    }
+
   /* This may be a call to a builtin function.
 
      Builtin function calls may be transformed into different
diff --git gcc/internal-fn.c gcc/internal-fn.c
index 68b2b66..78f59d6 100644
--- gcc/internal-fn.c
+++ gcc/internal-fn.c
@@ -20,8 +20,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
-#include "internal-fn.h"
 #include "tree.h"
+#include "internal-fn.h"
 #include "stor-layout.h"
 #include "expr.h"
 #include "optabs.h"
@@ -159,6 +159,14 @@ expand_UBSAN_NULL (gimple stmt ATTRIBUTE_UNUSED)
   gcc_unreachable ();
 }
 
+/* This should get expanded in the sanopt pass.  */
+
+static void
+expand_UBSAN_BOUNDS (gimple stmt ATTRIBUTE_UNUSED)
+{
+  gcc_unreachable ();
+}
+
 /* Add sub/add overflow checking to the statement STMT.
    CODE says whether the operation is +, or -.  */
 
diff --git gcc/internal-fn.def gcc/internal-fn.def
index 31dc4c9..f0766bc 100644
--- gcc/internal-fn.def
+++ gcc/internal-fn.def
@@ -48,6 +48,7 @@ DEF_INTERNAL_FN (MASK_LOAD, ECF_PURE | ECF_LEAF)
 DEF_INTERNAL_FN (MASK_STORE, ECF_LEAF)
 DEF_INTERNAL_FN (ANNOTATE,  ECF_CONST | ECF_LEAF | ECF_NOTHROW)
 DEF_INTERNAL_FN (UBSAN_NULL, ECF_LEAF | ECF_NOTHROW)
+DEF_INTERNAL_FN (UBSAN_BOUNDS, ECF_LEAF | ECF_NOTHROW)
 DEF_INTERNAL_FN (UBSAN_CHECK_ADD, ECF_CONST | ECF_LEAF | ECF_NOTHROW)
 DEF_INTERNAL_FN (UBSAN_CHECK_SUB, ECF_CONST | ECF_LEAF | ECF_NOTHROW)
 DEF_INTERNAL_FN (UBSAN_CHECK_MUL, ECF_CONST | ECF_LEAF | ECF_NOTHROW)
diff --git gcc/internal-fn.h gcc/internal-fn.h
index 9c3215f..2dcf44e 100644
--- gcc/internal-fn.h
+++ gcc/internal-fn.h
@@ -20,13 +20,6 @@ along with GCC; see the file COPYING3.  If not see
 #ifndef GCC_INTERNAL_FN_H
 #define GCC_INTERNAL_FN_H
 
-enum internal_fn {
-#define DEF_INTERNAL_FN(CODE, FLAGS) IFN_##CODE,
-#include "internal-fn.def"
-#undef DEF_INTERNAL_FN
-  IFN_LAST
-};
-
 /* Return the name of internal function FN.  The name is only meaningful
    for dumps; it has no linkage.  */
 
diff --git gcc/opts.c gcc/opts.c
index 324d545..3ab06c6 100644
--- gcc/opts.c
+++ gcc/opts.c
@@ -1467,6 +1467,7 @@ common_handle_option (struct gcc_options *opts,
 		sizeof "float-divide-by-zero" - 1 },
 	      { "float-cast-overflow", SANITIZE_FLOAT_CAST,
 		sizeof "float-cast-overflow" - 1 },
+	      { "bounds", SANITIZE_BOUNDS, sizeof "bounds" - 1 },
 	      { NULL, 0, 0 }
 	    };
 	    const char *comma;
diff --git gcc/sanitizer.def gcc/sanitizer.def
index b4af164..1f5ef21 100644
--- gcc/sanitizer.def
+++ gcc/sanitizer.def
@@ -409,3 +409,11 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW_ABORT,
 		      "__ubsan_handle_float_cast_overflow_abort",
 		      BT_FN_VOID_PTR_PTR,
 		      ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS,
+		      "__ubsan_handle_out_of_bounds",
+		      BT_FN_VOID_PTR_PTR,
+		      ATTR_COLD_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS_ABORT,
+		      "__ubsan_handle_out_of_bounds_abort",
+		      BT_FN_VOID_PTR_PTR,
+		      ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)
diff --git gcc/testsuite/c-c++-common/ubsan/bounds-1.c gcc/testsuite/c-c++-common/ubsan/bounds-1.c
index e69de29..aa192d3 100644
--- gcc/testsuite/c-c++-common/ubsan/bounds-1.c
+++ gcc/testsuite/c-c++-common/ubsan/bounds-1.c
@@ -0,0 +1,75 @@
+/* { dg-do run } */
+/* { dg-options "-fsanitize=bounds -fno-sanitize-recover -Wall" } */
+
+/* Don't fail on valid uses.  */
+
+struct S { int a[10]; };
+struct T { int l; int a[]; };
+struct U { int l; int a[0]; };
+
+__attribute__ ((noinline, noclone))
+void
+fn_p (int p)
+{
+}
+
+__attribute__ ((noinline, noclone))
+void
+fn_a (volatile int a[])
+{
+  /* This is not instrumented.  */
+  a[4] = 5;
+}
+
+__attribute__ ((noinline, noclone))
+int
+foo_i (int i)
+{
+  int a[5] = { };
+  int k = i ? a[i] : i;
+  return k;
+}
+
+int
+main (void)
+{
+  volatile int a[5];
+  a[4] = 1;
+  a[2] = a[3];
+  fn_p (a[4]);
+  fn_a (a);
+
+  int i = 4;
+  a[i] = 1;
+  a[2] = a[i];
+  fn_p (a[i]);
+  foo_i (i);
+
+  const int n = 5;
+  volatile int b[n];
+  b[4] = 1;
+  b[2] = b[3];
+  fn_p (b[4]);
+  fn_a (b);
+
+  volatile int c[n][n][n];
+  c[2][2][2] = 2;
+  i = c[4][4][4];
+
+  volatile struct S s;
+  s.a[9] = 1;
+  i = s.a[9];
+
+  /* Don't instrument flexible array members.  */
+  struct T *t = (struct T *) __builtin_malloc (sizeof (struct T) + 10);
+  t->a[1] = 1;
+
+  struct U *u = (struct U *) __builtin_malloc (sizeof (struct U) + 10);
+  u->a[1] = 1;
+
+  long int *d[10][5];
+  d[9][0] = (long int *) 0;
+  d[8][3] = d[9][0];
+
+  return 0;
+}
diff --git gcc/testsuite/c-c++-common/ubsan/bounds-2.c gcc/testsuite/c-c++-common/ubsan/bounds-2.c
index e69de29..95f77c2 100644
--- gcc/testsuite/c-c++-common/ubsan/bounds-2.c
+++ gcc/testsuite/c-c++-common/ubsan/bounds-2.c
@@ -0,0 +1,168 @@
+/* { dg-do run } */
+/* { dg-options "-fsanitize=bounds -Wall -Wextra -Wno-unused -Wno-array-bounds" } */
+
+/* Test runtime errors.  */
+
+struct S { int a[10]; };
+
+int
+foo_5 (void)
+{
+  return 5;
+}
+
+__attribute__ ((noinline, noclone))
+void
+fn_p (int p)
+{
+  (void) p;
+}
+
+static void __attribute__ ((noinline, noclone))
+fn1 (void)
+{
+  volatile int a[5];
+  a[5] = 1;
+  a[2] = a[5];
+}
+
+static void __attribute__ ((noinline, noclone))
+fn2 (void)
+{
+  volatile int a[5];
+  int i = 5;
+  int *p = &i;
+  a[*p] = 1;
+}
+
+static void __attribute__ ((noinline, noclone))
+fn3 (void)
+{
+  volatile int a[5];
+  fn_p (a[5]);
+}
+
+static void __attribute__ ((noinline, noclone))
+fn4 (void)
+{
+  volatile int a[5];
+  a[foo_5 ()] = 1;
+}
+
+static void __attribute__ ((noinline, noclone))
+fn5 (void)
+{
+  int i = 5;
+  volatile int a[i];
+  a[i] = 1;
+  a[2] = a[i];
+}
+
+static void __attribute__ ((noinline, noclone))
+fn6 (void)
+{
+  int i = 5;
+  volatile int a[i];
+  fn_p (a[i]);
+  a[foo_5 ()] = 1;
+}
+
+static void __attribute__ ((noinline, noclone))
+fn7 (void)
+{
+  int n = 5, i;
+  volatile int c[n][n][n];
+  c[5][2][2] = 2;
+  c[2][5][2] = 2;
+  c[2][2][5] = 2;
+  i = c[5][2][2];
+  i = c[2][5][2];
+  i = c[2][2][5];
+}
+
+static void __attribute__ ((noinline, noclone))
+fn8 (void)
+{
+  int i = 5;
+  volatile struct S s;
+  s.a[10] = 1;
+  i = s.a[10];
+}
+
+static void __attribute__ ((noinline, noclone))
+fn9 (void)
+{
+  long int *volatile d[10][5];
+  d[10][0] = 0;
+  d[8][3] = d[10][0];
+}
+
+static void __attribute__ ((noinline, noclone))
+fn10 (void)
+{
+  /* Beware of side-effects.  */
+  volatile int x = 10;
+  volatile int e[20];
+  e[x++] = 3;
+  if (x != 11)
+    __builtin_abort ();
+  e[x--] = 3;
+  if (x != 10)
+    __builtin_abort ();
+}
+
+static void __attribute__ ((noinline, noclone))
+fn11 (void)
+{
+  char ***volatile f[5];
+  f[5] = 0;
+  f[2] = f[5];
+}
+
+static void __attribute__ ((noinline, noclone))
+fn12 (int i)
+{
+  volatile int a[5] = { };
+  int k = i ? a[i] : i;
+}
+
+int
+main (void)
+{
+  fn1 ();
+  fn2 ();
+  fn3 ();
+  fn4 ();
+  fn5 ();
+  fn6 ();
+  fn7 ();
+  fn8 ();
+  fn9 ();
+  fn10 ();
+  fn11 ();
+  fn12 (5);
+  return 0;
+}
+
+/* { dg-output "index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]\\\[\\\*\\\]\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]\\\[\\\*\\\]\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 10 out of bounds for type 'int \\\[10\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 10 out of bounds for type 'int \\\[10\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 10 out of bounds for type 'long int \\\*\\\[10\\\]\\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 10 out of bounds for type 'long int \\\*\\\[10\\\]\\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'char \\\*\\\*\\\*\\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'char \\\*\\\*\\\*\\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
diff --git gcc/testsuite/c-c++-common/ubsan/bounds-3.c gcc/testsuite/c-c++-common/ubsan/bounds-3.c
index e69de29..fcf71a3 100644
--- gcc/testsuite/c-c++-common/ubsan/bounds-3.c
+++ gcc/testsuite/c-c++-common/ubsan/bounds-3.c
@@ -0,0 +1,23 @@
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=bounds -Wall -Wextra" } */
+
+/* Do not generate invalid diagnostics.  */
+
+extern const int a[10];
+extern int bar (int);
+void
+foo (int i, int j)
+{
+  bar (a[i] >> j);
+  bar ((unsigned long) a[i] >> j);
+  bar ((short int) (unsigned long) a[i] >> j);
+  bar (j >> a[i]);
+  bar (j >> (unsigned long) a[i]);
+  bar (j >> (short int) (unsigned long) a[i]);
+  bar (a[i] / j);
+  bar ((unsigned long) a[i] / j);
+  bar ((short int) (unsigned long) a[i] / j);
+  bar (j / a[i]);
+  bar (j / (unsigned long) a[i]);
+  bar (j / (short int) (unsigned long) a[i]);
+}
diff --git gcc/testsuite/c-c++-common/ubsan/bounds-4.c gcc/testsuite/c-c++-common/ubsan/bounds-4.c
index e69de29..7748780 100644
--- gcc/testsuite/c-c++-common/ubsan/bounds-4.c
+++ gcc/testsuite/c-c++-common/ubsan/bounds-4.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=bounds -Wall -Wextra -Wno-unused" } */
+
+/* Initializers of TREE_STATICs aren't instrumented.
+   But don't ICE on 'em.  */
+
+int A[2];
+int *gp = &A[4];
+int *gpi;
+
+int
+main (void)
+{
+  gpi = &A[4];
+  static int *pi = &A[4];
+  return 0;
+}
diff --git gcc/testsuite/c-c++-common/ubsan/bounds-5.c gcc/testsuite/c-c++-common/ubsan/bounds-5.c
index e69de29..b5468d7 100644
--- gcc/testsuite/c-c++-common/ubsan/bounds-5.c
+++ gcc/testsuite/c-c++-common/ubsan/bounds-5.c
@@ -0,0 +1,88 @@
+/* { dg-do run } */
+/* { dg-options "-fsanitize=bounds -Wall -Wextra -Wno-unused -Wno-array-bounds" } */
+
+/* Test flexible array member-like arrays.  Normal FMAs are tested
+   in bounds-1.c.  Test non-strict mode.  */
+
+__attribute__ ((noinline, noclone))
+void
+fn1 (void)
+{
+  volatile struct S { char a[1]; char b; } s;
+  s.a[0] = 1; // OK
+  s.a[1] = 2; // error
+  volatile struct S *p = &s;
+  p->a[0] = 1; // OK
+  p->a[1] = 1; // error
+}
+
+__attribute__ ((noinline, noclone))
+void
+fn2 (void)
+{
+  struct S { int c; char d[4]; };
+  volatile struct T { int e; struct S f; int g; } t;
+  t.f.d[3] = 1; // OK
+  t.f.d[4] = 1; // error
+  volatile struct T *p = &t;
+  p->f.d[3] = 1; // OK
+  p->f.d[4] = 1; // error
+}
+
+__attribute__ ((noinline, noclone))
+void
+fn3 (void)
+{
+  volatile struct S { char b; char a[1]; } s;
+  s.a[0] = 1; // OK
+  s.a[1] = 1; // error
+  volatile struct S *p = &s;
+  p->a[0] = 1; // OK
+  p->a[1] = 1; // error in strict mode
+}
+
+__attribute__ ((noinline, noclone))
+void
+fn4 (void)
+{
+  volatile struct S { char b; char a[1]; } s;
+  volatile struct T { struct S s; int i; } t;
+  t.s.a[0] = 1; // OK
+  t.s.a[1] = 1; // error
+  volatile struct T *pt = &t;
+  pt->s.a[0] = 1; // OK
+  pt->s.a[1] = 1; // error
+}
+
+__attribute__ ((noinline, noclone))
+void
+fn5 (void)
+{
+  volatile struct S { char b; char a[1]; } s;
+  volatile struct U { int a; struct S s; } u;
+  u.s.a[0] = 1; // OK
+  u.s.a[1] = 1; // error
+  volatile struct U *pu = &u;
+  pu->s.a[0] = 1; // OK
+  pu->s.a[1] = 1; // error in strict mode
+}
+
+int
+main (void)
+{
+  fn1 ();
+  fn2 ();
+  fn3 ();
+  fn4 ();
+  fn5 ();
+  return 0;
+}
+
+/* { dg-output "index 1 out of bounds for type 'char \\\[1\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 1 out of bounds for type 'char \\\[1\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 4 out of bounds for type 'char \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 4 out of bounds for type 'char \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 1 out of bounds for type 'char \\\[1\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 1 out of bounds for type 'char \\\[1\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 1 out of bounds for type 'char \\\[1\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 1 out of bounds for type 'char \\\[1\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
diff --git gcc/testsuite/c-c++-common/ubsan/bounds-6.c gcc/testsuite/c-c++-common/ubsan/bounds-6.c
index e69de29..78ad29a 100644
--- gcc/testsuite/c-c++-common/ubsan/bounds-6.c
+++ gcc/testsuite/c-c++-common/ubsan/bounds-6.c
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=bounds -Wall -Wextra" } */
+
+/* Test off-by-one.  */
+
+struct S { int a; int b; } s[4], *t;
+struct U { int a[10]; } u[4], *v;
+volatile int *a, *b, *c;
+volatile void *d;
+volatile int e[4][4];
+
+int
+main (void)
+{
+  t = &s[4];  // OK
+  a = &s[4].a; // Error
+  b = &s[4].b; // Error
+  d = &e[4];  // OK
+  c = &e[4][0]; // Error
+  c = &e[3][4]; // OK
+  c = &e[3][3]; // OK
+
+  a = &u[4].a[9]; // Error
+  a = &u[4].a[10]; // Error
+  a = &u[3].a[9]; // OK
+  a = &u[3].a[10]; // OK
+  a = &u[3].a[11]; // Error
+
+  return 0;
+}
+
+/* { dg-output "index 4 out of bounds for type 'S \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 4 out of bounds for type 'S \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 4 out of bounds for type 'int \\\[4\\\]\\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 4 out of bounds for type 'U \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 4 out of bounds for type 'U \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 11 out of bounds for type 'int \\\[10\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
diff --git gcc/tree-core.h gcc/tree-core.h
index c9d43d0..9ccf39a 100644
--- gcc/tree-core.h
+++ gcc/tree-core.h
@@ -665,6 +665,13 @@ enum annot_expr_kind {
   annot_expr_kind_last
 };
 
+/* Internal functions.  */
+enum internal_fn {
+#define DEF_INTERNAL_FN(CODE, FLAGS) IFN_##CODE,
+#include "internal-fn.def"
+#undef DEF_INTERNAL_FN
+  IFN_LAST
+};
 
 /*---------------------------------------------------------------------------
                                 Type definitions
@@ -787,6 +794,9 @@ struct GTY(()) tree_base {
 
     /* SSA version number.  This field is only used with SSA_NAME.  */
     unsigned int version;
+
+    /* Internal function code.  */
+    ENUM_BITFIELD(internal_fn) ifn : 5;
   } GTY((skip(""))) u;
 };
 
diff --git gcc/tree-pretty-print.c gcc/tree-pretty-print.c
index 59a825c..39ee9b6 100644
--- gcc/tree-pretty-print.c
+++ gcc/tree-pretty-print.c
@@ -3218,6 +3218,13 @@ print_call_name (pretty_printer *buffer, tree node, int flags)
 {
   tree op0 = node;
 
+  if (node == NULL_TREE)
+    {
+      /* TODO Print builtin name.  */
+      pp_string (buffer, "<internal function call>");
+      return;
+    }
+
   if (TREE_CODE (op0) == NON_LVALUE_EXPR)
     op0 = TREE_OPERAND (op0, 0);
 
diff --git gcc/tree.c gcc/tree.c
index 559e758..0317e4e 100644
--- gcc/tree.c
+++ gcc/tree.c
@@ -8995,6 +8995,10 @@ get_callee_fndecl (const_tree call)
      called.  */
   addr = CALL_EXPR_FN (call);
 
+  /* If there is no function, return early.  */
+  if (addr == NULL_TREE)
+    return NULL_TREE;
+
   STRIP_NOPS (addr);
 
   /* If this is a readonly function pointer, extract its initial value.  */
@@ -10622,6 +10626,27 @@ build_call_expr (tree fndecl, int n, ...)
   return build_call_expr_loc_array (UNKNOWN_LOCATION, fndecl, n, argarray);
 }
 
+/* Build internal call expression.  This is just like CALL_EXPR, except
+   its CALL_EXPR_FN is NULL.  It will get gimplified later into ordinary
+   internal function.  */
+
+tree
+build_call_expr_internal_loc (location_t loc, enum internal_fn ifn,
+			      tree type, int n, ...)
+{
+  va_list ap;
+  int i;
+
+  tree fn = build_call_1 (type, NULL_TREE, n);
+  va_start (ap, n);
+  for (i = 0; i < n; i++)
+    CALL_EXPR_ARG (fn, i) = va_arg (ap, tree);
+  va_end (ap);
+  SET_EXPR_LOCATION (fn, loc);
+  CALL_EXPR_IFN (fn) = ifn;
+  return fn;
+}
+
 /* Create a new constant string literal and return a char* pointer to it.
    The STRING_CST value is the LEN characters at STR.  */
 tree
diff --git gcc/tree.def gcc/tree.def
index 464c8c7..299aacc 100644
--- gcc/tree.def
+++ gcc/tree.def
@@ -574,7 +574,7 @@ DEFTREECODE (BIND_EXPR, "bind_expr", tcc_expression, 3)
 /* Function call.  CALL_EXPRs are represented by variably-sized expression
    nodes.  There are at least three fixed operands.  Operand 0 is an
    INTEGER_CST node containing the total operand count, the number of
-   arguments plus 3.  Operand 1 is the function, while operand 2 is
+   arguments plus 3.  Operand 1 is the function or NULL, while operand 2 is
    is static chain argument, or NULL.  The remaining operands are the
    arguments to the call.  */
 DEFTREECODE (CALL_EXPR, "call_expr", tcc_vl_exp, 3)
diff --git gcc/tree.h gcc/tree.h
index 0a334cc..7797291 100644
--- gcc/tree.h
+++ gcc/tree.h
@@ -1134,12 +1134,12 @@ extern void protected_set_expr_location (tree, location_t);
 #define ASSERT_EXPR_VAR(NODE)	TREE_OPERAND (ASSERT_EXPR_CHECK (NODE), 0)
 #define ASSERT_EXPR_COND(NODE)	TREE_OPERAND (ASSERT_EXPR_CHECK (NODE), 1)
 
-/* CALL_EXPR accessors.
- */
+/* CALL_EXPR accessors.  */
 #define CALL_EXPR_FN(NODE) TREE_OPERAND (CALL_EXPR_CHECK (NODE), 1)
 #define CALL_EXPR_STATIC_CHAIN(NODE) TREE_OPERAND (CALL_EXPR_CHECK (NODE), 2)
 #define CALL_EXPR_ARG(NODE, I) TREE_OPERAND (CALL_EXPR_CHECK (NODE), (I) + 3)
 #define call_expr_nargs(NODE) (VL_EXP_OPERAND_LENGTH (NODE) - 3)
+#define CALL_EXPR_IFN(NODE) (CALL_EXPR_CHECK (NODE)->base.u.ifn)
 
 /* CALL_EXPR_ARGP returns a pointer to the argument vector for NODE.
    We can't use &CALL_EXPR_ARG (NODE, 0) because that will complain if
@@ -3631,6 +3631,8 @@ extern tree build_call_expr_loc_array (location_t, tree, int, tree *);
 extern tree build_call_expr_loc_vec (location_t, tree, vec<tree, va_gc> *);
 extern tree build_call_expr_loc (location_t, tree, int, ...);
 extern tree build_call_expr (tree, int, ...);
+extern tree build_call_expr_internal_loc (location_t, enum internal_fn,
+					  tree, int, ...);
 extern tree build_string_literal (int, const char *);
 
 /* Construct various nodes representing data types.  */
diff --git gcc/ubsan.c gcc/ubsan.c
index 5a8a447..0422cbb 100644
--- gcc/ubsan.c
+++ gcc/ubsan.c
@@ -271,23 +271,24 @@ get_ubsan_type_info_for_type (tree type)
   gcc_assert (TYPE_SIZE (type) && tree_fits_uhwi_p (TYPE_SIZE (type)));
   if (TREE_CODE (type) == REAL_TYPE)
     return tree_to_uhwi (TYPE_SIZE (type));
-  else
+  else if (INTEGRAL_TYPE_P (type))
     {
       int prec = exact_log2 (tree_to_uhwi (TYPE_SIZE (type)));
       gcc_assert (prec != -1);
       return (prec << 1) | !TYPE_UNSIGNED (type);
     }
+  else
+    return 0;
 }
 
 /* Helper routine that returns ADDR_EXPR of a VAR_DECL of a type
    descriptor.  It first looks into the hash table; if not found,
    create the VAR_DECL, put it into the hash table and return the
-   ADDR_EXPR of it.  TYPE describes a particular type.  WANT_POINTER_TYPE_P
-   means whether we are interested in the pointer type and not the pointer
-   itself.  */
+   ADDR_EXPR of it.  TYPE describes a particular type.  PSTYLE is
+   an enum controlling how we want to print the type.  */
 
 tree
-ubsan_type_descriptor (tree type, bool want_pointer_type_p)
+ubsan_type_descriptor (tree type, enum ubsan_print_style pstyle)
 {
   /* See through any typedefs.  */
   type = TYPE_MAIN_VARIANT (type);
@@ -308,7 +309,7 @@ ubsan_type_descriptor (tree type, bool want_pointer_type_p)
   unsigned short tkind, tinfo;
 
   /* Get the name of the type, or the name of the pointer type.  */
-  if (want_pointer_type_p)
+  if (pstyle == UBSAN_PRINT_POINTER)
     {
       gcc_assert (POINTER_TYPE_P (type));
       type2 = TREE_TYPE (type);
@@ -324,6 +325,12 @@ ubsan_type_descriptor (tree type, bool want_pointer_type_p)
   /* If an array, get its type.  */
   type2 = strip_array_types (type2);
 
+  if (pstyle == UBSAN_PRINT_ARRAY)
+    {
+      while (POINTER_TYPE_P (type2))
+        deref_depth++, type2 = TREE_TYPE (type2);
+    }
+
   if (TYPE_NAME (type2) != NULL)
     {
       if (TREE_CODE (TYPE_NAME (type2)) == IDENTIFIER_NODE)
@@ -338,7 +345,7 @@ ubsan_type_descriptor (tree type, bool want_pointer_type_p)
 
   /* Decorate the type name with '', '*', "struct", or "union".  */
   pretty_name = (char *) alloca (strlen (tname) + 16 + deref_depth);
-  if (want_pointer_type_p)
+  if (pstyle == UBSAN_PRINT_POINTER)
     {
       int pos = sprintf (pretty_name, "'%s%s%s%s%s%s%s",
 			 TYPE_VOLATILE (type2) ? "volatile " : "",
@@ -355,6 +362,33 @@ ubsan_type_descriptor (tree type, bool want_pointer_type_p)
       pretty_name[pos++] = '\'';
       pretty_name[pos] = '\0';
     }
+  else if (pstyle == UBSAN_PRINT_ARRAY)
+    {
+      /* Pretty print the array dimensions.  */
+      gcc_assert (TREE_CODE (type) == ARRAY_TYPE);
+      tree t = type;
+      int pos = sprintf (pretty_name, "'%s ", tname);
+      while (deref_depth-- > 0)
+        pretty_name[pos++] = '*';
+      while (TREE_CODE (t) == ARRAY_TYPE)
+	{
+	  pretty_name[pos++] = '[';
+	  tree dom = TYPE_DOMAIN (t);
+	  if (dom && TREE_CODE (TYPE_MAX_VALUE (dom)) == INTEGER_CST)
+	    pos += sprintf (&pretty_name[pos], HOST_WIDE_INT_PRINT_DEC,
+			    tree_to_shwi (TYPE_MAX_VALUE (dom)) + 1);
+	  else
+	    /* ??? We can't determine the variable name; print VLA unspec.  */
+	    pretty_name[pos++] = '*';
+	  pretty_name[pos++] = ']';
+	  t = TREE_TYPE (t);
+	}
+      pretty_name[pos++] = '\'';
+      pretty_name[pos] = '\0';
+
+     /* Save the tree with stripped types.  */
+     type = t;
+    }
   else
     sprintf (pretty_name, "'%s'", tname);
 
@@ -550,6 +584,69 @@ is_ubsan_builtin_p (tree t)
 		     "__builtin___ubsan_", 18) == 0;
 }
 
+/* Expand the UBSAN_BOUNDS special builtin function.  */
+
+void
+ubsan_expand_bounds_btn (gimple_stmt_iterator *gsi)
+{
+  gimple stmt = gsi_stmt (*gsi);
+  location_t loc = gimple_location (stmt);
+  gcc_assert (gimple_call_num_args (stmt) == 3);
+
+  /* Pick up the arguments of the UBSAN_BOUNDS call.  */
+  tree type = TREE_TYPE (TREE_TYPE (gimple_call_arg (stmt, 0)));
+  tree index = gimple_call_arg (stmt, 1);
+  tree orig_index_type = TREE_TYPE (index);
+  tree bound = gimple_call_arg (stmt, 2);
+
+  gimple_stmt_iterator gsi_orig = *gsi;
+
+  /* Create condition "if (index > bound)".  */
+  basic_block then_bb, fallthru_bb;
+  gimple_stmt_iterator cond_insert_point
+    = create_cond_insert_point (gsi, 0/*before_p*/, false, true,
+				&then_bb, &fallthru_bb);
+  index = fold_convert (TREE_TYPE (bound), index);
+  index = force_gimple_operand_gsi (&cond_insert_point, index,
+				    true/*simple_p*/, NULL_TREE,
+				    false/*before*/, GSI_NEW_STMT);
+  gimple g = gimple_build_cond (GT_EXPR, index, bound, NULL_TREE, NULL_TREE);
+  gimple_set_location (g, loc);
+  gsi_insert_after (&cond_insert_point, g, GSI_NEW_STMT);
+
+  /* Generate __ubsan_handle_out_of_bounds call.  */
+  *gsi = gsi_after_labels (then_bb);
+  if (flag_sanitize_undefined_trap_on_error)
+    g = gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP), 0);
+  else
+    {
+      tree data
+	= ubsan_create_data ("__ubsan_out_of_bounds_data", &loc, NULL,
+			     ubsan_type_descriptor (type, UBSAN_PRINT_ARRAY),
+			     ubsan_type_descriptor (orig_index_type),
+			     NULL_TREE);
+      data = build_fold_addr_expr_loc (loc, data);
+      enum built_in_function bcode
+	= flag_sanitize_recover
+	  ? BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS
+	  : BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS_ABORT;
+      tree fn = builtin_decl_explicit (bcode);
+      tree val = force_gimple_operand_gsi (gsi, ubsan_encode_value (index),
+					   true, NULL_TREE, true,
+					   GSI_SAME_STMT);
+      g = gimple_build_call (fn, 2, data, val);
+    }
+  gimple_set_location (g, loc);
+  gsi_insert_before (gsi, g, GSI_SAME_STMT);
+
+  /* Get rid of the UBSAN_BOUNDS call from the IR.  */
+  unlink_stmt_vdef (stmt);
+  gsi_remove (&gsi_orig, true);
+
+  /* Point GSI to next logical statement.  */
+  *gsi = gsi_start_bb (fallthru_bb);
+}
+
 /* Expand UBSAN_NULL internal call.  */
 
 void
@@ -609,9 +706,11 @@ ubsan_expand_null_ifn (gimple_stmt_iterator gsi)
       tree fn = builtin_decl_implicit (bcode);
       const struct ubsan_mismatch_data m
 	= { build_zero_cst (pointer_sized_int_node), ckind };
-      tree data = ubsan_create_data ("__ubsan_null_data", &loc, &m,
-				     ubsan_type_descriptor (TREE_TYPE (ptr),
-							    true), NULL_TREE);
+      tree data
+	= ubsan_create_data ("__ubsan_null_data", &loc, &m,
+			     ubsan_type_descriptor (TREE_TYPE (ptr),
+						    UBSAN_PRINT_POINTER),
+			     NULL_TREE);
       data = build_fold_addr_expr_loc (loc, data);
       g = gimple_build_call (fn, 2, data,
 			     build_zero_cst (pointer_sized_int_node));
@@ -689,8 +788,7 @@ ubsan_build_overflow_builtin (tree_code code, location_t loc, tree lhstype,
     return build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0);
 
   tree data = ubsan_create_data ("__ubsan_overflow_data", &loc, NULL,
-				 ubsan_type_descriptor (lhstype, false),
-				 NULL_TREE);
+				 ubsan_type_descriptor (lhstype), NULL_TREE);
   enum built_in_function fn_code;
 
   switch (code)
@@ -884,8 +982,7 @@ instrument_bool_enum_load (gimple_stmt_iterator *gsi)
   else
     {
       tree data = ubsan_create_data ("__ubsan_invalid_value_data", &loc, NULL,
-				     ubsan_type_descriptor (type, false),
-				     NULL_TREE);
+				     ubsan_type_descriptor (type), NULL_TREE);
       data = build_fold_addr_expr_loc (loc, data);
       enum built_in_function bcode
 	= flag_sanitize_recover
@@ -1005,10 +1102,8 @@ ubsan_instrument_float_cast (location_t loc, tree type, tree expr)
     {
       /* Create the __ubsan_handle_float_cast_overflow fn call.  */
       tree data = ubsan_create_data ("__ubsan_float_cast_overflow_data", NULL,
-				     NULL,
-				     ubsan_type_descriptor (expr_type, false),
-				     ubsan_type_descriptor (type, false),
-				     NULL_TREE);
+				     NULL, ubsan_type_descriptor (expr_type),
+				     ubsan_type_descriptor (type), NULL_TREE);
       enum built_in_function bcode
 	= flag_sanitize_recover
 	  ? BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW
diff --git gcc/ubsan.h gcc/ubsan.h
index b008419..2a24a92 100644
--- gcc/ubsan.h
+++ gcc/ubsan.h
@@ -30,17 +30,25 @@ enum ubsan_null_ckind {
   UBSAN_MEMBER_CALL
 };
 
+/* This controls how ubsan prints types.  Used in ubsan_type_descriptor.  */
+enum ubsan_print_style {
+  UBSAN_PRINT_NORMAL,
+  UBSAN_PRINT_POINTER,
+  UBSAN_PRINT_ARRAY
+};
+
 /* An extra data used by ubsan pointer checking.  */
 struct ubsan_mismatch_data {
   tree align;
   tree ckind;
 };
 
+extern void ubsan_expand_bounds_btn (gimple_stmt_iterator *);
 extern void ubsan_expand_null_ifn (gimple_stmt_iterator);
 extern tree ubsan_instrument_unreachable (location_t);
 extern tree ubsan_create_data (const char *, const location_t *,
 			       const struct ubsan_mismatch_data *, ...);
-extern tree ubsan_type_descriptor (tree, bool);
+extern tree ubsan_type_descriptor (tree, enum ubsan_print_style = UBSAN_PRINT_NORMAL);
 extern tree ubsan_encode_value (tree, bool = false);
 extern bool is_ubsan_builtin_p (tree);
 extern tree ubsan_build_overflow_builtin (tree_code, location_t, tree, tree, tree);

	Marek

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

* Re: [PATCH] Implement -fsanitize=bounds and internal calls in FEs
  2014-06-19 15:00   ` Marek Polacek
@ 2014-06-19 16:24     ` Marek Polacek
  2014-06-19 17:19     ` Jakub Jelinek
  2014-06-19 17:48     ` Jakub Jelinek
  2 siblings, 0 replies; 17+ messages in thread
From: Marek Polacek @ 2014-06-19 16:24 UTC (permalink / raw)
  To: Jakub Jelinek
  Cc: GCC Patches, Jason Merrill, Joseph S. Myers, Richard Biener, Jeff Law

On Thu, Jun 19, 2014 at 04:56:53PM +0200, Marek Polacek wrote:
> Regtested/bootstrapped on x86_64-linux.  How does this look?

Now even bootstrap-ubsan passed, with 92258 runtime errors:
index 1 out of bounds for type 'rtunion [1]' - heh.

	Marek

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

* Re: [PATCH] Implement -fsanitize=bounds and internal calls in FEs
  2014-06-19 15:00   ` Marek Polacek
  2014-06-19 16:24     ` Marek Polacek
@ 2014-06-19 17:19     ` Jakub Jelinek
  2014-06-20  8:43       ` Marek Polacek
  2014-06-19 17:48     ` Jakub Jelinek
  2 siblings, 1 reply; 17+ messages in thread
From: Jakub Jelinek @ 2014-06-19 17:19 UTC (permalink / raw)
  To: Marek Polacek
  Cc: GCC Patches, Jason Merrill, Joseph S. Myers, Richard Biener, Jeff Law

Hi!

On Thu, Jun 19, 2014 at 04:56:53PM +0200, Marek Polacek wrote:

Thanks for working on this.

> --- gcc/asan.c
> +++ gcc/asan.c
> @@ -2761,6 +2761,9 @@ pass_sanopt::execute (function *fun)
>  	      case IFN_UBSAN_NULL:
>  		ubsan_expand_null_ifn (gsi);
>  		break;
> +	      case IFN_UBSAN_BOUNDS:
> +		ubsan_expand_bounds_btn (&gsi);
> +		break;
>  	      default:

Why *_btn instead of *_ifn ?

> +static tree
> +ubsan_walk_array_refs_r (tree *tp, int *walk_subtrees, void *data)
> +{
> +  struct pointer_set_t *pset = (struct pointer_set_t *) data;
> +
> +  if (TREE_CODE (*tp) == BIND_EXPR)
> +    {

I think it would be worth adding here a comment why do you handle BIND_EXPR
here, that it doesn't walk the vars, but only their initializers etc. and
thus in order to prevent walking DECL_INITIAL of TREE_STATIC decls
we have to duplicate this part of walk_tree.

> +      for (tree decl = BIND_EXPR_VARS (*tp); decl; decl = DECL_CHAIN (decl))
> +	{
> +	  if (TREE_STATIC (decl))
> +	    {
> +	      *walk_subtrees = 0;
> +	      continue;
> +	    }
> +	  walk_tree (&DECL_INITIAL (decl), ubsan_walk_array_refs_r, NULL, pset);
> +	  walk_tree (&DECL_SIZE (decl), ubsan_walk_array_refs_r, NULL, pset);
> +	  walk_tree (&DECL_SIZE_UNIT (decl), ubsan_walk_array_refs_r, NULL, pset);

Shouldn't that use pset, pset); or data, pset); ?
Also, too long lines (at least the last one, first one likely too).

> +  tree bound = TYPE_MAX_VALUE (domain);
> +  if (ignore_off_by_one)
> +    bound = fold_build2 (PLUS_EXPR, TREE_TYPE (bound), bound,
> +			 build_int_cst (TREE_TYPE (bound), 1));
> +
> +  /* Detect flexible array members and suchlike.  */
> +  tree base = get_base_address (array);
> +  if (base && TREE_CODE (base) == INDIRECT_REF)

I'd check also == MEM_REF here, while the FEs often use INDIRECT_REFs,
there are already spots where it creates MEM_REFs.

> +void
> +ubsan_maybe_instrument_array_ref (tree *expr_p, bool ignore_off_by_one)
> +{
> +  if (!ubsan_array_ref_instrumented_p (*expr_p)
> +      && current_function_decl != 0

Please use != NULL_TREE.

> +      && !lookup_attribute ("no_sanitize_undefined",
> +			    DECL_ATTRIBUTES (current_function_decl)))
> +    {
> +      tree t = copy_node (*expr_p);
> +      tree op0 = TREE_OPERAND (t, 0);
> +      tree op1 = TREE_OPERAND (t, 1);

Please don't call copy_node until you know you want to instrument it.
I.e.
      tree op0 = TREE_OPERAND (*expr_p, 0);
      tree op1 = TREE_OPERAND (*expr_p, 1);
> +      tree e = ubsan_instrument_bounds (EXPR_LOCATION (t), op0, &op1,

s/t/*expr_p/ above.

> +					ignore_off_by_one);
> +      if (e != NULL_TREE)
> +	{

and only here add:
	  tree t = copy_node (*expr_p);
> +	  TREE_OPERAND (t, 1) = build2 (COMPOUND_EXPR, TREE_TYPE (op1),
> +					e, op1);
> +	  *expr_p = t;
> +	}
> +    }
> +}

> --- gcc/tree-pretty-print.c
> +++ gcc/tree-pretty-print.c
> @@ -3218,6 +3218,13 @@ print_call_name (pretty_printer *buffer, tree node, int flags)
>  {
>    tree op0 = node;
>  
> +  if (node == NULL_TREE)
> +    {
> +      /* TODO Print builtin name.  */
> +      pp_string (buffer, "<internal function call>");

Use internal_fn_name function?

	Jakub

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

* Re: [PATCH] Implement -fsanitize=bounds and internal calls in FEs
  2014-06-19 15:00   ` Marek Polacek
  2014-06-19 16:24     ` Marek Polacek
  2014-06-19 17:19     ` Jakub Jelinek
@ 2014-06-19 17:48     ` Jakub Jelinek
  2014-06-20 15:43       ` Marek Polacek
  2 siblings, 1 reply; 17+ messages in thread
From: Jakub Jelinek @ 2014-06-19 17:48 UTC (permalink / raw)
  To: Marek Polacek
  Cc: GCC Patches, Jason Merrill, Joseph S. Myers, Richard Biener, Jeff Law

On Thu, Jun 19, 2014 at 04:56:53PM +0200, Marek Polacek wrote:
> +	/* Don't instrument this FMA-like array in non-strict

Also, please don't use FMA to mean flexible member array, it is
flexible array member, but more importantly, FMA is used for fused
multiply-add, so IMHO it is better to spell it without acronym.

	Jakub

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

* Re: [PATCH] Implement -fsanitize=bounds and internal calls in FEs
  2014-06-19 17:19     ` Jakub Jelinek
@ 2014-06-20  8:43       ` Marek Polacek
  2014-06-20  8:57         ` Jakub Jelinek
  0 siblings, 1 reply; 17+ messages in thread
From: Marek Polacek @ 2014-06-20  8:43 UTC (permalink / raw)
  To: Jakub Jelinek
  Cc: GCC Patches, Jason Merrill, Joseph S. Myers, Richard Biener, Jeff Law

On Thu, Jun 19, 2014 at 07:19:31PM +0200, Jakub Jelinek wrote:
> > +	      case IFN_UBSAN_BOUNDS:
> > +		ubsan_expand_bounds_btn (&gsi);
> > +		break;
> >  	      default:
> 
> Why *_btn instead of *_ifn ?

Remnant from when I was using __builtin.ubsan instead of the internal
call.  Fixed.
 
> > +static tree
> > +ubsan_walk_array_refs_r (tree *tp, int *walk_subtrees, void *data)
> > +{
> > +  struct pointer_set_t *pset = (struct pointer_set_t *) data;
> > +
> > +  if (TREE_CODE (*tp) == BIND_EXPR)
> > +    {
> 
> I think it would be worth adding here a comment why do you handle BIND_EXPR
> here, that it doesn't walk the vars, but only their initializers etc. and
> thus in order to prevent walking DECL_INITIAL of TREE_STATIC decls
> we have to duplicate this part of walk_tree.

Done.

> > +      for (tree decl = BIND_EXPR_VARS (*tp); decl; decl = DECL_CHAIN (decl))
> > +	{
> > +	  if (TREE_STATIC (decl))
> > +	    {
> > +	      *walk_subtrees = 0;
> > +	      continue;
> > +	    }
> > +	  walk_tree (&DECL_INITIAL (decl), ubsan_walk_array_refs_r, NULL, pset);
> > +	  walk_tree (&DECL_SIZE (decl), ubsan_walk_array_refs_r, NULL, pset);
> > +	  walk_tree (&DECL_SIZE_UNIT (decl), ubsan_walk_array_refs_r, NULL, pset);
> 
> Shouldn't that use pset, pset); or data, pset); ?
> Also, too long lines (at least the last one, first one likely too).

Oops, fixed.
 
> > +  tree bound = TYPE_MAX_VALUE (domain);
> > +  if (ignore_off_by_one)
> > +    bound = fold_build2 (PLUS_EXPR, TREE_TYPE (bound), bound,
> > +			 build_int_cst (TREE_TYPE (bound), 1));
> > +
> > +  /* Detect flexible array members and suchlike.  */
> > +  tree base = get_base_address (array);
> > +  if (base && TREE_CODE (base) == INDIRECT_REF)
> 
> I'd check also == MEM_REF here, while the FEs often use INDIRECT_REFs,
> there are already spots where it creates MEM_REFs.

Fixed.

> > +void
> > +ubsan_maybe_instrument_array_ref (tree *expr_p, bool ignore_off_by_one)
> > +{
> > +  if (!ubsan_array_ref_instrumented_p (*expr_p)
> > +      && current_function_decl != 0
> 
> Please use != NULL_TREE.

Ok.
 
> > +      && !lookup_attribute ("no_sanitize_undefined",
> > +			    DECL_ATTRIBUTES (current_function_decl)))
> > +    {
> > +      tree t = copy_node (*expr_p);
> > +      tree op0 = TREE_OPERAND (t, 0);
> > +      tree op1 = TREE_OPERAND (t, 1);
> 
> Please don't call copy_node until you know you want to instrument it.
> I.e.
>       tree op0 = TREE_OPERAND (*expr_p, 0);
>       tree op1 = TREE_OPERAND (*expr_p, 1);
> > +      tree e = ubsan_instrument_bounds (EXPR_LOCATION (t), op0, &op1,
> 
> s/t/*expr_p/ above.
> 
> > +					ignore_off_by_one);
> > +      if (e != NULL_TREE)
> > +	{
> 
> and only here add:
> 	  tree t = copy_node (*expr_p);
> > +	  TREE_OPERAND (t, 1) = build2 (COMPOUND_EXPR, TREE_TYPE (op1),
> > +					e, op1);
> > +	  *expr_p = t;
> > +	}
> > +    }
> > +}

Fixed as well.

> > --- gcc/tree-pretty-print.c
> > +++ gcc/tree-pretty-print.c
> > @@ -3218,6 +3218,13 @@ print_call_name (pretty_printer *buffer, tree node, int flags)
> >  {
> >    tree op0 = node;
> >  
> > +  if (node == NULL_TREE)
> > +    {
> > +      /* TODO Print builtin name.  */
> > +      pp_string (buffer, "<internal function call>");
> 
> Use internal_fn_name function?

Uh, not sure how I missed that.  I print the internal function before calling
print_call_name, since that gets NULL node - and we can't determine
CALL_EXPR_IFN.

I added some docs as well, as promised.

2014-06-20  Marek Polacek  <polacek@redhat.com>

	* asan.c (pass_sanopt::execute): Handle IFN_UBSAN_BOUNDS.
	* flag-types.h (enum sanitize_code): Add SANITIZE_BOUNDS and or it
	into SANITIZE_UNDEFINED.
	* doc/invoke.texi: Describe -fsanitize=bounds.
	* gimplify.c (gimplify_call_expr): Add gimplification of internal
	functions created in the FEs.
	* internal-fn.c: Move "internal-fn.h" after "tree.h".
	(expand_UBSAN_BOUNDS): New function.
	* internal-fn.def (UBSAN_BOUNDS): New internal function.
	* internal-fn.h: Don't define internal functions here.
	* opts.c (common_handle_option): Add -fsanitize=bounds.
	* sanitizer.def (BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS,
	BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS_ABORT): Add.
	* tree-core.h: Define internal functions here.
	(struct tree_base): Add ifn field.
	* tree-pretty-print.c: Include "internal-fn.h".
	(dump_generic_node): Handle functions without CALL_EXPR_FN.
	* tree.c (get_callee_fndecl): Likewise.
	(build_call_expr_internal_loc): New function.
	* tree.def (CALL_EXPR): Update description.
	* tree.h (CALL_EXPR_IFN): Define.
	(build_call_expr_internal_loc): Declare.
	* ubsan.c (get_ubsan_type_info_for_type): Return 0 for non-arithmetic
	types.
	(ubsan_type_descriptor): Change bool parameter to enum
	ubsan_print_style.  Adjust the code.  Add handling of
	UBSAN_PRINT_ARRAY.
	(ubsan_expand_bounds_ifn): New function.
	(ubsan_expand_null_ifn): Adjust ubsan_type_descriptor call.
	(ubsan_build_overflow_builtin): Likewise.
	(instrument_bool_enum_load): Likewise.
	(ubsan_instrument_float_cast): Likewise.
	* ubsan.h (enum ubsan_print_style): New enum.
	(ubsan_expand_bounds_ifn): Declare.
	(ubsan_type_descriptor): Adjust declaration.  Use a default parameter.
c-family/
	* c-gimplify.c: Include "c-ubsan.h" and "pointer-set.h".
	(ubsan_walk_array_refs_r): New function.
	(c_genericize): Instrument array bounds.
	* c-ubsan.c: Include "internal-fn.h".
	(ubsan_instrument_division): Mark instrumented arrays as having
	side effects.  Adjust ubsan_type_descriptor call.
	(ubsan_instrument_shift): Likewise.
	(ubsan_instrument_vla): Adjust ubsan_type_descriptor call.
	(ubsan_instrument_bounds): New function.
	(ubsan_array_ref_instrumented_p): New function.
	(ubsan_maybe_instrument_array_ref): New function.
	* c-ubsan.h (ubsan_instrument_bounds): Declare.
	(ubsan_array_ref_instrumented_p): Declare.
	(ubsan_maybe_instrument_array_ref): Declare.
testsuite/
	* c-c++-common/ubsan/bounds-1.c: New test.
	* c-c++-common/ubsan/bounds-2.c: New test.
	* c-c++-common/ubsan/bounds-3.c: New test.
	* c-c++-common/ubsan/bounds-4.c: New test.
	* c-c++-common/ubsan/bounds-5.c: New test.
	* c-c++-common/ubsan/bounds-6.c: New test.

diff --git gcc/asan.c gcc/asan.c
index 281a795..b7c76cf 100644
--- gcc/asan.c
+++ gcc/asan.c
@@ -2761,6 +2761,9 @@ pass_sanopt::execute (function *fun)
 	      case IFN_UBSAN_NULL:
 		ubsan_expand_null_ifn (gsi);
 		break;
+	      case IFN_UBSAN_BOUNDS:
+		ubsan_expand_bounds_ifn (&gsi);
+		break;
 	      default:
 		break;
 	      }
@@ -2771,6 +2774,10 @@ pass_sanopt::execute (function *fun)
 	      print_gimple_stmt (dump_file, stmt, 0, dump_flags);
 	      fprintf (dump_file, "\n");
 	    }
+
+	  /* ubsan_expand_bounds_ifn might move us to the end of the BB.  */
+	  if (gsi_end_p (gsi))
+	    break;
 	}
     }
   return 0;
diff --git gcc/c-family/c-gimplify.c gcc/c-family/c-gimplify.c
index 737be4d..c797d99 100644
--- gcc/c-family/c-gimplify.c
+++ gcc/c-family/c-gimplify.c
@@ -45,6 +45,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "c-pretty-print.h"
 #include "cgraph.h"
 #include "cilk.h"
+#include "c-ubsan.h"
+#include "pointer-set.h"
 
 /*  The gimplification pass converts the language-dependent trees
     (ld-trees) emitted by the parser into language-independent trees
@@ -67,6 +69,39 @@ along with GCC; see the file COPYING3.  If not see
     walk back up, we check that they fit our constraints, and copy them
     into temporaries if not.  */
 
+/* Callback for c_genericize.  */
+
+static tree
+ubsan_walk_array_refs_r (tree *tp, int *walk_subtrees, void *data)
+{
+  struct pointer_set_t *pset = (struct pointer_set_t *) data;
+
+  /* Since walk_tree doesn't call the callback function on the decls
+     in BIND_EXPR_VARS, we have to walk them manually.  */
+  if (TREE_CODE (*tp) == BIND_EXPR)
+    {
+      for (tree decl = BIND_EXPR_VARS (*tp); decl; decl = DECL_CHAIN (decl))
+	{
+	  if (TREE_STATIC (decl))
+	    {
+	      *walk_subtrees = 0;
+	      continue;
+	    }
+	  walk_tree (&DECL_INITIAL (decl), ubsan_walk_array_refs_r, pset,
+		     pset);
+	  walk_tree (&DECL_SIZE (decl), ubsan_walk_array_refs_r, pset, pset);
+	  walk_tree (&DECL_SIZE_UNIT (decl), ubsan_walk_array_refs_r, pset,
+		     pset);
+	}
+    }
+  else if (TREE_CODE (*tp) == ADDR_EXPR
+	   && TREE_CODE (TREE_OPERAND (*tp, 0)) == ARRAY_REF)
+    ubsan_maybe_instrument_array_ref (&TREE_OPERAND (*tp, 0), true);
+  else if (TREE_CODE (*tp) == ARRAY_REF)
+    ubsan_maybe_instrument_array_ref (tp, false);
+  return NULL_TREE;
+}
+
 /* Gimplification of statement trees.  */
 
 /* Convert the tree representation of FNDECL from C frontend trees to
@@ -79,6 +114,14 @@ c_genericize (tree fndecl)
   int local_dump_flags;
   struct cgraph_node *cgn;
 
+  if (flag_sanitize & SANITIZE_BOUNDS)
+    {
+      struct pointer_set_t *pset = pointer_set_create ();
+      walk_tree (&DECL_SAVED_TREE (fndecl), ubsan_walk_array_refs_r, pset,
+		 pset);
+      pointer_set_destroy (pset);
+    }
+
   /* Dump the C-specific tree IR.  */
   dump_orig = dump_begin (TDI_original, &local_dump_flags);
   if (dump_orig)
@@ -207,16 +250,16 @@ c_gimplify_expr (tree *expr_p, gimple_seq *pre_p ATTRIBUTE_UNUSED,
 	  }
 	break;
       }
-      
+
     case CILK_SPAWN_STMT:
-      gcc_assert 
-	(fn_contains_cilk_spawn_p (cfun) 
+      gcc_assert
+	(fn_contains_cilk_spawn_p (cfun)
 	 && cilk_detect_spawn_and_unwrap (expr_p));
-      
+
       /* If errors are seen, then just process it as a CALL_EXPR.  */
       if (!seen_error ())
 	return (enum gimplify_status) gimplify_cilk_spawn (expr_p);
-      
+
     case MODIFY_EXPR:
     case INIT_EXPR:
     case CALL_EXPR:
diff --git gcc/c-family/c-ubsan.c gcc/c-family/c-ubsan.c
index 8d5b685..3698580 100644
--- gcc/c-family/c-ubsan.c
+++ gcc/c-family/c-ubsan.c
@@ -30,6 +30,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "c-family/c-common.h"
 #include "c-family/c-ubsan.h"
 #include "asan.h"
+#include "internal-fn.h"
 
 /* Instrument division by zero and INT_MIN / -1.  If not instrumenting,
    return NULL_TREE.  */
@@ -77,15 +78,27 @@ ubsan_instrument_division (location_t loc, tree op0, tree op1)
     return NULL_TREE;
 
   /* In case we have a SAVE_EXPR in a conditional context, we need to
-     make sure it gets evaluated before the condition.  */
+     make sure it gets evaluated before the condition.  If the OP0 is
+     an instrumented array reference, mark it as having side effects so
+     it's not folded away.  */
+  if (flag_sanitize & SANITIZE_BOUNDS)
+    {
+      tree xop0 = op0;
+      while (CONVERT_EXPR_P (xop0))
+	xop0 = TREE_OPERAND (xop0, 0);
+      if (TREE_CODE (xop0) == ARRAY_REF)
+	{
+	  TREE_SIDE_EFFECTS (xop0) = 1;
+	  TREE_SIDE_EFFECTS (op0) = 1;
+	}
+    }
   t = fold_build2 (COMPOUND_EXPR, TREE_TYPE (t), op0, t);
   if (flag_sanitize_undefined_trap_on_error)
     tt = build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0);
   else
     {
       tree data = ubsan_create_data ("__ubsan_overflow_data", &loc, NULL,
-				     ubsan_type_descriptor (type, false),
-				     NULL_TREE);
+				     ubsan_type_descriptor (type), NULL_TREE);
       data = build_fold_addr_expr_loc (loc, data);
       enum built_in_function bcode
 	= flag_sanitize_recover
@@ -154,7 +167,20 @@ ubsan_instrument_shift (location_t loc, enum tree_code code,
     return NULL_TREE;
 
   /* In case we have a SAVE_EXPR in a conditional context, we need to
-     make sure it gets evaluated before the condition.  */
+     make sure it gets evaluated before the condition.  If the OP0 is
+     an instrumented array reference, mark it as having side effects so
+     it's not folded away.  */
+  if (flag_sanitize & SANITIZE_BOUNDS)
+    {
+      tree xop0 = op0;
+      while (CONVERT_EXPR_P (xop0))
+	xop0 = TREE_OPERAND (xop0, 0);
+      if (TREE_CODE (xop0) == ARRAY_REF)
+	{
+	  TREE_SIDE_EFFECTS (xop0) = 1;
+	  TREE_SIDE_EFFECTS (op0) = 1;
+	}
+    }
   t = fold_build2 (COMPOUND_EXPR, TREE_TYPE (t), op0, t);
   t = fold_build2 (TRUTH_OR_EXPR, boolean_type_node, t,
 		   tt ? tt : integer_zero_node);
@@ -164,10 +190,8 @@ ubsan_instrument_shift (location_t loc, enum tree_code code,
   else
     {
       tree data = ubsan_create_data ("__ubsan_shift_data", &loc, NULL,
-				     ubsan_type_descriptor (type0, false),
-				     ubsan_type_descriptor (type1, false),
-				     NULL_TREE);
-
+				     ubsan_type_descriptor (type0),
+				     ubsan_type_descriptor (type1), NULL_TREE);
       data = build_fold_addr_expr_loc (loc, data);
 
       enum built_in_function bcode
@@ -197,8 +221,7 @@ ubsan_instrument_vla (location_t loc, tree size)
   else
     {
       tree data = ubsan_create_data ("__ubsan_vla_data", &loc, NULL,
-				     ubsan_type_descriptor (type, false),
-				     NULL_TREE);
+				     ubsan_type_descriptor (type), NULL_TREE);
       data = build_fold_addr_expr_loc (loc, data);
       enum built_in_function bcode
 	= flag_sanitize_recover
@@ -228,3 +251,102 @@ ubsan_instrument_return (location_t loc)
   tree t = builtin_decl_explicit (BUILT_IN_UBSAN_HANDLE_MISSING_RETURN);
   return build_call_expr_loc (loc, t, 1, build_fold_addr_expr_loc (loc, data));
 }
+
+/* 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.
+   Return NULL_TREE if no instrumentation is emitted.
+   IGNORE_OFF_BY_ONE is true if the ARRAY_REF is inside a ADDR_EXPR.  */
+
+tree
+ubsan_instrument_bounds (location_t loc, tree array, tree *index,
+			 bool ignore_off_by_one)
+{
+  tree type = TREE_TYPE (array);
+  tree domain = TYPE_DOMAIN (type);
+
+  if (domain == NULL_TREE)
+    return NULL_TREE;
+
+  tree bound = TYPE_MAX_VALUE (domain);
+  if (ignore_off_by_one)
+    bound = fold_build2 (PLUS_EXPR, TREE_TYPE (bound), bound,
+			 build_int_cst (TREE_TYPE (bound), 1));
+
+  /* Detect flexible array members and suchlike.  */
+  tree base = get_base_address (array);
+  if (base && (TREE_CODE (base) == INDIRECT_REF
+	       || TREE_CODE (base) == MEM_REF))
+    {
+      tree next = NULL_TREE;
+      tree cref = array;
+
+      /* Walk all structs/unions.  */
+      while (TREE_CODE (cref) == COMPONENT_REF)
+	{
+	  if (TREE_CODE (TREE_TYPE (TREE_OPERAND (cref, 0))) == RECORD_TYPE)
+	    for (next = DECL_CHAIN (TREE_OPERAND (cref, 1));
+		 next && TREE_CODE (next) != FIELD_DECL;
+		 next = DECL_CHAIN (next))
+	      ;
+	  if (next)
+	    /* Not a last element.  Instrument it.  */
+	    break;
+	  /* Ok, this is the last field of the structure/union.  But the
+	     aggregate containing the field must be the last field too,
+	     recursively.  */
+	  cref = TREE_OPERAND (cref, 0);
+	}
+      if (!next)
+	/* Don't instrument this flexible array member-like array in non-strict
+	   -fsanitize=bounds mode.  */
+        return NULL_TREE;
+    }
+
+  *index = save_expr (*index);
+  /* Create a "(T *) 0" tree node to describe the array type.  */
+  tree zero_with_type = build_int_cst (build_pointer_type (type), 0);
+  return build_call_expr_internal_loc (loc, IFN_UBSAN_BOUNDS,
+				       void_type_node, 3, zero_with_type,
+				       *index, bound);
+}
+
+/* Return true iff T is an array that was instrumented by SANITIZE_BOUNDS.  */
+
+bool
+ubsan_array_ref_instrumented_p (const_tree t)
+{
+  if (TREE_CODE (t) != ARRAY_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;
+}
+
+/* Instrument an ARRAY_REF, 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)
+{
+  if (!ubsan_array_ref_instrumented_p (*expr_p)
+      && current_function_decl != NULL_TREE
+      && !lookup_attribute ("no_sanitize_undefined",
+			    DECL_ATTRIBUTES (current_function_decl)))
+    {
+      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 (e != NULL_TREE)
+	{
+	  tree t = copy_node (*expr_p);
+	  TREE_OPERAND (t, 1) = build2 (COMPOUND_EXPR, TREE_TYPE (op1),
+					e, op1);
+	  *expr_p = t;
+	}
+    }
+}
diff --git gcc/c-family/c-ubsan.h gcc/c-family/c-ubsan.h
index e504b90..edf5bc6 100644
--- gcc/c-family/c-ubsan.h
+++ gcc/c-family/c-ubsan.h
@@ -25,5 +25,8 @@ extern tree ubsan_instrument_division (location_t, tree, tree);
 extern tree ubsan_instrument_shift (location_t, enum tree_code, tree, tree);
 extern tree ubsan_instrument_vla (location_t, tree);
 extern tree ubsan_instrument_return (location_t);
+extern tree ubsan_instrument_bounds (location_t, tree, tree *, bool);
+extern bool ubsan_array_ref_instrumented_p (const_tree);
+extern void ubsan_maybe_instrument_array_ref (tree *, bool);
 
 #endif  /* GCC_C_UBSAN_H  */
diff --git gcc/doc/invoke.texi gcc/doc/invoke.texi
index f7a54fa..0d4bd00 100644
--- gcc/doc/invoke.texi
+++ gcc/doc/invoke.texi
@@ -5448,6 +5448,13 @@ This option enables floating-point type to integer conversion checking.
 We check that the result of the conversion does not overflow.
 This option does not work well with @code{FE_INVALID} exceptions enabled.
 
+@item -fsanitize=bounds
+@opindex fsanitize=bounds
+
+This option enables instrumentation of array bounds.  Various out of bounds
+accesses are detected.  Flexible array members are not instrumented, as well
+as initializers of variables with static storage.
+
 @item -fsanitize-recover
 @opindex fsanitize-recover
 By default @option{-fsanitize=undefined} sanitization (and its suboptions
diff --git gcc/flag-types.h gcc/flag-types.h
index ed00046..4b8e338 100644
--- gcc/flag-types.h
+++ gcc/flag-types.h
@@ -230,9 +230,11 @@ enum sanitize_code {
   SANITIZE_ENUM = 1 << 11,
   SANITIZE_FLOAT_DIVIDE = 1 << 12,
   SANITIZE_FLOAT_CAST = 1 << 13,
+  SANITIZE_BOUNDS = 1 << 14,
   SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE
 		       | SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN
-		       | SANITIZE_SI_OVERFLOW | SANITIZE_BOOL | SANITIZE_ENUM,
+		       | SANITIZE_SI_OVERFLOW | SANITIZE_BOOL | SANITIZE_ENUM
+		       | SANITIZE_BOUNDS,
   SANITIZE_NONDEFAULT = SANITIZE_FLOAT_DIVIDE | SANITIZE_FLOAT_CAST
 };
 
diff --git gcc/gimplify.c gcc/gimplify.c
index 3dcb4af..338c5c0 100644
--- gcc/gimplify.c
+++ gcc/gimplify.c
@@ -2264,6 +2264,24 @@ gimplify_call_expr (tree *expr_p, gimple_seq *pre_p, bool want_value)
   if (! EXPR_HAS_LOCATION (*expr_p))
     SET_EXPR_LOCATION (*expr_p, input_location);
 
+  /* Gimplify internal functions created in the FEs.  */
+  if (CALL_EXPR_FN (*expr_p) == NULL_TREE)
+    {
+      nargs = call_expr_nargs (*expr_p);
+      enum internal_fn ifn = CALL_EXPR_IFN (*expr_p);
+      auto_vec<tree> vargs (nargs);
+
+      for (i = 0; i < nargs; i++)
+	{
+	  gimplify_arg (&CALL_EXPR_ARG (*expr_p, i), pre_p,
+			EXPR_LOCATION (*expr_p));
+	  vargs.quick_push (CALL_EXPR_ARG (*expr_p, i));
+	}
+      gimple call = gimple_build_call_internal_vec (ifn, vargs);
+      gimplify_seq_add_stmt (pre_p, call);
+      return GS_ALL_DONE;
+    }
+
   /* This may be a call to a builtin function.
 
      Builtin function calls may be transformed into different
diff --git gcc/internal-fn.c gcc/internal-fn.c
index 68b2b66..78f59d6 100644
--- gcc/internal-fn.c
+++ gcc/internal-fn.c
@@ -20,8 +20,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
-#include "internal-fn.h"
 #include "tree.h"
+#include "internal-fn.h"
 #include "stor-layout.h"
 #include "expr.h"
 #include "optabs.h"
@@ -159,6 +159,14 @@ expand_UBSAN_NULL (gimple stmt ATTRIBUTE_UNUSED)
   gcc_unreachable ();
 }
 
+/* This should get expanded in the sanopt pass.  */
+
+static void
+expand_UBSAN_BOUNDS (gimple stmt ATTRIBUTE_UNUSED)
+{
+  gcc_unreachable ();
+}
+
 /* Add sub/add overflow checking to the statement STMT.
    CODE says whether the operation is +, or -.  */
 
diff --git gcc/internal-fn.def gcc/internal-fn.def
index 31dc4c9..f0766bc 100644
--- gcc/internal-fn.def
+++ gcc/internal-fn.def
@@ -48,6 +48,7 @@ DEF_INTERNAL_FN (MASK_LOAD, ECF_PURE | ECF_LEAF)
 DEF_INTERNAL_FN (MASK_STORE, ECF_LEAF)
 DEF_INTERNAL_FN (ANNOTATE,  ECF_CONST | ECF_LEAF | ECF_NOTHROW)
 DEF_INTERNAL_FN (UBSAN_NULL, ECF_LEAF | ECF_NOTHROW)
+DEF_INTERNAL_FN (UBSAN_BOUNDS, ECF_LEAF | ECF_NOTHROW)
 DEF_INTERNAL_FN (UBSAN_CHECK_ADD, ECF_CONST | ECF_LEAF | ECF_NOTHROW)
 DEF_INTERNAL_FN (UBSAN_CHECK_SUB, ECF_CONST | ECF_LEAF | ECF_NOTHROW)
 DEF_INTERNAL_FN (UBSAN_CHECK_MUL, ECF_CONST | ECF_LEAF | ECF_NOTHROW)
diff --git gcc/internal-fn.h gcc/internal-fn.h
index 9c3215f..2dcf44e 100644
--- gcc/internal-fn.h
+++ gcc/internal-fn.h
@@ -20,13 +20,6 @@ along with GCC; see the file COPYING3.  If not see
 #ifndef GCC_INTERNAL_FN_H
 #define GCC_INTERNAL_FN_H
 
-enum internal_fn {
-#define DEF_INTERNAL_FN(CODE, FLAGS) IFN_##CODE,
-#include "internal-fn.def"
-#undef DEF_INTERNAL_FN
-  IFN_LAST
-};
-
 /* Return the name of internal function FN.  The name is only meaningful
    for dumps; it has no linkage.  */
 
diff --git gcc/opts.c gcc/opts.c
index 324d545..3ab06c6 100644
--- gcc/opts.c
+++ gcc/opts.c
@@ -1467,6 +1467,7 @@ common_handle_option (struct gcc_options *opts,
 		sizeof "float-divide-by-zero" - 1 },
 	      { "float-cast-overflow", SANITIZE_FLOAT_CAST,
 		sizeof "float-cast-overflow" - 1 },
+	      { "bounds", SANITIZE_BOUNDS, sizeof "bounds" - 1 },
 	      { NULL, 0, 0 }
 	    };
 	    const char *comma;
diff --git gcc/sanitizer.def gcc/sanitizer.def
index b4af164..1f5ef21 100644
--- gcc/sanitizer.def
+++ gcc/sanitizer.def
@@ -409,3 +409,11 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW_ABORT,
 		      "__ubsan_handle_float_cast_overflow_abort",
 		      BT_FN_VOID_PTR_PTR,
 		      ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS,
+		      "__ubsan_handle_out_of_bounds",
+		      BT_FN_VOID_PTR_PTR,
+		      ATTR_COLD_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS_ABORT,
+		      "__ubsan_handle_out_of_bounds_abort",
+		      BT_FN_VOID_PTR_PTR,
+		      ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)
diff --git gcc/testsuite/c-c++-common/ubsan/bounds-1.c gcc/testsuite/c-c++-common/ubsan/bounds-1.c
index e69de29..aa192d3 100644
--- gcc/testsuite/c-c++-common/ubsan/bounds-1.c
+++ gcc/testsuite/c-c++-common/ubsan/bounds-1.c
@@ -0,0 +1,75 @@
+/* { dg-do run } */
+/* { dg-options "-fsanitize=bounds -fno-sanitize-recover -Wall" } */
+
+/* Don't fail on valid uses.  */
+
+struct S { int a[10]; };
+struct T { int l; int a[]; };
+struct U { int l; int a[0]; };
+
+__attribute__ ((noinline, noclone))
+void
+fn_p (int p)
+{
+}
+
+__attribute__ ((noinline, noclone))
+void
+fn_a (volatile int a[])
+{
+  /* This is not instrumented.  */
+  a[4] = 5;
+}
+
+__attribute__ ((noinline, noclone))
+int
+foo_i (int i)
+{
+  int a[5] = { };
+  int k = i ? a[i] : i;
+  return k;
+}
+
+int
+main (void)
+{
+  volatile int a[5];
+  a[4] = 1;
+  a[2] = a[3];
+  fn_p (a[4]);
+  fn_a (a);
+
+  int i = 4;
+  a[i] = 1;
+  a[2] = a[i];
+  fn_p (a[i]);
+  foo_i (i);
+
+  const int n = 5;
+  volatile int b[n];
+  b[4] = 1;
+  b[2] = b[3];
+  fn_p (b[4]);
+  fn_a (b);
+
+  volatile int c[n][n][n];
+  c[2][2][2] = 2;
+  i = c[4][4][4];
+
+  volatile struct S s;
+  s.a[9] = 1;
+  i = s.a[9];
+
+  /* Don't instrument flexible array members.  */
+  struct T *t = (struct T *) __builtin_malloc (sizeof (struct T) + 10);
+  t->a[1] = 1;
+
+  struct U *u = (struct U *) __builtin_malloc (sizeof (struct U) + 10);
+  u->a[1] = 1;
+
+  long int *d[10][5];
+  d[9][0] = (long int *) 0;
+  d[8][3] = d[9][0];
+
+  return 0;
+}
diff --git gcc/testsuite/c-c++-common/ubsan/bounds-2.c gcc/testsuite/c-c++-common/ubsan/bounds-2.c
index e69de29..95f77c2 100644
--- gcc/testsuite/c-c++-common/ubsan/bounds-2.c
+++ gcc/testsuite/c-c++-common/ubsan/bounds-2.c
@@ -0,0 +1,168 @@
+/* { dg-do run } */
+/* { dg-options "-fsanitize=bounds -Wall -Wextra -Wno-unused -Wno-array-bounds" } */
+
+/* Test runtime errors.  */
+
+struct S { int a[10]; };
+
+int
+foo_5 (void)
+{
+  return 5;
+}
+
+__attribute__ ((noinline, noclone))
+void
+fn_p (int p)
+{
+  (void) p;
+}
+
+static void __attribute__ ((noinline, noclone))
+fn1 (void)
+{
+  volatile int a[5];
+  a[5] = 1;
+  a[2] = a[5];
+}
+
+static void __attribute__ ((noinline, noclone))
+fn2 (void)
+{
+  volatile int a[5];
+  int i = 5;
+  int *p = &i;
+  a[*p] = 1;
+}
+
+static void __attribute__ ((noinline, noclone))
+fn3 (void)
+{
+  volatile int a[5];
+  fn_p (a[5]);
+}
+
+static void __attribute__ ((noinline, noclone))
+fn4 (void)
+{
+  volatile int a[5];
+  a[foo_5 ()] = 1;
+}
+
+static void __attribute__ ((noinline, noclone))
+fn5 (void)
+{
+  int i = 5;
+  volatile int a[i];
+  a[i] = 1;
+  a[2] = a[i];
+}
+
+static void __attribute__ ((noinline, noclone))
+fn6 (void)
+{
+  int i = 5;
+  volatile int a[i];
+  fn_p (a[i]);
+  a[foo_5 ()] = 1;
+}
+
+static void __attribute__ ((noinline, noclone))
+fn7 (void)
+{
+  int n = 5, i;
+  volatile int c[n][n][n];
+  c[5][2][2] = 2;
+  c[2][5][2] = 2;
+  c[2][2][5] = 2;
+  i = c[5][2][2];
+  i = c[2][5][2];
+  i = c[2][2][5];
+}
+
+static void __attribute__ ((noinline, noclone))
+fn8 (void)
+{
+  int i = 5;
+  volatile struct S s;
+  s.a[10] = 1;
+  i = s.a[10];
+}
+
+static void __attribute__ ((noinline, noclone))
+fn9 (void)
+{
+  long int *volatile d[10][5];
+  d[10][0] = 0;
+  d[8][3] = d[10][0];
+}
+
+static void __attribute__ ((noinline, noclone))
+fn10 (void)
+{
+  /* Beware of side-effects.  */
+  volatile int x = 10;
+  volatile int e[20];
+  e[x++] = 3;
+  if (x != 11)
+    __builtin_abort ();
+  e[x--] = 3;
+  if (x != 10)
+    __builtin_abort ();
+}
+
+static void __attribute__ ((noinline, noclone))
+fn11 (void)
+{
+  char ***volatile f[5];
+  f[5] = 0;
+  f[2] = f[5];
+}
+
+static void __attribute__ ((noinline, noclone))
+fn12 (int i)
+{
+  volatile int a[5] = { };
+  int k = i ? a[i] : i;
+}
+
+int
+main (void)
+{
+  fn1 ();
+  fn2 ();
+  fn3 ();
+  fn4 ();
+  fn5 ();
+  fn6 ();
+  fn7 ();
+  fn8 ();
+  fn9 ();
+  fn10 ();
+  fn11 ();
+  fn12 (5);
+  return 0;
+}
+
+/* { dg-output "index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]\\\[\\\*\\\]\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]\\\[\\\*\\\]\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 10 out of bounds for type 'int \\\[10\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 10 out of bounds for type 'int \\\[10\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 10 out of bounds for type 'long int \\\*\\\[10\\\]\\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 10 out of bounds for type 'long int \\\*\\\[10\\\]\\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'char \\\*\\\*\\\*\\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'char \\\*\\\*\\\*\\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
diff --git gcc/testsuite/c-c++-common/ubsan/bounds-3.c gcc/testsuite/c-c++-common/ubsan/bounds-3.c
index e69de29..fcf71a3 100644
--- gcc/testsuite/c-c++-common/ubsan/bounds-3.c
+++ gcc/testsuite/c-c++-common/ubsan/bounds-3.c
@@ -0,0 +1,23 @@
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=bounds -Wall -Wextra" } */
+
+/* Do not generate invalid diagnostics.  */
+
+extern const int a[10];
+extern int bar (int);
+void
+foo (int i, int j)
+{
+  bar (a[i] >> j);
+  bar ((unsigned long) a[i] >> j);
+  bar ((short int) (unsigned long) a[i] >> j);
+  bar (j >> a[i]);
+  bar (j >> (unsigned long) a[i]);
+  bar (j >> (short int) (unsigned long) a[i]);
+  bar (a[i] / j);
+  bar ((unsigned long) a[i] / j);
+  bar ((short int) (unsigned long) a[i] / j);
+  bar (j / a[i]);
+  bar (j / (unsigned long) a[i]);
+  bar (j / (short int) (unsigned long) a[i]);
+}
diff --git gcc/testsuite/c-c++-common/ubsan/bounds-4.c gcc/testsuite/c-c++-common/ubsan/bounds-4.c
index e69de29..7748780 100644
--- gcc/testsuite/c-c++-common/ubsan/bounds-4.c
+++ gcc/testsuite/c-c++-common/ubsan/bounds-4.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=bounds -Wall -Wextra -Wno-unused" } */
+
+/* Initializers of TREE_STATICs aren't instrumented.
+   But don't ICE on 'em.  */
+
+int A[2];
+int *gp = &A[4];
+int *gpi;
+
+int
+main (void)
+{
+  gpi = &A[4];
+  static int *pi = &A[4];
+  return 0;
+}
diff --git gcc/testsuite/c-c++-common/ubsan/bounds-5.c gcc/testsuite/c-c++-common/ubsan/bounds-5.c
index e69de29..7b7d76d 100644
--- gcc/testsuite/c-c++-common/ubsan/bounds-5.c
+++ gcc/testsuite/c-c++-common/ubsan/bounds-5.c
@@ -0,0 +1,88 @@
+/* { dg-do run } */
+/* { dg-options "-fsanitize=bounds -Wall -Wextra -Wno-unused -Wno-array-bounds" } */
+
+/* Test flexible array member-like arrays.  Normal flexible array members
+   are tested in bounds-1.c.  Test non-strict mode.  */
+
+__attribute__ ((noinline, noclone))
+void
+fn1 (void)
+{
+  volatile struct S { char a[1]; char b; } s;
+  s.a[0] = 1; // OK
+  s.a[1] = 2; // error
+  volatile struct S *p = &s;
+  p->a[0] = 1; // OK
+  p->a[1] = 1; // error
+}
+
+__attribute__ ((noinline, noclone))
+void
+fn2 (void)
+{
+  struct S { int c; char d[4]; };
+  volatile struct T { int e; struct S f; int g; } t;
+  t.f.d[3] = 1; // OK
+  t.f.d[4] = 1; // error
+  volatile struct T *p = &t;
+  p->f.d[3] = 1; // OK
+  p->f.d[4] = 1; // error
+}
+
+__attribute__ ((noinline, noclone))
+void
+fn3 (void)
+{
+  volatile struct S { char b; char a[1]; } s;
+  s.a[0] = 1; // OK
+  s.a[1] = 1; // error
+  volatile struct S *p = &s;
+  p->a[0] = 1; // OK
+  p->a[1] = 1; // error in strict mode
+}
+
+__attribute__ ((noinline, noclone))
+void
+fn4 (void)
+{
+  volatile struct S { char b; char a[1]; } s;
+  volatile struct T { struct S s; int i; } t;
+  t.s.a[0] = 1; // OK
+  t.s.a[1] = 1; // error
+  volatile struct T *pt = &t;
+  pt->s.a[0] = 1; // OK
+  pt->s.a[1] = 1; // error
+}
+
+__attribute__ ((noinline, noclone))
+void
+fn5 (void)
+{
+  volatile struct S { char b; char a[1]; } s;
+  volatile struct U { int a; struct S s; } u;
+  u.s.a[0] = 1; // OK
+  u.s.a[1] = 1; // error
+  volatile struct U *pu = &u;
+  pu->s.a[0] = 1; // OK
+  pu->s.a[1] = 1; // error in strict mode
+}
+
+int
+main (void)
+{
+  fn1 ();
+  fn2 ();
+  fn3 ();
+  fn4 ();
+  fn5 ();
+  return 0;
+}
+
+/* { dg-output "index 1 out of bounds for type 'char \\\[1\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 1 out of bounds for type 'char \\\[1\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 4 out of bounds for type 'char \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 4 out of bounds for type 'char \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 1 out of bounds for type 'char \\\[1\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 1 out of bounds for type 'char \\\[1\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 1 out of bounds for type 'char \\\[1\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 1 out of bounds for type 'char \\\[1\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
diff --git gcc/testsuite/c-c++-common/ubsan/bounds-6.c gcc/testsuite/c-c++-common/ubsan/bounds-6.c
index e69de29..78ad29a 100644
--- gcc/testsuite/c-c++-common/ubsan/bounds-6.c
+++ gcc/testsuite/c-c++-common/ubsan/bounds-6.c
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=bounds -Wall -Wextra" } */
+
+/* Test off-by-one.  */
+
+struct S { int a; int b; } s[4], *t;
+struct U { int a[10]; } u[4], *v;
+volatile int *a, *b, *c;
+volatile void *d;
+volatile int e[4][4];
+
+int
+main (void)
+{
+  t = &s[4];  // OK
+  a = &s[4].a; // Error
+  b = &s[4].b; // Error
+  d = &e[4];  // OK
+  c = &e[4][0]; // Error
+  c = &e[3][4]; // OK
+  c = &e[3][3]; // OK
+
+  a = &u[4].a[9]; // Error
+  a = &u[4].a[10]; // Error
+  a = &u[3].a[9]; // OK
+  a = &u[3].a[10]; // OK
+  a = &u[3].a[11]; // Error
+
+  return 0;
+}
+
+/* { dg-output "index 4 out of bounds for type 'S \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 4 out of bounds for type 'S \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 4 out of bounds for type 'int \\\[4\\\]\\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 4 out of bounds for type 'U \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 4 out of bounds for type 'U \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 11 out of bounds for type 'int \\\[10\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
diff --git gcc/tree-core.h gcc/tree-core.h
index 52d93ec..7320f8a 100644
--- gcc/tree-core.h
+++ gcc/tree-core.h
@@ -665,6 +665,13 @@ enum annot_expr_kind {
   annot_expr_kind_last
 };
 
+/* Internal functions.  */
+enum internal_fn {
+#define DEF_INTERNAL_FN(CODE, FLAGS) IFN_##CODE,
+#include "internal-fn.def"
+#undef DEF_INTERNAL_FN
+  IFN_LAST
+};
 
 /*---------------------------------------------------------------------------
                                 Type definitions
@@ -787,6 +794,9 @@ struct GTY(()) tree_base {
 
     /* SSA version number.  This field is only used with SSA_NAME.  */
     unsigned int version;
+
+    /* Internal function code.  */
+    ENUM_BITFIELD(internal_fn) ifn : 5;
   } GTY((skip(""))) u;
 };
 
diff --git gcc/tree-pretty-print.c gcc/tree-pretty-print.c
index 59a825c..8522d79 100644
--- gcc/tree-pretty-print.c
+++ gcc/tree-pretty-print.c
@@ -37,6 +37,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "value-prof.h"
 #include "predict.h"
 #include "wide-int-print.h"
+#include "internal-fn.h"
 
 #include <new>                           // For placement-new.
 
@@ -1753,7 +1754,10 @@ dump_generic_node (pretty_printer *buffer, tree node, int spc, int flags,
       break;
 
     case CALL_EXPR:
-      print_call_name (buffer, CALL_EXPR_FN (node), flags);
+      if (CALL_EXPR_FN (node) != NULL_TREE)
+	print_call_name (buffer, CALL_EXPR_FN (node), flags);
+      else
+	pp_string (buffer, internal_fn_name (CALL_EXPR_IFN (node)));
 
       /* Print parameters.  */
       pp_space (buffer);
diff --git gcc/tree.c gcc/tree.c
index 240fa92..ac4012c 100644
--- gcc/tree.c
+++ gcc/tree.c
@@ -8971,6 +8971,10 @@ get_callee_fndecl (const_tree call)
      called.  */
   addr = CALL_EXPR_FN (call);
 
+  /* If there is no function, return early.  */
+  if (addr == NULL_TREE)
+    return NULL_TREE;
+
   STRIP_NOPS (addr);
 
   /* If this is a readonly function pointer, extract its initial value.  */
@@ -10598,6 +10602,27 @@ build_call_expr (tree fndecl, int n, ...)
   return build_call_expr_loc_array (UNKNOWN_LOCATION, fndecl, n, argarray);
 }
 
+/* Build internal call expression.  This is just like CALL_EXPR, except
+   its CALL_EXPR_FN is NULL.  It will get gimplified later into ordinary
+   internal function.  */
+
+tree
+build_call_expr_internal_loc (location_t loc, enum internal_fn ifn,
+			      tree type, int n, ...)
+{
+  va_list ap;
+  int i;
+
+  tree fn = build_call_1 (type, NULL_TREE, n);
+  va_start (ap, n);
+  for (i = 0; i < n; i++)
+    CALL_EXPR_ARG (fn, i) = va_arg (ap, tree);
+  va_end (ap);
+  SET_EXPR_LOCATION (fn, loc);
+  CALL_EXPR_IFN (fn) = ifn;
+  return fn;
+}
+
 /* Create a new constant string literal and return a char* pointer to it.
    The STRING_CST value is the LEN characters at STR.  */
 tree
diff --git gcc/tree.def gcc/tree.def
index 464c8c7..299aacc 100644
--- gcc/tree.def
+++ gcc/tree.def
@@ -574,7 +574,7 @@ DEFTREECODE (BIND_EXPR, "bind_expr", tcc_expression, 3)
 /* Function call.  CALL_EXPRs are represented by variably-sized expression
    nodes.  There are at least three fixed operands.  Operand 0 is an
    INTEGER_CST node containing the total operand count, the number of
-   arguments plus 3.  Operand 1 is the function, while operand 2 is
+   arguments plus 3.  Operand 1 is the function or NULL, while operand 2 is
    is static chain argument, or NULL.  The remaining operands are the
    arguments to the call.  */
 DEFTREECODE (CALL_EXPR, "call_expr", tcc_vl_exp, 3)
diff --git gcc/tree.h gcc/tree.h
index a5dbc5f..41de5c1 100644
--- gcc/tree.h
+++ gcc/tree.h
@@ -1134,12 +1134,12 @@ extern void protected_set_expr_location (tree, location_t);
 #define ASSERT_EXPR_VAR(NODE)	TREE_OPERAND (ASSERT_EXPR_CHECK (NODE), 0)
 #define ASSERT_EXPR_COND(NODE)	TREE_OPERAND (ASSERT_EXPR_CHECK (NODE), 1)
 
-/* CALL_EXPR accessors.
- */
+/* CALL_EXPR accessors.  */
 #define CALL_EXPR_FN(NODE) TREE_OPERAND (CALL_EXPR_CHECK (NODE), 1)
 #define CALL_EXPR_STATIC_CHAIN(NODE) TREE_OPERAND (CALL_EXPR_CHECK (NODE), 2)
 #define CALL_EXPR_ARG(NODE, I) TREE_OPERAND (CALL_EXPR_CHECK (NODE), (I) + 3)
 #define call_expr_nargs(NODE) (VL_EXP_OPERAND_LENGTH (NODE) - 3)
+#define CALL_EXPR_IFN(NODE) (CALL_EXPR_CHECK (NODE)->base.u.ifn)
 
 /* CALL_EXPR_ARGP returns a pointer to the argument vector for NODE.
    We can't use &CALL_EXPR_ARG (NODE, 0) because that will complain if
@@ -3631,6 +3631,8 @@ extern tree build_call_expr_loc_array (location_t, tree, int, tree *);
 extern tree build_call_expr_loc_vec (location_t, tree, vec<tree, va_gc> *);
 extern tree build_call_expr_loc (location_t, tree, int, ...);
 extern tree build_call_expr (tree, int, ...);
+extern tree build_call_expr_internal_loc (location_t, enum internal_fn,
+					  tree, int, ...);
 extern tree build_string_literal (int, const char *);
 
 /* Construct various nodes representing data types.  */
diff --git gcc/ubsan.c gcc/ubsan.c
index 5a8a447..5e1c3e7 100644
--- gcc/ubsan.c
+++ gcc/ubsan.c
@@ -271,23 +271,24 @@ get_ubsan_type_info_for_type (tree type)
   gcc_assert (TYPE_SIZE (type) && tree_fits_uhwi_p (TYPE_SIZE (type)));
   if (TREE_CODE (type) == REAL_TYPE)
     return tree_to_uhwi (TYPE_SIZE (type));
-  else
+  else if (INTEGRAL_TYPE_P (type))
     {
       int prec = exact_log2 (tree_to_uhwi (TYPE_SIZE (type)));
       gcc_assert (prec != -1);
       return (prec << 1) | !TYPE_UNSIGNED (type);
     }
+  else
+    return 0;
 }
 
 /* Helper routine that returns ADDR_EXPR of a VAR_DECL of a type
    descriptor.  It first looks into the hash table; if not found,
    create the VAR_DECL, put it into the hash table and return the
-   ADDR_EXPR of it.  TYPE describes a particular type.  WANT_POINTER_TYPE_P
-   means whether we are interested in the pointer type and not the pointer
-   itself.  */
+   ADDR_EXPR of it.  TYPE describes a particular type.  PSTYLE is
+   an enum controlling how we want to print the type.  */
 
 tree
-ubsan_type_descriptor (tree type, bool want_pointer_type_p)
+ubsan_type_descriptor (tree type, enum ubsan_print_style pstyle)
 {
   /* See through any typedefs.  */
   type = TYPE_MAIN_VARIANT (type);
@@ -308,7 +309,7 @@ ubsan_type_descriptor (tree type, bool want_pointer_type_p)
   unsigned short tkind, tinfo;
 
   /* Get the name of the type, or the name of the pointer type.  */
-  if (want_pointer_type_p)
+  if (pstyle == UBSAN_PRINT_POINTER)
     {
       gcc_assert (POINTER_TYPE_P (type));
       type2 = TREE_TYPE (type);
@@ -324,6 +325,12 @@ ubsan_type_descriptor (tree type, bool want_pointer_type_p)
   /* If an array, get its type.  */
   type2 = strip_array_types (type2);
 
+  if (pstyle == UBSAN_PRINT_ARRAY)
+    {
+      while (POINTER_TYPE_P (type2))
+        deref_depth++, type2 = TREE_TYPE (type2);
+    }
+
   if (TYPE_NAME (type2) != NULL)
     {
       if (TREE_CODE (TYPE_NAME (type2)) == IDENTIFIER_NODE)
@@ -338,7 +345,7 @@ ubsan_type_descriptor (tree type, bool want_pointer_type_p)
 
   /* Decorate the type name with '', '*', "struct", or "union".  */
   pretty_name = (char *) alloca (strlen (tname) + 16 + deref_depth);
-  if (want_pointer_type_p)
+  if (pstyle == UBSAN_PRINT_POINTER)
     {
       int pos = sprintf (pretty_name, "'%s%s%s%s%s%s%s",
 			 TYPE_VOLATILE (type2) ? "volatile " : "",
@@ -355,6 +362,33 @@ ubsan_type_descriptor (tree type, bool want_pointer_type_p)
       pretty_name[pos++] = '\'';
       pretty_name[pos] = '\0';
     }
+  else if (pstyle == UBSAN_PRINT_ARRAY)
+    {
+      /* Pretty print the array dimensions.  */
+      gcc_assert (TREE_CODE (type) == ARRAY_TYPE);
+      tree t = type;
+      int pos = sprintf (pretty_name, "'%s ", tname);
+      while (deref_depth-- > 0)
+        pretty_name[pos++] = '*';
+      while (TREE_CODE (t) == ARRAY_TYPE)
+	{
+	  pretty_name[pos++] = '[';
+	  tree dom = TYPE_DOMAIN (t);
+	  if (dom && TREE_CODE (TYPE_MAX_VALUE (dom)) == INTEGER_CST)
+	    pos += sprintf (&pretty_name[pos], HOST_WIDE_INT_PRINT_DEC,
+			    tree_to_shwi (TYPE_MAX_VALUE (dom)) + 1);
+	  else
+	    /* ??? We can't determine the variable name; print VLA unspec.  */
+	    pretty_name[pos++] = '*';
+	  pretty_name[pos++] = ']';
+	  t = TREE_TYPE (t);
+	}
+      pretty_name[pos++] = '\'';
+      pretty_name[pos] = '\0';
+
+     /* Save the tree with stripped types.  */
+     type = t;
+    }
   else
     sprintf (pretty_name, "'%s'", tname);
 
@@ -550,6 +584,69 @@ is_ubsan_builtin_p (tree t)
 		     "__builtin___ubsan_", 18) == 0;
 }
 
+/* Expand the UBSAN_BOUNDS special builtin function.  */
+
+void
+ubsan_expand_bounds_ifn (gimple_stmt_iterator *gsi)
+{
+  gimple stmt = gsi_stmt (*gsi);
+  location_t loc = gimple_location (stmt);
+  gcc_assert (gimple_call_num_args (stmt) == 3);
+
+  /* Pick up the arguments of the UBSAN_BOUNDS call.  */
+  tree type = TREE_TYPE (TREE_TYPE (gimple_call_arg (stmt, 0)));
+  tree index = gimple_call_arg (stmt, 1);
+  tree orig_index_type = TREE_TYPE (index);
+  tree bound = gimple_call_arg (stmt, 2);
+
+  gimple_stmt_iterator gsi_orig = *gsi;
+
+  /* Create condition "if (index > bound)".  */
+  basic_block then_bb, fallthru_bb;
+  gimple_stmt_iterator cond_insert_point
+    = create_cond_insert_point (gsi, 0/*before_p*/, false, true,
+				&then_bb, &fallthru_bb);
+  index = fold_convert (TREE_TYPE (bound), index);
+  index = force_gimple_operand_gsi (&cond_insert_point, index,
+				    true/*simple_p*/, NULL_TREE,
+				    false/*before*/, GSI_NEW_STMT);
+  gimple g = gimple_build_cond (GT_EXPR, index, bound, NULL_TREE, NULL_TREE);
+  gimple_set_location (g, loc);
+  gsi_insert_after (&cond_insert_point, g, GSI_NEW_STMT);
+
+  /* Generate __ubsan_handle_out_of_bounds call.  */
+  *gsi = gsi_after_labels (then_bb);
+  if (flag_sanitize_undefined_trap_on_error)
+    g = gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP), 0);
+  else
+    {
+      tree data
+	= ubsan_create_data ("__ubsan_out_of_bounds_data", &loc, NULL,
+			     ubsan_type_descriptor (type, UBSAN_PRINT_ARRAY),
+			     ubsan_type_descriptor (orig_index_type),
+			     NULL_TREE);
+      data = build_fold_addr_expr_loc (loc, data);
+      enum built_in_function bcode
+	= flag_sanitize_recover
+	  ? BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS
+	  : BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS_ABORT;
+      tree fn = builtin_decl_explicit (bcode);
+      tree val = force_gimple_operand_gsi (gsi, ubsan_encode_value (index),
+					   true, NULL_TREE, true,
+					   GSI_SAME_STMT);
+      g = gimple_build_call (fn, 2, data, val);
+    }
+  gimple_set_location (g, loc);
+  gsi_insert_before (gsi, g, GSI_SAME_STMT);
+
+  /* Get rid of the UBSAN_BOUNDS call from the IR.  */
+  unlink_stmt_vdef (stmt);
+  gsi_remove (&gsi_orig, true);
+
+  /* Point GSI to next logical statement.  */
+  *gsi = gsi_start_bb (fallthru_bb);
+}
+
 /* Expand UBSAN_NULL internal call.  */
 
 void
@@ -609,9 +706,11 @@ ubsan_expand_null_ifn (gimple_stmt_iterator gsi)
       tree fn = builtin_decl_implicit (bcode);
       const struct ubsan_mismatch_data m
 	= { build_zero_cst (pointer_sized_int_node), ckind };
-      tree data = ubsan_create_data ("__ubsan_null_data", &loc, &m,
-				     ubsan_type_descriptor (TREE_TYPE (ptr),
-							    true), NULL_TREE);
+      tree data
+	= ubsan_create_data ("__ubsan_null_data", &loc, &m,
+			     ubsan_type_descriptor (TREE_TYPE (ptr),
+						    UBSAN_PRINT_POINTER),
+			     NULL_TREE);
       data = build_fold_addr_expr_loc (loc, data);
       g = gimple_build_call (fn, 2, data,
 			     build_zero_cst (pointer_sized_int_node));
@@ -689,8 +788,7 @@ ubsan_build_overflow_builtin (tree_code code, location_t loc, tree lhstype,
     return build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0);
 
   tree data = ubsan_create_data ("__ubsan_overflow_data", &loc, NULL,
-				 ubsan_type_descriptor (lhstype, false),
-				 NULL_TREE);
+				 ubsan_type_descriptor (lhstype), NULL_TREE);
   enum built_in_function fn_code;
 
   switch (code)
@@ -884,8 +982,7 @@ instrument_bool_enum_load (gimple_stmt_iterator *gsi)
   else
     {
       tree data = ubsan_create_data ("__ubsan_invalid_value_data", &loc, NULL,
-				     ubsan_type_descriptor (type, false),
-				     NULL_TREE);
+				     ubsan_type_descriptor (type), NULL_TREE);
       data = build_fold_addr_expr_loc (loc, data);
       enum built_in_function bcode
 	= flag_sanitize_recover
@@ -1005,10 +1102,8 @@ ubsan_instrument_float_cast (location_t loc, tree type, tree expr)
     {
       /* Create the __ubsan_handle_float_cast_overflow fn call.  */
       tree data = ubsan_create_data ("__ubsan_float_cast_overflow_data", NULL,
-				     NULL,
-				     ubsan_type_descriptor (expr_type, false),
-				     ubsan_type_descriptor (type, false),
-				     NULL_TREE);
+				     NULL, ubsan_type_descriptor (expr_type),
+				     ubsan_type_descriptor (type), NULL_TREE);
       enum built_in_function bcode
 	= flag_sanitize_recover
 	  ? BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW
diff --git gcc/ubsan.h gcc/ubsan.h
index b008419..485449c 100644
--- gcc/ubsan.h
+++ gcc/ubsan.h
@@ -30,17 +30,25 @@ enum ubsan_null_ckind {
   UBSAN_MEMBER_CALL
 };
 
+/* This controls how ubsan prints types.  Used in ubsan_type_descriptor.  */
+enum ubsan_print_style {
+  UBSAN_PRINT_NORMAL,
+  UBSAN_PRINT_POINTER,
+  UBSAN_PRINT_ARRAY
+};
+
 /* An extra data used by ubsan pointer checking.  */
 struct ubsan_mismatch_data {
   tree align;
   tree ckind;
 };
 
+extern void ubsan_expand_bounds_ifn (gimple_stmt_iterator *);
 extern void ubsan_expand_null_ifn (gimple_stmt_iterator);
 extern tree ubsan_instrument_unreachable (location_t);
 extern tree ubsan_create_data (const char *, const location_t *,
 			       const struct ubsan_mismatch_data *, ...);
-extern tree ubsan_type_descriptor (tree, bool);
+extern tree ubsan_type_descriptor (tree, enum ubsan_print_style = UBSAN_PRINT_NORMAL);
 extern tree ubsan_encode_value (tree, bool = false);
 extern bool is_ubsan_builtin_p (tree);
 extern tree ubsan_build_overflow_builtin (tree_code, location_t, tree, tree, tree);

	Marek

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

* Re: [PATCH] Implement -fsanitize=bounds and internal calls in FEs
  2014-06-20  8:43       ` Marek Polacek
@ 2014-06-20  8:57         ` Jakub Jelinek
  2014-06-20  9:34           ` Marek Polacek
  0 siblings, 1 reply; 17+ messages in thread
From: Jakub Jelinek @ 2014-06-20  8:57 UTC (permalink / raw)
  To: Marek Polacek
  Cc: GCC Patches, Jason Merrill, Joseph S. Myers, Richard Biener, Jeff Law

On Fri, Jun 20, 2014 at 10:43:04AM +0200, Marek Polacek wrote:
> +
> +    /* Internal function code.  */
> +    ENUM_BITFIELD(internal_fn) ifn : 5;

Any reason for the " : 5" here?  I mean, the union also contains
unsigned int, so it doesn't hurt if you use full 32 bits for it there,
and it should be faster and you won't run into problems when we'll have
more than 32 internal functions.

Otherwise the patch looks good to me, but please wait for comments
from Joseph and/or Jason.

	Jakub

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

* Re: [PATCH] Implement -fsanitize=bounds and internal calls in FEs
  2014-06-20  8:57         ` Jakub Jelinek
@ 2014-06-20  9:34           ` Marek Polacek
  2014-06-20  9:39             ` Jakub Jelinek
  0 siblings, 1 reply; 17+ messages in thread
From: Marek Polacek @ 2014-06-20  9:34 UTC (permalink / raw)
  To: Jakub Jelinek
  Cc: GCC Patches, Jason Merrill, Joseph S. Myers, Richard Biener, Jeff Law

On Fri, Jun 20, 2014 at 10:57:47AM +0200, Jakub Jelinek wrote:
> On Fri, Jun 20, 2014 at 10:43:04AM +0200, Marek Polacek wrote:
> > +
> > +    /* Internal function code.  */
> > +    ENUM_BITFIELD(internal_fn) ifn : 5;
> 
> Any reason for the " : 5" here?  I mean, the union also contains
> unsigned int, so it doesn't hurt if you use full 32 bits for it there,
> and it should be faster and you won't run into problems when we'll have
> more than 32 internal functions.

The sole reason was that all other ENUM_BITFIELDs have it - on the
other hand, they're not in a union and here the bit-field is
pointless.  I'll drop it.
 
> Otherwise the patch looks good to me, but please wait for comments
> from Joseph and/or Jason.

Thanks for all your help!

The following is the same patch with only " : 5" dropped.

2014-06-20  Marek Polacek  <polacek@redhat.com>

	* asan.c (pass_sanopt::execute): Handle IFN_UBSAN_BOUNDS.
	* flag-types.h (enum sanitize_code): Add SANITIZE_BOUNDS and or it
	into SANITIZE_UNDEFINED.
	* doc/invoke.texi: Describe -fsanitize=bounds.
	* gimplify.c (gimplify_call_expr): Add gimplification of internal
	functions created in the FEs.
	* internal-fn.c: Move "internal-fn.h" after "tree.h".
	(expand_UBSAN_BOUNDS): New function.
	* internal-fn.def (UBSAN_BOUNDS): New internal function.
	* internal-fn.h: Don't define internal functions here.
	* opts.c (common_handle_option): Add -fsanitize=bounds.
	* sanitizer.def (BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS,
	BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS_ABORT): Add.
	* tree-core.h: Define internal functions here.
	(struct tree_base): Add ifn field.
	* tree-pretty-print.c: Include "internal-fn.h".
	(dump_generic_node): Handle functions without CALL_EXPR_FN.
	* tree.c (get_callee_fndecl): Likewise.
	(build_call_expr_internal_loc): New function.
	* tree.def (CALL_EXPR): Update description.
	* tree.h (CALL_EXPR_IFN): Define.
	(build_call_expr_internal_loc): Declare.
	* ubsan.c (get_ubsan_type_info_for_type): Return 0 for non-arithmetic
	types.
	(ubsan_type_descriptor): Change bool parameter to enum
	ubsan_print_style.  Adjust the code.  Add handling of
	UBSAN_PRINT_ARRAY.
	(ubsan_expand_bounds_ifn): New function.
	(ubsan_expand_null_ifn): Adjust ubsan_type_descriptor call.
	(ubsan_build_overflow_builtin): Likewise.
	(instrument_bool_enum_load): Likewise.
	(ubsan_instrument_float_cast): Likewise.
	* ubsan.h (enum ubsan_print_style): New enum.
	(ubsan_expand_bounds_ifn): Declare.
	(ubsan_type_descriptor): Adjust declaration.  Use a default parameter.
c-family/
	* c-gimplify.c: Include "c-ubsan.h" and "pointer-set.h".
	(ubsan_walk_array_refs_r): New function.
	(c_genericize): Instrument array bounds.
	* c-ubsan.c: Include "internal-fn.h".
	(ubsan_instrument_division): Mark instrumented arrays as having
	side effects.  Adjust ubsan_type_descriptor call.
	(ubsan_instrument_shift): Likewise.
	(ubsan_instrument_vla): Adjust ubsan_type_descriptor call.
	(ubsan_instrument_bounds): New function.
	(ubsan_array_ref_instrumented_p): New function.
	(ubsan_maybe_instrument_array_ref): New function.
	* c-ubsan.h (ubsan_instrument_bounds): Declare.
	(ubsan_array_ref_instrumented_p): Declare.
	(ubsan_maybe_instrument_array_ref): Declare.
testsuite/
	* c-c++-common/ubsan/bounds-1.c: New test.
	* c-c++-common/ubsan/bounds-2.c: New test.
	* c-c++-common/ubsan/bounds-3.c: New test.
	* c-c++-common/ubsan/bounds-4.c: New test.
	* c-c++-common/ubsan/bounds-5.c: New test.
	* c-c++-common/ubsan/bounds-6.c: New test.

diff --git gcc/asan.c gcc/asan.c
index 281a795..b7c76cf 100644
--- gcc/asan.c
+++ gcc/asan.c
@@ -2761,6 +2761,9 @@ pass_sanopt::execute (function *fun)
 	      case IFN_UBSAN_NULL:
 		ubsan_expand_null_ifn (gsi);
 		break;
+	      case IFN_UBSAN_BOUNDS:
+		ubsan_expand_bounds_ifn (&gsi);
+		break;
 	      default:
 		break;
 	      }
@@ -2771,6 +2774,10 @@ pass_sanopt::execute (function *fun)
 	      print_gimple_stmt (dump_file, stmt, 0, dump_flags);
 	      fprintf (dump_file, "\n");
 	    }
+
+	  /* ubsan_expand_bounds_ifn might move us to the end of the BB.  */
+	  if (gsi_end_p (gsi))
+	    break;
 	}
     }
   return 0;
diff --git gcc/c-family/c-gimplify.c gcc/c-family/c-gimplify.c
index 737be4d..c797d99 100644
--- gcc/c-family/c-gimplify.c
+++ gcc/c-family/c-gimplify.c
@@ -45,6 +45,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "c-pretty-print.h"
 #include "cgraph.h"
 #include "cilk.h"
+#include "c-ubsan.h"
+#include "pointer-set.h"
 
 /*  The gimplification pass converts the language-dependent trees
     (ld-trees) emitted by the parser into language-independent trees
@@ -67,6 +69,39 @@ along with GCC; see the file COPYING3.  If not see
     walk back up, we check that they fit our constraints, and copy them
     into temporaries if not.  */
 
+/* Callback for c_genericize.  */
+
+static tree
+ubsan_walk_array_refs_r (tree *tp, int *walk_subtrees, void *data)
+{
+  struct pointer_set_t *pset = (struct pointer_set_t *) data;
+
+  /* Since walk_tree doesn't call the callback function on the decls
+     in BIND_EXPR_VARS, we have to walk them manually.  */
+  if (TREE_CODE (*tp) == BIND_EXPR)
+    {
+      for (tree decl = BIND_EXPR_VARS (*tp); decl; decl = DECL_CHAIN (decl))
+	{
+	  if (TREE_STATIC (decl))
+	    {
+	      *walk_subtrees = 0;
+	      continue;
+	    }
+	  walk_tree (&DECL_INITIAL (decl), ubsan_walk_array_refs_r, pset,
+		     pset);
+	  walk_tree (&DECL_SIZE (decl), ubsan_walk_array_refs_r, pset, pset);
+	  walk_tree (&DECL_SIZE_UNIT (decl), ubsan_walk_array_refs_r, pset,
+		     pset);
+	}
+    }
+  else if (TREE_CODE (*tp) == ADDR_EXPR
+	   && TREE_CODE (TREE_OPERAND (*tp, 0)) == ARRAY_REF)
+    ubsan_maybe_instrument_array_ref (&TREE_OPERAND (*tp, 0), true);
+  else if (TREE_CODE (*tp) == ARRAY_REF)
+    ubsan_maybe_instrument_array_ref (tp, false);
+  return NULL_TREE;
+}
+
 /* Gimplification of statement trees.  */
 
 /* Convert the tree representation of FNDECL from C frontend trees to
@@ -79,6 +114,14 @@ c_genericize (tree fndecl)
   int local_dump_flags;
   struct cgraph_node *cgn;
 
+  if (flag_sanitize & SANITIZE_BOUNDS)
+    {
+      struct pointer_set_t *pset = pointer_set_create ();
+      walk_tree (&DECL_SAVED_TREE (fndecl), ubsan_walk_array_refs_r, pset,
+		 pset);
+      pointer_set_destroy (pset);
+    }
+
   /* Dump the C-specific tree IR.  */
   dump_orig = dump_begin (TDI_original, &local_dump_flags);
   if (dump_orig)
@@ -207,16 +250,16 @@ c_gimplify_expr (tree *expr_p, gimple_seq *pre_p ATTRIBUTE_UNUSED,
 	  }
 	break;
       }
-      
+
     case CILK_SPAWN_STMT:
-      gcc_assert 
-	(fn_contains_cilk_spawn_p (cfun) 
+      gcc_assert
+	(fn_contains_cilk_spawn_p (cfun)
 	 && cilk_detect_spawn_and_unwrap (expr_p));
-      
+
       /* If errors are seen, then just process it as a CALL_EXPR.  */
       if (!seen_error ())
 	return (enum gimplify_status) gimplify_cilk_spawn (expr_p);
-      
+
     case MODIFY_EXPR:
     case INIT_EXPR:
     case CALL_EXPR:
diff --git gcc/c-family/c-ubsan.c gcc/c-family/c-ubsan.c
index 8d5b685..3698580 100644
--- gcc/c-family/c-ubsan.c
+++ gcc/c-family/c-ubsan.c
@@ -30,6 +30,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "c-family/c-common.h"
 #include "c-family/c-ubsan.h"
 #include "asan.h"
+#include "internal-fn.h"
 
 /* Instrument division by zero and INT_MIN / -1.  If not instrumenting,
    return NULL_TREE.  */
@@ -77,15 +78,27 @@ ubsan_instrument_division (location_t loc, tree op0, tree op1)
     return NULL_TREE;
 
   /* In case we have a SAVE_EXPR in a conditional context, we need to
-     make sure it gets evaluated before the condition.  */
+     make sure it gets evaluated before the condition.  If the OP0 is
+     an instrumented array reference, mark it as having side effects so
+     it's not folded away.  */
+  if (flag_sanitize & SANITIZE_BOUNDS)
+    {
+      tree xop0 = op0;
+      while (CONVERT_EXPR_P (xop0))
+	xop0 = TREE_OPERAND (xop0, 0);
+      if (TREE_CODE (xop0) == ARRAY_REF)
+	{
+	  TREE_SIDE_EFFECTS (xop0) = 1;
+	  TREE_SIDE_EFFECTS (op0) = 1;
+	}
+    }
   t = fold_build2 (COMPOUND_EXPR, TREE_TYPE (t), op0, t);
   if (flag_sanitize_undefined_trap_on_error)
     tt = build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0);
   else
     {
       tree data = ubsan_create_data ("__ubsan_overflow_data", &loc, NULL,
-				     ubsan_type_descriptor (type, false),
-				     NULL_TREE);
+				     ubsan_type_descriptor (type), NULL_TREE);
       data = build_fold_addr_expr_loc (loc, data);
       enum built_in_function bcode
 	= flag_sanitize_recover
@@ -154,7 +167,20 @@ ubsan_instrument_shift (location_t loc, enum tree_code code,
     return NULL_TREE;
 
   /* In case we have a SAVE_EXPR in a conditional context, we need to
-     make sure it gets evaluated before the condition.  */
+     make sure it gets evaluated before the condition.  If the OP0 is
+     an instrumented array reference, mark it as having side effects so
+     it's not folded away.  */
+  if (flag_sanitize & SANITIZE_BOUNDS)
+    {
+      tree xop0 = op0;
+      while (CONVERT_EXPR_P (xop0))
+	xop0 = TREE_OPERAND (xop0, 0);
+      if (TREE_CODE (xop0) == ARRAY_REF)
+	{
+	  TREE_SIDE_EFFECTS (xop0) = 1;
+	  TREE_SIDE_EFFECTS (op0) = 1;
+	}
+    }
   t = fold_build2 (COMPOUND_EXPR, TREE_TYPE (t), op0, t);
   t = fold_build2 (TRUTH_OR_EXPR, boolean_type_node, t,
 		   tt ? tt : integer_zero_node);
@@ -164,10 +190,8 @@ ubsan_instrument_shift (location_t loc, enum tree_code code,
   else
     {
       tree data = ubsan_create_data ("__ubsan_shift_data", &loc, NULL,
-				     ubsan_type_descriptor (type0, false),
-				     ubsan_type_descriptor (type1, false),
-				     NULL_TREE);
-
+				     ubsan_type_descriptor (type0),
+				     ubsan_type_descriptor (type1), NULL_TREE);
       data = build_fold_addr_expr_loc (loc, data);
 
       enum built_in_function bcode
@@ -197,8 +221,7 @@ ubsan_instrument_vla (location_t loc, tree size)
   else
     {
       tree data = ubsan_create_data ("__ubsan_vla_data", &loc, NULL,
-				     ubsan_type_descriptor (type, false),
-				     NULL_TREE);
+				     ubsan_type_descriptor (type), NULL_TREE);
       data = build_fold_addr_expr_loc (loc, data);
       enum built_in_function bcode
 	= flag_sanitize_recover
@@ -228,3 +251,102 @@ ubsan_instrument_return (location_t loc)
   tree t = builtin_decl_explicit (BUILT_IN_UBSAN_HANDLE_MISSING_RETURN);
   return build_call_expr_loc (loc, t, 1, build_fold_addr_expr_loc (loc, data));
 }
+
+/* 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.
+   Return NULL_TREE if no instrumentation is emitted.
+   IGNORE_OFF_BY_ONE is true if the ARRAY_REF is inside a ADDR_EXPR.  */
+
+tree
+ubsan_instrument_bounds (location_t loc, tree array, tree *index,
+			 bool ignore_off_by_one)
+{
+  tree type = TREE_TYPE (array);
+  tree domain = TYPE_DOMAIN (type);
+
+  if (domain == NULL_TREE)
+    return NULL_TREE;
+
+  tree bound = TYPE_MAX_VALUE (domain);
+  if (ignore_off_by_one)
+    bound = fold_build2 (PLUS_EXPR, TREE_TYPE (bound), bound,
+			 build_int_cst (TREE_TYPE (bound), 1));
+
+  /* Detect flexible array members and suchlike.  */
+  tree base = get_base_address (array);
+  if (base && (TREE_CODE (base) == INDIRECT_REF
+	       || TREE_CODE (base) == MEM_REF))
+    {
+      tree next = NULL_TREE;
+      tree cref = array;
+
+      /* Walk all structs/unions.  */
+      while (TREE_CODE (cref) == COMPONENT_REF)
+	{
+	  if (TREE_CODE (TREE_TYPE (TREE_OPERAND (cref, 0))) == RECORD_TYPE)
+	    for (next = DECL_CHAIN (TREE_OPERAND (cref, 1));
+		 next && TREE_CODE (next) != FIELD_DECL;
+		 next = DECL_CHAIN (next))
+	      ;
+	  if (next)
+	    /* Not a last element.  Instrument it.  */
+	    break;
+	  /* Ok, this is the last field of the structure/union.  But the
+	     aggregate containing the field must be the last field too,
+	     recursively.  */
+	  cref = TREE_OPERAND (cref, 0);
+	}
+      if (!next)
+	/* Don't instrument this flexible array member-like array in non-strict
+	   -fsanitize=bounds mode.  */
+        return NULL_TREE;
+    }
+
+  *index = save_expr (*index);
+  /* Create a "(T *) 0" tree node to describe the array type.  */
+  tree zero_with_type = build_int_cst (build_pointer_type (type), 0);
+  return build_call_expr_internal_loc (loc, IFN_UBSAN_BOUNDS,
+				       void_type_node, 3, zero_with_type,
+				       *index, bound);
+}
+
+/* Return true iff T is an array that was instrumented by SANITIZE_BOUNDS.  */
+
+bool
+ubsan_array_ref_instrumented_p (const_tree t)
+{
+  if (TREE_CODE (t) != ARRAY_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;
+}
+
+/* Instrument an ARRAY_REF, 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)
+{
+  if (!ubsan_array_ref_instrumented_p (*expr_p)
+      && current_function_decl != NULL_TREE
+      && !lookup_attribute ("no_sanitize_undefined",
+			    DECL_ATTRIBUTES (current_function_decl)))
+    {
+      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 (e != NULL_TREE)
+	{
+	  tree t = copy_node (*expr_p);
+	  TREE_OPERAND (t, 1) = build2 (COMPOUND_EXPR, TREE_TYPE (op1),
+					e, op1);
+	  *expr_p = t;
+	}
+    }
+}
diff --git gcc/c-family/c-ubsan.h gcc/c-family/c-ubsan.h
index e504b90..edf5bc6 100644
--- gcc/c-family/c-ubsan.h
+++ gcc/c-family/c-ubsan.h
@@ -25,5 +25,8 @@ extern tree ubsan_instrument_division (location_t, tree, tree);
 extern tree ubsan_instrument_shift (location_t, enum tree_code, tree, tree);
 extern tree ubsan_instrument_vla (location_t, tree);
 extern tree ubsan_instrument_return (location_t);
+extern tree ubsan_instrument_bounds (location_t, tree, tree *, bool);
+extern bool ubsan_array_ref_instrumented_p (const_tree);
+extern void ubsan_maybe_instrument_array_ref (tree *, bool);
 
 #endif  /* GCC_C_UBSAN_H  */
diff --git gcc/doc/invoke.texi gcc/doc/invoke.texi
index f7a54fa..0d4bd00 100644
--- gcc/doc/invoke.texi
+++ gcc/doc/invoke.texi
@@ -5448,6 +5448,13 @@ This option enables floating-point type to integer conversion checking.
 We check that the result of the conversion does not overflow.
 This option does not work well with @code{FE_INVALID} exceptions enabled.
 
+@item -fsanitize=bounds
+@opindex fsanitize=bounds
+
+This option enables instrumentation of array bounds.  Various out of bounds
+accesses are detected.  Flexible array members are not instrumented, as well
+as initializers of variables with static storage.
+
 @item -fsanitize-recover
 @opindex fsanitize-recover
 By default @option{-fsanitize=undefined} sanitization (and its suboptions
diff --git gcc/flag-types.h gcc/flag-types.h
index ed00046..4b8e338 100644
--- gcc/flag-types.h
+++ gcc/flag-types.h
@@ -230,9 +230,11 @@ enum sanitize_code {
   SANITIZE_ENUM = 1 << 11,
   SANITIZE_FLOAT_DIVIDE = 1 << 12,
   SANITIZE_FLOAT_CAST = 1 << 13,
+  SANITIZE_BOUNDS = 1 << 14,
   SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE
 		       | SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN
-		       | SANITIZE_SI_OVERFLOW | SANITIZE_BOOL | SANITIZE_ENUM,
+		       | SANITIZE_SI_OVERFLOW | SANITIZE_BOOL | SANITIZE_ENUM
+		       | SANITIZE_BOUNDS,
   SANITIZE_NONDEFAULT = SANITIZE_FLOAT_DIVIDE | SANITIZE_FLOAT_CAST
 };
 
diff --git gcc/gimplify.c gcc/gimplify.c
index 3dcb4af..338c5c0 100644
--- gcc/gimplify.c
+++ gcc/gimplify.c
@@ -2264,6 +2264,24 @@ gimplify_call_expr (tree *expr_p, gimple_seq *pre_p, bool want_value)
   if (! EXPR_HAS_LOCATION (*expr_p))
     SET_EXPR_LOCATION (*expr_p, input_location);
 
+  /* Gimplify internal functions created in the FEs.  */
+  if (CALL_EXPR_FN (*expr_p) == NULL_TREE)
+    {
+      nargs = call_expr_nargs (*expr_p);
+      enum internal_fn ifn = CALL_EXPR_IFN (*expr_p);
+      auto_vec<tree> vargs (nargs);
+
+      for (i = 0; i < nargs; i++)
+	{
+	  gimplify_arg (&CALL_EXPR_ARG (*expr_p, i), pre_p,
+			EXPR_LOCATION (*expr_p));
+	  vargs.quick_push (CALL_EXPR_ARG (*expr_p, i));
+	}
+      gimple call = gimple_build_call_internal_vec (ifn, vargs);
+      gimplify_seq_add_stmt (pre_p, call);
+      return GS_ALL_DONE;
+    }
+
   /* This may be a call to a builtin function.
 
      Builtin function calls may be transformed into different
diff --git gcc/internal-fn.c gcc/internal-fn.c
index 68b2b66..78f59d6 100644
--- gcc/internal-fn.c
+++ gcc/internal-fn.c
@@ -20,8 +20,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
-#include "internal-fn.h"
 #include "tree.h"
+#include "internal-fn.h"
 #include "stor-layout.h"
 #include "expr.h"
 #include "optabs.h"
@@ -159,6 +159,14 @@ expand_UBSAN_NULL (gimple stmt ATTRIBUTE_UNUSED)
   gcc_unreachable ();
 }
 
+/* This should get expanded in the sanopt pass.  */
+
+static void
+expand_UBSAN_BOUNDS (gimple stmt ATTRIBUTE_UNUSED)
+{
+  gcc_unreachable ();
+}
+
 /* Add sub/add overflow checking to the statement STMT.
    CODE says whether the operation is +, or -.  */
 
diff --git gcc/internal-fn.def gcc/internal-fn.def
index 31dc4c9..f0766bc 100644
--- gcc/internal-fn.def
+++ gcc/internal-fn.def
@@ -48,6 +48,7 @@ DEF_INTERNAL_FN (MASK_LOAD, ECF_PURE | ECF_LEAF)
 DEF_INTERNAL_FN (MASK_STORE, ECF_LEAF)
 DEF_INTERNAL_FN (ANNOTATE,  ECF_CONST | ECF_LEAF | ECF_NOTHROW)
 DEF_INTERNAL_FN (UBSAN_NULL, ECF_LEAF | ECF_NOTHROW)
+DEF_INTERNAL_FN (UBSAN_BOUNDS, ECF_LEAF | ECF_NOTHROW)
 DEF_INTERNAL_FN (UBSAN_CHECK_ADD, ECF_CONST | ECF_LEAF | ECF_NOTHROW)
 DEF_INTERNAL_FN (UBSAN_CHECK_SUB, ECF_CONST | ECF_LEAF | ECF_NOTHROW)
 DEF_INTERNAL_FN (UBSAN_CHECK_MUL, ECF_CONST | ECF_LEAF | ECF_NOTHROW)
diff --git gcc/internal-fn.h gcc/internal-fn.h
index 9c3215f..2dcf44e 100644
--- gcc/internal-fn.h
+++ gcc/internal-fn.h
@@ -20,13 +20,6 @@ along with GCC; see the file COPYING3.  If not see
 #ifndef GCC_INTERNAL_FN_H
 #define GCC_INTERNAL_FN_H
 
-enum internal_fn {
-#define DEF_INTERNAL_FN(CODE, FLAGS) IFN_##CODE,
-#include "internal-fn.def"
-#undef DEF_INTERNAL_FN
-  IFN_LAST
-};
-
 /* Return the name of internal function FN.  The name is only meaningful
    for dumps; it has no linkage.  */
 
diff --git gcc/opts.c gcc/opts.c
index 324d545..3ab06c6 100644
--- gcc/opts.c
+++ gcc/opts.c
@@ -1467,6 +1467,7 @@ common_handle_option (struct gcc_options *opts,
 		sizeof "float-divide-by-zero" - 1 },
 	      { "float-cast-overflow", SANITIZE_FLOAT_CAST,
 		sizeof "float-cast-overflow" - 1 },
+	      { "bounds", SANITIZE_BOUNDS, sizeof "bounds" - 1 },
 	      { NULL, 0, 0 }
 	    };
 	    const char *comma;
diff --git gcc/sanitizer.def gcc/sanitizer.def
index b4af164..1f5ef21 100644
--- gcc/sanitizer.def
+++ gcc/sanitizer.def
@@ -409,3 +409,11 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW_ABORT,
 		      "__ubsan_handle_float_cast_overflow_abort",
 		      BT_FN_VOID_PTR_PTR,
 		      ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS,
+		      "__ubsan_handle_out_of_bounds",
+		      BT_FN_VOID_PTR_PTR,
+		      ATTR_COLD_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS_ABORT,
+		      "__ubsan_handle_out_of_bounds_abort",
+		      BT_FN_VOID_PTR_PTR,
+		      ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)
diff --git gcc/testsuite/c-c++-common/ubsan/bounds-1.c gcc/testsuite/c-c++-common/ubsan/bounds-1.c
index e69de29..aa192d3 100644
--- gcc/testsuite/c-c++-common/ubsan/bounds-1.c
+++ gcc/testsuite/c-c++-common/ubsan/bounds-1.c
@@ -0,0 +1,75 @@
+/* { dg-do run } */
+/* { dg-options "-fsanitize=bounds -fno-sanitize-recover -Wall" } */
+
+/* Don't fail on valid uses.  */
+
+struct S { int a[10]; };
+struct T { int l; int a[]; };
+struct U { int l; int a[0]; };
+
+__attribute__ ((noinline, noclone))
+void
+fn_p (int p)
+{
+}
+
+__attribute__ ((noinline, noclone))
+void
+fn_a (volatile int a[])
+{
+  /* This is not instrumented.  */
+  a[4] = 5;
+}
+
+__attribute__ ((noinline, noclone))
+int
+foo_i (int i)
+{
+  int a[5] = { };
+  int k = i ? a[i] : i;
+  return k;
+}
+
+int
+main (void)
+{
+  volatile int a[5];
+  a[4] = 1;
+  a[2] = a[3];
+  fn_p (a[4]);
+  fn_a (a);
+
+  int i = 4;
+  a[i] = 1;
+  a[2] = a[i];
+  fn_p (a[i]);
+  foo_i (i);
+
+  const int n = 5;
+  volatile int b[n];
+  b[4] = 1;
+  b[2] = b[3];
+  fn_p (b[4]);
+  fn_a (b);
+
+  volatile int c[n][n][n];
+  c[2][2][2] = 2;
+  i = c[4][4][4];
+
+  volatile struct S s;
+  s.a[9] = 1;
+  i = s.a[9];
+
+  /* Don't instrument flexible array members.  */
+  struct T *t = (struct T *) __builtin_malloc (sizeof (struct T) + 10);
+  t->a[1] = 1;
+
+  struct U *u = (struct U *) __builtin_malloc (sizeof (struct U) + 10);
+  u->a[1] = 1;
+
+  long int *d[10][5];
+  d[9][0] = (long int *) 0;
+  d[8][3] = d[9][0];
+
+  return 0;
+}
diff --git gcc/testsuite/c-c++-common/ubsan/bounds-2.c gcc/testsuite/c-c++-common/ubsan/bounds-2.c
index e69de29..95f77c2 100644
--- gcc/testsuite/c-c++-common/ubsan/bounds-2.c
+++ gcc/testsuite/c-c++-common/ubsan/bounds-2.c
@@ -0,0 +1,168 @@
+/* { dg-do run } */
+/* { dg-options "-fsanitize=bounds -Wall -Wextra -Wno-unused -Wno-array-bounds" } */
+
+/* Test runtime errors.  */
+
+struct S { int a[10]; };
+
+int
+foo_5 (void)
+{
+  return 5;
+}
+
+__attribute__ ((noinline, noclone))
+void
+fn_p (int p)
+{
+  (void) p;
+}
+
+static void __attribute__ ((noinline, noclone))
+fn1 (void)
+{
+  volatile int a[5];
+  a[5] = 1;
+  a[2] = a[5];
+}
+
+static void __attribute__ ((noinline, noclone))
+fn2 (void)
+{
+  volatile int a[5];
+  int i = 5;
+  int *p = &i;
+  a[*p] = 1;
+}
+
+static void __attribute__ ((noinline, noclone))
+fn3 (void)
+{
+  volatile int a[5];
+  fn_p (a[5]);
+}
+
+static void __attribute__ ((noinline, noclone))
+fn4 (void)
+{
+  volatile int a[5];
+  a[foo_5 ()] = 1;
+}
+
+static void __attribute__ ((noinline, noclone))
+fn5 (void)
+{
+  int i = 5;
+  volatile int a[i];
+  a[i] = 1;
+  a[2] = a[i];
+}
+
+static void __attribute__ ((noinline, noclone))
+fn6 (void)
+{
+  int i = 5;
+  volatile int a[i];
+  fn_p (a[i]);
+  a[foo_5 ()] = 1;
+}
+
+static void __attribute__ ((noinline, noclone))
+fn7 (void)
+{
+  int n = 5, i;
+  volatile int c[n][n][n];
+  c[5][2][2] = 2;
+  c[2][5][2] = 2;
+  c[2][2][5] = 2;
+  i = c[5][2][2];
+  i = c[2][5][2];
+  i = c[2][2][5];
+}
+
+static void __attribute__ ((noinline, noclone))
+fn8 (void)
+{
+  int i = 5;
+  volatile struct S s;
+  s.a[10] = 1;
+  i = s.a[10];
+}
+
+static void __attribute__ ((noinline, noclone))
+fn9 (void)
+{
+  long int *volatile d[10][5];
+  d[10][0] = 0;
+  d[8][3] = d[10][0];
+}
+
+static void __attribute__ ((noinline, noclone))
+fn10 (void)
+{
+  /* Beware of side-effects.  */
+  volatile int x = 10;
+  volatile int e[20];
+  e[x++] = 3;
+  if (x != 11)
+    __builtin_abort ();
+  e[x--] = 3;
+  if (x != 10)
+    __builtin_abort ();
+}
+
+static void __attribute__ ((noinline, noclone))
+fn11 (void)
+{
+  char ***volatile f[5];
+  f[5] = 0;
+  f[2] = f[5];
+}
+
+static void __attribute__ ((noinline, noclone))
+fn12 (int i)
+{
+  volatile int a[5] = { };
+  int k = i ? a[i] : i;
+}
+
+int
+main (void)
+{
+  fn1 ();
+  fn2 ();
+  fn3 ();
+  fn4 ();
+  fn5 ();
+  fn6 ();
+  fn7 ();
+  fn8 ();
+  fn9 ();
+  fn10 ();
+  fn11 ();
+  fn12 (5);
+  return 0;
+}
+
+/* { dg-output "index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]\\\[\\\*\\\]\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]\\\[\\\*\\\]\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 10 out of bounds for type 'int \\\[10\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 10 out of bounds for type 'int \\\[10\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 10 out of bounds for type 'long int \\\*\\\[10\\\]\\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 10 out of bounds for type 'long int \\\*\\\[10\\\]\\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'char \\\*\\\*\\\*\\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'char \\\*\\\*\\\*\\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
diff --git gcc/testsuite/c-c++-common/ubsan/bounds-3.c gcc/testsuite/c-c++-common/ubsan/bounds-3.c
index e69de29..fcf71a3 100644
--- gcc/testsuite/c-c++-common/ubsan/bounds-3.c
+++ gcc/testsuite/c-c++-common/ubsan/bounds-3.c
@@ -0,0 +1,23 @@
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=bounds -Wall -Wextra" } */
+
+/* Do not generate invalid diagnostics.  */
+
+extern const int a[10];
+extern int bar (int);
+void
+foo (int i, int j)
+{
+  bar (a[i] >> j);
+  bar ((unsigned long) a[i] >> j);
+  bar ((short int) (unsigned long) a[i] >> j);
+  bar (j >> a[i]);
+  bar (j >> (unsigned long) a[i]);
+  bar (j >> (short int) (unsigned long) a[i]);
+  bar (a[i] / j);
+  bar ((unsigned long) a[i] / j);
+  bar ((short int) (unsigned long) a[i] / j);
+  bar (j / a[i]);
+  bar (j / (unsigned long) a[i]);
+  bar (j / (short int) (unsigned long) a[i]);
+}
diff --git gcc/testsuite/c-c++-common/ubsan/bounds-4.c gcc/testsuite/c-c++-common/ubsan/bounds-4.c
index e69de29..7748780 100644
--- gcc/testsuite/c-c++-common/ubsan/bounds-4.c
+++ gcc/testsuite/c-c++-common/ubsan/bounds-4.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=bounds -Wall -Wextra -Wno-unused" } */
+
+/* Initializers of TREE_STATICs aren't instrumented.
+   But don't ICE on 'em.  */
+
+int A[2];
+int *gp = &A[4];
+int *gpi;
+
+int
+main (void)
+{
+  gpi = &A[4];
+  static int *pi = &A[4];
+  return 0;
+}
diff --git gcc/testsuite/c-c++-common/ubsan/bounds-5.c gcc/testsuite/c-c++-common/ubsan/bounds-5.c
index e69de29..7b7d76d 100644
--- gcc/testsuite/c-c++-common/ubsan/bounds-5.c
+++ gcc/testsuite/c-c++-common/ubsan/bounds-5.c
@@ -0,0 +1,88 @@
+/* { dg-do run } */
+/* { dg-options "-fsanitize=bounds -Wall -Wextra -Wno-unused -Wno-array-bounds" } */
+
+/* Test flexible array member-like arrays.  Normal flexible array members
+   are tested in bounds-1.c.  Test non-strict mode.  */
+
+__attribute__ ((noinline, noclone))
+void
+fn1 (void)
+{
+  volatile struct S { char a[1]; char b; } s;
+  s.a[0] = 1; // OK
+  s.a[1] = 2; // error
+  volatile struct S *p = &s;
+  p->a[0] = 1; // OK
+  p->a[1] = 1; // error
+}
+
+__attribute__ ((noinline, noclone))
+void
+fn2 (void)
+{
+  struct S { int c; char d[4]; };
+  volatile struct T { int e; struct S f; int g; } t;
+  t.f.d[3] = 1; // OK
+  t.f.d[4] = 1; // error
+  volatile struct T *p = &t;
+  p->f.d[3] = 1; // OK
+  p->f.d[4] = 1; // error
+}
+
+__attribute__ ((noinline, noclone))
+void
+fn3 (void)
+{
+  volatile struct S { char b; char a[1]; } s;
+  s.a[0] = 1; // OK
+  s.a[1] = 1; // error
+  volatile struct S *p = &s;
+  p->a[0] = 1; // OK
+  p->a[1] = 1; // error in strict mode
+}
+
+__attribute__ ((noinline, noclone))
+void
+fn4 (void)
+{
+  volatile struct S { char b; char a[1]; } s;
+  volatile struct T { struct S s; int i; } t;
+  t.s.a[0] = 1; // OK
+  t.s.a[1] = 1; // error
+  volatile struct T *pt = &t;
+  pt->s.a[0] = 1; // OK
+  pt->s.a[1] = 1; // error
+}
+
+__attribute__ ((noinline, noclone))
+void
+fn5 (void)
+{
+  volatile struct S { char b; char a[1]; } s;
+  volatile struct U { int a; struct S s; } u;
+  u.s.a[0] = 1; // OK
+  u.s.a[1] = 1; // error
+  volatile struct U *pu = &u;
+  pu->s.a[0] = 1; // OK
+  pu->s.a[1] = 1; // error in strict mode
+}
+
+int
+main (void)
+{
+  fn1 ();
+  fn2 ();
+  fn3 ();
+  fn4 ();
+  fn5 ();
+  return 0;
+}
+
+/* { dg-output "index 1 out of bounds for type 'char \\\[1\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 1 out of bounds for type 'char \\\[1\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 4 out of bounds for type 'char \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 4 out of bounds for type 'char \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 1 out of bounds for type 'char \\\[1\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 1 out of bounds for type 'char \\\[1\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 1 out of bounds for type 'char \\\[1\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 1 out of bounds for type 'char \\\[1\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
diff --git gcc/testsuite/c-c++-common/ubsan/bounds-6.c gcc/testsuite/c-c++-common/ubsan/bounds-6.c
index e69de29..78ad29a 100644
--- gcc/testsuite/c-c++-common/ubsan/bounds-6.c
+++ gcc/testsuite/c-c++-common/ubsan/bounds-6.c
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=bounds -Wall -Wextra" } */
+
+/* Test off-by-one.  */
+
+struct S { int a; int b; } s[4], *t;
+struct U { int a[10]; } u[4], *v;
+volatile int *a, *b, *c;
+volatile void *d;
+volatile int e[4][4];
+
+int
+main (void)
+{
+  t = &s[4];  // OK
+  a = &s[4].a; // Error
+  b = &s[4].b; // Error
+  d = &e[4];  // OK
+  c = &e[4][0]; // Error
+  c = &e[3][4]; // OK
+  c = &e[3][3]; // OK
+
+  a = &u[4].a[9]; // Error
+  a = &u[4].a[10]; // Error
+  a = &u[3].a[9]; // OK
+  a = &u[3].a[10]; // OK
+  a = &u[3].a[11]; // Error
+
+  return 0;
+}
+
+/* { dg-output "index 4 out of bounds for type 'S \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 4 out of bounds for type 'S \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 4 out of bounds for type 'int \\\[4\\\]\\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 4 out of bounds for type 'U \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 4 out of bounds for type 'U \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 11 out of bounds for type 'int \\\[10\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
diff --git gcc/tree-core.h gcc/tree-core.h
index 52d93ec..7320f8a 100644
--- gcc/tree-core.h
+++ gcc/tree-core.h
@@ -665,6 +665,13 @@ enum annot_expr_kind {
   annot_expr_kind_last
 };
 
+/* Internal functions.  */
+enum internal_fn {
+#define DEF_INTERNAL_FN(CODE, FLAGS) IFN_##CODE,
+#include "internal-fn.def"
+#undef DEF_INTERNAL_FN
+  IFN_LAST
+};
 
 /*---------------------------------------------------------------------------
                                 Type definitions
@@ -787,6 +794,9 @@ struct GTY(()) tree_base {
 
     /* SSA version number.  This field is only used with SSA_NAME.  */
     unsigned int version;
+
+    /* Internal function code.  */
+    ENUM_BITFIELD(internal_fn) ifn;
   } GTY((skip(""))) u;
 };
 
diff --git gcc/tree-pretty-print.c gcc/tree-pretty-print.c
index 59a825c..8522d79 100644
--- gcc/tree-pretty-print.c
+++ gcc/tree-pretty-print.c
@@ -37,6 +37,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "value-prof.h"
 #include "predict.h"
 #include "wide-int-print.h"
+#include "internal-fn.h"
 
 #include <new>                           // For placement-new.
 
@@ -1753,7 +1754,10 @@ dump_generic_node (pretty_printer *buffer, tree node, int spc, int flags,
       break;
 
     case CALL_EXPR:
-      print_call_name (buffer, CALL_EXPR_FN (node), flags);
+      if (CALL_EXPR_FN (node) != NULL_TREE)
+	print_call_name (buffer, CALL_EXPR_FN (node), flags);
+      else
+	pp_string (buffer, internal_fn_name (CALL_EXPR_IFN (node)));
 
       /* Print parameters.  */
       pp_space (buffer);
diff --git gcc/tree.c gcc/tree.c
index 240fa92..ac4012c 100644
--- gcc/tree.c
+++ gcc/tree.c
@@ -8971,6 +8971,10 @@ get_callee_fndecl (const_tree call)
      called.  */
   addr = CALL_EXPR_FN (call);
 
+  /* If there is no function, return early.  */
+  if (addr == NULL_TREE)
+    return NULL_TREE;
+
   STRIP_NOPS (addr);
 
   /* If this is a readonly function pointer, extract its initial value.  */
@@ -10598,6 +10602,27 @@ build_call_expr (tree fndecl, int n, ...)
   return build_call_expr_loc_array (UNKNOWN_LOCATION, fndecl, n, argarray);
 }
 
+/* Build internal call expression.  This is just like CALL_EXPR, except
+   its CALL_EXPR_FN is NULL.  It will get gimplified later into ordinary
+   internal function.  */
+
+tree
+build_call_expr_internal_loc (location_t loc, enum internal_fn ifn,
+			      tree type, int n, ...)
+{
+  va_list ap;
+  int i;
+
+  tree fn = build_call_1 (type, NULL_TREE, n);
+  va_start (ap, n);
+  for (i = 0; i < n; i++)
+    CALL_EXPR_ARG (fn, i) = va_arg (ap, tree);
+  va_end (ap);
+  SET_EXPR_LOCATION (fn, loc);
+  CALL_EXPR_IFN (fn) = ifn;
+  return fn;
+}
+
 /* Create a new constant string literal and return a char* pointer to it.
    The STRING_CST value is the LEN characters at STR.  */
 tree
diff --git gcc/tree.def gcc/tree.def
index 464c8c7..299aacc 100644
--- gcc/tree.def
+++ gcc/tree.def
@@ -574,7 +574,7 @@ DEFTREECODE (BIND_EXPR, "bind_expr", tcc_expression, 3)
 /* Function call.  CALL_EXPRs are represented by variably-sized expression
    nodes.  There are at least three fixed operands.  Operand 0 is an
    INTEGER_CST node containing the total operand count, the number of
-   arguments plus 3.  Operand 1 is the function, while operand 2 is
+   arguments plus 3.  Operand 1 is the function or NULL, while operand 2 is
    is static chain argument, or NULL.  The remaining operands are the
    arguments to the call.  */
 DEFTREECODE (CALL_EXPR, "call_expr", tcc_vl_exp, 3)
diff --git gcc/tree.h gcc/tree.h
index a5dbc5f..41de5c1 100644
--- gcc/tree.h
+++ gcc/tree.h
@@ -1134,12 +1134,12 @@ extern void protected_set_expr_location (tree, location_t);
 #define ASSERT_EXPR_VAR(NODE)	TREE_OPERAND (ASSERT_EXPR_CHECK (NODE), 0)
 #define ASSERT_EXPR_COND(NODE)	TREE_OPERAND (ASSERT_EXPR_CHECK (NODE), 1)
 
-/* CALL_EXPR accessors.
- */
+/* CALL_EXPR accessors.  */
 #define CALL_EXPR_FN(NODE) TREE_OPERAND (CALL_EXPR_CHECK (NODE), 1)
 #define CALL_EXPR_STATIC_CHAIN(NODE) TREE_OPERAND (CALL_EXPR_CHECK (NODE), 2)
 #define CALL_EXPR_ARG(NODE, I) TREE_OPERAND (CALL_EXPR_CHECK (NODE), (I) + 3)
 #define call_expr_nargs(NODE) (VL_EXP_OPERAND_LENGTH (NODE) - 3)
+#define CALL_EXPR_IFN(NODE) (CALL_EXPR_CHECK (NODE)->base.u.ifn)
 
 /* CALL_EXPR_ARGP returns a pointer to the argument vector for NODE.
    We can't use &CALL_EXPR_ARG (NODE, 0) because that will complain if
@@ -3631,6 +3631,8 @@ extern tree build_call_expr_loc_array (location_t, tree, int, tree *);
 extern tree build_call_expr_loc_vec (location_t, tree, vec<tree, va_gc> *);
 extern tree build_call_expr_loc (location_t, tree, int, ...);
 extern tree build_call_expr (tree, int, ...);
+extern tree build_call_expr_internal_loc (location_t, enum internal_fn,
+					  tree, int, ...);
 extern tree build_string_literal (int, const char *);
 
 /* Construct various nodes representing data types.  */
diff --git gcc/ubsan.c gcc/ubsan.c
index 5a8a447..5e1c3e7 100644
--- gcc/ubsan.c
+++ gcc/ubsan.c
@@ -271,23 +271,24 @@ get_ubsan_type_info_for_type (tree type)
   gcc_assert (TYPE_SIZE (type) && tree_fits_uhwi_p (TYPE_SIZE (type)));
   if (TREE_CODE (type) == REAL_TYPE)
     return tree_to_uhwi (TYPE_SIZE (type));
-  else
+  else if (INTEGRAL_TYPE_P (type))
     {
       int prec = exact_log2 (tree_to_uhwi (TYPE_SIZE (type)));
       gcc_assert (prec != -1);
       return (prec << 1) | !TYPE_UNSIGNED (type);
     }
+  else
+    return 0;
 }
 
 /* Helper routine that returns ADDR_EXPR of a VAR_DECL of a type
    descriptor.  It first looks into the hash table; if not found,
    create the VAR_DECL, put it into the hash table and return the
-   ADDR_EXPR of it.  TYPE describes a particular type.  WANT_POINTER_TYPE_P
-   means whether we are interested in the pointer type and not the pointer
-   itself.  */
+   ADDR_EXPR of it.  TYPE describes a particular type.  PSTYLE is
+   an enum controlling how we want to print the type.  */
 
 tree
-ubsan_type_descriptor (tree type, bool want_pointer_type_p)
+ubsan_type_descriptor (tree type, enum ubsan_print_style pstyle)
 {
   /* See through any typedefs.  */
   type = TYPE_MAIN_VARIANT (type);
@@ -308,7 +309,7 @@ ubsan_type_descriptor (tree type, bool want_pointer_type_p)
   unsigned short tkind, tinfo;
 
   /* Get the name of the type, or the name of the pointer type.  */
-  if (want_pointer_type_p)
+  if (pstyle == UBSAN_PRINT_POINTER)
     {
       gcc_assert (POINTER_TYPE_P (type));
       type2 = TREE_TYPE (type);
@@ -324,6 +325,12 @@ ubsan_type_descriptor (tree type, bool want_pointer_type_p)
   /* If an array, get its type.  */
   type2 = strip_array_types (type2);
 
+  if (pstyle == UBSAN_PRINT_ARRAY)
+    {
+      while (POINTER_TYPE_P (type2))
+        deref_depth++, type2 = TREE_TYPE (type2);
+    }
+
   if (TYPE_NAME (type2) != NULL)
     {
       if (TREE_CODE (TYPE_NAME (type2)) == IDENTIFIER_NODE)
@@ -338,7 +345,7 @@ ubsan_type_descriptor (tree type, bool want_pointer_type_p)
 
   /* Decorate the type name with '', '*', "struct", or "union".  */
   pretty_name = (char *) alloca (strlen (tname) + 16 + deref_depth);
-  if (want_pointer_type_p)
+  if (pstyle == UBSAN_PRINT_POINTER)
     {
       int pos = sprintf (pretty_name, "'%s%s%s%s%s%s%s",
 			 TYPE_VOLATILE (type2) ? "volatile " : "",
@@ -355,6 +362,33 @@ ubsan_type_descriptor (tree type, bool want_pointer_type_p)
       pretty_name[pos++] = '\'';
       pretty_name[pos] = '\0';
     }
+  else if (pstyle == UBSAN_PRINT_ARRAY)
+    {
+      /* Pretty print the array dimensions.  */
+      gcc_assert (TREE_CODE (type) == ARRAY_TYPE);
+      tree t = type;
+      int pos = sprintf (pretty_name, "'%s ", tname);
+      while (deref_depth-- > 0)
+        pretty_name[pos++] = '*';
+      while (TREE_CODE (t) == ARRAY_TYPE)
+	{
+	  pretty_name[pos++] = '[';
+	  tree dom = TYPE_DOMAIN (t);
+	  if (dom && TREE_CODE (TYPE_MAX_VALUE (dom)) == INTEGER_CST)
+	    pos += sprintf (&pretty_name[pos], HOST_WIDE_INT_PRINT_DEC,
+			    tree_to_shwi (TYPE_MAX_VALUE (dom)) + 1);
+	  else
+	    /* ??? We can't determine the variable name; print VLA unspec.  */
+	    pretty_name[pos++] = '*';
+	  pretty_name[pos++] = ']';
+	  t = TREE_TYPE (t);
+	}
+      pretty_name[pos++] = '\'';
+      pretty_name[pos] = '\0';
+
+     /* Save the tree with stripped types.  */
+     type = t;
+    }
   else
     sprintf (pretty_name, "'%s'", tname);
 
@@ -550,6 +584,69 @@ is_ubsan_builtin_p (tree t)
 		     "__builtin___ubsan_", 18) == 0;
 }
 
+/* Expand the UBSAN_BOUNDS special builtin function.  */
+
+void
+ubsan_expand_bounds_ifn (gimple_stmt_iterator *gsi)
+{
+  gimple stmt = gsi_stmt (*gsi);
+  location_t loc = gimple_location (stmt);
+  gcc_assert (gimple_call_num_args (stmt) == 3);
+
+  /* Pick up the arguments of the UBSAN_BOUNDS call.  */
+  tree type = TREE_TYPE (TREE_TYPE (gimple_call_arg (stmt, 0)));
+  tree index = gimple_call_arg (stmt, 1);
+  tree orig_index_type = TREE_TYPE (index);
+  tree bound = gimple_call_arg (stmt, 2);
+
+  gimple_stmt_iterator gsi_orig = *gsi;
+
+  /* Create condition "if (index > bound)".  */
+  basic_block then_bb, fallthru_bb;
+  gimple_stmt_iterator cond_insert_point
+    = create_cond_insert_point (gsi, 0/*before_p*/, false, true,
+				&then_bb, &fallthru_bb);
+  index = fold_convert (TREE_TYPE (bound), index);
+  index = force_gimple_operand_gsi (&cond_insert_point, index,
+				    true/*simple_p*/, NULL_TREE,
+				    false/*before*/, GSI_NEW_STMT);
+  gimple g = gimple_build_cond (GT_EXPR, index, bound, NULL_TREE, NULL_TREE);
+  gimple_set_location (g, loc);
+  gsi_insert_after (&cond_insert_point, g, GSI_NEW_STMT);
+
+  /* Generate __ubsan_handle_out_of_bounds call.  */
+  *gsi = gsi_after_labels (then_bb);
+  if (flag_sanitize_undefined_trap_on_error)
+    g = gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP), 0);
+  else
+    {
+      tree data
+	= ubsan_create_data ("__ubsan_out_of_bounds_data", &loc, NULL,
+			     ubsan_type_descriptor (type, UBSAN_PRINT_ARRAY),
+			     ubsan_type_descriptor (orig_index_type),
+			     NULL_TREE);
+      data = build_fold_addr_expr_loc (loc, data);
+      enum built_in_function bcode
+	= flag_sanitize_recover
+	  ? BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS
+	  : BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS_ABORT;
+      tree fn = builtin_decl_explicit (bcode);
+      tree val = force_gimple_operand_gsi (gsi, ubsan_encode_value (index),
+					   true, NULL_TREE, true,
+					   GSI_SAME_STMT);
+      g = gimple_build_call (fn, 2, data, val);
+    }
+  gimple_set_location (g, loc);
+  gsi_insert_before (gsi, g, GSI_SAME_STMT);
+
+  /* Get rid of the UBSAN_BOUNDS call from the IR.  */
+  unlink_stmt_vdef (stmt);
+  gsi_remove (&gsi_orig, true);
+
+  /* Point GSI to next logical statement.  */
+  *gsi = gsi_start_bb (fallthru_bb);
+}
+
 /* Expand UBSAN_NULL internal call.  */
 
 void
@@ -609,9 +706,11 @@ ubsan_expand_null_ifn (gimple_stmt_iterator gsi)
       tree fn = builtin_decl_implicit (bcode);
       const struct ubsan_mismatch_data m
 	= { build_zero_cst (pointer_sized_int_node), ckind };
-      tree data = ubsan_create_data ("__ubsan_null_data", &loc, &m,
-				     ubsan_type_descriptor (TREE_TYPE (ptr),
-							    true), NULL_TREE);
+      tree data
+	= ubsan_create_data ("__ubsan_null_data", &loc, &m,
+			     ubsan_type_descriptor (TREE_TYPE (ptr),
+						    UBSAN_PRINT_POINTER),
+			     NULL_TREE);
       data = build_fold_addr_expr_loc (loc, data);
       g = gimple_build_call (fn, 2, data,
 			     build_zero_cst (pointer_sized_int_node));
@@ -689,8 +788,7 @@ ubsan_build_overflow_builtin (tree_code code, location_t loc, tree lhstype,
     return build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0);
 
   tree data = ubsan_create_data ("__ubsan_overflow_data", &loc, NULL,
-				 ubsan_type_descriptor (lhstype, false),
-				 NULL_TREE);
+				 ubsan_type_descriptor (lhstype), NULL_TREE);
   enum built_in_function fn_code;
 
   switch (code)
@@ -884,8 +982,7 @@ instrument_bool_enum_load (gimple_stmt_iterator *gsi)
   else
     {
       tree data = ubsan_create_data ("__ubsan_invalid_value_data", &loc, NULL,
-				     ubsan_type_descriptor (type, false),
-				     NULL_TREE);
+				     ubsan_type_descriptor (type), NULL_TREE);
       data = build_fold_addr_expr_loc (loc, data);
       enum built_in_function bcode
 	= flag_sanitize_recover
@@ -1005,10 +1102,8 @@ ubsan_instrument_float_cast (location_t loc, tree type, tree expr)
     {
       /* Create the __ubsan_handle_float_cast_overflow fn call.  */
       tree data = ubsan_create_data ("__ubsan_float_cast_overflow_data", NULL,
-				     NULL,
-				     ubsan_type_descriptor (expr_type, false),
-				     ubsan_type_descriptor (type, false),
-				     NULL_TREE);
+				     NULL, ubsan_type_descriptor (expr_type),
+				     ubsan_type_descriptor (type), NULL_TREE);
       enum built_in_function bcode
 	= flag_sanitize_recover
 	  ? BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW
diff --git gcc/ubsan.h gcc/ubsan.h
index b008419..485449c 100644
--- gcc/ubsan.h
+++ gcc/ubsan.h
@@ -30,17 +30,25 @@ enum ubsan_null_ckind {
   UBSAN_MEMBER_CALL
 };
 
+/* This controls how ubsan prints types.  Used in ubsan_type_descriptor.  */
+enum ubsan_print_style {
+  UBSAN_PRINT_NORMAL,
+  UBSAN_PRINT_POINTER,
+  UBSAN_PRINT_ARRAY
+};
+
 /* An extra data used by ubsan pointer checking.  */
 struct ubsan_mismatch_data {
   tree align;
   tree ckind;
 };
 
+extern void ubsan_expand_bounds_ifn (gimple_stmt_iterator *);
 extern void ubsan_expand_null_ifn (gimple_stmt_iterator);
 extern tree ubsan_instrument_unreachable (location_t);
 extern tree ubsan_create_data (const char *, const location_t *,
 			       const struct ubsan_mismatch_data *, ...);
-extern tree ubsan_type_descriptor (tree, bool);
+extern tree ubsan_type_descriptor (tree, enum ubsan_print_style = UBSAN_PRINT_NORMAL);
 extern tree ubsan_encode_value (tree, bool = false);
 extern bool is_ubsan_builtin_p (tree);
 extern tree ubsan_build_overflow_builtin (tree_code, location_t, tree, tree, tree);

	Marek

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

* Re: [PATCH] Implement -fsanitize=bounds and internal calls in FEs
  2014-06-20  9:34           ` Marek Polacek
@ 2014-06-20  9:39             ` Jakub Jelinek
  2014-06-20 10:02               ` Marek Polacek
  0 siblings, 1 reply; 17+ messages in thread
From: Jakub Jelinek @ 2014-06-20  9:39 UTC (permalink / raw)
  To: Marek Polacek
  Cc: GCC Patches, Jason Merrill, Joseph S. Myers, Richard Biener, Jeff Law

On Fri, Jun 20, 2014 at 11:34:26AM +0200, Marek Polacek wrote:
> On Fri, Jun 20, 2014 at 10:57:47AM +0200, Jakub Jelinek wrote:
> > On Fri, Jun 20, 2014 at 10:43:04AM +0200, Marek Polacek wrote:
> > > +
> > > +    /* Internal function code.  */
> > > +    ENUM_BITFIELD(internal_fn) ifn : 5;
> > 
> > Any reason for the " : 5" here?  I mean, the union also contains
> > unsigned int, so it doesn't hurt if you use full 32 bits for it there,
> > and it should be faster and you won't run into problems when we'll have
> > more than 32 internal functions.
> 
> The sole reason was that all other ENUM_BITFIELDs have it - on the
> other hand, they're not in a union and here the bit-field is
> pointless.  I'll drop it.

Well, no point to use ENUM_BITFIELD either, just use
    enum internal_fn ifn;
?

	Jakub

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

* Re: [PATCH] Implement -fsanitize=bounds and internal calls in FEs
  2014-06-20  9:39             ` Jakub Jelinek
@ 2014-06-20 10:02               ` Marek Polacek
  2014-06-20 15:35                 ` Joseph S. Myers
  2014-06-28 16:52                 ` Gerald Pfeifer
  0 siblings, 2 replies; 17+ messages in thread
From: Marek Polacek @ 2014-06-20 10:02 UTC (permalink / raw)
  To: Jakub Jelinek
  Cc: GCC Patches, Jason Merrill, Joseph S. Myers, Richard Biener, Jeff Law

On Fri, Jun 20, 2014 at 11:39:23AM +0200, Jakub Jelinek wrote:
> On Fri, Jun 20, 2014 at 11:34:26AM +0200, Marek Polacek wrote:
> > On Fri, Jun 20, 2014 at 10:57:47AM +0200, Jakub Jelinek wrote:
> > > On Fri, Jun 20, 2014 at 10:43:04AM +0200, Marek Polacek wrote:
> > > > +
> > > > +    /* Internal function code.  */
> > > > +    ENUM_BITFIELD(internal_fn) ifn : 5;
> > > 
> > > Any reason for the " : 5" here?  I mean, the union also contains
> > > unsigned int, so it doesn't hurt if you use full 32 bits for it there,
> > > and it should be faster and you won't run into problems when we'll have
> > > more than 32 internal functions.
> > 
> > The sole reason was that all other ENUM_BITFIELDs have it - on the
> > other hand, they're not in a union and here the bit-field is
> > pointless.  I'll drop it.
> 
> Well, no point to use ENUM_BITFIELD either, just use
>     enum internal_fn ifn;

Works as well.

2014-06-20  Marek Polacek  <polacek@redhat.com>

	* asan.c (pass_sanopt::execute): Handle IFN_UBSAN_BOUNDS.
	* flag-types.h (enum sanitize_code): Add SANITIZE_BOUNDS and or it
	into SANITIZE_UNDEFINED.
	* doc/invoke.texi: Describe -fsanitize=bounds.
	* gimplify.c (gimplify_call_expr): Add gimplification of internal
	functions created in the FEs.
	* internal-fn.c: Move "internal-fn.h" after "tree.h".
	(expand_UBSAN_BOUNDS): New function.
	* internal-fn.def (UBSAN_BOUNDS): New internal function.
	* internal-fn.h: Don't define internal functions here.
	* opts.c (common_handle_option): Add -fsanitize=bounds.
	* sanitizer.def (BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS,
	BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS_ABORT): Add.
	* tree-core.h: Define internal functions here.
	(struct tree_base): Add ifn field.
	* tree-pretty-print.c: Include "internal-fn.h".
	(dump_generic_node): Handle functions without CALL_EXPR_FN.
	* tree.c (get_callee_fndecl): Likewise.
	(build_call_expr_internal_loc): New function.
	* tree.def (CALL_EXPR): Update description.
	* tree.h (CALL_EXPR_IFN): Define.
	(build_call_expr_internal_loc): Declare.
	* ubsan.c (get_ubsan_type_info_for_type): Return 0 for non-arithmetic
	types.
	(ubsan_type_descriptor): Change bool parameter to enum
	ubsan_print_style.  Adjust the code.  Add handling of
	UBSAN_PRINT_ARRAY.
	(ubsan_expand_bounds_ifn): New function.
	(ubsan_expand_null_ifn): Adjust ubsan_type_descriptor call.
	(ubsan_build_overflow_builtin): Likewise.
	(instrument_bool_enum_load): Likewise.
	(ubsan_instrument_float_cast): Likewise.
	* ubsan.h (enum ubsan_print_style): New enum.
	(ubsan_expand_bounds_ifn): Declare.
	(ubsan_type_descriptor): Adjust declaration.  Use a default parameter.
c-family/
	* c-gimplify.c: Include "c-ubsan.h" and "pointer-set.h".
	(ubsan_walk_array_refs_r): New function.
	(c_genericize): Instrument array bounds.
	* c-ubsan.c: Include "internal-fn.h".
	(ubsan_instrument_division): Mark instrumented arrays as having
	side effects.  Adjust ubsan_type_descriptor call.
	(ubsan_instrument_shift): Likewise.
	(ubsan_instrument_vla): Adjust ubsan_type_descriptor call.
	(ubsan_instrument_bounds): New function.
	(ubsan_array_ref_instrumented_p): New function.
	(ubsan_maybe_instrument_array_ref): New function.
	* c-ubsan.h (ubsan_instrument_bounds): Declare.
	(ubsan_array_ref_instrumented_p): Declare.
	(ubsan_maybe_instrument_array_ref): Declare.
testsuite/
	* c-c++-common/ubsan/bounds-1.c: New test.
	* c-c++-common/ubsan/bounds-2.c: New test.
	* c-c++-common/ubsan/bounds-3.c: New test.
	* c-c++-common/ubsan/bounds-4.c: New test.
	* c-c++-common/ubsan/bounds-5.c: New test.
	* c-c++-common/ubsan/bounds-6.c: New test.

diff --git gcc/asan.c gcc/asan.c
index 281a795..b7c76cf 100644
--- gcc/asan.c
+++ gcc/asan.c
@@ -2761,6 +2761,9 @@ pass_sanopt::execute (function *fun)
 	      case IFN_UBSAN_NULL:
 		ubsan_expand_null_ifn (gsi);
 		break;
+	      case IFN_UBSAN_BOUNDS:
+		ubsan_expand_bounds_ifn (&gsi);
+		break;
 	      default:
 		break;
 	      }
@@ -2771,6 +2774,10 @@ pass_sanopt::execute (function *fun)
 	      print_gimple_stmt (dump_file, stmt, 0, dump_flags);
 	      fprintf (dump_file, "\n");
 	    }
+
+	  /* ubsan_expand_bounds_ifn might move us to the end of the BB.  */
+	  if (gsi_end_p (gsi))
+	    break;
 	}
     }
   return 0;
diff --git gcc/c-family/c-gimplify.c gcc/c-family/c-gimplify.c
index 737be4d..c797d99 100644
--- gcc/c-family/c-gimplify.c
+++ gcc/c-family/c-gimplify.c
@@ -45,6 +45,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "c-pretty-print.h"
 #include "cgraph.h"
 #include "cilk.h"
+#include "c-ubsan.h"
+#include "pointer-set.h"
 
 /*  The gimplification pass converts the language-dependent trees
     (ld-trees) emitted by the parser into language-independent trees
@@ -67,6 +69,39 @@ along with GCC; see the file COPYING3.  If not see
     walk back up, we check that they fit our constraints, and copy them
     into temporaries if not.  */
 
+/* Callback for c_genericize.  */
+
+static tree
+ubsan_walk_array_refs_r (tree *tp, int *walk_subtrees, void *data)
+{
+  struct pointer_set_t *pset = (struct pointer_set_t *) data;
+
+  /* Since walk_tree doesn't call the callback function on the decls
+     in BIND_EXPR_VARS, we have to walk them manually.  */
+  if (TREE_CODE (*tp) == BIND_EXPR)
+    {
+      for (tree decl = BIND_EXPR_VARS (*tp); decl; decl = DECL_CHAIN (decl))
+	{
+	  if (TREE_STATIC (decl))
+	    {
+	      *walk_subtrees = 0;
+	      continue;
+	    }
+	  walk_tree (&DECL_INITIAL (decl), ubsan_walk_array_refs_r, pset,
+		     pset);
+	  walk_tree (&DECL_SIZE (decl), ubsan_walk_array_refs_r, pset, pset);
+	  walk_tree (&DECL_SIZE_UNIT (decl), ubsan_walk_array_refs_r, pset,
+		     pset);
+	}
+    }
+  else if (TREE_CODE (*tp) == ADDR_EXPR
+	   && TREE_CODE (TREE_OPERAND (*tp, 0)) == ARRAY_REF)
+    ubsan_maybe_instrument_array_ref (&TREE_OPERAND (*tp, 0), true);
+  else if (TREE_CODE (*tp) == ARRAY_REF)
+    ubsan_maybe_instrument_array_ref (tp, false);
+  return NULL_TREE;
+}
+
 /* Gimplification of statement trees.  */
 
 /* Convert the tree representation of FNDECL from C frontend trees to
@@ -79,6 +114,14 @@ c_genericize (tree fndecl)
   int local_dump_flags;
   struct cgraph_node *cgn;
 
+  if (flag_sanitize & SANITIZE_BOUNDS)
+    {
+      struct pointer_set_t *pset = pointer_set_create ();
+      walk_tree (&DECL_SAVED_TREE (fndecl), ubsan_walk_array_refs_r, pset,
+		 pset);
+      pointer_set_destroy (pset);
+    }
+
   /* Dump the C-specific tree IR.  */
   dump_orig = dump_begin (TDI_original, &local_dump_flags);
   if (dump_orig)
@@ -207,16 +250,16 @@ c_gimplify_expr (tree *expr_p, gimple_seq *pre_p ATTRIBUTE_UNUSED,
 	  }
 	break;
       }
-      
+
     case CILK_SPAWN_STMT:
-      gcc_assert 
-	(fn_contains_cilk_spawn_p (cfun) 
+      gcc_assert
+	(fn_contains_cilk_spawn_p (cfun)
 	 && cilk_detect_spawn_and_unwrap (expr_p));
-      
+
       /* If errors are seen, then just process it as a CALL_EXPR.  */
       if (!seen_error ())
 	return (enum gimplify_status) gimplify_cilk_spawn (expr_p);
-      
+
     case MODIFY_EXPR:
     case INIT_EXPR:
     case CALL_EXPR:
diff --git gcc/c-family/c-ubsan.c gcc/c-family/c-ubsan.c
index 8d5b685..3698580 100644
--- gcc/c-family/c-ubsan.c
+++ gcc/c-family/c-ubsan.c
@@ -30,6 +30,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "c-family/c-common.h"
 #include "c-family/c-ubsan.h"
 #include "asan.h"
+#include "internal-fn.h"
 
 /* Instrument division by zero and INT_MIN / -1.  If not instrumenting,
    return NULL_TREE.  */
@@ -77,15 +78,27 @@ ubsan_instrument_division (location_t loc, tree op0, tree op1)
     return NULL_TREE;
 
   /* In case we have a SAVE_EXPR in a conditional context, we need to
-     make sure it gets evaluated before the condition.  */
+     make sure it gets evaluated before the condition.  If the OP0 is
+     an instrumented array reference, mark it as having side effects so
+     it's not folded away.  */
+  if (flag_sanitize & SANITIZE_BOUNDS)
+    {
+      tree xop0 = op0;
+      while (CONVERT_EXPR_P (xop0))
+	xop0 = TREE_OPERAND (xop0, 0);
+      if (TREE_CODE (xop0) == ARRAY_REF)
+	{
+	  TREE_SIDE_EFFECTS (xop0) = 1;
+	  TREE_SIDE_EFFECTS (op0) = 1;
+	}
+    }
   t = fold_build2 (COMPOUND_EXPR, TREE_TYPE (t), op0, t);
   if (flag_sanitize_undefined_trap_on_error)
     tt = build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0);
   else
     {
       tree data = ubsan_create_data ("__ubsan_overflow_data", &loc, NULL,
-				     ubsan_type_descriptor (type, false),
-				     NULL_TREE);
+				     ubsan_type_descriptor (type), NULL_TREE);
       data = build_fold_addr_expr_loc (loc, data);
       enum built_in_function bcode
 	= flag_sanitize_recover
@@ -154,7 +167,20 @@ ubsan_instrument_shift (location_t loc, enum tree_code code,
     return NULL_TREE;
 
   /* In case we have a SAVE_EXPR in a conditional context, we need to
-     make sure it gets evaluated before the condition.  */
+     make sure it gets evaluated before the condition.  If the OP0 is
+     an instrumented array reference, mark it as having side effects so
+     it's not folded away.  */
+  if (flag_sanitize & SANITIZE_BOUNDS)
+    {
+      tree xop0 = op0;
+      while (CONVERT_EXPR_P (xop0))
+	xop0 = TREE_OPERAND (xop0, 0);
+      if (TREE_CODE (xop0) == ARRAY_REF)
+	{
+	  TREE_SIDE_EFFECTS (xop0) = 1;
+	  TREE_SIDE_EFFECTS (op0) = 1;
+	}
+    }
   t = fold_build2 (COMPOUND_EXPR, TREE_TYPE (t), op0, t);
   t = fold_build2 (TRUTH_OR_EXPR, boolean_type_node, t,
 		   tt ? tt : integer_zero_node);
@@ -164,10 +190,8 @@ ubsan_instrument_shift (location_t loc, enum tree_code code,
   else
     {
       tree data = ubsan_create_data ("__ubsan_shift_data", &loc, NULL,
-				     ubsan_type_descriptor (type0, false),
-				     ubsan_type_descriptor (type1, false),
-				     NULL_TREE);
-
+				     ubsan_type_descriptor (type0),
+				     ubsan_type_descriptor (type1), NULL_TREE);
       data = build_fold_addr_expr_loc (loc, data);
 
       enum built_in_function bcode
@@ -197,8 +221,7 @@ ubsan_instrument_vla (location_t loc, tree size)
   else
     {
       tree data = ubsan_create_data ("__ubsan_vla_data", &loc, NULL,
-				     ubsan_type_descriptor (type, false),
-				     NULL_TREE);
+				     ubsan_type_descriptor (type), NULL_TREE);
       data = build_fold_addr_expr_loc (loc, data);
       enum built_in_function bcode
 	= flag_sanitize_recover
@@ -228,3 +251,102 @@ ubsan_instrument_return (location_t loc)
   tree t = builtin_decl_explicit (BUILT_IN_UBSAN_HANDLE_MISSING_RETURN);
   return build_call_expr_loc (loc, t, 1, build_fold_addr_expr_loc (loc, data));
 }
+
+/* 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.
+   Return NULL_TREE if no instrumentation is emitted.
+   IGNORE_OFF_BY_ONE is true if the ARRAY_REF is inside a ADDR_EXPR.  */
+
+tree
+ubsan_instrument_bounds (location_t loc, tree array, tree *index,
+			 bool ignore_off_by_one)
+{
+  tree type = TREE_TYPE (array);
+  tree domain = TYPE_DOMAIN (type);
+
+  if (domain == NULL_TREE)
+    return NULL_TREE;
+
+  tree bound = TYPE_MAX_VALUE (domain);
+  if (ignore_off_by_one)
+    bound = fold_build2 (PLUS_EXPR, TREE_TYPE (bound), bound,
+			 build_int_cst (TREE_TYPE (bound), 1));
+
+  /* Detect flexible array members and suchlike.  */
+  tree base = get_base_address (array);
+  if (base && (TREE_CODE (base) == INDIRECT_REF
+	       || TREE_CODE (base) == MEM_REF))
+    {
+      tree next = NULL_TREE;
+      tree cref = array;
+
+      /* Walk all structs/unions.  */
+      while (TREE_CODE (cref) == COMPONENT_REF)
+	{
+	  if (TREE_CODE (TREE_TYPE (TREE_OPERAND (cref, 0))) == RECORD_TYPE)
+	    for (next = DECL_CHAIN (TREE_OPERAND (cref, 1));
+		 next && TREE_CODE (next) != FIELD_DECL;
+		 next = DECL_CHAIN (next))
+	      ;
+	  if (next)
+	    /* Not a last element.  Instrument it.  */
+	    break;
+	  /* Ok, this is the last field of the structure/union.  But the
+	     aggregate containing the field must be the last field too,
+	     recursively.  */
+	  cref = TREE_OPERAND (cref, 0);
+	}
+      if (!next)
+	/* Don't instrument this flexible array member-like array in non-strict
+	   -fsanitize=bounds mode.  */
+        return NULL_TREE;
+    }
+
+  *index = save_expr (*index);
+  /* Create a "(T *) 0" tree node to describe the array type.  */
+  tree zero_with_type = build_int_cst (build_pointer_type (type), 0);
+  return build_call_expr_internal_loc (loc, IFN_UBSAN_BOUNDS,
+				       void_type_node, 3, zero_with_type,
+				       *index, bound);
+}
+
+/* Return true iff T is an array that was instrumented by SANITIZE_BOUNDS.  */
+
+bool
+ubsan_array_ref_instrumented_p (const_tree t)
+{
+  if (TREE_CODE (t) != ARRAY_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;
+}
+
+/* Instrument an ARRAY_REF, 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)
+{
+  if (!ubsan_array_ref_instrumented_p (*expr_p)
+      && current_function_decl != NULL_TREE
+      && !lookup_attribute ("no_sanitize_undefined",
+			    DECL_ATTRIBUTES (current_function_decl)))
+    {
+      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 (e != NULL_TREE)
+	{
+	  tree t = copy_node (*expr_p);
+	  TREE_OPERAND (t, 1) = build2 (COMPOUND_EXPR, TREE_TYPE (op1),
+					e, op1);
+	  *expr_p = t;
+	}
+    }
+}
diff --git gcc/c-family/c-ubsan.h gcc/c-family/c-ubsan.h
index e504b90..edf5bc6 100644
--- gcc/c-family/c-ubsan.h
+++ gcc/c-family/c-ubsan.h
@@ -25,5 +25,8 @@ extern tree ubsan_instrument_division (location_t, tree, tree);
 extern tree ubsan_instrument_shift (location_t, enum tree_code, tree, tree);
 extern tree ubsan_instrument_vla (location_t, tree);
 extern tree ubsan_instrument_return (location_t);
+extern tree ubsan_instrument_bounds (location_t, tree, tree *, bool);
+extern bool ubsan_array_ref_instrumented_p (const_tree);
+extern void ubsan_maybe_instrument_array_ref (tree *, bool);
 
 #endif  /* GCC_C_UBSAN_H  */
diff --git gcc/doc/invoke.texi gcc/doc/invoke.texi
index f7a54fa..0d4bd00 100644
--- gcc/doc/invoke.texi
+++ gcc/doc/invoke.texi
@@ -5448,6 +5448,13 @@ This option enables floating-point type to integer conversion checking.
 We check that the result of the conversion does not overflow.
 This option does not work well with @code{FE_INVALID} exceptions enabled.
 
+@item -fsanitize=bounds
+@opindex fsanitize=bounds
+
+This option enables instrumentation of array bounds.  Various out of bounds
+accesses are detected.  Flexible array members are not instrumented, as well
+as initializers of variables with static storage.
+
 @item -fsanitize-recover
 @opindex fsanitize-recover
 By default @option{-fsanitize=undefined} sanitization (and its suboptions
diff --git gcc/flag-types.h gcc/flag-types.h
index ed00046..4b8e338 100644
--- gcc/flag-types.h
+++ gcc/flag-types.h
@@ -230,9 +230,11 @@ enum sanitize_code {
   SANITIZE_ENUM = 1 << 11,
   SANITIZE_FLOAT_DIVIDE = 1 << 12,
   SANITIZE_FLOAT_CAST = 1 << 13,
+  SANITIZE_BOUNDS = 1 << 14,
   SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE
 		       | SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN
-		       | SANITIZE_SI_OVERFLOW | SANITIZE_BOOL | SANITIZE_ENUM,
+		       | SANITIZE_SI_OVERFLOW | SANITIZE_BOOL | SANITIZE_ENUM
+		       | SANITIZE_BOUNDS,
   SANITIZE_NONDEFAULT = SANITIZE_FLOAT_DIVIDE | SANITIZE_FLOAT_CAST
 };
 
diff --git gcc/gimplify.c gcc/gimplify.c
index 3dcb4af..338c5c0 100644
--- gcc/gimplify.c
+++ gcc/gimplify.c
@@ -2264,6 +2264,24 @@ gimplify_call_expr (tree *expr_p, gimple_seq *pre_p, bool want_value)
   if (! EXPR_HAS_LOCATION (*expr_p))
     SET_EXPR_LOCATION (*expr_p, input_location);
 
+  /* Gimplify internal functions created in the FEs.  */
+  if (CALL_EXPR_FN (*expr_p) == NULL_TREE)
+    {
+      nargs = call_expr_nargs (*expr_p);
+      enum internal_fn ifn = CALL_EXPR_IFN (*expr_p);
+      auto_vec<tree> vargs (nargs);
+
+      for (i = 0; i < nargs; i++)
+	{
+	  gimplify_arg (&CALL_EXPR_ARG (*expr_p, i), pre_p,
+			EXPR_LOCATION (*expr_p));
+	  vargs.quick_push (CALL_EXPR_ARG (*expr_p, i));
+	}
+      gimple call = gimple_build_call_internal_vec (ifn, vargs);
+      gimplify_seq_add_stmt (pre_p, call);
+      return GS_ALL_DONE;
+    }
+
   /* This may be a call to a builtin function.
 
      Builtin function calls may be transformed into different
diff --git gcc/internal-fn.c gcc/internal-fn.c
index 68b2b66..78f59d6 100644
--- gcc/internal-fn.c
+++ gcc/internal-fn.c
@@ -20,8 +20,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
-#include "internal-fn.h"
 #include "tree.h"
+#include "internal-fn.h"
 #include "stor-layout.h"
 #include "expr.h"
 #include "optabs.h"
@@ -159,6 +159,14 @@ expand_UBSAN_NULL (gimple stmt ATTRIBUTE_UNUSED)
   gcc_unreachable ();
 }
 
+/* This should get expanded in the sanopt pass.  */
+
+static void
+expand_UBSAN_BOUNDS (gimple stmt ATTRIBUTE_UNUSED)
+{
+  gcc_unreachable ();
+}
+
 /* Add sub/add overflow checking to the statement STMT.
    CODE says whether the operation is +, or -.  */
 
diff --git gcc/internal-fn.def gcc/internal-fn.def
index 31dc4c9..f0766bc 100644
--- gcc/internal-fn.def
+++ gcc/internal-fn.def
@@ -48,6 +48,7 @@ DEF_INTERNAL_FN (MASK_LOAD, ECF_PURE | ECF_LEAF)
 DEF_INTERNAL_FN (MASK_STORE, ECF_LEAF)
 DEF_INTERNAL_FN (ANNOTATE,  ECF_CONST | ECF_LEAF | ECF_NOTHROW)
 DEF_INTERNAL_FN (UBSAN_NULL, ECF_LEAF | ECF_NOTHROW)
+DEF_INTERNAL_FN (UBSAN_BOUNDS, ECF_LEAF | ECF_NOTHROW)
 DEF_INTERNAL_FN (UBSAN_CHECK_ADD, ECF_CONST | ECF_LEAF | ECF_NOTHROW)
 DEF_INTERNAL_FN (UBSAN_CHECK_SUB, ECF_CONST | ECF_LEAF | ECF_NOTHROW)
 DEF_INTERNAL_FN (UBSAN_CHECK_MUL, ECF_CONST | ECF_LEAF | ECF_NOTHROW)
diff --git gcc/internal-fn.h gcc/internal-fn.h
index 9c3215f..2dcf44e 100644
--- gcc/internal-fn.h
+++ gcc/internal-fn.h
@@ -20,13 +20,6 @@ along with GCC; see the file COPYING3.  If not see
 #ifndef GCC_INTERNAL_FN_H
 #define GCC_INTERNAL_FN_H
 
-enum internal_fn {
-#define DEF_INTERNAL_FN(CODE, FLAGS) IFN_##CODE,
-#include "internal-fn.def"
-#undef DEF_INTERNAL_FN
-  IFN_LAST
-};
-
 /* Return the name of internal function FN.  The name is only meaningful
    for dumps; it has no linkage.  */
 
diff --git gcc/opts.c gcc/opts.c
index 324d545..3ab06c6 100644
--- gcc/opts.c
+++ gcc/opts.c
@@ -1467,6 +1467,7 @@ common_handle_option (struct gcc_options *opts,
 		sizeof "float-divide-by-zero" - 1 },
 	      { "float-cast-overflow", SANITIZE_FLOAT_CAST,
 		sizeof "float-cast-overflow" - 1 },
+	      { "bounds", SANITIZE_BOUNDS, sizeof "bounds" - 1 },
 	      { NULL, 0, 0 }
 	    };
 	    const char *comma;
diff --git gcc/sanitizer.def gcc/sanitizer.def
index b4af164..1f5ef21 100644
--- gcc/sanitizer.def
+++ gcc/sanitizer.def
@@ -409,3 +409,11 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW_ABORT,
 		      "__ubsan_handle_float_cast_overflow_abort",
 		      BT_FN_VOID_PTR_PTR,
 		      ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS,
+		      "__ubsan_handle_out_of_bounds",
+		      BT_FN_VOID_PTR_PTR,
+		      ATTR_COLD_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS_ABORT,
+		      "__ubsan_handle_out_of_bounds_abort",
+		      BT_FN_VOID_PTR_PTR,
+		      ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)
diff --git gcc/testsuite/c-c++-common/ubsan/bounds-1.c gcc/testsuite/c-c++-common/ubsan/bounds-1.c
index e69de29..aa192d3 100644
--- gcc/testsuite/c-c++-common/ubsan/bounds-1.c
+++ gcc/testsuite/c-c++-common/ubsan/bounds-1.c
@@ -0,0 +1,75 @@
+/* { dg-do run } */
+/* { dg-options "-fsanitize=bounds -fno-sanitize-recover -Wall" } */
+
+/* Don't fail on valid uses.  */
+
+struct S { int a[10]; };
+struct T { int l; int a[]; };
+struct U { int l; int a[0]; };
+
+__attribute__ ((noinline, noclone))
+void
+fn_p (int p)
+{
+}
+
+__attribute__ ((noinline, noclone))
+void
+fn_a (volatile int a[])
+{
+  /* This is not instrumented.  */
+  a[4] = 5;
+}
+
+__attribute__ ((noinline, noclone))
+int
+foo_i (int i)
+{
+  int a[5] = { };
+  int k = i ? a[i] : i;
+  return k;
+}
+
+int
+main (void)
+{
+  volatile int a[5];
+  a[4] = 1;
+  a[2] = a[3];
+  fn_p (a[4]);
+  fn_a (a);
+
+  int i = 4;
+  a[i] = 1;
+  a[2] = a[i];
+  fn_p (a[i]);
+  foo_i (i);
+
+  const int n = 5;
+  volatile int b[n];
+  b[4] = 1;
+  b[2] = b[3];
+  fn_p (b[4]);
+  fn_a (b);
+
+  volatile int c[n][n][n];
+  c[2][2][2] = 2;
+  i = c[4][4][4];
+
+  volatile struct S s;
+  s.a[9] = 1;
+  i = s.a[9];
+
+  /* Don't instrument flexible array members.  */
+  struct T *t = (struct T *) __builtin_malloc (sizeof (struct T) + 10);
+  t->a[1] = 1;
+
+  struct U *u = (struct U *) __builtin_malloc (sizeof (struct U) + 10);
+  u->a[1] = 1;
+
+  long int *d[10][5];
+  d[9][0] = (long int *) 0;
+  d[8][3] = d[9][0];
+
+  return 0;
+}
diff --git gcc/testsuite/c-c++-common/ubsan/bounds-2.c gcc/testsuite/c-c++-common/ubsan/bounds-2.c
index e69de29..95f77c2 100644
--- gcc/testsuite/c-c++-common/ubsan/bounds-2.c
+++ gcc/testsuite/c-c++-common/ubsan/bounds-2.c
@@ -0,0 +1,168 @@
+/* { dg-do run } */
+/* { dg-options "-fsanitize=bounds -Wall -Wextra -Wno-unused -Wno-array-bounds" } */
+
+/* Test runtime errors.  */
+
+struct S { int a[10]; };
+
+int
+foo_5 (void)
+{
+  return 5;
+}
+
+__attribute__ ((noinline, noclone))
+void
+fn_p (int p)
+{
+  (void) p;
+}
+
+static void __attribute__ ((noinline, noclone))
+fn1 (void)
+{
+  volatile int a[5];
+  a[5] = 1;
+  a[2] = a[5];
+}
+
+static void __attribute__ ((noinline, noclone))
+fn2 (void)
+{
+  volatile int a[5];
+  int i = 5;
+  int *p = &i;
+  a[*p] = 1;
+}
+
+static void __attribute__ ((noinline, noclone))
+fn3 (void)
+{
+  volatile int a[5];
+  fn_p (a[5]);
+}
+
+static void __attribute__ ((noinline, noclone))
+fn4 (void)
+{
+  volatile int a[5];
+  a[foo_5 ()] = 1;
+}
+
+static void __attribute__ ((noinline, noclone))
+fn5 (void)
+{
+  int i = 5;
+  volatile int a[i];
+  a[i] = 1;
+  a[2] = a[i];
+}
+
+static void __attribute__ ((noinline, noclone))
+fn6 (void)
+{
+  int i = 5;
+  volatile int a[i];
+  fn_p (a[i]);
+  a[foo_5 ()] = 1;
+}
+
+static void __attribute__ ((noinline, noclone))
+fn7 (void)
+{
+  int n = 5, i;
+  volatile int c[n][n][n];
+  c[5][2][2] = 2;
+  c[2][5][2] = 2;
+  c[2][2][5] = 2;
+  i = c[5][2][2];
+  i = c[2][5][2];
+  i = c[2][2][5];
+}
+
+static void __attribute__ ((noinline, noclone))
+fn8 (void)
+{
+  int i = 5;
+  volatile struct S s;
+  s.a[10] = 1;
+  i = s.a[10];
+}
+
+static void __attribute__ ((noinline, noclone))
+fn9 (void)
+{
+  long int *volatile d[10][5];
+  d[10][0] = 0;
+  d[8][3] = d[10][0];
+}
+
+static void __attribute__ ((noinline, noclone))
+fn10 (void)
+{
+  /* Beware of side-effects.  */
+  volatile int x = 10;
+  volatile int e[20];
+  e[x++] = 3;
+  if (x != 11)
+    __builtin_abort ();
+  e[x--] = 3;
+  if (x != 10)
+    __builtin_abort ();
+}
+
+static void __attribute__ ((noinline, noclone))
+fn11 (void)
+{
+  char ***volatile f[5];
+  f[5] = 0;
+  f[2] = f[5];
+}
+
+static void __attribute__ ((noinline, noclone))
+fn12 (int i)
+{
+  volatile int a[5] = { };
+  int k = i ? a[i] : i;
+}
+
+int
+main (void)
+{
+  fn1 ();
+  fn2 ();
+  fn3 ();
+  fn4 ();
+  fn5 ();
+  fn6 ();
+  fn7 ();
+  fn8 ();
+  fn9 ();
+  fn10 ();
+  fn11 ();
+  fn12 (5);
+  return 0;
+}
+
+/* { dg-output "index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]\\\[\\\*\\\]\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]\\\[\\\*\\\]\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 10 out of bounds for type 'int \\\[10\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 10 out of bounds for type 'int \\\[10\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 10 out of bounds for type 'long int \\\*\\\[10\\\]\\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 10 out of bounds for type 'long int \\\*\\\[10\\\]\\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'char \\\*\\\*\\\*\\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'char \\\*\\\*\\\*\\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
diff --git gcc/testsuite/c-c++-common/ubsan/bounds-3.c gcc/testsuite/c-c++-common/ubsan/bounds-3.c
index e69de29..fcf71a3 100644
--- gcc/testsuite/c-c++-common/ubsan/bounds-3.c
+++ gcc/testsuite/c-c++-common/ubsan/bounds-3.c
@@ -0,0 +1,23 @@
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=bounds -Wall -Wextra" } */
+
+/* Do not generate invalid diagnostics.  */
+
+extern const int a[10];
+extern int bar (int);
+void
+foo (int i, int j)
+{
+  bar (a[i] >> j);
+  bar ((unsigned long) a[i] >> j);
+  bar ((short int) (unsigned long) a[i] >> j);
+  bar (j >> a[i]);
+  bar (j >> (unsigned long) a[i]);
+  bar (j >> (short int) (unsigned long) a[i]);
+  bar (a[i] / j);
+  bar ((unsigned long) a[i] / j);
+  bar ((short int) (unsigned long) a[i] / j);
+  bar (j / a[i]);
+  bar (j / (unsigned long) a[i]);
+  bar (j / (short int) (unsigned long) a[i]);
+}
diff --git gcc/testsuite/c-c++-common/ubsan/bounds-4.c gcc/testsuite/c-c++-common/ubsan/bounds-4.c
index e69de29..7748780 100644
--- gcc/testsuite/c-c++-common/ubsan/bounds-4.c
+++ gcc/testsuite/c-c++-common/ubsan/bounds-4.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=bounds -Wall -Wextra -Wno-unused" } */
+
+/* Initializers of TREE_STATICs aren't instrumented.
+   But don't ICE on 'em.  */
+
+int A[2];
+int *gp = &A[4];
+int *gpi;
+
+int
+main (void)
+{
+  gpi = &A[4];
+  static int *pi = &A[4];
+  return 0;
+}
diff --git gcc/testsuite/c-c++-common/ubsan/bounds-5.c gcc/testsuite/c-c++-common/ubsan/bounds-5.c
index e69de29..7b7d76d 100644
--- gcc/testsuite/c-c++-common/ubsan/bounds-5.c
+++ gcc/testsuite/c-c++-common/ubsan/bounds-5.c
@@ -0,0 +1,88 @@
+/* { dg-do run } */
+/* { dg-options "-fsanitize=bounds -Wall -Wextra -Wno-unused -Wno-array-bounds" } */
+
+/* Test flexible array member-like arrays.  Normal flexible array members
+   are tested in bounds-1.c.  Test non-strict mode.  */
+
+__attribute__ ((noinline, noclone))
+void
+fn1 (void)
+{
+  volatile struct S { char a[1]; char b; } s;
+  s.a[0] = 1; // OK
+  s.a[1] = 2; // error
+  volatile struct S *p = &s;
+  p->a[0] = 1; // OK
+  p->a[1] = 1; // error
+}
+
+__attribute__ ((noinline, noclone))
+void
+fn2 (void)
+{
+  struct S { int c; char d[4]; };
+  volatile struct T { int e; struct S f; int g; } t;
+  t.f.d[3] = 1; // OK
+  t.f.d[4] = 1; // error
+  volatile struct T *p = &t;
+  p->f.d[3] = 1; // OK
+  p->f.d[4] = 1; // error
+}
+
+__attribute__ ((noinline, noclone))
+void
+fn3 (void)
+{
+  volatile struct S { char b; char a[1]; } s;
+  s.a[0] = 1; // OK
+  s.a[1] = 1; // error
+  volatile struct S *p = &s;
+  p->a[0] = 1; // OK
+  p->a[1] = 1; // error in strict mode
+}
+
+__attribute__ ((noinline, noclone))
+void
+fn4 (void)
+{
+  volatile struct S { char b; char a[1]; } s;
+  volatile struct T { struct S s; int i; } t;
+  t.s.a[0] = 1; // OK
+  t.s.a[1] = 1; // error
+  volatile struct T *pt = &t;
+  pt->s.a[0] = 1; // OK
+  pt->s.a[1] = 1; // error
+}
+
+__attribute__ ((noinline, noclone))
+void
+fn5 (void)
+{
+  volatile struct S { char b; char a[1]; } s;
+  volatile struct U { int a; struct S s; } u;
+  u.s.a[0] = 1; // OK
+  u.s.a[1] = 1; // error
+  volatile struct U *pu = &u;
+  pu->s.a[0] = 1; // OK
+  pu->s.a[1] = 1; // error in strict mode
+}
+
+int
+main (void)
+{
+  fn1 ();
+  fn2 ();
+  fn3 ();
+  fn4 ();
+  fn5 ();
+  return 0;
+}
+
+/* { dg-output "index 1 out of bounds for type 'char \\\[1\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 1 out of bounds for type 'char \\\[1\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 4 out of bounds for type 'char \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 4 out of bounds for type 'char \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 1 out of bounds for type 'char \\\[1\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 1 out of bounds for type 'char \\\[1\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 1 out of bounds for type 'char \\\[1\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 1 out of bounds for type 'char \\\[1\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
diff --git gcc/testsuite/c-c++-common/ubsan/bounds-6.c gcc/testsuite/c-c++-common/ubsan/bounds-6.c
index e69de29..78ad29a 100644
--- gcc/testsuite/c-c++-common/ubsan/bounds-6.c
+++ gcc/testsuite/c-c++-common/ubsan/bounds-6.c
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=bounds -Wall -Wextra" } */
+
+/* Test off-by-one.  */
+
+struct S { int a; int b; } s[4], *t;
+struct U { int a[10]; } u[4], *v;
+volatile int *a, *b, *c;
+volatile void *d;
+volatile int e[4][4];
+
+int
+main (void)
+{
+  t = &s[4];  // OK
+  a = &s[4].a; // Error
+  b = &s[4].b; // Error
+  d = &e[4];  // OK
+  c = &e[4][0]; // Error
+  c = &e[3][4]; // OK
+  c = &e[3][3]; // OK
+
+  a = &u[4].a[9]; // Error
+  a = &u[4].a[10]; // Error
+  a = &u[3].a[9]; // OK
+  a = &u[3].a[10]; // OK
+  a = &u[3].a[11]; // Error
+
+  return 0;
+}
+
+/* { dg-output "index 4 out of bounds for type 'S \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 4 out of bounds for type 'S \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 4 out of bounds for type 'int \\\[4\\\]\\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 4 out of bounds for type 'U \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 4 out of bounds for type 'U \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 11 out of bounds for type 'int \\\[10\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
diff --git gcc/tree-core.h gcc/tree-core.h
index 52d93ec..7320f8a 100644
--- gcc/tree-core.h
+++ gcc/tree-core.h
@@ -665,6 +665,13 @@ enum annot_expr_kind {
   annot_expr_kind_last
 };
 
+/* Internal functions.  */
+enum internal_fn {
+#define DEF_INTERNAL_FN(CODE, FLAGS) IFN_##CODE,
+#include "internal-fn.def"
+#undef DEF_INTERNAL_FN
+  IFN_LAST
+};
 
 /*---------------------------------------------------------------------------
                                 Type definitions
@@ -787,6 +794,9 @@ struct GTY(()) tree_base {
 
     /* SSA version number.  This field is only used with SSA_NAME.  */
     unsigned int version;
+
+    /* Internal function code.  */
+    enum internal_fn ifn;
   } GTY((skip(""))) u;
 };
 
diff --git gcc/tree-pretty-print.c gcc/tree-pretty-print.c
index 59a825c..8522d79 100644
--- gcc/tree-pretty-print.c
+++ gcc/tree-pretty-print.c
@@ -37,6 +37,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "value-prof.h"
 #include "predict.h"
 #include "wide-int-print.h"
+#include "internal-fn.h"
 
 #include <new>                           // For placement-new.
 
@@ -1753,7 +1754,10 @@ dump_generic_node (pretty_printer *buffer, tree node, int spc, int flags,
       break;
 
     case CALL_EXPR:
-      print_call_name (buffer, CALL_EXPR_FN (node), flags);
+      if (CALL_EXPR_FN (node) != NULL_TREE)
+	print_call_name (buffer, CALL_EXPR_FN (node), flags);
+      else
+	pp_string (buffer, internal_fn_name (CALL_EXPR_IFN (node)));
 
       /* Print parameters.  */
       pp_space (buffer);
diff --git gcc/tree.c gcc/tree.c
index 240fa92..ac4012c 100644
--- gcc/tree.c
+++ gcc/tree.c
@@ -8971,6 +8971,10 @@ get_callee_fndecl (const_tree call)
      called.  */
   addr = CALL_EXPR_FN (call);
 
+  /* If there is no function, return early.  */
+  if (addr == NULL_TREE)
+    return NULL_TREE;
+
   STRIP_NOPS (addr);
 
   /* If this is a readonly function pointer, extract its initial value.  */
@@ -10598,6 +10602,27 @@ build_call_expr (tree fndecl, int n, ...)
   return build_call_expr_loc_array (UNKNOWN_LOCATION, fndecl, n, argarray);
 }
 
+/* Build internal call expression.  This is just like CALL_EXPR, except
+   its CALL_EXPR_FN is NULL.  It will get gimplified later into ordinary
+   internal function.  */
+
+tree
+build_call_expr_internal_loc (location_t loc, enum internal_fn ifn,
+			      tree type, int n, ...)
+{
+  va_list ap;
+  int i;
+
+  tree fn = build_call_1 (type, NULL_TREE, n);
+  va_start (ap, n);
+  for (i = 0; i < n; i++)
+    CALL_EXPR_ARG (fn, i) = va_arg (ap, tree);
+  va_end (ap);
+  SET_EXPR_LOCATION (fn, loc);
+  CALL_EXPR_IFN (fn) = ifn;
+  return fn;
+}
+
 /* Create a new constant string literal and return a char* pointer to it.
    The STRING_CST value is the LEN characters at STR.  */
 tree
diff --git gcc/tree.def gcc/tree.def
index 464c8c7..299aacc 100644
--- gcc/tree.def
+++ gcc/tree.def
@@ -574,7 +574,7 @@ DEFTREECODE (BIND_EXPR, "bind_expr", tcc_expression, 3)
 /* Function call.  CALL_EXPRs are represented by variably-sized expression
    nodes.  There are at least three fixed operands.  Operand 0 is an
    INTEGER_CST node containing the total operand count, the number of
-   arguments plus 3.  Operand 1 is the function, while operand 2 is
+   arguments plus 3.  Operand 1 is the function or NULL, while operand 2 is
    is static chain argument, or NULL.  The remaining operands are the
    arguments to the call.  */
 DEFTREECODE (CALL_EXPR, "call_expr", tcc_vl_exp, 3)
diff --git gcc/tree.h gcc/tree.h
index a5dbc5f..41de5c1 100644
--- gcc/tree.h
+++ gcc/tree.h
@@ -1134,12 +1134,12 @@ extern void protected_set_expr_location (tree, location_t);
 #define ASSERT_EXPR_VAR(NODE)	TREE_OPERAND (ASSERT_EXPR_CHECK (NODE), 0)
 #define ASSERT_EXPR_COND(NODE)	TREE_OPERAND (ASSERT_EXPR_CHECK (NODE), 1)
 
-/* CALL_EXPR accessors.
- */
+/* CALL_EXPR accessors.  */
 #define CALL_EXPR_FN(NODE) TREE_OPERAND (CALL_EXPR_CHECK (NODE), 1)
 #define CALL_EXPR_STATIC_CHAIN(NODE) TREE_OPERAND (CALL_EXPR_CHECK (NODE), 2)
 #define CALL_EXPR_ARG(NODE, I) TREE_OPERAND (CALL_EXPR_CHECK (NODE), (I) + 3)
 #define call_expr_nargs(NODE) (VL_EXP_OPERAND_LENGTH (NODE) - 3)
+#define CALL_EXPR_IFN(NODE) (CALL_EXPR_CHECK (NODE)->base.u.ifn)
 
 /* CALL_EXPR_ARGP returns a pointer to the argument vector for NODE.
    We can't use &CALL_EXPR_ARG (NODE, 0) because that will complain if
@@ -3631,6 +3631,8 @@ extern tree build_call_expr_loc_array (location_t, tree, int, tree *);
 extern tree build_call_expr_loc_vec (location_t, tree, vec<tree, va_gc> *);
 extern tree build_call_expr_loc (location_t, tree, int, ...);
 extern tree build_call_expr (tree, int, ...);
+extern tree build_call_expr_internal_loc (location_t, enum internal_fn,
+					  tree, int, ...);
 extern tree build_string_literal (int, const char *);
 
 /* Construct various nodes representing data types.  */
diff --git gcc/ubsan.c gcc/ubsan.c
index 5a8a447..5e1c3e7 100644
--- gcc/ubsan.c
+++ gcc/ubsan.c
@@ -271,23 +271,24 @@ get_ubsan_type_info_for_type (tree type)
   gcc_assert (TYPE_SIZE (type) && tree_fits_uhwi_p (TYPE_SIZE (type)));
   if (TREE_CODE (type) == REAL_TYPE)
     return tree_to_uhwi (TYPE_SIZE (type));
-  else
+  else if (INTEGRAL_TYPE_P (type))
     {
       int prec = exact_log2 (tree_to_uhwi (TYPE_SIZE (type)));
       gcc_assert (prec != -1);
       return (prec << 1) | !TYPE_UNSIGNED (type);
     }
+  else
+    return 0;
 }
 
 /* Helper routine that returns ADDR_EXPR of a VAR_DECL of a type
    descriptor.  It first looks into the hash table; if not found,
    create the VAR_DECL, put it into the hash table and return the
-   ADDR_EXPR of it.  TYPE describes a particular type.  WANT_POINTER_TYPE_P
-   means whether we are interested in the pointer type and not the pointer
-   itself.  */
+   ADDR_EXPR of it.  TYPE describes a particular type.  PSTYLE is
+   an enum controlling how we want to print the type.  */
 
 tree
-ubsan_type_descriptor (tree type, bool want_pointer_type_p)
+ubsan_type_descriptor (tree type, enum ubsan_print_style pstyle)
 {
   /* See through any typedefs.  */
   type = TYPE_MAIN_VARIANT (type);
@@ -308,7 +309,7 @@ ubsan_type_descriptor (tree type, bool want_pointer_type_p)
   unsigned short tkind, tinfo;
 
   /* Get the name of the type, or the name of the pointer type.  */
-  if (want_pointer_type_p)
+  if (pstyle == UBSAN_PRINT_POINTER)
     {
       gcc_assert (POINTER_TYPE_P (type));
       type2 = TREE_TYPE (type);
@@ -324,6 +325,12 @@ ubsan_type_descriptor (tree type, bool want_pointer_type_p)
   /* If an array, get its type.  */
   type2 = strip_array_types (type2);
 
+  if (pstyle == UBSAN_PRINT_ARRAY)
+    {
+      while (POINTER_TYPE_P (type2))
+        deref_depth++, type2 = TREE_TYPE (type2);
+    }
+
   if (TYPE_NAME (type2) != NULL)
     {
       if (TREE_CODE (TYPE_NAME (type2)) == IDENTIFIER_NODE)
@@ -338,7 +345,7 @@ ubsan_type_descriptor (tree type, bool want_pointer_type_p)
 
   /* Decorate the type name with '', '*', "struct", or "union".  */
   pretty_name = (char *) alloca (strlen (tname) + 16 + deref_depth);
-  if (want_pointer_type_p)
+  if (pstyle == UBSAN_PRINT_POINTER)
     {
       int pos = sprintf (pretty_name, "'%s%s%s%s%s%s%s",
 			 TYPE_VOLATILE (type2) ? "volatile " : "",
@@ -355,6 +362,33 @@ ubsan_type_descriptor (tree type, bool want_pointer_type_p)
       pretty_name[pos++] = '\'';
       pretty_name[pos] = '\0';
     }
+  else if (pstyle == UBSAN_PRINT_ARRAY)
+    {
+      /* Pretty print the array dimensions.  */
+      gcc_assert (TREE_CODE (type) == ARRAY_TYPE);
+      tree t = type;
+      int pos = sprintf (pretty_name, "'%s ", tname);
+      while (deref_depth-- > 0)
+        pretty_name[pos++] = '*';
+      while (TREE_CODE (t) == ARRAY_TYPE)
+	{
+	  pretty_name[pos++] = '[';
+	  tree dom = TYPE_DOMAIN (t);
+	  if (dom && TREE_CODE (TYPE_MAX_VALUE (dom)) == INTEGER_CST)
+	    pos += sprintf (&pretty_name[pos], HOST_WIDE_INT_PRINT_DEC,
+			    tree_to_shwi (TYPE_MAX_VALUE (dom)) + 1);
+	  else
+	    /* ??? We can't determine the variable name; print VLA unspec.  */
+	    pretty_name[pos++] = '*';
+	  pretty_name[pos++] = ']';
+	  t = TREE_TYPE (t);
+	}
+      pretty_name[pos++] = '\'';
+      pretty_name[pos] = '\0';
+
+     /* Save the tree with stripped types.  */
+     type = t;
+    }
   else
     sprintf (pretty_name, "'%s'", tname);
 
@@ -550,6 +584,69 @@ is_ubsan_builtin_p (tree t)
 		     "__builtin___ubsan_", 18) == 0;
 }
 
+/* Expand the UBSAN_BOUNDS special builtin function.  */
+
+void
+ubsan_expand_bounds_ifn (gimple_stmt_iterator *gsi)
+{
+  gimple stmt = gsi_stmt (*gsi);
+  location_t loc = gimple_location (stmt);
+  gcc_assert (gimple_call_num_args (stmt) == 3);
+
+  /* Pick up the arguments of the UBSAN_BOUNDS call.  */
+  tree type = TREE_TYPE (TREE_TYPE (gimple_call_arg (stmt, 0)));
+  tree index = gimple_call_arg (stmt, 1);
+  tree orig_index_type = TREE_TYPE (index);
+  tree bound = gimple_call_arg (stmt, 2);
+
+  gimple_stmt_iterator gsi_orig = *gsi;
+
+  /* Create condition "if (index > bound)".  */
+  basic_block then_bb, fallthru_bb;
+  gimple_stmt_iterator cond_insert_point
+    = create_cond_insert_point (gsi, 0/*before_p*/, false, true,
+				&then_bb, &fallthru_bb);
+  index = fold_convert (TREE_TYPE (bound), index);
+  index = force_gimple_operand_gsi (&cond_insert_point, index,
+				    true/*simple_p*/, NULL_TREE,
+				    false/*before*/, GSI_NEW_STMT);
+  gimple g = gimple_build_cond (GT_EXPR, index, bound, NULL_TREE, NULL_TREE);
+  gimple_set_location (g, loc);
+  gsi_insert_after (&cond_insert_point, g, GSI_NEW_STMT);
+
+  /* Generate __ubsan_handle_out_of_bounds call.  */
+  *gsi = gsi_after_labels (then_bb);
+  if (flag_sanitize_undefined_trap_on_error)
+    g = gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP), 0);
+  else
+    {
+      tree data
+	= ubsan_create_data ("__ubsan_out_of_bounds_data", &loc, NULL,
+			     ubsan_type_descriptor (type, UBSAN_PRINT_ARRAY),
+			     ubsan_type_descriptor (orig_index_type),
+			     NULL_TREE);
+      data = build_fold_addr_expr_loc (loc, data);
+      enum built_in_function bcode
+	= flag_sanitize_recover
+	  ? BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS
+	  : BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS_ABORT;
+      tree fn = builtin_decl_explicit (bcode);
+      tree val = force_gimple_operand_gsi (gsi, ubsan_encode_value (index),
+					   true, NULL_TREE, true,
+					   GSI_SAME_STMT);
+      g = gimple_build_call (fn, 2, data, val);
+    }
+  gimple_set_location (g, loc);
+  gsi_insert_before (gsi, g, GSI_SAME_STMT);
+
+  /* Get rid of the UBSAN_BOUNDS call from the IR.  */
+  unlink_stmt_vdef (stmt);
+  gsi_remove (&gsi_orig, true);
+
+  /* Point GSI to next logical statement.  */
+  *gsi = gsi_start_bb (fallthru_bb);
+}
+
 /* Expand UBSAN_NULL internal call.  */
 
 void
@@ -609,9 +706,11 @@ ubsan_expand_null_ifn (gimple_stmt_iterator gsi)
       tree fn = builtin_decl_implicit (bcode);
       const struct ubsan_mismatch_data m
 	= { build_zero_cst (pointer_sized_int_node), ckind };
-      tree data = ubsan_create_data ("__ubsan_null_data", &loc, &m,
-				     ubsan_type_descriptor (TREE_TYPE (ptr),
-							    true), NULL_TREE);
+      tree data
+	= ubsan_create_data ("__ubsan_null_data", &loc, &m,
+			     ubsan_type_descriptor (TREE_TYPE (ptr),
+						    UBSAN_PRINT_POINTER),
+			     NULL_TREE);
       data = build_fold_addr_expr_loc (loc, data);
       g = gimple_build_call (fn, 2, data,
 			     build_zero_cst (pointer_sized_int_node));
@@ -689,8 +788,7 @@ ubsan_build_overflow_builtin (tree_code code, location_t loc, tree lhstype,
     return build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0);
 
   tree data = ubsan_create_data ("__ubsan_overflow_data", &loc, NULL,
-				 ubsan_type_descriptor (lhstype, false),
-				 NULL_TREE);
+				 ubsan_type_descriptor (lhstype), NULL_TREE);
   enum built_in_function fn_code;
 
   switch (code)
@@ -884,8 +982,7 @@ instrument_bool_enum_load (gimple_stmt_iterator *gsi)
   else
     {
       tree data = ubsan_create_data ("__ubsan_invalid_value_data", &loc, NULL,
-				     ubsan_type_descriptor (type, false),
-				     NULL_TREE);
+				     ubsan_type_descriptor (type), NULL_TREE);
       data = build_fold_addr_expr_loc (loc, data);
       enum built_in_function bcode
 	= flag_sanitize_recover
@@ -1005,10 +1102,8 @@ ubsan_instrument_float_cast (location_t loc, tree type, tree expr)
     {
       /* Create the __ubsan_handle_float_cast_overflow fn call.  */
       tree data = ubsan_create_data ("__ubsan_float_cast_overflow_data", NULL,
-				     NULL,
-				     ubsan_type_descriptor (expr_type, false),
-				     ubsan_type_descriptor (type, false),
-				     NULL_TREE);
+				     NULL, ubsan_type_descriptor (expr_type),
+				     ubsan_type_descriptor (type), NULL_TREE);
       enum built_in_function bcode
 	= flag_sanitize_recover
 	  ? BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW
diff --git gcc/ubsan.h gcc/ubsan.h
index b008419..485449c 100644
--- gcc/ubsan.h
+++ gcc/ubsan.h
@@ -30,17 +30,25 @@ enum ubsan_null_ckind {
   UBSAN_MEMBER_CALL
 };
 
+/* This controls how ubsan prints types.  Used in ubsan_type_descriptor.  */
+enum ubsan_print_style {
+  UBSAN_PRINT_NORMAL,
+  UBSAN_PRINT_POINTER,
+  UBSAN_PRINT_ARRAY
+};
+
 /* An extra data used by ubsan pointer checking.  */
 struct ubsan_mismatch_data {
   tree align;
   tree ckind;
 };
 
+extern void ubsan_expand_bounds_ifn (gimple_stmt_iterator *);
 extern void ubsan_expand_null_ifn (gimple_stmt_iterator);
 extern tree ubsan_instrument_unreachable (location_t);
 extern tree ubsan_create_data (const char *, const location_t *,
 			       const struct ubsan_mismatch_data *, ...);
-extern tree ubsan_type_descriptor (tree, bool);
+extern tree ubsan_type_descriptor (tree, enum ubsan_print_style = UBSAN_PRINT_NORMAL);
 extern tree ubsan_encode_value (tree, bool = false);
 extern bool is_ubsan_builtin_p (tree);
 extern tree ubsan_build_overflow_builtin (tree_code, location_t, tree, tree, tree);

	Marek

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

* Re: [PATCH] Implement -fsanitize=bounds and internal calls in FEs
  2014-06-20 10:02               ` Marek Polacek
@ 2014-06-20 15:35                 ` Joseph S. Myers
  2014-06-28 16:52                 ` Gerald Pfeifer
  1 sibling, 0 replies; 17+ messages in thread
From: Joseph S. Myers @ 2014-06-20 15:35 UTC (permalink / raw)
  To: Marek Polacek
  Cc: Jakub Jelinek, GCC Patches, Jason Merrill, Richard Biener, Jeff Law

I don't have any comments on this patch.

-- 
Joseph S. Myers
joseph@codesourcery.com

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

* Re: [PATCH] Implement -fsanitize=bounds and internal calls in FEs
  2014-06-19 17:48     ` Jakub Jelinek
@ 2014-06-20 15:43       ` Marek Polacek
  0 siblings, 0 replies; 17+ messages in thread
From: Marek Polacek @ 2014-06-20 15:43 UTC (permalink / raw)
  To: Jakub Jelinek
  Cc: GCC Patches, Jason Merrill, Joseph S. Myers, Richard Biener, Jeff Law

On Thu, Jun 19, 2014 at 07:47:54PM +0200, Jakub Jelinek wrote:
> On Thu, Jun 19, 2014 at 04:56:53PM +0200, Marek Polacek wrote:
> > +	/* Don't instrument this FMA-like array in non-strict
> 
> Also, please don't use FMA to mean flexible member array, it is
> flexible array member, but more importantly, FMA is used for fused
> multiply-add, so IMHO it is better to spell it without acronym.

Just for the record, I've fixed this up in some earlier patch.

	Marek

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

* Re: [PATCH] Implement -fsanitize=bounds and internal calls in FEs
  2014-06-20 10:02               ` Marek Polacek
  2014-06-20 15:35                 ` Joseph S. Myers
@ 2014-06-28 16:52                 ` Gerald Pfeifer
  2014-07-03 10:41                   ` Marek Polacek
  1 sibling, 1 reply; 17+ messages in thread
From: Gerald Pfeifer @ 2014-06-28 16:52 UTC (permalink / raw)
  To: Marek Polacek
  Cc: Jakub Jelinek, GCC Patches, Jason Merrill, Joseph S. Myers,
	Richard Biener, Jeff Law

On Fri, 20 Jun 2014, Marek Polacek wrote:
+@item -fsanitize=bounds
+@opindex fsanitize=bounds
+
+This option enables instrumentation of array bounds.  Various out of bounds
+accesses are detected.  Flexible array members are not instrumented, as well
+as initializers of variables with static storage.

Can you make this "Flexible array members and initializers..." (or
"...as well as...")?  The current wording confused me a bit at first.

And I believe there should be no empty line after @opindex.

Gerald

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

* Re: [PATCH] Implement -fsanitize=bounds and internal calls in FEs
  2014-06-28 16:52                 ` Gerald Pfeifer
@ 2014-07-03 10:41                   ` Marek Polacek
  2014-07-03 10:46                     ` Jakub Jelinek
  0 siblings, 1 reply; 17+ messages in thread
From: Marek Polacek @ 2014-07-03 10:41 UTC (permalink / raw)
  To: Gerald Pfeifer
  Cc: Jakub Jelinek, GCC Patches, Jason Merrill, Joseph S. Myers,
	Richard Biener, Jeff Law

On Sat, Jun 28, 2014 at 06:52:00PM +0200, Gerald Pfeifer wrote:
> On Fri, 20 Jun 2014, Marek Polacek wrote:
> +@item -fsanitize=bounds
> +@opindex fsanitize=bounds
> +
> +This option enables instrumentation of array bounds.  Various out of bounds
> +accesses are detected.  Flexible array members are not instrumented, as well
> +as initializers of variables with static storage.
> 
> Can you make this "Flexible array members and initializers..." (or
> "...as well as...")?  The current wording confused me a bit at first.
> 
> And I believe there should be no empty line after @opindex.

Thanks, I'll fix both with the following.  Also
-fsanitize=float-divide-by-zero and -fsanitize=float-cast-overflow
descriptions were at a wrong place, so moved a little bit above.

Applying to trunk as obvious.

2014-07-03  Marek Polacek  <polacek@redhat.com>

	* doc/invoke.texi (-fsanitize=bounds): Tweak wording.
	(-fsanitize=float-divide-by-zero): Move to the table with
	-fsanitize=undefined suboptions.
	(-fsanitize=float-cast-overflow): Likewise.

diff --git gcc/doc/invoke.texi gcc/doc/invoke.texi
index b1f6f4b..046ea58 100644
--- gcc/doc/invoke.texi
+++ gcc/doc/invoke.texi
@@ -5400,26 +5400,22 @@ at runtime.  Current suboptions are:
 
 @item -fsanitize=shift
 @opindex fsanitize=shift
-
 This option enables checking that the result of a shift operation is
 not undefined.  Note that what exactly is considered undefined differs
 slightly between C and C++, as well as between ISO C90 and C99, etc.
 
 @item -fsanitize=integer-divide-by-zero
 @opindex fsanitize=integer-divide-by-zero
-
 Detect integer division by zero as well as @code{INT_MIN / -1} division.
 
 @item -fsanitize=unreachable
 @opindex fsanitize=unreachable
-
 With this option, the compiler will turn the @code{__builtin_unreachable}
 call into a diagnostics message call instead.  When reaching the
 @code{__builtin_unreachable} call, the behavior is undefined.
 
 @item -fsanitize=vla-bound
 @opindex fsanitize=vla-bound
-
 This option instructs the compiler to check that the size of a variable
 length array is positive.  This option does not have any effect in
 @option{-std=c++1y} mode, as the standard requires the exception be thrown
@@ -5427,7 +5423,6 @@ instead.
 
 @item -fsanitize=null
 @opindex fsanitize=null
-
 This option enables pointer checking.  Particularly, the application
 built with this option turned on will issue an error message when it
 tries to dereference a NULL pointer, or if a reference (possibly an
@@ -5435,7 +5430,6 @@ rvalue reference) is bound to a NULL pointer.
 
 @item -fsanitize=return
 @opindex fsanitize=return
-
 This option enables return statement checking.  Programs
 built with this option turned on will issue an error message
 when the end of a non-void function is reached without actually
@@ -5443,7 +5437,6 @@ returning a value.  This option works in C++ only.
 
 @item -fsanitize=signed-integer-overflow
 @opindex fsanitize=signed-integer-overflow
-
 This option enables signed integer overflow checking.  We check that
 the result of @code{+}, @code{*}, and both unary and binary @code{-}
 does not overflow in the signed arithmetics.  Note, integer promotion
@@ -5456,20 +5449,12 @@ a++;
 
 @item -fsanitize=bounds
 @opindex fsanitize=bounds
-
 This option enables instrumentation of array bounds.  Various out of bounds
-accesses are detected.  Flexible array members are not instrumented, as well
-as initializers of variables with static storage.
-
-@end table
-
-While @option{-ftrapv} causes traps for signed overflows to be emitted,
-@option{-fsanitize=undefined} gives a diagnostic message.
-This currently works only for the C family of languages.
+accesses are detected.  Flexible array members and initializers of variables
+with static storage are not instrumented.
 
 @item -fsanitize=float-divide-by-zero
 @opindex fsanitize=float-divide-by-zero
-
 Detect floating-point division by zero.  Unlike other similar options,
 @option{-fsanitize=float-divide-by-zero} is not enabled by
 @option{-fsanitize=undefined}, since floating-point division by zero can
@@ -5477,11 +5462,16 @@ be a legitimate way of obtaining infinities and NaNs.
 
 @item -fsanitize=float-cast-overflow
 @opindex fsanitize=float-cast-overflow
-
 This option enables floating-point type to integer conversion checking.
 We check that the result of the conversion does not overflow.
 This option does not work well with @code{FE_INVALID} exceptions enabled.
 
+@end table
+
+While @option{-ftrapv} causes traps for signed overflows to be emitted,
+@option{-fsanitize=undefined} gives a diagnostic message.
+This currently works only for the C family of languages.
+
 @item -fsanitize-recover
 @opindex fsanitize-recover
 By default @option{-fsanitize=undefined} sanitization (and its suboptions

	Marek

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

* Re: [PATCH] Implement -fsanitize=bounds and internal calls in FEs
  2014-07-03 10:41                   ` Marek Polacek
@ 2014-07-03 10:46                     ` Jakub Jelinek
  2014-07-03 10:55                       ` Marek Polacek
  0 siblings, 1 reply; 17+ messages in thread
From: Jakub Jelinek @ 2014-07-03 10:46 UTC (permalink / raw)
  To: Marek Polacek
  Cc: Gerald Pfeifer, GCC Patches, Jason Merrill, Joseph S. Myers,
	Richard Biener, Jeff Law

On Thu, Jul 03, 2014 at 12:41:46PM +0200, Marek Polacek wrote:
> On Sat, Jun 28, 2014 at 06:52:00PM +0200, Gerald Pfeifer wrote:
> > On Fri, 20 Jun 2014, Marek Polacek wrote:
> > +@item -fsanitize=bounds
> > +@opindex fsanitize=bounds
> > +
> > +This option enables instrumentation of array bounds.  Various out of bounds
> > +accesses are detected.  Flexible array members are not instrumented, as well
> > +as initializers of variables with static storage.
> > 
> > Can you make this "Flexible array members and initializers..." (or
> > "...as well as...")?  The current wording confused me a bit at first.
> > 
> > And I believe there should be no empty line after @opindex.
> 
> Thanks, I'll fix both with the following.  Also
> -fsanitize=float-divide-by-zero and -fsanitize=float-cast-overflow
> descriptions were at a wrong place, so moved a little bit above.
> 
> Applying to trunk as obvious.
> 
> 2014-07-03  Marek Polacek  <polacek@redhat.com>
> 
> 	* doc/invoke.texi (-fsanitize=bounds): Tweak wording.
> 	(-fsanitize=float-divide-by-zero): Move to the table with
> 	-fsanitize=undefined suboptions.
> 	(-fsanitize=float-cast-overflow): Likewise.

Those two aren't -fsanitize=undefined suboptions, so shouldn't be included
in there.

	Jakub

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

* Re: [PATCH] Implement -fsanitize=bounds and internal calls in FEs
  2014-07-03 10:46                     ` Jakub Jelinek
@ 2014-07-03 10:55                       ` Marek Polacek
  0 siblings, 0 replies; 17+ messages in thread
From: Marek Polacek @ 2014-07-03 10:55 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Gerald Pfeifer, GCC Patches

On Thu, Jul 03, 2014 at 12:46:35PM +0200, Jakub Jelinek wrote:
> On Thu, Jul 03, 2014 at 12:41:46PM +0200, Marek Polacek wrote:
> > On Sat, Jun 28, 2014 at 06:52:00PM +0200, Gerald Pfeifer wrote:
> > > On Fri, 20 Jun 2014, Marek Polacek wrote:
> > > +@item -fsanitize=bounds
> > > +@opindex fsanitize=bounds
> > > +
> > > +This option enables instrumentation of array bounds.  Various out of bounds
> > > +accesses are detected.  Flexible array members are not instrumented, as well
> > > +as initializers of variables with static storage.
> > > 
> > > Can you make this "Flexible array members and initializers..." (or
> > > "...as well as...")?  The current wording confused me a bit at first.
> > > 
> > > And I believe there should be no empty line after @opindex.
> > 
> > Thanks, I'll fix both with the following.  Also
> > -fsanitize=float-divide-by-zero and -fsanitize=float-cast-overflow
> > descriptions were at a wrong place, so moved a little bit above.
> > 
> > Applying to trunk as obvious.
> > 
> > 2014-07-03  Marek Polacek  <polacek@redhat.com>
> > 
> > 	* doc/invoke.texi (-fsanitize=bounds): Tweak wording.
> > 	(-fsanitize=float-divide-by-zero): Move to the table with
> > 	-fsanitize=undefined suboptions.
> > 	(-fsanitize=float-cast-overflow): Likewise.
> 
> Those two aren't -fsanitize=undefined suboptions, so shouldn't be included
> in there.

But they're parts of ubsan and at least
-fsanitize=float-divide-by-zero says it is not enabled by
-fsanitize=undefined.  Dunno, I can move it back if you want.

	Marek

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

end of thread, other threads:[~2014-07-03 10:55 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-06-16 10:39 [PATCH] Implement -fsanitize=bounds and internal calls in FEs Marek Polacek
2014-06-16 11:23 ` Jakub Jelinek
2014-06-19 15:00   ` Marek Polacek
2014-06-19 16:24     ` Marek Polacek
2014-06-19 17:19     ` Jakub Jelinek
2014-06-20  8:43       ` Marek Polacek
2014-06-20  8:57         ` Jakub Jelinek
2014-06-20  9:34           ` Marek Polacek
2014-06-20  9:39             ` Jakub Jelinek
2014-06-20 10:02               ` Marek Polacek
2014-06-20 15:35                 ` Joseph S. Myers
2014-06-28 16:52                 ` Gerald Pfeifer
2014-07-03 10:41                   ` Marek Polacek
2014-07-03 10:46                     ` Jakub Jelinek
2014-07-03 10:55                       ` Marek Polacek
2014-06-19 17:48     ` Jakub Jelinek
2014-06-20 15:43       ` Marek Polacek

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).