diff options
author | Marek Polacek <mpolacek@gcc.gnu.org> | 2014-06-20 21:20:51 +0000 |
---|---|---|
committer | Marek Polacek <mpolacek@gcc.gnu.org> | 2014-06-20 21:20:51 +0000 |
commit | 0e37a2f33da9dcc33921ded88f700c28dff7960e (patch) | |
tree | 909724d7e8bcbb98c38c8f42a5f641e17c4ac7ee /gcc/c-family | |
parent | 87681fb55051518fb40426e61ee4fbc2a47073c3 (diff) | |
download | gcc-0e37a2f33da9dcc33921ded88f700c28dff7960e.zip gcc-0e37a2f33da9dcc33921ded88f700c28dff7960e.tar.gz gcc-0e37a2f33da9dcc33921ded88f700c28dff7960e.tar.bz2 |
asan.c (pass_sanopt::execute): Handle IFN_UBSAN_BOUNDS.
* 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.
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.
From-SVN: r211859
Diffstat (limited to 'gcc/c-family')
-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 |
4 files changed, 201 insertions, 16 deletions
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 */ |