[C] (for STAGE 1) UBSan instrumentation for assignment of VM types

Message ID 710940157fea32e9f628f8286891cfeb21646f37.camel@tugraz.at
State New
Headers
Series [C] (for STAGE 1) UBSan instrumentation for assignment of VM types |

Commit Message

Martin Uecker Dec. 22, 2022, 5:44 p.m. UTC
  Here is a first patch to add UBSan instrumentation to
assignment, return, initialization of pointers
to variably modified types. This is based on the
other patch I just sent. Separating these should make
reviewing easier.

Here, I did not add tests for function arguments as
this is more complicated, but this will follow...




c: UBSan instrumentation for assignment of VM types

This adds instrumentation that checks that corresponding
size expression in variably modified types evaluate to
the same value in assignment, function return, and
initialization.

gcc/c-family/
	* c-ubsan.cc (ubsan_instrument_vm_assign): New.

gcc/c/
	* c-typeck.cc (comptypes_check_enum_int_instr,
	comptypes_check_enum_int): New interface for
	instrumentation.
	(comptypes_internal,convert_for_assignment):
	Add instrumentation.
	(comp_target_types_instr,comp_target_types): Add
	new interface for instrumentation.

gcc/testsuite/gcc.dg/ubsan/
	* vm-bounds-1.c: New test.
	* vm-bounds-2.c: New test.
  

Patch

diff --git a/gcc/c-family/c-ubsan.cc b/gcc/c-family/c-ubsan.cc
index 360ba82250c..7de4e6e7057 100644
--- a/gcc/c-family/c-ubsan.cc
+++ b/gcc/c-family/c-ubsan.cc
@@ -334,6 +334,48 @@  ubsan_instrument_vla (location_t loc, tree size)
   return t;
 }
 
+/* Instrument assignment of variably modified types.  */
+
+tree
+ubsan_instrument_vm_assign (location_t loc, tree a, tree b)
+{
+  tree t, tt;
+
+  gcc_assert (TREE_CODE (a) == ARRAY_TYPE);
+  gcc_assert (TREE_CODE (b) == ARRAY_TYPE);
+
+  tree as = TYPE_MAX_VALUE (TYPE_DOMAIN (a));
+  tree bs = TYPE_MAX_VALUE (TYPE_DOMAIN (b));
+
+  as = fold_build2 (PLUS_EXPR, sizetype, as, size_one_node);
+  bs = fold_build2 (PLUS_EXPR, sizetype, bs, size_one_node);
+
+  t = build2 (NE_EXPR, boolean_type_node, as, bs);
+  if (flag_sanitize_trap & SANITIZE_VLA)
+    tt = build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0);
+  else
+    {
+      tree data = ubsan_create_data ("__ubsan_vm_data", 1, &loc,
+				     ubsan_type_descriptor (a, UBSAN_PRINT_ARRAY),
+				     ubsan_type_descriptor (b, UBSAN_PRINT_ARRAY),
+				     ubsan_type_descriptor (sizetype),
+				     NULL_TREE, NULL_TREE);
+      data = build_fold_addr_expr_loc (loc, data);
+      enum built_in_function bcode
+	= (flag_sanitize_recover & SANITIZE_VLA)
+	  ? BUILT_IN_UBSAN_HANDLE_VM_BOUNDS_MISMATCH
+	  : BUILT_IN_UBSAN_HANDLE_VM_BOUNDS_MISMATCH_ABORT;
+      tt = builtin_decl_explicit (bcode);
+      tt = build_call_expr_loc (loc, tt, 3, data,
+				ubsan_encode_value (as),
+				ubsan_encode_value (bs));
+    }
+  t = build3 (COND_EXPR, void_type_node, t, tt, void_node);
+
+  return t;
+}
+
+
 /* Instrument missing return in C++ functions returning non-void.  */
 
 tree
