diff options
Diffstat (limited to 'gcc/c-family/c-ubsan.c')
-rw-r--r-- | gcc/c-family/c-ubsan.c | 142 |
1 files changed, 132 insertions, 10 deletions
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; + } + } +} |