From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mailrelay.tugraz.at (mailrelay.tugraz.at [129.27.2.202]) by sourceware.org (Postfix) with ESMTPS id 1AAE9385B500 for ; Thu, 22 Dec 2022 17:44:31 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 1AAE9385B500 Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=tugraz.at Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=tugraz.at Received: from [192.168.0.221] (84-115-221-28.cable.dynamic.surfer.at [84.115.221.28]) by mailrelay.tugraz.at (Postfix) with ESMTPSA id 4NdHkN1T8bz3wct; Thu, 22 Dec 2022 18:44:28 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=tugraz.at; s=mailrelay; t=1671731068; bh=SXCFEQDc6Yv+Fo6XcZs2lbBSn8QkjJ3lXxyWqQgwz/Y=; h=Subject:From:To:Cc:Date; b=uuGT5WoalxPgY+yhgku+0n5tvW3X5JTdNZDW88A9rW7P5TDee31btB/ch614k9sY5 KmSTlAuNSr0py8QosXU96pFJn3MaTDG9Tio+aGQ5SttU6Pdzo1ehe/nOor0muj/i98 w6PVkWuyF+n0yfhNDmWr4dZkb1WaDS+uvC+SWt3g= Message-ID: <710940157fea32e9f628f8286891cfeb21646f37.camel@tugraz.at> Subject: [C PATCH] (for STAGE 1) UBSan instrumentation for assignment of VM types From: Martin Uecker To: gcc-patches@gcc.gnu.org Cc: Martin =?UTF-8?Q?Li=C5=A1ka?= Date: Thu, 22 Dec 2022 18:44:27 +0100 Content-Type: text/plain; charset="UTF-8" User-Agent: Evolution 3.38.3-1 MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-TUG-Backscatter-control: G/VXY7/6zeyuAY/PU2/0qw X-Spam-Scanner: SpamAssassin 3.003001 X-Spam-Score-relay: -1.9 X-Scanned-By: MIMEDefang 2.74 on 129.27.10.116 X-Spam-Status: No, score=-10.6 required=5.0 tests=BAYES_00,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,GIT_PATCH_0,RCVD_IN_MSPIKE_H3,RCVD_IN_MSPIKE_WL,SPF_HELO_NONE,SPF_PASS,TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: 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. 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 {