From patchwork Thu Jun 19 14:56:53 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marek Polacek X-Patchwork-Id: 361898 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 1488F140096 for ; Fri, 20 Jun 2014 01:00:58 +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:references:mime-version :content-type:in-reply-to; q=dns; s=default; b=a4m9H9l9tqCkPXKH3 /rEjsXmMeOv6IY50/LF8GmHeRi2rw9a04uBi3KofxMi2EaWfleVWEPQrtIkYbOiF Mz9iil64/abLGjHieWau7JmZlPZEw4XKLutRND7vkoMLrnJmI/xMcStSZIDifkSd LACiW5WgC0xYqoCL4Me81/3U9U= 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:references:mime-version :content-type:in-reply-to; s=default; bh=+wPDUYvKQd8yyh1/1P4uWN2 vh54=; b=hTKI/4BKjDKmOca894IGLxEb+lMx+xEKpXprxHYVw0dDxZGSwSM/9LV 4S8Q6Xo6uwDYMRas5baN/8s6vOjJJbQ6PBBuxUzCuTIuSanS2KxnsMW/MH/tpudJ qJbDWdS+scfCHNdJ0NHkhhVSdwm7tyNYBGM9UT7SA06AQjrmWX0w= Received: (qmail 5177 invoked by alias); 19 Jun 2014 15:00:49 -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 5166 invoked by uid 89); 19 Jun 2014 15:00:47 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-2.7 required=5.0 tests=AWL, BAYES_00, SPF_HELO_PASS, SPF_PASS, T_RP_MATCHES_RCVD 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 (AES256-GCM-SHA384 encrypted) ESMTPS; Thu, 19 Jun 2014 15:00:43 +0000 Received: from int-mx11.intmail.prod.int.phx2.redhat.com (int-mx11.intmail.prod.int.phx2.redhat.com [10.5.11.24]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id s5JF0aO8000604 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Thu, 19 Jun 2014 11:00:39 -0400 Received: from redhat.com (ovpn-116-31.ams2.redhat.com [10.36.116.31]) by int-mx11.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id s5JEus6T029574 (version=TLSv1/SSLv3 cipher=AES128-GCM-SHA256 bits=128 verify=NO); Thu, 19 Jun 2014 10:56:56 -0400 Date: Thu, 19 Jun 2014 16:56:53 +0200 From: Marek Polacek To: Jakub Jelinek Cc: GCC Patches , Jason Merrill , "Joseph S. Myers" , Richard Biener , Jeff Law Subject: Re: [PATCH] Implement -fsanitize=bounds and internal calls in FEs Message-ID: <20140619145653.GL17965@redhat.com> References: <20140616103907.GD17965@redhat.com> <20140616112304.GD19001@tucnak.redhat.com> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <20140616112304.GD19001@tucnak.redhat.com> User-Agent: Mutt/1.5.23 (2014-03-12) On Mon, Jun 16, 2014 at 01:23:04PM +0200, Jakub Jelinek wrote: > On Mon, Jun 16, 2014 at 12:39:07PM +0200, Marek Polacek wrote: > > Jason/Joseph, could you please look at the C++/C FE parts? > > As mentioned on IRC, you need to differentiate between taking address > and not taking address. > > struct S { int a; int b; } s[4], *t; > int *a, *b, *c; > void *d; > int e[4][4]; > > void > foo () > { > t = &s[4]; // Should be fine > a = &s[4].a; // Error > b = &s[4].b; // Error > d = &e[4]; // Should be fine > c = &e[4][0]; // Error > } > > So, supposedly when e.g. in cp_genericize_r, for ADDR_EXPR > allow off-by-one, for all other ARRAY_REFs (e.g. those not appearing > inside of ADDR_EXPR, or not directly inside of ADDR_EXPR, e.g. with > COMPONENT_REF or another ARRAY_REF in between) disallow off-by-one. Should be fixed in this new patch. Another change is in handling flexible array member-like arrays, what I had in last patch was wrong. I use the non-strict mode by default, which means flexible array member-like arrays are not instrumented. We could have e.g. -fsanitize=bounds-strict option that would instrument even those. (Regular FMAs are never instrumented.) I moved the code that instruments array to c_genericize (works for both C and C++). Also I fixed an ICE (we shouldn't instrument initializers of TREE_STATIC, otherwise internal calls from FE survive up to expansion). Oh, I see I forgot to update docs, I'll write something up in another iteration. Regtested/bootstrapped on x86_64-linux. How does this look? 2014-06-19 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-gimplify.c: Include "c-ubsan.h" and "pointer-set.h". (ubsan_walk_array_refs_r): New function. (c_genericize): 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. (ubsan_array_ref_instrumented_p): New function. (ubsan_maybe_instrument_array_ref): New function. * c-ubsan.h (ubsan_instrument_bounds): Declare. (ubsan_array_ref_instrumented_p): Declare. (ubsan_maybe_instrument_array_ref): Declare. 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. * c-c++-common/ubsan/bounds-4.c: New test. * c-c++-common/ubsan/bounds-5.c: New test. * c-c++-common/ubsan/bounds-6.c: New test. Marek diff --git gcc/asan.c gcc/asan.c index 281a795..5f5dcaa 100644 --- gcc/asan.c +++ gcc/asan.c @@ -2761,6 +2761,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; } @@ -2771,6 +2774,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-gimplify.c gcc/c-family/c-gimplify.c index 737be4d..4e1ff4b 100644 --- gcc/c-family/c-gimplify.c +++ gcc/c-family/c-gimplify.c @@ -45,6 +45,8 @@ along with GCC; see the file COPYING3. If not see #include "c-pretty-print.h" #include "cgraph.h" #include "cilk.h" +#include "c-ubsan.h" +#include "pointer-set.h" /* The gimplification pass converts the language-dependent trees (ld-trees) emitted by the parser into language-independent trees @@ -67,6 +69,35 @@ along with GCC; see the file COPYING3. If not see walk back up, we check that they fit our constraints, and copy them into temporaries if not. */ +/* Callback for c_genericize. */ + +static tree +ubsan_walk_array_refs_r (tree *tp, int *walk_subtrees, void *data) +{ + struct pointer_set_t *pset = (struct pointer_set_t *) data; + + if (TREE_CODE (*tp) == BIND_EXPR) + { + for (tree decl = BIND_EXPR_VARS (*tp); decl; decl = DECL_CHAIN (decl)) + { + if (TREE_STATIC (decl)) + { + *walk_subtrees = 0; + continue; + } + walk_tree (&DECL_INITIAL (decl), ubsan_walk_array_refs_r, NULL, pset); + walk_tree (&DECL_SIZE (decl), ubsan_walk_array_refs_r, NULL, pset); + walk_tree (&DECL_SIZE_UNIT (decl), ubsan_walk_array_refs_r, NULL, pset); + } + } + else if (TREE_CODE (*tp) == ADDR_EXPR + && TREE_CODE (TREE_OPERAND (*tp, 0)) == ARRAY_REF) + ubsan_maybe_instrument_array_ref (&TREE_OPERAND (*tp, 0), true); + else if (TREE_CODE (*tp) == ARRAY_REF) + ubsan_maybe_instrument_array_ref (tp, false); + return NULL_TREE; +} + /* Gimplification of statement trees. */ /* Convert the tree representation of FNDECL from C frontend trees to @@ -79,6 +110,14 @@ c_genericize (tree fndecl) int local_dump_flags; struct cgraph_node *cgn; + if (flag_sanitize & SANITIZE_BOUNDS) + { + struct pointer_set_t *pset = pointer_set_create (); + walk_tree (&DECL_SAVED_TREE (fndecl), ubsan_walk_array_refs_r, pset, + pset); + pointer_set_destroy (pset); + } + /* Dump the C-specific tree IR. */ dump_orig = dump_begin (TDI_original, &local_dump_flags); if (dump_orig) @@ -207,16 +246,16 @@ c_gimplify_expr (tree *expr_p, gimple_seq *pre_p ATTRIBUTE_UNUSED, } break; } - + case CILK_SPAWN_STMT: - gcc_assert - (fn_contains_cilk_spawn_p (cfun) + gcc_assert + (fn_contains_cilk_spawn_p (cfun) && cilk_detect_spawn_and_unwrap (expr_p)); - + /* If errors are seen, then just process it as a CALL_EXPR. */ if (!seen_error ()) return (enum gimplify_status) gimplify_cilk_spawn (expr_p); - + case MODIFY_EXPR: case INIT_EXPR: case CALL_EXPR: diff --git gcc/c-family/c-ubsan.c gcc/c-family/c-ubsan.c index 8d5b685..80b3dbe 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,100 @@ 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. + IGNORE_OFF_BY_ONE is true if the ARRAY_REF is inside a ADDR_EXPR. */ + +tree +ubsan_instrument_bounds (location_t loc, tree array, tree *index, + bool ignore_off_by_one) +{ + tree type = TREE_TYPE (array); + tree domain = TYPE_DOMAIN (type); + + if (domain == NULL_TREE) + return NULL_TREE; + + tree bound = TYPE_MAX_VALUE (domain); + if (ignore_off_by_one) + bound = fold_build2 (PLUS_EXPR, TREE_TYPE (bound), bound, + build_int_cst (TREE_TYPE (bound), 1)); + + /* Detect flexible array members and suchlike. */ + tree base = get_base_address (array); + if (base && TREE_CODE (base) == INDIRECT_REF) + { + tree next = NULL_TREE; + tree cref = array; + + /* Walk all structs. */ + while (TREE_CODE (cref) == COMPONENT_REF) + { + if (TREE_CODE (TREE_TYPE (TREE_OPERAND (cref, 0))) == RECORD_TYPE) + for (next = DECL_CHAIN (TREE_OPERAND (cref, 1)); + next && TREE_CODE (next) != FIELD_DECL; + next = DECL_CHAIN (next)) + ; + if (next) + /* Not a last element. Instrument it. */ + break; + /* Ok, this is the last field of the structure. But the struct + containing it must be the last field too, recursively. */ + cref = TREE_OPERAND (cref, 0); + } + if (!next) + /* Don't instrument this FMA-like array in non-strict + -fsanitize=bounds mode. */ + return NULL_TREE; + } + + *index = save_expr (*index); + /* 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); +} + +/* Return true iff T is an array that was instrumented by SANITIZE_BOUNDS. */ + +bool +ubsan_array_ref_instrumented_p (const_tree t) +{ + if (TREE_CODE (t) != ARRAY_REF) + return false; + + tree op1 = TREE_OPERAND (t, 1); + return TREE_CODE (op1) == COMPOUND_EXPR + && TREE_CODE (TREE_OPERAND (op1, 0)) == CALL_EXPR + && CALL_EXPR_FN (TREE_OPERAND (op1, 0)) == NULL_TREE + && CALL_EXPR_IFN (TREE_OPERAND (op1, 0)) == IFN_UBSAN_BOUNDS; +} + +/* Instrument an ARRAY_REF, if it hasn't already been instrumented. + IGNORE_OFF_BY_ONE is true if the ARRAY_REF is inside a ADDR_EXPR. */ + +void +ubsan_maybe_instrument_array_ref (tree *expr_p, bool ignore_off_by_one) +{ + if (!ubsan_array_ref_instrumented_p (*expr_p) + && current_function_decl != 0 + && !lookup_attribute ("no_sanitize_undefined", + DECL_ATTRIBUTES (current_function_decl))) + { + tree t = copy_node (*expr_p); + tree op0 = TREE_OPERAND (t, 0); + tree op1 = TREE_OPERAND (t, 1); + tree e = ubsan_instrument_bounds (EXPR_LOCATION (t), op0, &op1, + ignore_off_by_one); + if (e != NULL_TREE) + { + TREE_OPERAND (t, 1) = build2 (COMPOUND_EXPR, TREE_TYPE (op1), + e, op1); + *expr_p = t; + } + } +} diff --git gcc/c-family/c-ubsan.h gcc/c-family/c-ubsan.h index e504b90..edf5bc6 100644 --- gcc/c-family/c-ubsan.h +++ gcc/c-family/c-ubsan.h @@ -25,5 +25,8 @@ 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 *, bool); +extern bool ubsan_array_ref_instrumented_p (const_tree); +extern void ubsan_maybe_instrument_array_ref (tree *, bool); #endif /* GCC_C_UBSAN_H */ 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 3dcb4af..338c5c0 100644 --- gcc/gimplify.c +++ gcc/gimplify.c @@ -2264,6 +2264,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 324d545..3ab06c6 100644 --- gcc/opts.c +++ gcc/opts.c @@ -1467,6 +1467,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 b4af164..1f5ef21 100644 --- gcc/sanitizer.def +++ gcc/sanitizer.def @@ -409,3 +409,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/testsuite/c-c++-common/ubsan/bounds-4.c gcc/testsuite/c-c++-common/ubsan/bounds-4.c index e69de29..7748780 100644 --- gcc/testsuite/c-c++-common/ubsan/bounds-4.c +++ gcc/testsuite/c-c++-common/ubsan/bounds-4.c @@ -0,0 +1,17 @@ +/* { dg-do compile } */ +/* { dg-options "-fsanitize=bounds -Wall -Wextra -Wno-unused" } */ + +/* Initializers of TREE_STATICs aren't instrumented. + But don't ICE on 'em. */ + +int A[2]; +int *gp = &A[4]; +int *gpi; + +int +main (void) +{ + gpi = &A[4]; + static int *pi = &A[4]; + return 0; +} diff --git gcc/testsuite/c-c++-common/ubsan/bounds-5.c gcc/testsuite/c-c++-common/ubsan/bounds-5.c index e69de29..b5468d7 100644 --- gcc/testsuite/c-c++-common/ubsan/bounds-5.c +++ gcc/testsuite/c-c++-common/ubsan/bounds-5.c @@ -0,0 +1,88 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=bounds -Wall -Wextra -Wno-unused -Wno-array-bounds" } */ + +/* Test flexible array member-like arrays. Normal FMAs are tested + in bounds-1.c. Test non-strict mode. */ + +__attribute__ ((noinline, noclone)) +void +fn1 (void) +{ + volatile struct S { char a[1]; char b; } s; + s.a[0] = 1; // OK + s.a[1] = 2; // error + volatile struct S *p = &s; + p->a[0] = 1; // OK + p->a[1] = 1; // error +} + +__attribute__ ((noinline, noclone)) +void +fn2 (void) +{ + struct S { int c; char d[4]; }; + volatile struct T { int e; struct S f; int g; } t; + t.f.d[3] = 1; // OK + t.f.d[4] = 1; // error + volatile struct T *p = &t; + p->f.d[3] = 1; // OK + p->f.d[4] = 1; // error +} + +__attribute__ ((noinline, noclone)) +void +fn3 (void) +{ + volatile struct S { char b; char a[1]; } s; + s.a[0] = 1; // OK + s.a[1] = 1; // error + volatile struct S *p = &s; + p->a[0] = 1; // OK + p->a[1] = 1; // error in strict mode +} + +__attribute__ ((noinline, noclone)) +void +fn4 (void) +{ + volatile struct S { char b; char a[1]; } s; + volatile struct T { struct S s; int i; } t; + t.s.a[0] = 1; // OK + t.s.a[1] = 1; // error + volatile struct T *pt = &t; + pt->s.a[0] = 1; // OK + pt->s.a[1] = 1; // error +} + +__attribute__ ((noinline, noclone)) +void +fn5 (void) +{ + volatile struct S { char b; char a[1]; } s; + volatile struct U { int a; struct S s; } u; + u.s.a[0] = 1; // OK + u.s.a[1] = 1; // error + volatile struct U *pu = &u; + pu->s.a[0] = 1; // OK + pu->s.a[1] = 1; // error in strict mode +} + +int +main (void) +{ + fn1 (); + fn2 (); + fn3 (); + fn4 (); + fn5 (); + return 0; +} + +/* { dg-output "index 1 out of bounds for type 'char \\\[1\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*index 1 out of bounds for type 'char \\\[1\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*index 4 out of bounds for type 'char \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*index 4 out of bounds for type 'char \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*index 1 out of bounds for type 'char \\\[1\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*index 1 out of bounds for type 'char \\\[1\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*index 1 out of bounds for type 'char \\\[1\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*index 1 out of bounds for type 'char \\\[1\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ diff --git gcc/testsuite/c-c++-common/ubsan/bounds-6.c gcc/testsuite/c-c++-common/ubsan/bounds-6.c index e69de29..78ad29a 100644 --- gcc/testsuite/c-c++-common/ubsan/bounds-6.c +++ gcc/testsuite/c-c++-common/ubsan/bounds-6.c @@ -0,0 +1,37 @@ +/* { dg-do compile } */ +/* { dg-options "-fsanitize=bounds -Wall -Wextra" } */ + +/* Test off-by-one. */ + +struct S { int a; int b; } s[4], *t; +struct U { int a[10]; } u[4], *v; +volatile int *a, *b, *c; +volatile void *d; +volatile int e[4][4]; + +int +main (void) +{ + t = &s[4]; // OK + a = &s[4].a; // Error + b = &s[4].b; // Error + d = &e[4]; // OK + c = &e[4][0]; // Error + c = &e[3][4]; // OK + c = &e[3][3]; // OK + + a = &u[4].a[9]; // Error + a = &u[4].a[10]; // Error + a = &u[3].a[9]; // OK + a = &u[3].a[10]; // OK + a = &u[3].a[11]; // Error + + return 0; +} + +/* { dg-output "index 4 out of bounds for type 'S \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*index 4 out of bounds for type 'S \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*index 4 out of bounds for type 'int \\\[4\\\]\\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*index 4 out of bounds for type 'U \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*index 4 out of bounds for type 'U \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*index 11 out of bounds for type 'int \\\[10\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ diff --git gcc/tree-core.h gcc/tree-core.h index c9d43d0..9ccf39a 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 59a825c..39ee9b6 100644 --- gcc/tree-pretty-print.c +++ gcc/tree-pretty-print.c @@ -3218,6 +3218,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 0a334cc..7797291 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 @@ -3631,6 +3631,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);