diff options
author | jakub <jakub@138bc75d-0d04-0410-961f-82ee72b054a4> | 2014-08-01 07:52:43 +0000 |
---|---|---|
committer | jakub <jakub@138bc75d-0d04-0410-961f-82ee72b054a4> | 2014-08-01 07:52:43 +0000 |
commit | 392dee1e8f7cb38a6a76811511b38df41ce06a48 (patch) | |
tree | 893b88f145db7b629d24315901550a2b349d00ae /gcc/ubsan.c | |
parent | 22c270a2557a3237c238d0dc0d3f9e8a3f033b3d (diff) | |
download | linaro-gcc-392dee1e8f7cb38a6a76811511b38df41ce06a48.tar.gz linaro-gcc-392dee1e8f7cb38a6a76811511b38df41ce06a48.tar.bz2 linaro-gcc-392dee1e8f7cb38a6a76811511b38df41ce06a48.zip |
* opts.c (common_handle_option): Handle -fsanitize=alignment.
* ubsan.h (enum ubsan_null_ckind): Add UBSAN_CTOR_CALL.
(ubsan_expand_bounds_ifn, ubsan_expand_null_ifn): Change return
type to bool.
* stor-layout.h (min_align_of_type): New prototype.
* asan.c (pass_sanopt::execute): Don't perform gsi_next if
ubsan_expand* told us not to do it. Remove the extra gsi_end_p
check.
* ubsan.c: Include builtins.h.
(ubsan_expand_bounds_ifn): Change return type to bool,
always return true.
(ubsan_expand_null_ifn): Change return type to bool, change
argument to gimple_stmt_iterator *. Handle both null and alignment
sanitization, take type from ckind argument's type rather than
first argument.
(instrument_member_call): Removed.
(instrument_mem_ref): Remove t argument, add mem and base arguments.
Handle both null and alignment sanitization, don't say whole
struct access is member access. Build 3 argument IFN_UBSAN_NULL
call instead of 2 argument.
(instrument_null): Adjust instrument_mem_ref caller. Don't
instrument calls here.
(pass_ubsan::gate, pass_ubsan::execute): Handle SANITIZE_ALIGNMENT
like SANITIZE_NULL.
* stor-layout.c (min_align_of_type): New function.
* flag-types.h (enum sanitize_code): Add SANITIZE_ALIGNMENT.
Or it into SANITIZE_UNDEFINED.
* doc/invoke.texi (-fsanitize=alignment): Document.
cp/
* cp-gimplify.c (cp_genericize_r): For -fsanitize=null and/or
-fsanitize=alignment call ubsan_maybe_instrument_reference
for casts to REFERENCE_TYPE and ubsan_maybe_instrument_member_call
for calls to member functions.
c-family/
* c-common.h (min_align_of_type): Removed prototype.
* c-common.c (min_align_of_type): Removed.
* c-ubsan.h (ubsan_maybe_instrument_reference,
ubsan_maybe_instrument_member_call): New prototypes.
* c-ubsan.c: Include stor-layout.h and builtins.h.
(ubsan_maybe_instrument_reference_or_call,
ubsan_maybe_instrument_reference, ubsan_maybe_instrument_call): New
functions.
testsuite/
* c-c++-common/ubsan/align-1.c: New test.
* c-c++-common/ubsan/align-2.c: New test.
* c-c++-common/ubsan/align-3.c: New test.
* c-c++-common/ubsan/align-4.c: New test.
* c-c++-common/ubsan/align-5.c: New test.
* c-c++-common/ubsan/attrib-4.c: New test.
* g++.dg/ubsan/align-1.C: New test.
* g++.dg/ubsan/align-2.C: New test.
* g++.dg/ubsan/align-3.C: New test.
* g++.dg/ubsan/attrib-1.C: New test.
* g++.dg/ubsan/null-1.C: New test.
* g++.dg/ubsan/null-2.C: New test.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@213406 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/ubsan.c')
-rw-r--r-- | gcc/ubsan.c | 171 |
1 files changed, 130 insertions, 41 deletions
diff --git a/gcc/ubsan.c b/gcc/ubsan.c index 4e7e4878c66..0dbb104d2e8 100644 --- a/gcc/ubsan.c +++ b/gcc/ubsan.c @@ -49,6 +49,7 @@ along with GCC; see the file COPYING3. If not see #include "intl.h" #include "realmpfr.h" #include "dfp.h" +#include "builtins.h" /* Map from a tree to a VAR_DECL tree. */ @@ -586,7 +587,7 @@ is_ubsan_builtin_p (tree t) /* Expand the UBSAN_BOUNDS special builtin function. */ -void +bool ubsan_expand_bounds_ifn (gimple_stmt_iterator *gsi) { gimple stmt = gsi_stmt (*gsi); @@ -645,21 +646,52 @@ ubsan_expand_bounds_ifn (gimple_stmt_iterator *gsi) /* Point GSI to next logical statement. */ *gsi = gsi_start_bb (fallthru_bb); + return true; } -/* Expand UBSAN_NULL internal call. */ +/* Expand UBSAN_NULL internal call. The type is kept on the ckind + argument which is a constant, because the middle-end treats pointer + conversions as useless and therefore the type of the first argument + could be changed to any other pointer type. */ -void -ubsan_expand_null_ifn (gimple_stmt_iterator gsi) +bool +ubsan_expand_null_ifn (gimple_stmt_iterator *gsip) { + gimple_stmt_iterator gsi = *gsip; gimple stmt = gsi_stmt (gsi); location_t loc = gimple_location (stmt); - gcc_assert (gimple_call_num_args (stmt) == 2); + gcc_assert (gimple_call_num_args (stmt) == 3); tree ptr = gimple_call_arg (stmt, 0); tree ckind = gimple_call_arg (stmt, 1); + tree align = gimple_call_arg (stmt, 2); + tree check_align = NULL_TREE; + bool check_null; basic_block cur_bb = gsi_bb (gsi); + gimple g; + if (!integer_zerop (align)) + { + unsigned int ptralign = get_pointer_alignment (ptr) / BITS_PER_UNIT; + if (compare_tree_int (align, ptralign) == 1) + { + check_align = make_ssa_name (pointer_sized_int_node, NULL); + g = gimple_build_assign_with_ops (NOP_EXPR, check_align, + ptr, NULL_TREE); + gimple_set_location (g, loc); + gsi_insert_before (&gsi, g, GSI_SAME_STMT); + } + } + check_null = (flag_sanitize & SANITIZE_NULL) != 0; + + if (check_align == NULL_TREE && !check_null) + { + gsi_remove (gsip, true); + /* Unlink the UBSAN_NULLs vops before replacing it. */ + unlink_stmt_vdef (stmt); + return true; + } + /* Split the original block holding the pointer dereference. */ edge e = split_block (cur_bb, stmt); @@ -689,12 +721,11 @@ ubsan_expand_null_ifn (gimple_stmt_iterator gsi) /* Update dominance info for the newly created then_bb; note that fallthru_bb's dominance info has already been updated by - split_bock. */ + split_block. */ if (dom_info_available_p (CDI_DOMINATORS)) set_immediate_dominator (CDI_DOMINATORS, then_bb, cond_bb); /* Put the ubsan builtin call into the newly created BB. */ - gimple g; if (flag_sanitize_undefined_trap_on_error) g = gimple_build_call (builtin_decl_implicit (BUILT_IN_TRAP), 0); else @@ -705,54 +736,115 @@ ubsan_expand_null_ifn (gimple_stmt_iterator gsi) : BUILT_IN_UBSAN_HANDLE_TYPE_MISMATCH_ABORT; tree fn = builtin_decl_implicit (bcode); const struct ubsan_mismatch_data m - = { build_zero_cst (pointer_sized_int_node), ckind }; + = { align, fold_convert (unsigned_char_type_node, ckind) }; tree data = ubsan_create_data ("__ubsan_null_data", &loc, &m, - ubsan_type_descriptor (TREE_TYPE (ptr), + ubsan_type_descriptor (TREE_TYPE (ckind), 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)); + check_align ? check_align + : build_zero_cst (pointer_sized_int_node)); } - gimple_set_location (g, loc); gimple_stmt_iterator gsi2 = gsi_start_bb (then_bb); + gimple_set_location (g, loc); gsi_insert_after (&gsi2, g, GSI_NEW_STMT); /* Unlink the UBSAN_NULLs vops before replacing it. */ unlink_stmt_vdef (stmt); - g = gimple_build_cond (EQ_EXPR, ptr, build_int_cst (TREE_TYPE (ptr), 0), - NULL_TREE, NULL_TREE); - gimple_set_location (g, loc); + if (check_null) + { + g = gimple_build_cond (EQ_EXPR, ptr, build_int_cst (TREE_TYPE (ptr), 0), + NULL_TREE, NULL_TREE); + gimple_set_location (g, loc); - /* Replace the UBSAN_NULL with a GIMPLE_COND stmt. */ - gsi_replace (&gsi, g, false); -} + /* Replace the UBSAN_NULL with a GIMPLE_COND stmt. */ + gsi_replace (&gsi, g, false); + } -/* Instrument a member call. We check whether 'this' is NULL. */ + if (check_align) + { + if (check_null) + { + /* Split the block with the condition again. */ + e = split_block (cond_bb, stmt); + basic_block cond1_bb = e->src; + basic_block cond2_bb = e->dest; + + /* Make an edge coming from the 'cond1 block' into the 'then block'; + this edge is unlikely taken, so set up the probability + accordingly. */ + e = make_edge (cond1_bb, then_bb, EDGE_TRUE_VALUE); + e->probability = PROB_VERY_UNLIKELY; + + /* Set up the fallthrough basic block. */ + e = find_edge (cond1_bb, cond2_bb); + e->flags = EDGE_FALSE_VALUE; + e->count = cond1_bb->count; + e->probability = REG_BR_PROB_BASE - PROB_VERY_UNLIKELY; + + /* Update dominance info. */ + if (dom_info_available_p (CDI_DOMINATORS)) + { + set_immediate_dominator (CDI_DOMINATORS, fallthru_bb, cond1_bb); + set_immediate_dominator (CDI_DOMINATORS, then_bb, cond1_bb); + } -static void -instrument_member_call (gimple_stmt_iterator *iter) -{ - tree this_parm = gimple_call_arg (gsi_stmt (*iter), 0); - tree kind = build_int_cst (unsigned_char_type_node, UBSAN_MEMBER_CALL); - gimple g = gimple_build_call_internal (IFN_UBSAN_NULL, 2, this_parm, kind); - gimple_set_location (g, gimple_location (gsi_stmt (*iter))); - gsi_insert_before (iter, g, GSI_SAME_STMT); + gsi2 = gsi_start_bb (cond2_bb); + } + + tree mask = build_int_cst (pointer_sized_int_node, + tree_to_uhwi (align) - 1); + g = gimple_build_assign_with_ops (BIT_AND_EXPR, + make_ssa_name (pointer_sized_int_node, + NULL), + check_align, mask); + gimple_set_location (g, loc); + if (check_null) + gsi_insert_after (&gsi2, g, GSI_NEW_STMT); + else + gsi_insert_before (&gsi, g, GSI_SAME_STMT); + + g = gimple_build_cond (NE_EXPR, gimple_assign_lhs (g), + build_int_cst (pointer_sized_int_node, 0), + NULL_TREE, NULL_TREE); + gimple_set_location (g, loc); + if (check_null) + gsi_insert_after (&gsi2, g, GSI_NEW_STMT); + else + /* Replace the UBSAN_NULL with a GIMPLE_COND stmt. */ + gsi_replace (&gsi, g, false); + } + return false; } -/* Instrument a memory reference. T is the pointer, IS_LHS says +/* Instrument a memory reference. BASE is the base of MEM, IS_LHS says whether the pointer is on the left hand side of the assignment. */ static void -instrument_mem_ref (tree t, gimple_stmt_iterator *iter, bool is_lhs) +instrument_mem_ref (tree mem, tree base, gimple_stmt_iterator *iter, + bool is_lhs) { enum ubsan_null_ckind ikind = is_lhs ? UBSAN_STORE_OF : UBSAN_LOAD_OF; - if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (TREE_TYPE (t)))) + unsigned int align = 0; + if (flag_sanitize & SANITIZE_ALIGNMENT) + { + align = min_align_of_type (TREE_TYPE (base)); + if (align <= 1) + align = 0; + } + if (align == 0 && (flag_sanitize & SANITIZE_NULL) == 0) + return; + tree t = TREE_OPERAND (base, 0); + if (!POINTER_TYPE_P (TREE_TYPE (t))) + return; + if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (TREE_TYPE (t))) && mem != base) ikind = UBSAN_MEMBER_ACCESS; - tree kind = build_int_cst (unsigned_char_type_node, ikind); - gimple g = gimple_build_call_internal (IFN_UBSAN_NULL, 2, t, kind); + tree kind = build_int_cst (TREE_TYPE (t), ikind); + tree alignt = build_int_cst (pointer_sized_int_node, align); + gimple g = gimple_build_call_internal (IFN_UBSAN_NULL, 3, t, kind, alignt); gimple_set_location (g, gimple_location (gsi_stmt (*iter))); gsi_insert_before (iter, g, GSI_SAME_STMT); } @@ -764,15 +856,11 @@ instrument_null (gimple_stmt_iterator gsi, bool is_lhs) { gimple stmt = gsi_stmt (gsi); tree t = is_lhs ? gimple_get_lhs (stmt) : gimple_assign_rhs1 (stmt); - t = get_base_address (t); - const enum tree_code code = TREE_CODE (t); + tree base = get_base_address (t); + const enum tree_code code = TREE_CODE (base); if (code == MEM_REF - && TREE_CODE (TREE_OPERAND (t, 0)) == SSA_NAME) - instrument_mem_ref (TREE_OPERAND (t, 0), &gsi, is_lhs); - else if (code == ADDR_EXPR - && POINTER_TYPE_P (TREE_TYPE (t)) - && TREE_CODE (TREE_TYPE (TREE_TYPE (t))) == METHOD_TYPE) - instrument_member_call (&gsi); + && TREE_CODE (TREE_OPERAND (base, 0)) == SSA_NAME) + instrument_mem_ref (t, base, &gsi, is_lhs); } /* Build an ubsan builtin call for the signed-integer-overflow @@ -1147,7 +1235,8 @@ public: virtual bool gate (function *) { return flag_sanitize & (SANITIZE_NULL | SANITIZE_SI_OVERFLOW - | SANITIZE_BOOL | SANITIZE_ENUM) + | SANITIZE_BOOL | SANITIZE_ENUM + | SANITIZE_ALIGNMENT) && current_function_decl != NULL_TREE && !lookup_attribute ("no_sanitize_undefined", DECL_ATTRIBUTES (current_function_decl)); @@ -1180,7 +1269,7 @@ pass_ubsan::execute (function *fun) && is_gimple_assign (stmt)) instrument_si_overflow (gsi); - if (flag_sanitize & SANITIZE_NULL) + if (flag_sanitize & (SANITIZE_NULL | SANITIZE_ALIGNMENT)) { if (gimple_store_p (stmt)) instrument_null (gsi, true); |