diff --git a/gcc/c-family/c-ubsan.h b/gcc/c-family/c-ubsan.h
index 2f31ba36df4..327313c6684 100644
--- a/gcc/c-family/c-ubsan.h
+++ b/gcc/c-family/c-ubsan.h
@@ -26,6 +26,7 @@  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 tree ubsan_instrument_vm_assign (location_t, tree, tree);
 extern bool ubsan_array_ref_instrumented_p (const_tree);
 extern void ubsan_maybe_instrument_array_ref (tree *, bool);
 extern void ubsan_maybe_instrument_reference (tree *);
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index ebc9ba88afe..a58b96083e9 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -93,6 +93,7 @@  static tree qualify_type (tree, tree);
 struct comptypes_data;
 static int tagged_types_compatible_p (const_tree, const_tree, struct comptypes_data *);
 static int comp_target_types (location_t, tree, tree);
+static int comp_target_types_instr (location_t, tree, tree, tree *);
 static int function_types_compatible_p (const_tree, const_tree, struct comptypes_data *);
 static int type_lists_compatible_p (const_tree, const_tree, struct comptypes_data *);
 static tree lookup_field (tree, tree);
@@ -1053,6 +1054,9 @@  struct comptypes_data {
 
   bool enum_and_int_p;
   bool different_types_p;
+
+  location_t loc;
+  tree instrument_expr;
 };
 
 /* Return 1 if TYPE1 and TYPE2 are compatible types for assignment
@@ -1075,20 +1079,35 @@  comptypes (tree type1, tree type2)
 /* Like comptypes, but if it returns non-zero because enum and int are
    compatible, it sets *ENUM_AND_INT_P to true.  */
 
-int
-comptypes_check_enum_int (tree type1, tree type2, bool *enum_and_int_p)
+static int
+comptypes_check_enum_int_instr (tree type1, tree type2, bool *enum_and_int_p, location_t loc, tree *instrument_expr)
 {
   int val;
 
   struct comptypes_data data = { };
+
+  data.loc = loc;
+
+  if (NULL != instrument_expr)
+    data.instrument_expr = void_node;
+
   val = comptypes_internal (type1, type2, &data);
   *enum_and_int_p = data.enum_and_int_p;
 
+  if (NULL != instrument_expr)
+    *instrument_expr = data.instrument_expr;
+
   free_all_tagged_seen (data.seen_base);
 
   return val;
 }
 
+int
+comptypes_check_enum_int (tree type1, tree type2, bool *enum_and_int_p)
+{
+  return comptypes_check_enum_int_instr (type1, type2, enum_and_int_p, UNKNOWN_LOCATION, NULL);
+}
+
 /* Like comptypes, but if it returns nonzero for different types, it
    sets *DIFFERENT_TYPES_P to true.  */
 
