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 --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 2a1b7321b45..a8fccc6f6ed 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); +} + /* 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; } @@ -3517,10 +3563,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) @@ -3530,6 +3577,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. @@ -6858,7 +6925,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; @@ -7102,11 +7206,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; @@ -7487,7 +7591,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 {
Re: [C PATCH 2/4] introduce ubsan checking for assigment of VM types 2/4
Martin Uecker via Gcc-patches Mon, 29 May 2023 03:31:56 -0700
- [C PATCH 1/4] introduce ubsan checking for ... Martin Uecker via Gcc-patches
- Re: [C PATCH 4/4] introduce ubsan chec... Martin Uecker via Gcc-patches
- Re: [C PATCH 2/4] introduce ubsan chec... Martin Uecker via Gcc-patches
- Re: [C PATCH 3/4] introduce ubsan chec... Martin Uecker via Gcc-patches
- Re: [C PATCH 3/4] introduce ubsan ... Joseph Myers
- Re: [C PATCH 3/4] introduce ub... Martin Uecker via Gcc-patches