diff options
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/ChangeLog | 38 | ||||
-rw-r--r-- | gcc/asan.c | 7 | ||||
-rw-r--r-- | gcc/c-family/ChangeLog | 19 | ||||
-rw-r--r-- | gcc/c-family/c-gimplify.c | 53 | ||||
-rw-r--r-- | gcc/c-family/c-ubsan.c | 142 | ||||
-rw-r--r-- | gcc/c-family/c-ubsan.h | 3 | ||||
-rw-r--r-- | gcc/doc/invoke.texi | 7 | ||||
-rw-r--r-- | gcc/flag-types.h | 4 | ||||
-rw-r--r-- | gcc/gimplify.c | 18 | ||||
-rw-r--r-- | gcc/internal-fn.c | 10 | ||||
-rw-r--r-- | gcc/internal-fn.def | 1 | ||||
-rw-r--r-- | gcc/internal-fn.h | 7 | ||||
-rw-r--r-- | gcc/opts.c | 1 | ||||
-rw-r--r-- | gcc/sanitizer.def | 8 | ||||
-rw-r--r-- | gcc/testsuite/ChangeLog | 9 | ||||
-rw-r--r-- | gcc/testsuite/c-c++-common/ubsan/bounds-1.c | 75 | ||||
-rw-r--r-- | gcc/testsuite/c-c++-common/ubsan/bounds-2.c | 168 | ||||
-rw-r--r-- | gcc/testsuite/c-c++-common/ubsan/bounds-3.c | 23 | ||||
-rw-r--r-- | gcc/testsuite/c-c++-common/ubsan/bounds-4.c | 17 | ||||
-rw-r--r-- | gcc/testsuite/c-c++-common/ubsan/bounds-5.c | 88 | ||||
-rw-r--r-- | gcc/testsuite/c-c++-common/ubsan/bounds-6.c | 37 | ||||
-rw-r--r-- | gcc/tree-core.h | 10 | ||||
-rw-r--r-- | gcc/tree-pretty-print.c | 6 | ||||
-rw-r--r-- | gcc/tree.c | 25 | ||||
-rw-r--r-- | gcc/tree.def | 2 | ||||
-rw-r--r-- | gcc/tree.h | 6 | ||||
-rw-r--r-- | gcc/ubsan.c | 131 | ||||
-rw-r--r-- | gcc/ubsan.h | 10 |
28 files changed, 877 insertions, 48 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 7d5a064..43b9939 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,41 @@ +2014-06-20 Marek Polacek <polacek@redhat.com> + + * asan.c (pass_sanopt::execute): Handle IFN_UBSAN_BOUNDS. + * flag-types.h (enum sanitize_code): Add SANITIZE_BOUNDS and or it + into SANITIZE_UNDEFINED. + * doc/invoke.texi: Describe -fsanitize=bounds. + * 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: Include "internal-fn.h". + (dump_generic_node): 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_ifn): 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_ifn): Declare. + (ubsan_type_descriptor): Adjust declaration. Use a default parameter. + 2014-06-20 Maciej W. Rozycki <macro@codesourcery.com> * config/rs6000/rs6000.md: Append `DONE' to preparation @@ -2762,6 +2762,9 @@ pass_sanopt::execute (function *fun) case IFN_UBSAN_NULL: ubsan_expand_null_ifn (gsi); break; + case IFN_UBSAN_BOUNDS: + ubsan_expand_bounds_ifn (&gsi); + break; default: break; } @@ -2772,6 +2775,10 @@ pass_sanopt::execute (function *fun) print_gimple_stmt (dump_file, stmt, 0, dump_flags); fprintf (dump_file, "\n"); } + + /* ubsan_expand_bounds_ifn might move us to the end of the BB. */ + if (gsi_end_p (gsi)) + break; } } return 0; diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index d195db7..e37aa46d 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,4 +1,21 @@ -2014-06-20 Hale Wang <hale.wang@arm.com> +2014-06-20 Marek Polacek <polacek@redhat.com> + + * 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. + +2014-06-20 Hale Wang <hale.wang@arm.com> PR lto/61123 * c.opt (fshort-enums): Add to LTO. diff --git a/gcc/c-family/c-gimplify.c b/gcc/c-family/c-gimplify.c index 737be4d..c797d99 100644 --- a/gcc/c-family/c-gimplify.c +++ b/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,39 @@ 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; + + /* Since walk_tree doesn't call the callback function on the decls + in BIND_EXPR_VARS, we have to walk them manually. */ + 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, pset, + pset); + walk_tree (&DECL_SIZE (decl), ubsan_walk_array_refs_r, pset, pset); + walk_tree (&DECL_SIZE_UNIT (decl), ubsan_walk_array_refs_r, pset, + 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 +114,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 +250,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 a/gcc/c-family/c-ubsan.c b/gcc/c-family/c-ubsan.c index 8d5b685..3698580 100644 --- a/gcc/c-family/c-ubsan.c +++ b/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,102 @@ 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_CODE (base) == MEM_REF)) + { + tree next = NULL_TREE; + tree cref = array; + + /* Walk all structs/unions. */ + 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/union. But the + aggregate containing the field must be the last field too, + recursively. */ + cref = TREE_OPERAND (cref, 0); + } + if (!next) + /* Don't instrument this flexible array member-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 != NULL_TREE + && !lookup_attribute ("no_sanitize_undefined", + DECL_ATTRIBUTES (current_function_decl))) + { + tree op0 = TREE_OPERAND (*expr_p, 0); + tree op1 = TREE_OPERAND (*expr_p, 1); + tree e = ubsan_instrument_bounds (EXPR_LOCATION (*expr_p), op0, &op1, + ignore_off_by_one); + if (e != NULL_TREE) + { + tree t = copy_node (*expr_p); + TREE_OPERAND (t, 1) = build2 (COMPOUND_EXPR, TREE_TYPE (op1), + e, op1); + *expr_p = t; + } + } +} diff --git a/gcc/c-family/c-ubsan.h b/gcc/c-family/c-ubsan.h index e504b90..edf5bc6 100644 --- a/gcc/c-family/c-ubsan.h +++ b/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 a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index f7a54fa..0d4bd00 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -5448,6 +5448,13 @@ This option enables floating-point type to integer conversion checking. We check that the result of the conversion does not overflow. This option does not work well with @code{FE_INVALID} exceptions enabled. +@item -fsanitize=bounds +@opindex fsanitize=bounds + +This option enables instrumentation of array bounds. Various out of bounds +accesses are detected. Flexible array members are not instrumented, as well +as initializers of variables with static storage. + @item -fsanitize-recover @opindex fsanitize-recover By default @option{-fsanitize=undefined} sanitization (and its suboptions diff --git a/gcc/flag-types.h b/gcc/flag-types.h index ed00046..4b8e338 100644 --- a/gcc/flag-types.h +++ b/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 a/gcc/gimplify.c b/gcc/gimplify.c index 3dcb4af..338c5c0 100644 --- a/gcc/gimplify.c +++ b/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<tree> 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 a/gcc/internal-fn.c b/gcc/internal-fn.c index 68b2b66..78f59d6 100644 --- a/gcc/internal-fn.c +++ b/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 a/gcc/internal-fn.def b/gcc/internal-fn.def index 31dc4c9..f0766bc 100644 --- a/gcc/internal-fn.def +++ b/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 a/gcc/internal-fn.h b/gcc/internal-fn.h index 9c3215f..2dcf44e 100644 --- a/gcc/internal-fn.h +++ b/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. */ @@ -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 a/gcc/sanitizer.def b/gcc/sanitizer.def index b4af164..1f5ef21 100644 --- a/gcc/sanitizer.def +++ b/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 a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 98d6379..dcab923 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,12 @@ +2014-06-20 Marek Polacek <polacek@redhat.com> + + * 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. + 2014-06-20 Yufeng Zhang <yufeng.zhang@arm.com> Make the tests big-endian friendly. diff --git a/gcc/testsuite/c-c++-common/ubsan/bounds-1.c b/gcc/testsuite/c-c++-common/ubsan/bounds-1.c new file mode 100644 index 0000000..aa192d3 --- /dev/null +++ b/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 a/gcc/testsuite/c-c++-common/ubsan/bounds-2.c b/gcc/testsuite/c-c++-common/ubsan/bounds-2.c new file mode 100644 index 0000000..95f77c2 --- /dev/null +++ b/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 a/gcc/testsuite/c-c++-common/ubsan/bounds-3.c b/gcc/testsuite/c-c++-common/ubsan/bounds-3.c new file mode 100644 index 0000000..fcf71a3 --- /dev/null +++ b/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 a/gcc/testsuite/c-c++-common/ubsan/bounds-4.c b/gcc/testsuite/c-c++-common/ubsan/bounds-4.c new file mode 100644 index 0000000..7748780 --- /dev/null +++ b/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 a/gcc/testsuite/c-c++-common/ubsan/bounds-5.c b/gcc/testsuite/c-c++-common/ubsan/bounds-5.c new file mode 100644 index 0000000..7b7d76d --- /dev/null +++ b/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 flexible array members + 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 a/gcc/testsuite/c-c++-common/ubsan/bounds-6.c b/gcc/testsuite/c-c++-common/ubsan/bounds-6.c new file mode 100644 index 0000000..78ad29a --- /dev/null +++ b/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 a/gcc/tree-core.h b/gcc/tree-core.h index 52d93ec..a2c88f9 100644 --- a/gcc/tree-core.h +++ b/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 internal_fn ifn; } GTY((skip(""))) u; }; diff --git a/gcc/tree-pretty-print.c b/gcc/tree-pretty-print.c index 59a825c..8522d79 100644 --- a/gcc/tree-pretty-print.c +++ b/gcc/tree-pretty-print.c @@ -37,6 +37,7 @@ along with GCC; see the file COPYING3. If not see #include "value-prof.h" #include "predict.h" #include "wide-int-print.h" +#include "internal-fn.h" #include <new> // For placement-new. @@ -1753,7 +1754,10 @@ dump_generic_node (pretty_printer *buffer, tree node, int spc, int flags, break; case CALL_EXPR: - print_call_name (buffer, CALL_EXPR_FN (node), flags); + if (CALL_EXPR_FN (node) != NULL_TREE) + print_call_name (buffer, CALL_EXPR_FN (node), flags); + else + pp_string (buffer, internal_fn_name (CALL_EXPR_IFN (node))); /* Print parameters. */ pp_space (buffer); @@ -8971,6 +8971,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. */ @@ -10598,6 +10602,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 a/gcc/tree.def b/gcc/tree.def index 464c8c7..299aacc 100644 --- a/gcc/tree.def +++ b/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) @@ -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<tree, va_gc> *); 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 a/gcc/ubsan.c b/gcc/ubsan.c index 5a8a447..5e1c3e7 100644 --- a/gcc/ubsan.c +++ b/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_ifn (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 a/gcc/ubsan.h b/gcc/ubsan.h index b008419..485449c 100644 --- a/gcc/ubsan.h +++ b/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_ifn (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); |