Patchwork Implement -fsanitize=bounds and internal calls in FEs

login
register
mail settings
Submitter Marek Polacek
Date June 20, 2014, 8:43 a.m.
Message ID <20140620084304.GN17965@redhat.com>
Download mbox | patch
Permalink /patch/362130/
State New
Headers show

Comments

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


	Marek
Jakub Jelinek - June 20, 2014, 8:57 a.m.
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

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