public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc(refs/users/uecker/heads/vla)] c: introduce ubsan checking for assigment of VM types 2/4
@ 2023-07-29 19:45 Martin Uecker
  0 siblings, 0 replies; only message in thread
From: Martin Uecker @ 2023-07-29 19:45 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:19db0b143a57f161255410cb53b92769798b8a23

commit 19db0b143a57f161255410cb53b92769798b8a23
Author: Martin Uecker <uecker@tugraz.at>
Date:   Sat Apr 15 20:53:39 2023 +0200

    c: introduce ubsan checking for assigment of VM types 2/4
    
    When checking compatibility of types during assignment, collect
    all pairs of types where the outermost bound needs to match at
    run-time.  This list is then processed to add UBSan checks for
    each bound.
    
    gcc/c-family:
            * c-ubsan.cc (ubsan_instrument_vm_assign): New function.
            * c-ubsan.h (ubsan_instrument_vm_assign: New function.
    
    gcc/c:
            * c-typeck.cc (struct instrument_data). New structure.
            (comp_target_types_instr convert_for_assignment_instrument): New
            interfaces for existing functions.
            (struct comptypes_data): Add instrumentation.
            (comptypes_check_enum_int_intr): New interface.
            (comptypes_check_enum_int): Old interface (calls new).
            (comptypes_internal): Collect VLA types needed for UBSan.
            (comp_target_types_instr): New interface.
            (comp_target_types): Old interface (calls new).
            (function_types_compatible_p): No instrumentation for function
            arguments.
            (process_vm_constraints): New function.
            (convert_for_assignment_instrument): New interface.
            (convert_for_assignment): Instrument assignments.
            * sanitizer.def: Add sanitizer builtins.
    
    gcc/testsuite:
            * gcc.dg/ubsan/vm-bounds-1.c: New test.
            * gcc.dg/ubsan/vm-bounds-1b.c: New test.
            * gcc.dg/ubsan/vm-bounds-2.c: New test.
    
    libsanitizer/ubsan:
            * ubsan_checks.inc: Add UBSan check.
            * ubsan_handlers.cpp (handleVMBoundsMismatch): New function.
            * ubsan_handlers.h (struct VMBoundsMismatchData): New structure.
            (vm_bounds_mismatch): New handler.

Diff:
---
 gcc/c-family/c-ubsan.cc                   |  42 ++++++++
 gcc/c-family/c-ubsan.h                    |   1 +
 gcc/c/c-typeck.cc                         | 140 +++++++++++++++++++++++----
 gcc/sanitizer.def                         |   8 ++
 gcc/testsuite/gcc.dg/ubsan/vm-bounds-1.c  | 153 ++++++++++++++++++++++++++++++
 gcc/testsuite/gcc.dg/ubsan/vm-bounds-1b.c |  32 +++++++
 gcc/testsuite/gcc.dg/ubsan/vm-bounds-2.c  |  56 +++++++++++
 libsanitizer/ubsan/ubsan_checks.inc       |   1 +
 libsanitizer/ubsan/ubsan_handlers.cpp     |  27 ++++++
 libsanitizer/ubsan/ubsan_handlers.h       |  11 +++
 10 files changed, 453 insertions(+), 18 deletions(-)

diff --git a/gcc/c-family/c-ubsan.cc b/gcc/c-family/c-ubsan.cc
index 51aa83a378d..59ef9708188 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 fef1033e1e4..1b07b0645f2 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 3b826f2899d..9882fc03c40 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -94,6 +94,9 @@ struct comptypes_data;
 static int tagged_types_tu_compatible_p (const_tree, const_tree,
 					 struct comptypes_data *);
 static int comp_target_types (location_t, tree, tree);
+struct instrument_data;
+static int comp_target_types_instr (location_t, tree, tree,
+				    vec<struct instrument_data, va_gc> *);
 static int function_types_compatible_p (const_tree, const_tree,
 					struct comptypes_data *);
 static int type_lists_compatible_p (const_tree, const_tree,
@@ -106,6 +109,9 @@ static tree pointer_diff (location_t, tree, tree, tree *);
 static tree convert_for_assignment (location_t, location_t, tree, tree, tree,
 				    enum impl_conv, bool, tree, tree, int,
 				    int = 0);
+static tree convert_for_assignment_instrument (location_t, location_t, tree, tree, tree,
+				    enum impl_conv, bool, tree, tree, int, int,
+				    vec<struct instrument_data, va_gc> *);
 static tree valid_compound_expr_initializer (tree, tree);
 static void push_string (const char *);
 static void push_member_name (tree);
@@ -1042,10 +1048,18 @@ common_type (tree t1, tree t2)
   return c_common_type (t1, t2);
 }
 
+struct instrument_data {
+
+  tree t1;
+  tree t2;
+};
+
 struct comptypes_data {
 
   bool enum_and_int_p;
   bool different_types_p;
+
+  vec<struct instrument_data, va_gc>* instr_vec;
 };
 
 /* Return 1 if TYPE1 and TYPE2 are compatible types for assignment
@@ -1069,13 +1083,17 @@ 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,
+				vec<struct instrument_data, va_gc> *instr_vec)
 {
   const struct tagged_tu_seen_cache * tagged_tu_seen_base1 = tagged_tu_seen_base;
   int val;
 
   struct comptypes_data data = { };
+
+  data.instr_vec = instr_vec;
+
   val = comptypes_internal (type1, type2, &data);
   *enum_and_int_p = data.enum_and_int_p;
 
@@ -1084,6 +1102,12 @@ comptypes_check_enum_int (tree type1, tree type2, bool *enum_and_int_p)
   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, NULL);
+}
+
 /* Like comptypes, but if it returns nonzero for different types, it
    sets *DIFFERENT_TYPES_P to true.  */
 
@@ -1252,7 +1276,16 @@ 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->instr_vec)
+	      {
+		struct instrument_data id;
+		id.t1 = TYPE_MAIN_VARIANT (t2);
+		id.t2 = TYPE_MAIN_VARIANT (t1);
+		vec_safe_push(data->instr_vec, id);
+	      }
+	    break;
+	  }
 	if (d1_zero && d2_zero)
 	  break;
 	if (d1_zero || d2_zero
@@ -1299,7 +1332,8 @@ 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,
+			 vec<struct instrument_data, va_gc> *instr_vec)
 {
   int val;
   int val_ped;
@@ -1333,8 +1367,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, instr_vec);
 
   if (val == 1 && val_ped != 1)
     pedwarn_c11 (location, OPT_Wpedantic, "invalid use of pointers to arrays with different qualifiers "
@@ -1349,6 +1382,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);
+}
+
 \f
 /* Subroutines of `comptypes'.  */
 
