Patchwork Implement -fsanitize=bounds and internal calls in FEs

login
register
mail settings
Submitter Marek Polacek
Date June 16, 2014, 10:39 a.m.
Message ID <20140616103907.GD17965@redhat.com>
Download mbox | patch
Permalink /patch/360047/
State New
Headers show

Comments

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

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

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

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

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

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


	Marek
Jakub Jelinek - June 16, 2014, 11:23 a.m.
On Mon, Jun 16, 2014 at 12:39:07PM +0200, Marek Polacek wrote:
> Jason/Joseph, could you please look at the C++/C FE parts?

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

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

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

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

	Jakub

Patch

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