From patchwork Mon Jun 16 10:39:07 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marek Polacek X-Patchwork-Id: 360047 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id DFF49140085 for ; Mon, 16 Jun 2014 20:39:27 +1000 (EST) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:date :from:to:cc:subject:message-id:mime-version:content-type; q=dns; s=default; b=IXrThNjzQqXSlBUW8molxRMBVQK1hIz5euUwWFBB8gLOdgaswF AUVxAyIgKZCVrTVSOWi/FO8Yjf0h4g5uksMazYikDBRGYkgUIjnOJR66iuVZSEjb PzS2jdnsf334MunQ7eTl+hBCzFDiUIdRX8EHhZ29sYhenDnbQLvx/VI6M= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:date :from:to:cc:subject:message-id:mime-version:content-type; s= default; bh=vz+SdIKqXW3uJLTunitnGE46teI=; b=ap2cdc+ezJmft2lK2ogP OBcJal9MOqw2/MR3fDiCj+lpsh6CGk7JGUfiqO8KnvkEc70SR/3m0swl/qAPMk5k MzwBMutI5YyRiVpDrzLbtLY4/DUUt8sG0iO4CSFmLrMp1J0FK73sBJX/n7lfSPkt vH6Nf9b3byAzVDNhWM2YqC8= Received: (qmail 2805 invoked by alias); 16 Jun 2014 10:39:19 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 2794 invoked by uid 89); 16 Jun 2014 10:39:19 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-3.2 required=5.0 tests=AWL, BAYES_00, RP_MATCHES_RCVD, SPF_HELO_PASS, SPF_PASS autolearn=ham version=3.3.2 X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Mon, 16 Jun 2014 10:39:15 +0000 Received: from int-mx13.intmail.prod.int.phx2.redhat.com (int-mx13.intmail.prod.int.phx2.redhat.com [10.5.11.26]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id s5GAdCjC003364 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Mon, 16 Jun 2014 06:39:12 -0400 Received: from redhat.com (ovpn-116-31.ams2.redhat.com [10.36.116.31]) by int-mx13.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id s5GAd7Ll010021 (version=TLSv1/SSLv3 cipher=AES128-GCM-SHA256 bits=128 verify=NO); Mon, 16 Jun 2014 06:39:10 -0400 Date: Mon, 16 Jun 2014 12:39:07 +0200 From: Marek Polacek To: GCC Patches Cc: Jakub Jelinek , Jason Merrill , "Joseph S. Myers" , Richard Biener , Jeff Law Subject: [PATCH] Implement -fsanitize=bounds and internal calls in FEs Message-ID: <20140616103907.GD17965@redhat.com> MIME-Version: 1.0 Content-Disposition: inline User-Agent: Mutt/1.5.23 (2014-03-12) The following is quite large patch for something rather simple as bounds checking. The idea is to instrument ARRAY_REFs in such a way that iff the array index is greater than the max value of the TYPE_DOMAIN of the ARRAY_TYPE, call __ubsan builtin and report error. My first attempts failed, however - e.g. if we add the instrumentation (the conditional, not an internal call) in some early pass, VRP spuriously warns about array bounds - it can't figure out that some path in the jump threaded code never executes. Another attempt was adding internal calls in the .ubsan pass right after .ssa - same thing that we do for NULL sanitization - and expand those in the .sanopt pass. But this doesn't work either, the issue is with VLAs, where after gimplification TYPE_MAX_VALUE of TYPE_DOMAIN is not usable - so this is dead in the water too. So next thing to do was to generate internal calls in the front ends; we can use TYPE_MAX_VALUE of TYPE_DOMAIN therein. But we don't have the infrastructure for that, so I had to implement it. To avoid creating a new tree code, I'm reusing CALL_EXPR whose CALL_EXPR_FN is NULL_TREE, but it has new "ifn" field in tree_base.u, which says which internal function it is. The .ifn field is accessed by new macro CALL_EXPR_IFN. Now everything seems to work, even though it might not be up to snuff yet. Jason/Joseph, could you please look at the C++/C FE parts? Jakub/Richi, could you please look at the gimplification part? I'm pretty sure I missed something in there (LHS of internal calls? TREE_SIDE_EFFECTs?). Pretty printing of internal calls is lame - maybe add a string param to DEF_INTERNAL_FN? I just wanted to make sure we don't ICE on it. Regtested, bootstraped, "boostrap-ubsan-ed" on x86_64-linux. 2014-06-16 Marek Polacek * asan.c (pass_sanopt::execute): Handle IFN_UBSAN_BOUNDS. * flag-types.h (enum sanitize_code): Add SANITIZE_BOUNDS and or it into SANITIZE_UNDEFINED. * gimplify.c (gimplify_call_expr): Add gimplification of internal functions created in the FEs. * internal-fn.c: Move "internal-fn.h" after "tree.h". (expand_UBSAN_BOUNDS): New function. * internal-fn.def (UBSAN_BOUNDS): New internal function. * internal-fn.h: Don't define internal functions here. * opts.c (common_handle_option): Add -fsanitize=bounds. * sanitizer.def (BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS, BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS_ABORT): Add. * tree-core.h: Define internal functions here. (struct tree_base): Add ifn field. * tree-pretty-print.c (print_call_name): Handle functions without CALL_EXPR_FN. * tree.c (get_callee_fndecl): Likewise. (build_call_expr_internal_loc): New function. * tree.def (CALL_EXPR): Update description. * tree.h (CALL_EXPR_IFN): Define. (build_call_expr_internal_loc): Declare. * ubsan.c (get_ubsan_type_info_for_type): Return 0 for non-arithmetic types. (ubsan_type_descriptor): Change bool parameter to enum ubsan_print_style. Adjust the code. Add handling of UBSAN_PRINT_ARRAY. (ubsan_expand_bounds_btn): New function. (ubsan_expand_null_ifn): Adjust ubsan_type_descriptor call. (ubsan_build_overflow_builtin): Likewise. (instrument_bool_enum_load): Likewise. (ubsan_instrument_float_cast): Likewise. * ubsan.h (enum ubsan_print_style): New enum. (ubsan_expand_bounds_btn): Declare. (ubsan_type_descriptor): Adjust declaration. Use a default parameter. c-family/ * c-common.c: Inlcude "c-ubsan.h". (c_fully_fold_internal): Instrument array bounds. * c-ubsan.c: Include "internal-fn.h". (ubsan_instrument_division): Mark instrumented arrays as having side effects. Adjust ubsan_type_descriptor call. (ubsan_instrument_shift): Likewise. (ubsan_instrument_vla): Adjust ubsan_type_descriptor call. (ubsan_instrument_bounds): New function. * c-ubsan.h (ubsan_instrument_bounds): Declare. cp/ * cp-gimplify.c (cp_genericize_r): Instrument array bounds. testsuite/ * c-c++-common/ubsan/bounds-1.c: New test. * c-c++-common/ubsan/bounds-2.c: New test. * c-c++-common/ubsan/bounds-3.c: New test. Marek diff --git gcc/asan.c gcc/asan.c index 651cfed..e0ca376 100644 --- gcc/asan.c +++ gcc/asan.c @@ -2708,6 +2708,9 @@ pass_sanopt::execute (function *fun) case IFN_UBSAN_NULL: ubsan_expand_null_ifn (gsi); break; + case IFN_UBSAN_BOUNDS: + ubsan_expand_bounds_btn (&gsi); + break; default: break; } @@ -2718,6 +2721,10 @@ pass_sanopt::execute (function *fun) print_gimple_stmt (dump_file, stmt, 0, dump_flags); fprintf (dump_file, "\n"); } + + /* ubsan_expand_bounds_btn might move us to the end of the BB. */ + if (gsi_end_p (gsi)) + break; } } return 0; diff --git gcc/c-family/c-common.c gcc/c-family/c-common.c index 077263e..ccb4971 100644 --- gcc/c-family/c-common.c +++ gcc/c-family/c-common.c @@ -34,6 +34,7 @@ along with GCC; see the file COPYING3. If not see #include "c-pragma.h" #include "c-common.h" #include "c-objc.h" +#include "c-ubsan.h" #include "tm_p.h" #include "obstack.h" #include "cpplib.h" @@ -1228,6 +1229,19 @@ c_fully_fold_internal (tree expr, bool in_init, bool *maybe_const_operands, maybe_const_itself); STRIP_TYPE_NOPS (op1); op1 = decl_constant_value_for_optimization (op1); + + if (flag_sanitize & SANITIZE_BOUNDS + && current_function_decl != 0 + && !lookup_attribute ("no_sanitize_undefined", + DECL_ATTRIBUTES (current_function_decl))) + { + op1 = c_save_expr (op1); + op1 = c_fully_fold (op1, false, NULL); + tree e = ubsan_instrument_bounds (loc, op0, op1); + if (e != NULL_TREE) + op1 = build2 (COMPOUND_EXPR, TREE_TYPE (op1), e, op1); + } + if (op0 != orig_op0 || op1 != orig_op1) ret = build4 (ARRAY_REF, TREE_TYPE (expr), op0, op1, op2, op3); if (ret != expr) diff --git gcc/c-family/c-ubsan.c gcc/c-family/c-ubsan.c index 8d5b685..65b0f2a 100644 --- gcc/c-family/c-ubsan.c +++ gcc/c-family/c-ubsan.c @@ -30,6 +30,7 @@ along with GCC; see the file COPYING3. If not see #include "c-family/c-common.h" #include "c-family/c-ubsan.h" #include "asan.h" +#include "internal-fn.h" /* Instrument division by zero and INT_MIN / -1. If not instrumenting, return NULL_TREE. */ @@ -77,15 +78,27 @@ ubsan_instrument_division (location_t loc, tree op0, tree op1) return NULL_TREE; /* In case we have a SAVE_EXPR in a conditional context, we need to - make sure it gets evaluated before the condition. */ + make sure it gets evaluated before the condition. If the OP0 is + an instrumented array reference, mark it as having side effects so + it's not folded away. */ + if (flag_sanitize & SANITIZE_BOUNDS) + { + tree xop0 = op0; + while (CONVERT_EXPR_P (xop0)) + xop0 = TREE_OPERAND (xop0, 0); + if (TREE_CODE (xop0) == ARRAY_REF) + { + TREE_SIDE_EFFECTS (xop0) = 1; + TREE_SIDE_EFFECTS (op0) = 1; + } + } t = fold_build2 (COMPOUND_EXPR, TREE_TYPE (t), op0, t); if (flag_sanitize_undefined_trap_on_error) tt = build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0); else { tree data = ubsan_create_data ("__ubsan_overflow_data", &loc, NULL, - ubsan_type_descriptor (type, false), - NULL_TREE); + ubsan_type_descriptor (type), NULL_TREE); data = build_fold_addr_expr_loc (loc, data); enum built_in_function bcode = flag_sanitize_recover @@ -154,7 +167,20 @@ ubsan_instrument_shift (location_t loc, enum tree_code code, return NULL_TREE; /* In case we have a SAVE_EXPR in a conditional context, we need to - make sure it gets evaluated before the condition. */ + make sure it gets evaluated before the condition. If the OP0 is + an instrumented array reference, mark it as having side effects so + it's not folded away. */ + if (flag_sanitize & SANITIZE_BOUNDS) + { + tree xop0 = op0; + while (CONVERT_EXPR_P (xop0)) + xop0 = TREE_OPERAND (xop0, 0); + if (TREE_CODE (xop0) == ARRAY_REF) + { + TREE_SIDE_EFFECTS (xop0) = 1; + TREE_SIDE_EFFECTS (op0) = 1; + } + } t = fold_build2 (COMPOUND_EXPR, TREE_TYPE (t), op0, t); t = fold_build2 (TRUTH_OR_EXPR, boolean_type_node, t, tt ? tt : integer_zero_node); @@ -164,10 +190,8 @@ ubsan_instrument_shift (location_t loc, enum tree_code code, else { tree data = ubsan_create_data ("__ubsan_shift_data", &loc, NULL, - ubsan_type_descriptor (type0, false), - ubsan_type_descriptor (type1, false), - NULL_TREE); - + ubsan_type_descriptor (type0), + ubsan_type_descriptor (type1), NULL_TREE); data = build_fold_addr_expr_loc (loc, data); enum built_in_function bcode @@ -197,8 +221,7 @@ ubsan_instrument_vla (location_t loc, tree size) else { tree data = ubsan_create_data ("__ubsan_vla_data", &loc, NULL, - ubsan_type_descriptor (type, false), - NULL_TREE); + ubsan_type_descriptor (type), NULL_TREE); data = build_fold_addr_expr_loc (loc, data); enum built_in_function bcode = flag_sanitize_recover @@ -228,3 +251,42 @@ ubsan_instrument_return (location_t loc) tree t = builtin_decl_explicit (BUILT_IN_UBSAN_HANDLE_MISSING_RETURN); return build_call_expr_loc (loc, t, 1, build_fold_addr_expr_loc (loc, data)); } + +/* Instrument array bounds for ARRAY_REFs. We create special builtin, + that gets expanded in the sanopt pass, and make an array dimension + of it. ARRAY is the array, INDEX is an index to the array. + Return NULL_TREE if no instrumentation is emitted. */ + +tree +ubsan_instrument_bounds (location_t loc, tree array, tree index) +{ + tree type = TREE_TYPE (array); + tree domain = TYPE_DOMAIN (type); + + if (domain == NULL_TREE) + return NULL_TREE; + + tree bound = TYPE_MAX_VALUE (domain); + + /* Detect flexible array members and suchlike. */ + if (TREE_CODE (array) == COMPONENT_REF + /* ISO C99 flexible array members don't have a size. */ + && (TYPE_SIZE (type) == NULL_TREE + || integer_zerop (TYPE_SIZE_UNIT (type)) + || integer_zerop (bound) + || integer_onep (bound))) + return NULL_TREE; + + /* If the index and bound are constants, we can check early. */ + if (TREE_CODE (index) == INTEGER_CST + && TREE_CODE (bound) == INTEGER_CST + && tree_int_cst_le (index, bound) + && tree_int_cst_sgn (index) == 1) + return NULL_TREE; + + /* Create a "(T *) 0" tree node to describe the array type. */ + tree zero_with_type = build_int_cst (build_pointer_type (type), 0); + return build_call_expr_internal_loc (loc, IFN_UBSAN_BOUNDS, + void_type_node, 3, zero_with_type, + index, bound); +} diff --git gcc/c-family/c-ubsan.h gcc/c-family/c-ubsan.h index e504b90..9197d78 100644 --- gcc/c-family/c-ubsan.h +++ gcc/c-family/c-ubsan.h @@ -25,5 +25,6 @@ extern tree ubsan_instrument_division (location_t, tree, tree); extern tree ubsan_instrument_shift (location_t, enum tree_code, tree, tree); extern tree ubsan_instrument_vla (location_t, tree); extern tree ubsan_instrument_return (location_t); +extern tree ubsan_instrument_bounds (location_t, tree, tree); #endif /* GCC_C_UBSAN_H */ diff --git gcc/cp/cp-gimplify.c gcc/cp/cp-gimplify.c index 2798358..3207d87 100644 --- gcc/cp/cp-gimplify.c +++ gcc/cp/cp-gimplify.c @@ -1197,7 +1197,22 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data) if (*stmt_p == error_mark_node) *stmt_p = size_one_node; return NULL; - } + } + else if (flag_sanitize & SANITIZE_BOUNDS + && TREE_CODE (stmt) == ARRAY_REF + && !processing_template_decl + && current_function_decl != 0 + && !lookup_attribute ("no_sanitize_undefined", + DECL_ATTRIBUTES (current_function_decl))) + { + tree op0 = TREE_OPERAND (stmt, 0); + tree op1 = TREE_OPERAND (stmt, 1); + op1 = cp_save_expr (op1); + tree e = ubsan_instrument_bounds (input_location, op0, op1); + if (e != NULL_TREE) + TREE_OPERAND (stmt, 1) = build2 (COMPOUND_EXPR, TREE_TYPE (op1), + e, op1); + } pointer_set_insert (p_set, *stmt_p); diff --git gcc/flag-types.h gcc/flag-types.h index ed00046..4b8e338 100644 --- gcc/flag-types.h +++ gcc/flag-types.h @@ -230,9 +230,11 @@ enum sanitize_code { SANITIZE_ENUM = 1 << 11, SANITIZE_FLOAT_DIVIDE = 1 << 12, SANITIZE_FLOAT_CAST = 1 << 13, + SANITIZE_BOUNDS = 1 << 14, SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE | SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN - | SANITIZE_SI_OVERFLOW | SANITIZE_BOOL | SANITIZE_ENUM, + | SANITIZE_SI_OVERFLOW | SANITIZE_BOOL | SANITIZE_ENUM + | SANITIZE_BOUNDS, SANITIZE_NONDEFAULT = SANITIZE_FLOAT_DIVIDE | SANITIZE_FLOAT_CAST }; diff --git gcc/gimplify.c gcc/gimplify.c index 10f8ac6..475e749 100644 --- gcc/gimplify.c +++ gcc/gimplify.c @@ -2245,6 +2245,24 @@ gimplify_call_expr (tree *expr_p, gimple_seq *pre_p, bool want_value) if (! EXPR_HAS_LOCATION (*expr_p)) SET_EXPR_LOCATION (*expr_p, input_location); + /* Gimplify internal functions created in the FEs. */ + if (CALL_EXPR_FN (*expr_p) == NULL_TREE) + { + nargs = call_expr_nargs (*expr_p); + enum internal_fn ifn = CALL_EXPR_IFN (*expr_p); + auto_vec vargs (nargs); + + for (i = 0; i < nargs; i++) + { + gimplify_arg (&CALL_EXPR_ARG (*expr_p, i), pre_p, + EXPR_LOCATION (*expr_p)); + vargs.quick_push (CALL_EXPR_ARG (*expr_p, i)); + } + gimple call = gimple_build_call_internal_vec (ifn, vargs); + gimplify_seq_add_stmt (pre_p, call); + return GS_ALL_DONE; + } + /* This may be a call to a builtin function. Builtin function calls may be transformed into different diff --git gcc/internal-fn.c gcc/internal-fn.c index 68b2b66..78f59d6 100644 --- gcc/internal-fn.c +++ gcc/internal-fn.c @@ -20,8 +20,8 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #include "system.h" #include "coretypes.h" -#include "internal-fn.h" #include "tree.h" +#include "internal-fn.h" #include "stor-layout.h" #include "expr.h" #include "optabs.h" @@ -159,6 +159,14 @@ expand_UBSAN_NULL (gimple stmt ATTRIBUTE_UNUSED) gcc_unreachable (); } +/* This should get expanded in the sanopt pass. */ + +static void +expand_UBSAN_BOUNDS (gimple stmt ATTRIBUTE_UNUSED) +{ + gcc_unreachable (); +} + /* Add sub/add overflow checking to the statement STMT. CODE says whether the operation is +, or -. */ diff --git gcc/internal-fn.def gcc/internal-fn.def index 31dc4c9..f0766bc 100644 --- gcc/internal-fn.def +++ gcc/internal-fn.def @@ -48,6 +48,7 @@ DEF_INTERNAL_FN (MASK_LOAD, ECF_PURE | ECF_LEAF) DEF_INTERNAL_FN (MASK_STORE, ECF_LEAF) DEF_INTERNAL_FN (ANNOTATE, ECF_CONST | ECF_LEAF | ECF_NOTHROW) DEF_INTERNAL_FN (UBSAN_NULL, ECF_LEAF | ECF_NOTHROW) +DEF_INTERNAL_FN (UBSAN_BOUNDS, ECF_LEAF | ECF_NOTHROW) DEF_INTERNAL_FN (UBSAN_CHECK_ADD, ECF_CONST | ECF_LEAF | ECF_NOTHROW) DEF_INTERNAL_FN (UBSAN_CHECK_SUB, ECF_CONST | ECF_LEAF | ECF_NOTHROW) DEF_INTERNAL_FN (UBSAN_CHECK_MUL, ECF_CONST | ECF_LEAF | ECF_NOTHROW) diff --git gcc/internal-fn.h gcc/internal-fn.h index 9c3215f..2dcf44e 100644 --- gcc/internal-fn.h +++ gcc/internal-fn.h @@ -20,13 +20,6 @@ along with GCC; see the file COPYING3. If not see #ifndef GCC_INTERNAL_FN_H #define GCC_INTERNAL_FN_H -enum internal_fn { -#define DEF_INTERNAL_FN(CODE, FLAGS) IFN_##CODE, -#include "internal-fn.def" -#undef DEF_INTERNAL_FN - IFN_LAST -}; - /* Return the name of internal function FN. The name is only meaningful for dumps; it has no linkage. */ diff --git gcc/opts.c gcc/opts.c index 2b1280a..79650da 100644 --- gcc/opts.c +++ gcc/opts.c @@ -1466,6 +1466,7 @@ common_handle_option (struct gcc_options *opts, sizeof "float-divide-by-zero" - 1 }, { "float-cast-overflow", SANITIZE_FLOAT_CAST, sizeof "float-cast-overflow" - 1 }, + { "bounds", SANITIZE_BOUNDS, sizeof "bounds" - 1 }, { NULL, 0, 0 } }; const char *comma; diff --git gcc/sanitizer.def gcc/sanitizer.def index 4016fc5..aaa8846 100644 --- gcc/sanitizer.def +++ gcc/sanitizer.def @@ -385,3 +385,11 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW_ABORT, "__ubsan_handle_float_cast_overflow_abort", BT_FN_VOID_PTR_PTR, ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS, + "__ubsan_handle_out_of_bounds", + BT_FN_VOID_PTR_PTR, + ATTR_COLD_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS_ABORT, + "__ubsan_handle_out_of_bounds_abort", + BT_FN_VOID_PTR_PTR, + ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST) diff --git gcc/testsuite/c-c++-common/ubsan/bounds-1.c gcc/testsuite/c-c++-common/ubsan/bounds-1.c index e69de29..aa192d3 100644 --- gcc/testsuite/c-c++-common/ubsan/bounds-1.c +++ gcc/testsuite/c-c++-common/ubsan/bounds-1.c @@ -0,0 +1,75 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=bounds -fno-sanitize-recover -Wall" } */ + +/* Don't fail on valid uses. */ + +struct S { int a[10]; }; +struct T { int l; int a[]; }; +struct U { int l; int a[0]; }; + +__attribute__ ((noinline, noclone)) +void +fn_p (int p) +{ +} + +__attribute__ ((noinline, noclone)) +void +fn_a (volatile int a[]) +{ + /* This is not instrumented. */ + a[4] = 5; +} + +__attribute__ ((noinline, noclone)) +int +foo_i (int i) +{ + int a[5] = { }; + int k = i ? a[i] : i; + return k; +} + +int +main (void) +{ + volatile int a[5]; + a[4] = 1; + a[2] = a[3]; + fn_p (a[4]); + fn_a (a); + + int i = 4; + a[i] = 1; + a[2] = a[i]; + fn_p (a[i]); + foo_i (i); + + const int n = 5; + volatile int b[n]; + b[4] = 1; + b[2] = b[3]; + fn_p (b[4]); + fn_a (b); + + volatile int c[n][n][n]; + c[2][2][2] = 2; + i = c[4][4][4]; + + volatile struct S s; + s.a[9] = 1; + i = s.a[9]; + + /* Don't instrument flexible array members. */ + struct T *t = (struct T *) __builtin_malloc (sizeof (struct T) + 10); + t->a[1] = 1; + + struct U *u = (struct U *) __builtin_malloc (sizeof (struct U) + 10); + u->a[1] = 1; + + long int *d[10][5]; + d[9][0] = (long int *) 0; + d[8][3] = d[9][0]; + + return 0; +} diff --git gcc/testsuite/c-c++-common/ubsan/bounds-2.c gcc/testsuite/c-c++-common/ubsan/bounds-2.c index e69de29..95f77c2 100644 --- gcc/testsuite/c-c++-common/ubsan/bounds-2.c +++ gcc/testsuite/c-c++-common/ubsan/bounds-2.c @@ -0,0 +1,168 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=bounds -Wall -Wextra -Wno-unused -Wno-array-bounds" } */ + +/* Test runtime errors. */ + +struct S { int a[10]; }; + +int +foo_5 (void) +{ + return 5; +} + +__attribute__ ((noinline, noclone)) +void +fn_p (int p) +{ + (void) p; +} + +static void __attribute__ ((noinline, noclone)) +fn1 (void) +{ + volatile int a[5]; + a[5] = 1; + a[2] = a[5]; +} + +static void __attribute__ ((noinline, noclone)) +fn2 (void) +{ + volatile int a[5]; + int i = 5; + int *p = &i; + a[*p] = 1; +} + +static void __attribute__ ((noinline, noclone)) +fn3 (void) +{ + volatile int a[5]; + fn_p (a[5]); +} + +static void __attribute__ ((noinline, noclone)) +fn4 (void) +{ + volatile int a[5]; + a[foo_5 ()] = 1; +} + +static void __attribute__ ((noinline, noclone)) +fn5 (void) +{ + int i = 5; + volatile int a[i]; + a[i] = 1; + a[2] = a[i]; +} + +static void __attribute__ ((noinline, noclone)) +fn6 (void) +{ + int i = 5; + volatile int a[i]; + fn_p (a[i]); + a[foo_5 ()] = 1; +} + +static void __attribute__ ((noinline, noclone)) +fn7 (void) +{ + int n = 5, i; + volatile int c[n][n][n]; + c[5][2][2] = 2; + c[2][5][2] = 2; + c[2][2][5] = 2; + i = c[5][2][2]; + i = c[2][5][2]; + i = c[2][2][5]; +} + +static void __attribute__ ((noinline, noclone)) +fn8 (void) +{ + int i = 5; + volatile struct S s; + s.a[10] = 1; + i = s.a[10]; +} + +static void __attribute__ ((noinline, noclone)) +fn9 (void) +{ + long int *volatile d[10][5]; + d[10][0] = 0; + d[8][3] = d[10][0]; +} + +static void __attribute__ ((noinline, noclone)) +fn10 (void) +{ + /* Beware of side-effects. */ + volatile int x = 10; + volatile int e[20]; + e[x++] = 3; + if (x != 11) + __builtin_abort (); + e[x--] = 3; + if (x != 10) + __builtin_abort (); +} + +static void __attribute__ ((noinline, noclone)) +fn11 (void) +{ + char ***volatile f[5]; + f[5] = 0; + f[2] = f[5]; +} + +static void __attribute__ ((noinline, noclone)) +fn12 (int i) +{ + volatile int a[5] = { }; + int k = i ? a[i] : i; +} + +int +main (void) +{ + fn1 (); + fn2 (); + fn3 (); + fn4 (); + fn5 (); + fn6 (); + fn7 (); + fn8 (); + fn9 (); + fn10 (); + fn11 (); + fn12 (5); + return 0; +} + +/* { dg-output "index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]\\\[\\\*\\\]\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]\\\[\\\*\\\]\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*index 10 out of bounds for type 'int \\\[10\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*index 10 out of bounds for type 'int \\\[10\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*index 10 out of bounds for type 'long int \\\*\\\[10\\\]\\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*index 10 out of bounds for type 'long int \\\*\\\[10\\\]\\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'char \\\*\\\*\\\*\\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'char \\\*\\\*\\\*\\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ diff --git gcc/testsuite/c-c++-common/ubsan/bounds-3.c gcc/testsuite/c-c++-common/ubsan/bounds-3.c index e69de29..fcf71a3 100644 --- gcc/testsuite/c-c++-common/ubsan/bounds-3.c +++ gcc/testsuite/c-c++-common/ubsan/bounds-3.c @@ -0,0 +1,23 @@ +/* { dg-do compile } */ +/* { dg-options "-fsanitize=bounds -Wall -Wextra" } */ + +/* Do not generate invalid diagnostics. */ + +extern const int a[10]; +extern int bar (int); +void +foo (int i, int j) +{ + bar (a[i] >> j); + bar ((unsigned long) a[i] >> j); + bar ((short int) (unsigned long) a[i] >> j); + bar (j >> a[i]); + bar (j >> (unsigned long) a[i]); + bar (j >> (short int) (unsigned long) a[i]); + bar (a[i] / j); + bar ((unsigned long) a[i] / j); + bar ((short int) (unsigned long) a[i] / j); + bar (j / a[i]); + bar (j / (unsigned long) a[i]); + bar (j / (short int) (unsigned long) a[i]); +} diff --git gcc/tree-core.h gcc/tree-core.h index aa7498b..bf5540d 100644 --- gcc/tree-core.h +++ gcc/tree-core.h @@ -665,6 +665,13 @@ enum annot_expr_kind { annot_expr_kind_last }; +/* Internal functions. */ +enum internal_fn { +#define DEF_INTERNAL_FN(CODE, FLAGS) IFN_##CODE, +#include "internal-fn.def" +#undef DEF_INTERNAL_FN + IFN_LAST +}; /*--------------------------------------------------------------------------- Type definitions @@ -787,6 +794,9 @@ struct GTY(()) tree_base { /* SSA version number. This field is only used with SSA_NAME. */ unsigned int version; + + /* Internal function code. */ + ENUM_BITFIELD(internal_fn) ifn : 5; } GTY((skip(""))) u; }; diff --git gcc/tree-pretty-print.c gcc/tree-pretty-print.c index c5b4aee..523dc09 100644 --- gcc/tree-pretty-print.c +++ gcc/tree-pretty-print.c @@ -3214,6 +3214,13 @@ print_call_name (pretty_printer *buffer, tree node, int flags) { tree op0 = node; + if (node == NULL_TREE) + { + /* TODO Print builtin name. */ + pp_string (buffer, ""); + return; + } + if (TREE_CODE (op0) == NON_LVALUE_EXPR) op0 = TREE_OPERAND (op0, 0); diff --git gcc/tree.c gcc/tree.c index 559e758..0317e4e 100644 --- gcc/tree.c +++ gcc/tree.c @@ -8995,6 +8995,10 @@ get_callee_fndecl (const_tree call) called. */ addr = CALL_EXPR_FN (call); + /* If there is no function, return early. */ + if (addr == NULL_TREE) + return NULL_TREE; + STRIP_NOPS (addr); /* If this is a readonly function pointer, extract its initial value. */ @@ -10622,6 +10626,27 @@ build_call_expr (tree fndecl, int n, ...) return build_call_expr_loc_array (UNKNOWN_LOCATION, fndecl, n, argarray); } +/* Build internal call expression. This is just like CALL_EXPR, except + its CALL_EXPR_FN is NULL. It will get gimplified later into ordinary + internal function. */ + +tree +build_call_expr_internal_loc (location_t loc, enum internal_fn ifn, + tree type, int n, ...) +{ + va_list ap; + int i; + + tree fn = build_call_1 (type, NULL_TREE, n); + va_start (ap, n); + for (i = 0; i < n; i++) + CALL_EXPR_ARG (fn, i) = va_arg (ap, tree); + va_end (ap); + SET_EXPR_LOCATION (fn, loc); + CALL_EXPR_IFN (fn) = ifn; + return fn; +} + /* Create a new constant string literal and return a char* pointer to it. The STRING_CST value is the LEN characters at STR. */ tree diff --git gcc/tree.def gcc/tree.def index 464c8c7..299aacc 100644 --- gcc/tree.def +++ gcc/tree.def @@ -574,7 +574,7 @@ DEFTREECODE (BIND_EXPR, "bind_expr", tcc_expression, 3) /* Function call. CALL_EXPRs are represented by variably-sized expression nodes. There are at least three fixed operands. Operand 0 is an INTEGER_CST node containing the total operand count, the number of - arguments plus 3. Operand 1 is the function, while operand 2 is + arguments plus 3. Operand 1 is the function or NULL, while operand 2 is is static chain argument, or NULL. The remaining operands are the arguments to the call. */ DEFTREECODE (CALL_EXPR, "call_expr", tcc_vl_exp, 3) diff --git gcc/tree.h gcc/tree.h index 4a29aa2..c6fbb7e 100644 --- gcc/tree.h +++ gcc/tree.h @@ -1134,12 +1134,12 @@ extern void protected_set_expr_location (tree, location_t); #define ASSERT_EXPR_VAR(NODE) TREE_OPERAND (ASSERT_EXPR_CHECK (NODE), 0) #define ASSERT_EXPR_COND(NODE) TREE_OPERAND (ASSERT_EXPR_CHECK (NODE), 1) -/* CALL_EXPR accessors. - */ +/* CALL_EXPR accessors. */ #define CALL_EXPR_FN(NODE) TREE_OPERAND (CALL_EXPR_CHECK (NODE), 1) #define CALL_EXPR_STATIC_CHAIN(NODE) TREE_OPERAND (CALL_EXPR_CHECK (NODE), 2) #define CALL_EXPR_ARG(NODE, I) TREE_OPERAND (CALL_EXPR_CHECK (NODE), (I) + 3) #define call_expr_nargs(NODE) (VL_EXP_OPERAND_LENGTH (NODE) - 3) +#define CALL_EXPR_IFN(NODE) (CALL_EXPR_CHECK (NODE)->base.u.ifn) /* CALL_EXPR_ARGP returns a pointer to the argument vector for NODE. We can't use &CALL_EXPR_ARG (NODE, 0) because that will complain if @@ -3626,6 +3626,8 @@ extern tree build_call_expr_loc_array (location_t, tree, int, tree *); extern tree build_call_expr_loc_vec (location_t, tree, vec *); extern tree build_call_expr_loc (location_t, tree, int, ...); extern tree build_call_expr (tree, int, ...); +extern tree build_call_expr_internal_loc (location_t, enum internal_fn, + tree, int, ...); extern tree build_string_literal (int, const char *); /* Construct various nodes representing data types. */ diff --git gcc/ubsan.c gcc/ubsan.c index 5a8a447..0422cbb 100644 --- gcc/ubsan.c +++ gcc/ubsan.c @@ -271,23 +271,24 @@ get_ubsan_type_info_for_type (tree type) gcc_assert (TYPE_SIZE (type) && tree_fits_uhwi_p (TYPE_SIZE (type))); if (TREE_CODE (type) == REAL_TYPE) return tree_to_uhwi (TYPE_SIZE (type)); - else + else if (INTEGRAL_TYPE_P (type)) { int prec = exact_log2 (tree_to_uhwi (TYPE_SIZE (type))); gcc_assert (prec != -1); return (prec << 1) | !TYPE_UNSIGNED (type); } + else + return 0; } /* Helper routine that returns ADDR_EXPR of a VAR_DECL of a type descriptor. It first looks into the hash table; if not found, create the VAR_DECL, put it into the hash table and return the - ADDR_EXPR of it. TYPE describes a particular type. WANT_POINTER_TYPE_P - means whether we are interested in the pointer type and not the pointer - itself. */ + ADDR_EXPR of it. TYPE describes a particular type. PSTYLE is + an enum controlling how we want to print the type. */ tree -ubsan_type_descriptor (tree type, bool want_pointer_type_p) +ubsan_type_descriptor (tree type, enum ubsan_print_style pstyle) { /* See through any typedefs. */ type = TYPE_MAIN_VARIANT (type); @@ -308,7 +309,7 @@ ubsan_type_descriptor (tree type, bool want_pointer_type_p) unsigned short tkind, tinfo; /* Get the name of the type, or the name of the pointer type. */ - if (want_pointer_type_p) + if (pstyle == UBSAN_PRINT_POINTER) { gcc_assert (POINTER_TYPE_P (type)); type2 = TREE_TYPE (type); @@ -324,6 +325,12 @@ ubsan_type_descriptor (tree type, bool want_pointer_type_p) /* If an array, get its type. */ type2 = strip_array_types (type2); + if (pstyle == UBSAN_PRINT_ARRAY) + { + while (POINTER_TYPE_P (type2)) + deref_depth++, type2 = TREE_TYPE (type2); + } + if (TYPE_NAME (type2) != NULL) { if (TREE_CODE (TYPE_NAME (type2)) == IDENTIFIER_NODE) @@ -338,7 +345,7 @@ ubsan_type_descriptor (tree type, bool want_pointer_type_p) /* Decorate the type name with '', '*', "struct", or "union". */ pretty_name = (char *) alloca (strlen (tname) + 16 + deref_depth); - if (want_pointer_type_p) + if (pstyle == UBSAN_PRINT_POINTER) { int pos = sprintf (pretty_name, "'%s%s%s%s%s%s%s", TYPE_VOLATILE (type2) ? "volatile " : "", @@ -355,6 +362,33 @@ ubsan_type_descriptor (tree type, bool want_pointer_type_p) pretty_name[pos++] = '\''; pretty_name[pos] = '\0'; } + else if (pstyle == UBSAN_PRINT_ARRAY) + { + /* Pretty print the array dimensions. */ + gcc_assert (TREE_CODE (type) == ARRAY_TYPE); + tree t = type; + int pos = sprintf (pretty_name, "'%s ", tname); + while (deref_depth-- > 0) + pretty_name[pos++] = '*'; + while (TREE_CODE (t) == ARRAY_TYPE) + { + pretty_name[pos++] = '['; + tree dom = TYPE_DOMAIN (t); + if (dom && TREE_CODE (TYPE_MAX_VALUE (dom)) == INTEGER_CST) + pos += sprintf (&pretty_name[pos], HOST_WIDE_INT_PRINT_DEC, + tree_to_shwi (TYPE_MAX_VALUE (dom)) + 1); + else + /* ??? We can't determine the variable name; print VLA unspec. */ + pretty_name[pos++] = '*'; + pretty_name[pos++] = ']'; + t = TREE_TYPE (t); + } + pretty_name[pos++] = '\''; + pretty_name[pos] = '\0'; + + /* Save the tree with stripped types. */ + type = t; + } else sprintf (pretty_name, "'%s'", tname); @@ -550,6 +584,69 @@ is_ubsan_builtin_p (tree t) "__builtin___ubsan_", 18) == 0; } +/* Expand the UBSAN_BOUNDS special builtin function. */ + +void +ubsan_expand_bounds_btn (gimple_stmt_iterator *gsi) +{ + gimple stmt = gsi_stmt (*gsi); + location_t loc = gimple_location (stmt); + gcc_assert (gimple_call_num_args (stmt) == 3); + + /* Pick up the arguments of the UBSAN_BOUNDS call. */ + tree type = TREE_TYPE (TREE_TYPE (gimple_call_arg (stmt, 0))); + tree index = gimple_call_arg (stmt, 1); + tree orig_index_type = TREE_TYPE (index); + tree bound = gimple_call_arg (stmt, 2); + + gimple_stmt_iterator gsi_orig = *gsi; + + /* Create condition "if (index > bound)". */ + basic_block then_bb, fallthru_bb; + gimple_stmt_iterator cond_insert_point + = create_cond_insert_point (gsi, 0/*before_p*/, false, true, + &then_bb, &fallthru_bb); + index = fold_convert (TREE_TYPE (bound), index); + index = force_gimple_operand_gsi (&cond_insert_point, index, + true/*simple_p*/, NULL_TREE, + false/*before*/, GSI_NEW_STMT); + gimple g = gimple_build_cond (GT_EXPR, index, bound, NULL_TREE, NULL_TREE); + gimple_set_location (g, loc); + gsi_insert_after (&cond_insert_point, g, GSI_NEW_STMT); + + /* Generate __ubsan_handle_out_of_bounds call. */ + *gsi = gsi_after_labels (then_bb); + if (flag_sanitize_undefined_trap_on_error) + g = gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP), 0); + else + { + tree data + = ubsan_create_data ("__ubsan_out_of_bounds_data", &loc, NULL, + ubsan_type_descriptor (type, UBSAN_PRINT_ARRAY), + ubsan_type_descriptor (orig_index_type), + NULL_TREE); + data = build_fold_addr_expr_loc (loc, data); + enum built_in_function bcode + = flag_sanitize_recover + ? BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS + : BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS_ABORT; + tree fn = builtin_decl_explicit (bcode); + tree val = force_gimple_operand_gsi (gsi, ubsan_encode_value (index), + true, NULL_TREE, true, + GSI_SAME_STMT); + g = gimple_build_call (fn, 2, data, val); + } + gimple_set_location (g, loc); + gsi_insert_before (gsi, g, GSI_SAME_STMT); + + /* Get rid of the UBSAN_BOUNDS call from the IR. */ + unlink_stmt_vdef (stmt); + gsi_remove (&gsi_orig, true); + + /* Point GSI to next logical statement. */ + *gsi = gsi_start_bb (fallthru_bb); +} + /* Expand UBSAN_NULL internal call. */ void @@ -609,9 +706,11 @@ ubsan_expand_null_ifn (gimple_stmt_iterator gsi) tree fn = builtin_decl_implicit (bcode); const struct ubsan_mismatch_data m = { build_zero_cst (pointer_sized_int_node), ckind }; - tree data = ubsan_create_data ("__ubsan_null_data", &loc, &m, - ubsan_type_descriptor (TREE_TYPE (ptr), - true), NULL_TREE); + tree data + = ubsan_create_data ("__ubsan_null_data", &loc, &m, + ubsan_type_descriptor (TREE_TYPE (ptr), + UBSAN_PRINT_POINTER), + NULL_TREE); data = build_fold_addr_expr_loc (loc, data); g = gimple_build_call (fn, 2, data, build_zero_cst (pointer_sized_int_node)); @@ -689,8 +788,7 @@ ubsan_build_overflow_builtin (tree_code code, location_t loc, tree lhstype, return build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0); tree data = ubsan_create_data ("__ubsan_overflow_data", &loc, NULL, - ubsan_type_descriptor (lhstype, false), - NULL_TREE); + ubsan_type_descriptor (lhstype), NULL_TREE); enum built_in_function fn_code; switch (code) @@ -884,8 +982,7 @@ instrument_bool_enum_load (gimple_stmt_iterator *gsi) else { tree data = ubsan_create_data ("__ubsan_invalid_value_data", &loc, NULL, - ubsan_type_descriptor (type, false), - NULL_TREE); + ubsan_type_descriptor (type), NULL_TREE); data = build_fold_addr_expr_loc (loc, data); enum built_in_function bcode = flag_sanitize_recover @@ -1005,10 +1102,8 @@ ubsan_instrument_float_cast (location_t loc, tree type, tree expr) { /* Create the __ubsan_handle_float_cast_overflow fn call. */ tree data = ubsan_create_data ("__ubsan_float_cast_overflow_data", NULL, - NULL, - ubsan_type_descriptor (expr_type, false), - ubsan_type_descriptor (type, false), - NULL_TREE); + NULL, ubsan_type_descriptor (expr_type), + ubsan_type_descriptor (type), NULL_TREE); enum built_in_function bcode = flag_sanitize_recover ? BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW diff --git gcc/ubsan.h gcc/ubsan.h index b008419..2a24a92 100644 --- gcc/ubsan.h +++ gcc/ubsan.h @@ -30,17 +30,25 @@ enum ubsan_null_ckind { UBSAN_MEMBER_CALL }; +/* This controls how ubsan prints types. Used in ubsan_type_descriptor. */ +enum ubsan_print_style { + UBSAN_PRINT_NORMAL, + UBSAN_PRINT_POINTER, + UBSAN_PRINT_ARRAY +}; + /* An extra data used by ubsan pointer checking. */ struct ubsan_mismatch_data { tree align; tree ckind; }; +extern void ubsan_expand_bounds_btn (gimple_stmt_iterator *); extern void ubsan_expand_null_ifn (gimple_stmt_iterator); extern tree ubsan_instrument_unreachable (location_t); extern tree ubsan_create_data (const char *, const location_t *, const struct ubsan_mismatch_data *, ...); -extern tree ubsan_type_descriptor (tree, bool); +extern tree ubsan_type_descriptor (tree, enum ubsan_print_style = UBSAN_PRINT_NORMAL); extern tree ubsan_encode_value (tree, bool = false); extern bool is_ubsan_builtin_p (tree); extern tree ubsan_build_overflow_builtin (tree_code, location_t, tree, tree, tree);