@@ -1694,8 +1734,14 @@ function_types_compatible_p (const_tree f1, const_tree f2,
       return val;
     }
 
-  /* Both types have argument lists: compare them and propagate results.  */
+  /* Both types have argument lists: compare them and propagate results.
+     Turn off UBSan instrumentation for bounds as these are all arrays
+     of unspecified bound.  */
+  auto instr_vec_tmp = data->instr_vec;
+  data->instr_vec = NULL;
   val1 = type_lists_compatible_p (args1, args2, data);
+  data->instr_vec = instr_vec_tmp;
+
   return val1 != 1 ? val1 : val;
 }
 
@@ -3518,10 +3564,11 @@ convert_argument (location_t ploc, tree function, tree fundecl,
   if (excess_precision)
     val = build1 (EXCESS_PRECISION_EXPR, valtype, val);
 
-  tree parmval = convert_for_assignment (ploc, ploc, type,
-					 val, origtype, ic_argpass,
-					 npc, fundecl, function,
-					 parmnum + 1, warnopt);
+  tree parmval = convert_for_assignment_instrument (ploc, ploc, type,
+						    val, origtype, ic_argpass,
+						    npc, fundecl, function,
+						    parmnum + 1, warnopt,
+						    NULL);
 
   if (targetm.calls.promote_prototypes (fundecl ? TREE_TYPE (fundecl) : 0)
       && INTEGRAL_TYPE_P (type)
@@ -3531,6 +3578,26 @@ convert_argument (location_t ploc, tree function, tree fundecl,
   return parmval;
 }
 
+
+/* Process all constraints for variably-modified types.  */
+
+static tree
+process_vm_constraints (location_t location,
+			vec<struct instrument_data, va_gc> *instr_vec)
+{
+  unsigned int i;
+  struct instrument_data* d;
+  tree instr_expr = void_node;
+
+  FOR_EACH_VEC_SAFE_ELT (instr_vec, i, d)
+    {
+      tree in = ubsan_instrument_vm_assign (location, d->t1, d->t2);
+      instr_expr = fold_build2 (COMPOUND_EXPR, void_type_node, in, instr_expr);
+    }
+  return instr_expr;
+}
+
+
 /* Convert the argument expressions in the vector VALUES
    to the types in the list TYPELIST.
 
@@ -6859,7 +6926,44 @@ static tree
 convert_for_assignment (location_t location, location_t expr_loc, tree type,
 			tree rhs, tree origtype, enum impl_conv errtype,
 			bool null_pointer_constant, tree fundecl,
-			tree function, int parmnum, int warnopt /* = 0 */)
+			tree function, int parmnum, int warnopt)
+{
+  vec<struct instrument_data, va_gc> *instr_vec = NULL;
+
+  if (sanitize_flags_p (SANITIZE_VLA)
+      && (ic_init_const != errtype))
+    vec_alloc (instr_vec, 10);
+
+  tree ret = convert_for_assignment_instrument (location, expr_loc, type,
+						rhs, origtype, errtype,
+						null_pointer_constant, fundecl,
+						function, parmnum, warnopt,
+						instr_vec);
+  if (instr_vec)
+    {
+      if (ret && error_mark_node != ret && 0 < vec_safe_length (instr_vec))
+	{
+	  /* We have to make sure that the rhs is evaluated first,
+	     because we may use size expressions in it to check bounds.  */
+	  tree instr_expr = process_vm_constraints (location, instr_vec);
+	  if (void_node != instr_expr)
+	    {
+	      ret = save_expr (ret);
+	      instr_expr = fold_build2 (COMPOUND_EXPR, void_type_node, ret, instr_expr);
+	      ret = fold_build2 (COMPOUND_EXPR, TREE_TYPE (ret), instr_expr, ret);
+	    }
+	}
+      vec_free (instr_vec);
+    }
+  return ret;
+}
+
+static tree
+convert_for_assignment_instrument (location_t location, location_t expr_loc, tree type,
+			tree rhs, tree origtype, enum impl_conv errtype,
+			bool null_pointer_constant, tree fundecl,
+			tree function, int parmnum, int warnopt,
+			vec<struct instrument_data, va_gc>* instr_vec)
 {
   enum tree_code codel = TREE_CODE (type);
   tree orig_rhs = rhs;
@@ -7103,11 +7207,11 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
       rhs = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (rhs)), rhs);
       SET_EXPR_LOCATION (rhs, location);
 