@@ -1257,7 +1276,18 @@  comptypes_internal (const_tree type1, const_tree type2,
 	if (d1_variable != d2_variable)
 	  data->different_types_p = true;
 	if (d1_variable || d2_variable)
-	  break;
+	  {
+	    if (NULL != data->instrument_expr)
+	      {
+		tree instrument_expr2 = ubsan_instrument_vm_assign (data->loc,
+								    TYPE_MAIN_VARIANT (t2),
+								    TYPE_MAIN_VARIANT (t1));
+		/* chain into series of COMPOUND_EXPR */
+		data->instrument_expr = fold_build2 (COMPOUND_EXPR, void_type_node,
+						     instrument_expr2, data->instrument_expr);
+	      }
+	    break;
+	  }
 	if (d1_zero && d2_zero)
 	  break;
 	if (d1_zero || d2_zero
@@ -1304,7 +1334,7 @@  comptypes_internal (const_tree type1, const_tree type2,
    subset of the other.  */
 
 static int
-comp_target_types (location_t location, tree ttl, tree ttr)
+comp_target_types_instr (location_t location, tree ttl, tree ttr, tree *instrument_expr)
 {
   int val;
   int val_ped;
@@ -1338,8 +1368,7 @@  comp_target_types (location_t location, tree ttl, tree ttr)
 	 ? c_build_qualified_type (TYPE_MAIN_VARIANT (mvr), TYPE_QUAL_ATOMIC)
 	 : TYPE_MAIN_VARIANT (mvr));
 
-  enum_and_int_p = false;
-  val = comptypes_check_enum_int (mvl, mvr, &enum_and_int_p);
+  val = comptypes_check_enum_int_instr (mvl, mvr, &enum_and_int_p, location, instrument_expr);
 
   if (val == 1 && val_ped != 1)
     pedwarn_c11 (location, OPT_Wpedantic, "invalid use of pointers to arrays with different qualifiers "
@@ -1354,6 +1383,13 @@  comp_target_types (location_t location, tree ttl, tree ttr)
 
   return val;
 }
+
+static int
+comp_target_types (location_t location, tree ttl, tree ttr)
+{
+  return comp_target_types_instr (location, ttl, ttr, NULL);
+}
+
 
 /* Subroutines of `comptypes'.  */
 
@@ -1404,7 +1440,7 @@  free_all_tagged_seen (const struct tagged_seen_cache *cache)
 
 static int
 tagged_types_compatible_p (const_tree t1, const_tree t2,
-			      struct comptypes_data* data)
+			   struct comptypes_data* data)
 {
   tree s1, s2;
   bool needs_warning = false;
@@ -7080,6 +7116,7 @@  convert_for_assignment (location_t location, location_t expr_loc, tree type,
   if (coder == POINTER_TYPE && reject_gcc_builtin (rhs))
     return error_mark_node;
 
+
   /* A non-reference type can convert to a reference.  This handles
      va_start, va_copy and possibly port built-ins.  */
   if (codel == REFERENCE_TYPE && coder != REFERENCE_TYPE)
@@ -7284,6 +7321,7 @@  convert_for_assignment (location_t location, location_t expr_loc, tree type,
       int target_cmp = 0;   /* Cache comp_target_types () result.  */
       addr_space_t asl;
       addr_space_t asr;
+      tree instrument_expr = NULL;
 
       if (TREE_CODE (mvl) != ARRAY_TYPE)
 	mvl = (TYPE_ATOMIC (mvl)
@@ -7478,12 +7516,17 @@  convert_for_assignment (location_t location, location_t expr_loc, tree type,
 	  }
 	}
 
+      bool instrument_p = sanitize_flags_p (SANITIZE_VLA)
+			  && (ic_init_const != errtype)
+			  && (ic_argpass != errtype);
+
       /* Any non-function converts to a [const][volatile] void *
 	 and vice versa; otherwise, targets must be the same.
 	 Meanwhile, the lhs target must have all the qualifiers of the rhs.  */
       if ((VOID_TYPE_P (ttl) && !TYPE_ATOMIC (ttl))
 	  || (VOID_TYPE_P (ttr) && !TYPE_ATOMIC (ttr))
-	  || (target_cmp = comp_target_types (location, type, rhstype))
+	  || (target_cmp = comp_target_types_instr (location, type, rhstype,
+						    instrument_p ? &instrument_expr : NULL))
 	  || is_opaque_pointer
 	  || ((c_common_unsigned_type (mvl)
 	       == c_common_unsigned_type (mvr))
@@ -7684,6 +7727,15 @@  convert_for_assignment (location_t location, location_t expr_loc, tree type,
 	 struct or union.  */
       warn_for_address_or_pointer_of_packed_member (type, orig_rhs);
 
+      if (instrument_expr != NULL)
+      {
+	/* We have to make sure that the rhs is evaluated first,
+	   because we may use size expressions in it to check bounds.  */
+	rhs = save_expr (rhs);
+	instrument_expr = fold_build2 (COMPOUND_EXPR, void_type_node, rhs, instrument_expr);
+	rhs = fold_build2 (COMPOUND_EXPR, TREE_TYPE (rhs), instrument_expr, rhs);
+      }
+
       return convert (type, rhs);
     }
   else if (codel == POINTER_TYPE && coder == ARRAY_TYPE)
diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def
index 4c71858d528..b3c52eecf8c 100644
--- a/gcc/sanitizer.def
+++ b/gcc/sanitizer.def
@@ -506,6 +506,10 @@  DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_MISSING_RETURN,
 		      "__ubsan_handle_missing_return",
 		      BT_FN_VOID_PTR,
 		      ATTR_NORETURN_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_VM_BOUNDS_MISMATCH,
+		      "__ubsan_handle_vm_bounds_mismatch",
+		      BT_FN_VOID_PTR_PTR_PTR,
+		      ATTR_COLD_NOTHROW_LEAF_LIST)
 DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_VLA_BOUND_NOT_POSITIVE,
 		      "__ubsan_handle_vla_bound_not_positive",
 		      BT_FN_VOID_PTR_PTR,
@@ -542,6 +546,10 @@  DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_DIVREM_OVERFLOW_ABORT,
 		      "__ubsan_handle_divrem_overflow_abort",
 		      BT_FN_VOID_PTR_PTR_PTR,
 		      ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_VM_BOUNDS_MISMATCH_ABORT,
+		      "__ubsan_handle_vm_bounds_mismatch_about",
+		      BT_FN_VOID_PTR_PTR_PTR,
+		      ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)
 DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_SHIFT_OUT_OF_BOUNDS_ABORT,
 		      "__ubsan_handle_shift_out_of_bounds_abort",
 		      BT_FN_VOID_PTR_PTR_PTR,
diff --git a/gcc/testsuite/gcc.dg/ubsan/vm-bounds-1.c b/gcc/testsuite/gcc.dg/ubsan/vm-bounds-1.c
new file mode 100644
index 00000000000..f5122b30150
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ubsan/vm-bounds-1.c
@@ -0,0 +1,149 @@ 
+/* { dg-do run } */
+/* { dg-options "-fsanitize=vla-bound" } */
+
+
+/* test return types */
+
+int m, n;
+
+static char (*z0(void))[5][5] { char (*p)[m][n] = 0; return p; }
+static char (*z1(void))[5][5] { char (*p)[5][n] = 0; return p; }
+static char (*z2(void))[5][5] { char (*p)[m][5] = 0; return p; }
+
+
+int main()
+{
+	m = 4, n = 3;
+
+	int u = 4;
+	int v = 3;
+
+	/* initialization */
+
+	char a[4];
+	char (*pa)[u] = &a;
+	char (*qa)[v] = &a;
+	/* { dg-output "bound 3 of type 'char \\\[\\\*\\\]' does not match bound 4 of type 'char \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+
+	char b[u];
+	const char (*pb)[u] = &b;
+	char (*qb)[v] = &b;
+	/* { dg-output "\[^\n\r]*bound 3 of type 'char \\\[\\\*\\\]' does not match bound 4 of type 'char \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+
+	char c[4][3];
+	char (*pc0)[u][v] = &c;
+	char (*qc0)[v][u] = &c;	
+	/* { dg-output "\[^\n\r]*bound 3 of type 'char \\\[\\\*\\\]\\\[\\\*\\\]' does not match bound 4 of type 'char \\\[4\\\]\\\[3\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+	/* { dg-output "\[^\n\r]*bound 4 of type 'char \\\[\\\*\\\]' does not match bound 3 of type 'char \\\[3\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+
+	char (*pc1)[u][3] = &c;
+	char (*qc1)[v][3] = &c;
+	/* { dg-output "\[^\n\r]*bound 3 of type 'char \\\[\\\*\\\]\\\[3\\\]' does not match bound 4 of type 'char \\\[4\\\]\\\[3\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+
+	char (*pc2)[4][v] = &c;
+	char (*qc2)[4][u] = &c;
+	/* { dg-output "\[^\n\r]*bound 4 of type 'char \\\[\\\*\\\]' does not match bound 3 of type 'char \\\[3\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+
+	char (*pc3)[][v] = &c;	
+	char (*qc3)[][u] = &c;
+	/* { dg-output "\[^\n\r]*bound 4 of type 'char \\\[\\\*\\\]' does not match bound 3 of type 'char \\\[3\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+
+	char d[u][v];
+	char (*pd0)[4][3] = &d;
+	char (*qd0)[3][4] = &d;	
+	/* { dg-output "\[^\n\r]*bound 3 of type 'char \\\[3\\\]\\\[4\\\]' does not match bound 4 of type 'char \\\[\\\*\\\]\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+	/* { dg-output "\[^\n\r]*bound 4 of type 'char \\\[4\\\]' does not match bound 3 of type 'char \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+	
+	char (*pd1)[u][3] = &d;	
+	char (*qd1)[v][4] = &d;	
+	/* { dg-output "\[^\n\r]*bound 3 of type 'char \\\[\\\*\\\]\\\[4\\\]' does not match bound 4 of type 'char \\\[\\\*\\\]\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+	/* { dg-output "\[^\n\r]*bound 4 of type 'char \\\[4\\\]' does not match bound 3 of type 'char \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+	
+	char (*pd2)[4][v] = &d;
+	char (*qd2)[3][u] = &d;
+	/* { dg-output "\[^\n\r]*bound 3 of type 'char \\\[3\\\]\\\[\\\*\\\]' does not match bound 4 of type 'char \\\[\\\*\\\]\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+	/* { dg-output "\[^\n\r]*bound 4 of type 'char \\\[\\\*\\\]' does not match bound 3 of type 'char \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+	
+	char (*pd3)[u][v] = &d;
+	char (*qd3)[v][u] = &d;
+	/* { dg-output "\[^\n\r]*bound 3 of type 'char \\\[\\\*\\\]\\\[\\\*\\\]' does not match bound 4 of type 'char \\\[\\\*\\\]\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+	/* { dg-output "\[^\n\r]*bound 4 of type 'char \\\[\\\*\\\]' does not match bound 3 of type 'char \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+	
+	char e[4][v];
+	char (*pe0)[4][3] = &e;
+	char (*qe0)[4][4] = &e;	
+	/* { dg-output "\[^\n\r]*bound 4 of type 'char \\\[4\\\]' does not match bound 3 of type 'char \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+
+	char f[u][3];
+	char (*pf)[4][3] = &f;
+	char (*qf)[3][3] = &f;
+	/* { dg-output "\[^\n\r]*bound 3 of type 'char \\\[3\\\]\\\[3\\\]' does not match bound 4 of type 'char \\\[\\\*\\\]\\\[3\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+
+	char (*g[u])[v];
+	char (*(*pg)[u])[v] = &g;
+	char (*(*qg)[v])[u] = &g;	
+	/* { dg-output "\[^\n\r]*bound 3 of type '\[^\]]*\\\[\\\*\\\]' does not match bound 4 of type '\[^\]]*\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+	/* { dg-output "\[^\n\r]*bound 4 of type 'char \\\[\\\*\\\]' does not match bound 3 of type 'char \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+	
+	/* assignment */
+
+	pa = &a;
+	qa = &a;
+	/* { dg-output "\[^\n\r]*bound 3 of type 'char \\\[\\\*\\\]' does not match bound 4 of type 'char \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+
+	pb = &b;
+	qb = &b;
+	/* { dg-output "\[^\n\r]*bound 3 of type 'char \\\[\\\*\\\]' does not match bound 4 of type 'char \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+	
+	pc0 = &c;
+	qc0 = &c;	
+	/* { dg-output "\[^\n\r]*bound 3 of type 'char \\\[\\\*\\\]\\\[\\\*\\\]' does not match bound 4 of type 'char \\\[4\\\]\\\[3\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+	/* { dg-output "\[^\n\r]*bound 4 of type 'char \\\[\\\*\\\]' does not match bound 3 of type 'char \\\[3\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+	
+	pc1 = &c;
+	qc1 = &c;
+	/* { dg-output "\[^\n\r]*bound 3 of type 'char \\\[\\\*\\\]\\\[3\\\]' does not match bound 4 of type 'char \\\[4\\\]\\\[3\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+
+	pc2 = &c;
+	qc2 = &c;
+	/* { dg-output "\[^\n\r]*bound 4 of type 'char \\\[\\\*\\\]' does not match bound 3 of type 'char \\\[3\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+
+	pd0 = &d;
+	qd0 = &d;	
+	/* { dg-output "\[^\n\r]*bound 3 of type 'char \\\[3\\\]\\\[4\\\]' does not match bound 4 of type 'char \\\[\\\*\\\]\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+	/* { dg-output "\[^\n\r]*bound 4 of type 'char \\\[4\\\]' does not match bound 3 of type 'char \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+
+	pd1 = &d;
+	qd1 = &d;	
+	/* { dg-output "\[^\n\r]*bound 3 of type 'char \\\[\\\*\\\]\\\[4\\\]' does not match bound 4 of type 'char \\\[\\\*\\\]\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+	/* { dg-output "\[^\n\r]*bound 4 of type 'char \\\[4\\\]' does not match bound 3 of type 'char \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+	
+	pd2 = &d;
+	qd2 = &d;
+	/* { dg-output "\[^\n\r]*bound 3 of type 'char \\\[3\\\]\\\[\\\*\\\]' does not match bound 4 of type 'char \\\[\\\*\\\]\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+	/* { dg-output "\[^\n\r]*bound 4 of type 'char \\\[\\\*\\\]' does not match bound 3 of type 'char \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+	
+	pd3 = &d;
+	qd3 = &d;
+	/* { dg-output "\[^\n\r]*bound 3 of type 'char \\\[\\\*\\\]\\\[\\\*\\\]' does not match bound 4 of type 'char \\\[\\\*\\\]\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+	/* { dg-output "\[^\n\r]*bound 4 of type 'char \\\[\\\*\\\]' does not match bound 3 of type 'char \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+	
+	pe0 = &e;
+	qe0 = &e;
+	/* { dg-output "\[^\n\r]*bound 4 of type 'char \\\[4\\\]' does not match bound 3 of type 'char \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+
+	pf = &f;
+	qf = &f;
+	/* { dg-output "\[^\n\r]*bound 3 of type 'char \\\[3\\\]\\\[3\\\]' does not match bound 4 of type 'char \\\[\\\*\\\]\\\[3\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+
+	/* return */
+	z0();		
+	/* { dg-output "\[^\n\r]*bound 5 of type 'char \\\[5\\\]\\\[5\\\]' does not match bound 4 of type 'char \\\[\\\*\\\]\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+	/* { dg-output "\[^\n\r]*bound 5 of type 'char \\\[5\\\]' does not match bound 3 of type 'char \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+
+	z1();		
+	/* { dg-output "\[^\n\r]*bound 5 of type 'char \\\[5\\\]' does not match bound 3 of type 'char \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+	z2();	
+	/* { dg-output "\[^\n\r]*bound 5 of type 'char \\\[5\\\]\\\[5\\\]' does not match bound 4 of type 'char \\\[\\\*\\\]\\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+}
+
diff --git a/gcc/testsuite/gcc.dg/ubsan/vm-bounds-2.c b/gcc/testsuite/gcc.dg/ubsan/vm-bounds-2.c
new file mode 100644
index 00000000000..59a6c353266
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ubsan/vm-bounds-2.c
@@ -0,0 +1,15 @@ 
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=vla-bound" } */
+
+
+
+const char* name = "hallo";
+
+
+void f(void)
+{
+	int x = 1;
+	int (*m)[x] = 0;
+	m = ({ long* d2; (int (*)[d2[0]])(0); });
+}
+
diff --git a/libsanitizer/ubsan/ubsan_checks.inc b/libsanitizer/ubsan/ubsan_checks.inc
index 846cd89ee19..3182a48a0e3 100644
--- a/libsanitizer/ubsan/ubsan_checks.inc
+++ b/libsanitizer/ubsan/ubsan_checks.inc
@@ -56,6 +56,7 @@  UBSAN_CHECK(OutOfBoundsIndex, "out-of-bounds-index", "bounds")
 UBSAN_CHECK(UnreachableCall, "unreachable-call", "unreachable")
 UBSAN_CHECK(MissingReturn, "missing-return", "return")
 UBSAN_CHECK(NonPositiveVLAIndex, "non-positive-vla-index", "vla-bound")
+UBSAN_CHECK(VMBoundsMismatch, "vm-bounds-mismatch", "vm-bounds")
 UBSAN_CHECK(FloatCastOverflow, "float-cast-overflow", "float-cast-overflow")
 UBSAN_CHECK(InvalidBoolLoad, "invalid-bool-load", "bool")
 UBSAN_CHECK(InvalidEnumLoad, "invalid-enum-load", "enum")
diff --git a/libsanitizer/ubsan/ubsan_handlers.cpp b/libsanitizer/ubsan/ubsan_handlers.cpp
index 970075e69a6..cbe03ca37e4 100644
--- a/libsanitizer/ubsan/ubsan_handlers.cpp
+++ b/libsanitizer/ubsan/ubsan_handlers.cpp
@@ -433,6 +433,33 @@  void __ubsan::__ubsan_handle_missing_return(UnreachableData *Data) {
   Die();
 }
 
+static void handleVMBoundsMismatch(VMBoundsMismatchData *Data, ValueHandle Bound1,
+				   ValueHandle Bound2, ReportOptions Opts) {
+  SourceLocation Loc = Data->Loc.acquire();
+  ErrorType ET = ErrorType::NonPositiveVLAIndex;
+
+  if (ignoreReport(Loc, Opts, ET))
+    return;
+
+  ScopedReport R(Opts, Loc, ET);
+
+  Diag(Loc, DL_Error, ET, "bound %0 of type %1 does not match bound %2 of type %3")
+      << Value(Data->IndexType, Bound2) << Data->ToType
+      << Value(Data->IndexType, Bound1) << Data->FromType;
+}
+
+void __ubsan::__ubsan_handle_vm_bounds_mismatch(VMBoundsMismatchData *Data,
+                                                ValueHandle Bound1, ValueHandle Bound2) {
+  GET_REPORT_OPTIONS(false);
+  handleVMBoundsMismatch(Data, Bound1, Bound2, Opts);
+}
+void __ubsan::__ubsan_handle_vm_bounds_mismatch_abort(VMBoundsMismatchData *Data,
+                                                          ValueHandle Bound1, ValueHandle Bound2) {
+  GET_REPORT_OPTIONS(true);
+  handleVMBoundsMismatch(Data, Bound1, Bound2, Opts);
+  Die();
+}
+
 static void handleVLABoundNotPositive(VLABoundData *Data, ValueHandle Bound,
                                       ReportOptions Opts) {
   SourceLocation Loc = Data->Loc.acquire();
diff --git a/libsanitizer/ubsan/ubsan_handlers.h b/libsanitizer/ubsan/ubsan_handlers.h
index 9f412353fc0..4765710e9f1 100644
--- a/libsanitizer/ubsan/ubsan_handlers.h
+++ b/libsanitizer/ubsan/ubsan_handlers.h
@@ -107,6 +107,17 @@  struct VLABoundData {
 /// \brief Handle a VLA with a non-positive bound.
 RECOVERABLE(vla_bound_not_positive, VLABoundData *Data, ValueHandle Bound)
 
+struct VMBoundsMismatchData {
+  SourceLocation Loc;
+  const TypeDescriptor &FromType;
+  const TypeDescriptor &ToType;
+  const TypeDescriptor &IndexType;
+};
+
+/// \brief Handle a VM types with run-time bounds mismatch
+RECOVERABLE(vm_bounds_mismatch, VMBoundsMismatchData *Data, ValueHandle Bound1, ValueHandle Bound2)
+
+
 // Keeping this around for binary compatibility with (sanitized) programs
 // compiled with older compilers.
 struct FloatCastOverflowData {