Patchwork Implement -fsanitize=bounds and internal calls in FEs

login
register
mail settings
Submitter Marek Polacek
Date June 20, 2014, 10:02 a.m.
Message ID <20140620100234.GA14420@redhat.com>
Download mbox | patch
Permalink /patch/362141/
State New
Headers show

Comments

Marek Polacek - June 20, 2014, 10:02 a.m.
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.


	Marek
Joseph S. Myers - June 20, 2014, 3:34 p.m.
I don't have any comments on this patch.
Gerald Pfeifer - June 28, 2014, 4:52 p.m.
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

Patch

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