-      rhs = convert_for_assignment (location, expr_loc,
-				    build_pointer_type (TREE_TYPE (type)),
-				    rhs, origtype, errtype,
-				    null_pointer_constant, fundecl, function,
-				    parmnum, warnopt);
+      rhs = convert_for_assignment_instrument (location, expr_loc,
+					       build_pointer_type (TREE_TYPE (type)),
+					       rhs, origtype, errtype,
+					       null_pointer_constant, fundecl, function,
+					       parmnum, warnopt, instr_vec);
       if (rhs == error_mark_node)
 	return error_mark_node;
 
@@ -7488,7 +7592,7 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
 	 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, instr_vec))
 	  || is_opaque_pointer
 	  || ((c_common_unsigned_type (mvl)
 	       == c_common_unsigned_type (mvr))
diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def
index d47cc7dd9d7..9a25f1db4bd 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..003202ac9b7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ubsan/vm-bounds-1.c
@@ -0,0 +1,153 @@
+/* { 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)" } */
+
+	char (*(*p)(void))[u][v] = &z0;
+	/* { dg-output "\[^\n\r]*bound 4 of type 'char \\\[\\\*\\\]\\\[\\\*\\\]' does not match bound 5 of type 'char \\\[5\\\]\\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+	/* { dg-output "\[^\n\r]*bound 3 of type 'char \\\[\\\*\\\]' does not match bound 5 of type 'char \\\[5\\\]'" } */
+}
+
diff --git a/gcc/testsuite/gcc.dg/ubsan/vm-bounds-1b.c b/gcc/testsuite/gcc.dg/ubsan/vm-bounds-1b.c
new file mode 100644
index 00000000000..5b51aeacbcb
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ubsan/vm-bounds-1b.c
@@ -0,0 +1,32 @@
+/* { dg-do run } */
+/* { dg-options "-fsanitize=vla-bound" } */
+/* { dg-require-effective-target trampolines } */
+
+
+static char bb[4][4];
+static char (*g())[4][4] { return &bb; }
+
+int main()
+{
+	int n = 3;
+	char b[4];
+	char (*f())[++n] { return &b; }
+
+	if (4 != sizeof(*f()))
+		__builtin_abort();
+
+	char (*(*p)())[++n] = &f;	/* { dg-output "bound 5 of type 'char \\\[\\\*\\\]' does not match bound 4 of type 'char \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+
+
+	if (5 != sizeof(*(*p)()))
+		__builtin_abort();
+
+	char (*(*q)())[++n][4] = &g;	/* { dg-output "\[^\n\r]*bound 6 of type 'char \\\[\\\*\\\]\\\[4\\\]' does not match bound 4 of type 'char \\\[4\\\]\\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+
+
+	if (6 * 4 != sizeof(*(*q)()))
+		__builtin_abort();
+
+	return 0;
+}
+
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..ebc63d32144
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ubsan/vm-bounds-2.c
@@ -0,0 +1,56 @@
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=vla-bound" } */
+
+// make sure we do not ICE on any of these
+
+const char* name = "hallo";
+
+
+typedef void (*ht)(int n, int m, char x[n][m]);
+void e(ht) { }
+
+int n, m;
+static void f0(char a[n][m]) { }
+static void f1(int u, int v, char a[u][v]) { }
+static void f2(int u, int v, char a[u][v]) { }
+
+void f(void)
+{
+	int x = 1;
+	int (*m)[x] = 0;
+	m = ({ long* d2; (int (*)[d2[0]])(0); });
+
+	/* function pointer assignments */
+
+	void (*gp)(char x[4][3]) = f0;
+	void (*hp)(int n, int m, char x[n][m]) = f1;
+	ht hp2 = f1;
+	e(f1);
+
+	/* composite type */
+
+	int u = 3; int v = 4;
+	char a[u][v];
+	(1 ? f1 : f2)(u, v, a);
+}
+
+/* size expression in parameter */
+
+extern void a(long N, char (*a)[N]);
+
+static void b(void)
+{
+	a(1, ({ int d = 0; (char (*)[d])0; }) );
+}
+
+/* composite type */
+
+int c(int u, char (*a)[u]);
+int c(int u, char (*a)[u]) { }
+
+int d(void)
+{
+	char a[3];
+	c(3, &a);
+}
+
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 {

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2023-07-29 19:45 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-07-29 19:45 [gcc(refs/users/uecker/heads/vla)] c: introduce ubsan checking for assigment of VM types 2/4 Martin Uecker

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).