On 01/20/2017 03:27 PM, Jakub Jelinek wrote: > On Fri, Jan 20, 2017 at 03:08:21PM +0100, Martin Liška wrote: >> Unfortunately this way would not work as clobber marks content of the memory >> as uninitialize >> is different behavior that just marking a memory can be used (and maybe >> already contains a value). >> >> This shows the problem: >> >> #include <string.h> >> >> char cc; >> char ptr[] = "sparta2"; >> >> void get(char **x) >> { >> *x = ptr; >> } >> >> int main() >> { >> char *here = &cc; >> >> for (;;) >> { >> next_line: >> if (here == NULL) >> __builtin_abort(); >> get (&here); >> if (strcmp (here, "sparta") == 0) >> goto next_line; >> else if (strcmp (here, "sparta2") == 0) >> break; >> } >> } >> >> With the patch, DSE would optimize out '*here = &cc;' and thus aborts. The >> problem is definitely >> related to goto magic, where we are more defensive in placement of >> ASAN_MARK(UNPOISON,...). >> Hope your optimization is still valid for situations w/o artificial >> ASAN_MARK(UNPOISON,...) placed due >> to goto magic. >> >> Do we still want to do it now, or postponing to GCC 8 would be better option? > > I'd still like to resolve it for GCC 7 if at all possible, I think otherwise > -fsanitize=address is by default unnecessarily slower (so it is a regression > anyway).
Good, I hope I have patch that finally works as we want. It add attribute to variables that are unpoisoned as live switch variables, or are defined in a label which address is taken. With [1] I can bootstrap-asan and the patch can bootstrap on ppc64le-redhat-linux and survives regression tests. I'm sending latest diff, as well as the final pair of patches I would like to install. Ready to be installed? Martin > So, do we always amit ASAN_MARK(UNPOISON, ...) at the start of scope and > then yet another ASAN_MARK(UNPOISON, ...) at the goto destination? > At least on the above testcase that is the case, so if we say split > ASAN_MARK_UNPOISON into something that is used at the start of scope > (we'd emit the clobber for those) and others (we would not), then perhaps we > could get around that. The above is BTW a clear case where shouldn't emit > UNPOISON on the label, as the goto doesn't cross its initialization. > But I can see with gotos from outside of some var's scope into it we > wouldn't handle it properly. Perhaps for now set some > flag/attribute/whatever on vars for which we emit the conservative > UNPOISON and never allow those to be made non-addressable (i.e. for those > say that POISON/UNPOISON actually makes them always addressable)? > > Jakub > [1] https://gcc.gnu.org/ml/gcc-patches/2017-01/msg01446.html
>From 97a900584714411521fc8ad3c7b99b0e74955fe6 Mon Sep 17 00:00:00 2001 From: marxin <mli...@suse.cz> Date: Mon, 19 Dec 2016 15:36:11 +0100 Subject: [PATCH 1/2] Speed up use-after-scope (v2): rewrite into SSA gcc/ChangeLog: 2016-12-22 Martin Liska <mli...@suse.cz> * asan.c (create_asan_shadow_var): New function. (asan_expand_poison_ifn): Likewise. * asan.h (asan_expand_poison_ifn): New declaration. * internal-fn.c (expand_ASAN_POISON): Likewise. * internal-fn.def (ASAN_POISON): New builtin. * sanopt.c (pass_sanopt::execute): Expand asan_expand_poison_ifn. * tree-inline.c (copy_decl_for_dup_finish): Make function external. * tree-inline.h (copy_decl_for_dup_finish): Likewise. * tree-ssa.c (is_asan_mark_p): New function. (execute_update_addresses_taken): Rewrite local variables (identified just by use-after-scope as addressable) into SSA. gcc/testsuite/ChangeLog: 2016-12-22 Martin Liska <mli...@suse.cz> * gcc.dg/asan/use-after-scope-3.c: Add additional flags. * gcc.dg/asan/use-after-scope-9.c: Likewise and grep for sanopt optimization for ASAN_POISON. --- gcc/asan.c | 109 +++++++++++++++++++++++++- gcc/asan.h | 2 + gcc/internal-fn.c | 7 ++ gcc/internal-fn.def | 1 + gcc/sanopt.c | 11 +++ gcc/testsuite/gcc.dg/asan/use-after-scope-3.c | 1 + gcc/testsuite/gcc.dg/asan/use-after-scope-9.c | 2 + gcc/tree-inline.c | 2 +- gcc/tree-inline.h | 1 + gcc/tree-ssa.c | 69 +++++++++++++--- 10 files changed, 193 insertions(+), 12 deletions(-) diff --git a/gcc/asan.c b/gcc/asan.c index 9a59fe4f100..4aa5a4015ea 100644 --- a/gcc/asan.c +++ b/gcc/asan.c @@ -32,8 +32,8 @@ along with GCC; see the file COPYING3. If not see #include "tree-pass.h" #include "memmodel.h" #include "tm_p.h" +#include "ssa.h" #include "stringpool.h" -#include "tree-vrp.h" #include "tree-ssanames.h" #include "optabs.h" #include "emit-rtl.h" @@ -59,6 +59,7 @@ along with GCC; see the file COPYING3. If not see #include "params.h" #include "builtins.h" #include "fnmatch.h" +#include "tree-inline.h" /* AddressSanitizer finds out-of-bounds and use-after-free bugs with <2x slowdown on average. @@ -3087,6 +3088,112 @@ asan_expand_check_ifn (gimple_stmt_iterator *iter, bool use_calls) return true; } +/* Create ASAN shadow variable for a VAR_DECL which has been rewritten + into SSA. Already seen VAR_DECLs are stored in SHADOW_VARS_MAPPING. */ + +static tree +create_asan_shadow_var (tree var_decl, + hash_map<tree, tree> &shadow_vars_mapping) +{ + tree *slot = shadow_vars_mapping.get (var_decl); + if (slot == NULL) + { + tree shadow_var = copy_node (var_decl); + + copy_body_data id; + memset (&id, 0, sizeof (copy_body_data)); + id.src_fn = id.dst_fn = current_function_decl; + copy_decl_for_dup_finish (&id, var_decl, shadow_var); + + DECL_ARTIFICIAL (shadow_var) = 1; + DECL_IGNORED_P (shadow_var) = 1; + DECL_SEEN_IN_BIND_EXPR_P (shadow_var) = 0; + gimple_add_tmp_var (shadow_var); + + shadow_vars_mapping.put (var_decl, shadow_var); + return shadow_var; + } + else + return *slot; +} + +bool +asan_expand_poison_ifn (gimple_stmt_iterator *iter, + bool *need_commit_edge_insert, + hash_map<tree, tree> &shadow_vars_mapping) +{ + gimple *g = gsi_stmt (*iter); + tree poisoned_var = gimple_call_lhs (g); + if (!poisoned_var) + { + gsi_remove (iter, true); + return true; + } + + tree shadow_var = create_asan_shadow_var (SSA_NAME_VAR (poisoned_var), + shadow_vars_mapping); + + bool recover_p; + if (flag_sanitize & SANITIZE_USER_ADDRESS) + recover_p = (flag_sanitize_recover & SANITIZE_USER_ADDRESS) != 0; + else + recover_p = (flag_sanitize_recover & SANITIZE_KERNEL_ADDRESS) != 0; + tree size = DECL_SIZE_UNIT (shadow_var); + gimple *poison_call + = gimple_build_call_internal (IFN_ASAN_MARK, 3, + build_int_cst (integer_type_node, + ASAN_MARK_POISON), + build_fold_addr_expr (shadow_var), size); + + use_operand_p use_p; + imm_use_iterator imm_iter; + FOR_EACH_IMM_USE_FAST (use_p, imm_iter, poisoned_var) + { + gimple *use = USE_STMT (use_p); + if (is_gimple_debug (use)) + continue; + + int nargs; + tree fun = report_error_func (false, recover_p, tree_to_uhwi (size), + &nargs); + + gcall *call = gimple_build_call (fun, 1, + build_fold_addr_expr (shadow_var)); + gimple_set_location (call, gimple_location (use)); + gimple *call_to_insert = call; + + /* The USE can be a gimple PHI node. If so, insert the call on + all edges leading to the PHI node. */ + if (is_a <gphi *> (use)) + { + gphi *phi = dyn_cast<gphi *> (use); + for (unsigned i = 0; i < gimple_phi_num_args (phi); ++i) + if (gimple_phi_arg_def (phi, i) == poisoned_var) + { + edge e = gimple_phi_arg_edge (phi, i); + + if (call_to_insert == NULL) + call_to_insert = gimple_copy (call); + + gsi_insert_seq_on_edge (e, call_to_insert); + *need_commit_edge_insert = true; + call_to_insert = NULL; + } + } + else + { + gimple_stmt_iterator gsi = gsi_for_stmt (use); + gsi_insert_before (&gsi, call, GSI_NEW_STMT); + } + } + + SSA_NAME_IS_DEFAULT_DEF (poisoned_var) = true; + SSA_NAME_DEF_STMT (poisoned_var) = gimple_build_nop (); + gsi_replace (iter, poison_call, false); + + return true; +} + /* Instrument the current function. */ static unsigned int diff --git a/gcc/asan.h b/gcc/asan.h index 2f1f2eeaba7..2895bdee645 100644 --- a/gcc/asan.h +++ b/gcc/asan.h @@ -30,6 +30,8 @@ extern void initialize_sanitizer_builtins (void); extern tree asan_dynamic_init_call (bool); extern bool asan_expand_check_ifn (gimple_stmt_iterator *, bool); extern bool asan_expand_mark_ifn (gimple_stmt_iterator *); +extern bool asan_expand_poison_ifn (gimple_stmt_iterator *, bool *, + hash_map<tree, tree> &); extern gimple_stmt_iterator create_cond_insert_point (gimple_stmt_iterator *, bool, bool, bool, basic_block *, basic_block *); diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c index 5d71cb2e08d..45e4ce05b86 100644 --- a/gcc/internal-fn.c +++ b/gcc/internal-fn.c @@ -380,6 +380,13 @@ expand_ASAN_MARK (internal_fn, gcall *) gcc_unreachable (); } +/* This should get expanded in the sanopt pass. */ + +static void +expand_ASAN_POISON (internal_fn, gcall *) +{ + gcc_unreachable (); +} /* This should get expanded in the tsan pass. */ diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def index 4bf8383a77e..7b28b6722ff 100644 --- a/gcc/internal-fn.def +++ b/gcc/internal-fn.def @@ -167,6 +167,7 @@ DEF_INTERNAL_FN (ABNORMAL_DISPATCHER, ECF_NORETURN, NULL) DEF_INTERNAL_FN (BUILTIN_EXPECT, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL) DEF_INTERNAL_FN (ASAN_CHECK, ECF_TM_PURE | ECF_LEAF | ECF_NOTHROW, ".R...") DEF_INTERNAL_FN (ASAN_MARK, ECF_LEAF | ECF_NOTHROW, ".R..") +DEF_INTERNAL_FN (ASAN_POISON, ECF_LEAF | ECF_NOTHROW | ECF_NOVOPS, NULL) DEF_INTERNAL_FN (ADD_OVERFLOW, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL) DEF_INTERNAL_FN (SUB_OVERFLOW, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL) DEF_INTERNAL_FN (MUL_OVERFLOW, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL) diff --git a/gcc/sanopt.c b/gcc/sanopt.c index 55e07c0c646..70b7aeb80d3 100644 --- a/gcc/sanopt.c +++ b/gcc/sanopt.c @@ -894,6 +894,8 @@ pass_sanopt::execute (function *fun) bool use_calls = ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD < INT_MAX && asan_num_accesses >= ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD; + hash_map<tree, tree> shadow_vars_mapping; + bool need_commit_edge_insert = false; FOR_EACH_BB_FN (bb, fun) { gimple_stmt_iterator gsi; @@ -931,6 +933,11 @@ pass_sanopt::execute (function *fun) case IFN_ASAN_MARK: no_next = asan_expand_mark_ifn (&gsi); break; + case IFN_ASAN_POISON: + no_next = asan_expand_poison_ifn (&gsi, + &need_commit_edge_insert, + shadow_vars_mapping); + break; default: break; } @@ -962,6 +969,10 @@ pass_sanopt::execute (function *fun) gsi_next (&gsi); } } + + if (need_commit_edge_insert) + gsi_commit_edge_inserts (); + return 0; } diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-3.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-3.c index 9aeed51a770..8b11bea9940 100644 --- a/gcc/testsuite/gcc.dg/asan/use-after-scope-3.c +++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-3.c @@ -1,5 +1,6 @@ // { dg-do run } // { dg-shouldfail "asan" } +// { dg-additional-options "-O0" } int main (void) diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-9.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-9.c index 2e30deffa18..5d069dd18ea 100644 --- a/gcc/testsuite/gcc.dg/asan/use-after-scope-9.c +++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-9.c @@ -1,5 +1,6 @@ // { dg-do run } // { dg-shouldfail "asan" } +// { dg-additional-options "-O2 -fdump-tree-asan1" } int main (int argc, char **argv) @@ -15,6 +16,7 @@ main (int argc, char **argv) return *ptr; } +// { dg-final { scan-tree-dump-times "= ASAN_POISON \\(\\)" 1 "asan1" } } // { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" } // { dg-output "READ of size .*" } // { dg-output ".*'a' <== Memory access at offset \[0-9\]* is inside this variable.*" } diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c index 42055bd8318..d63c70f2a12 100644 --- a/gcc/tree-inline.c +++ b/gcc/tree-inline.c @@ -5449,7 +5449,7 @@ declare_inline_vars (tree block, tree vars) but now it will be in the TO_FN. PARM_TO_VAR means enable PARM_DECL to VAR_DECL translation. */ -static tree +tree copy_decl_for_dup_finish (copy_body_data *id, tree decl, tree copy) { /* Don't generate debug information for the copy if we wouldn't have diff --git a/gcc/tree-inline.h b/gcc/tree-inline.h index ecfae6b048e..41402a315ec 100644 --- a/gcc/tree-inline.h +++ b/gcc/tree-inline.h @@ -218,6 +218,7 @@ extern gimple_seq copy_gimple_seq_and_replace_locals (gimple_seq seq); extern bool debug_find_tree (tree, tree); extern tree copy_fn (tree, tree&, tree&); extern const char *copy_forbidden (struct function *fun); +extern tree copy_decl_for_dup_finish (copy_body_data *id, tree decl, tree copy); /* This is in tree-inline.c since the routine uses data structures from the inliner. */ diff --git a/gcc/tree-ssa.c b/gcc/tree-ssa.c index 067143f49b8..f1826b2c9c4 100644 --- a/gcc/tree-ssa.c +++ b/gcc/tree-ssa.c @@ -41,6 +41,7 @@ along with GCC; see the file COPYING3. If not see #include "cfgexpand.h" #include "tree-cfg.h" #include "tree-dfa.h" +#include "asan.h" /* Pointer map of variable mappings, keyed by edge. */ static hash_map<edge, auto_vec<edge_var_map> > *edge_var_maps; @@ -1575,6 +1576,30 @@ maybe_optimize_var (tree var, bitmap addresses_taken, bitmap not_reg_needs, } } +/* Return true when STMT is ASAN mark where second argument is an address + of a local variable. */ + +static bool +is_asan_mark_p (gimple *stmt) +{ + if (!gimple_call_internal_p (stmt, IFN_ASAN_MARK)) + return false; + + tree addr = get_base_address (gimple_call_arg (stmt, 1)); + if (TREE_CODE (addr) == ADDR_EXPR + && VAR_P (TREE_OPERAND (addr, 0))) + { + tree var = TREE_OPERAND (addr, 0); + unsigned addressable = TREE_ADDRESSABLE (var); + TREE_ADDRESSABLE (var) = 0; + bool r = is_gimple_reg (var); + TREE_ADDRESSABLE (var) = addressable; + return r; + } + + return false; +} + /* Compute TREE_ADDRESSABLE and DECL_GIMPLE_REG_P for local variables. */ void @@ -1600,17 +1625,23 @@ execute_update_addresses_taken (void) enum gimple_code code = gimple_code (stmt); tree decl; - if (code == GIMPLE_CALL - && optimize_atomic_compare_exchange_p (stmt)) + if (code == GIMPLE_CALL) { - /* For __atomic_compare_exchange_N if the second argument - is &var, don't mark var addressable; - if it becomes non-addressable, we'll rewrite it into - ATOMIC_COMPARE_EXCHANGE call. */ - tree arg = gimple_call_arg (stmt, 1); - gimple_call_set_arg (stmt, 1, null_pointer_node); - gimple_ior_addresses_taken (addresses_taken, stmt); - gimple_call_set_arg (stmt, 1, arg); + if (optimize_atomic_compare_exchange_p (stmt)) + { + /* For __atomic_compare_exchange_N if the second argument + is &var, don't mark var addressable; + if it becomes non-addressable, we'll rewrite it into + ATOMIC_COMPARE_EXCHANGE call. */ + tree arg = gimple_call_arg (stmt, 1); + gimple_call_set_arg (stmt, 1, null_pointer_node); + gimple_ior_addresses_taken (addresses_taken, stmt); + gimple_call_set_arg (stmt, 1, arg); + } + else if (is_asan_mark_p (stmt)) + ; + else + gimple_ior_addresses_taken (addresses_taken, stmt); } else /* Note all addresses taken by the stmt. */ @@ -1866,6 +1897,24 @@ execute_update_addresses_taken (void) continue; } } + else if (is_asan_mark_p (stmt)) + { + tree var = TREE_OPERAND (gimple_call_arg (stmt, 1), 0); + if (bitmap_bit_p (suitable_for_renaming, DECL_UID (var))) + { + unlink_stmt_vdef (stmt); + if (asan_mark_p (stmt, ASAN_MARK_POISON)) + { + gcall *call + = gimple_build_call_internal (IFN_ASAN_POISON, 0); + gimple_call_set_lhs (call, var); + gsi_replace (&gsi, call, GSI_SAME_STMT); + } + else + gsi_remove (&gsi, true); + continue; + } + } for (i = 0; i < gimple_call_num_args (stmt); ++i) { tree *argp = gimple_call_arg_ptr (stmt, i); -- 2.11.0
>From 82c77c43969ebd7fcb17094820111a0075311a49 Mon Sep 17 00:00:00 2001 From: marxin <mli...@suse.cz> Date: Tue, 17 Jan 2017 16:49:29 +0100 Subject: [PATCH 2/2] use-after-scope: handle writes to a poisoned variable gcc/testsuite/ChangeLog: 2017-01-17 Martin Liska <mli...@suse.cz> * gcc.dg/asan/use-after-scope-10.c: New test. * gcc.dg/asan/use-after-scope-11.c: New test. * g++.dg/asan/use-after-scope-5.C: New test. gcc/ChangeLog: 2017-01-16 Jakub Jelinek <ja...@redhat.com> Martin Liska <mli...@suse.cz> * asan.c (asan_expand_poison_ifn): Support stores and use appropriate ASAN report function. * internal-fn.c (expand_ASAN_POISON_USE): New function. * internal-fn.def (ASAN_POISON_USE): Declare. * tree-into-ssa.c (maybe_add_asan_poison_write): New function. (maybe_register_def): Create ASAN_POISON_USE when sanitizing. * tree-ssa-dce.c (eliminate_unnecessary_stmts): Remove ASAN_POISON calls w/o LHS. * tree-ssa.c (execute_update_addresses_taken): Create clobber for ASAN_MARK (UNPOISON, &x, ...) in order to prevent usage of a LHS from ASAN_MARK (POISON, &x, ...) coming to a PHI node. * gimplify.c (asan_poison_variables): Add attribute use_after_scope_memory to variables that really needs to live in memory. * tree-ssa.c (is_asan_mark_p): Do not rewrite into SSA when having the attribute. --- gcc/asan.c | 19 ++++++++++------ gcc/gimplify.c | 15 +++++++++++-- gcc/internal-fn.c | 8 +++++++ gcc/internal-fn.def | 1 + gcc/testsuite/g++.dg/asan/use-after-scope-5.C | 23 ++++++++++++++++++++ gcc/testsuite/gcc.dg/asan/use-after-scope-10.c | 22 +++++++++++++++++++ gcc/testsuite/gcc.dg/asan/use-after-scope-11.c | 30 ++++++++++++++++++++++++++ gcc/tree-into-ssa.c | 27 ++++++++++++++++++++++- gcc/tree-ssa-dce.c | 16 ++++++++++---- gcc/tree-ssa.c | 15 ++++++++++++- 10 files changed, 161 insertions(+), 15 deletions(-) create mode 100644 gcc/testsuite/g++.dg/asan/use-after-scope-5.C create mode 100644 gcc/testsuite/gcc.dg/asan/use-after-scope-10.c create mode 100644 gcc/testsuite/gcc.dg/asan/use-after-scope-11.c diff --git a/gcc/asan.c b/gcc/asan.c index 4aa5a4015ea..040e949fc85 100644 --- a/gcc/asan.c +++ b/gcc/asan.c @@ -3117,6 +3117,8 @@ create_asan_shadow_var (tree var_decl, return *slot; } +/* Expand ASAN_POISON ifn. */ + bool asan_expand_poison_ifn (gimple_stmt_iterator *iter, bool *need_commit_edge_insert, @@ -3130,8 +3132,8 @@ asan_expand_poison_ifn (gimple_stmt_iterator *iter, return true; } - tree shadow_var = create_asan_shadow_var (SSA_NAME_VAR (poisoned_var), - shadow_vars_mapping); + tree shadow_var = create_asan_shadow_var (SSA_NAME_VAR (poisoned_var), + shadow_vars_mapping); bool recover_p; if (flag_sanitize & SANITIZE_USER_ADDRESS) @@ -3145,16 +3147,16 @@ asan_expand_poison_ifn (gimple_stmt_iterator *iter, ASAN_MARK_POISON), build_fold_addr_expr (shadow_var), size); - use_operand_p use_p; + gimple *use; imm_use_iterator imm_iter; - FOR_EACH_IMM_USE_FAST (use_p, imm_iter, poisoned_var) + FOR_EACH_IMM_USE_STMT (use, imm_iter, poisoned_var) { - gimple *use = USE_STMT (use_p); if (is_gimple_debug (use)) continue; int nargs; - tree fun = report_error_func (false, recover_p, tree_to_uhwi (size), + bool store_p = gimple_call_internal_p (use, IFN_ASAN_POISON_USE); + tree fun = report_error_func (store_p, recover_p, tree_to_uhwi (size), &nargs); gcall *call = gimple_build_call (fun, 1, @@ -3183,7 +3185,10 @@ asan_expand_poison_ifn (gimple_stmt_iterator *iter, else { gimple_stmt_iterator gsi = gsi_for_stmt (use); - gsi_insert_before (&gsi, call, GSI_NEW_STMT); + if (store_p) + gsi_replace (&gsi, call, true); + else + gsi_insert_before (&gsi, call, GSI_NEW_STMT); } } diff --git a/gcc/gimplify.c b/gcc/gimplify.c index d382eea69f8..c6f7d7c4505 100644 --- a/gcc/gimplify.c +++ b/gcc/gimplify.c @@ -1206,8 +1206,19 @@ asan_poison_variables (hash_set<tree> *variables, bool poison, gimple_seq *seq_p sorted_variables.qsort (sort_by_decl_uid); - for (unsigned i = 0; i < sorted_variables.length (); i++) - asan_poison_variable (sorted_variables[i], poison, seq_p); + unsigned i; + tree var; + FOR_EACH_VEC_ELT (sorted_variables, i, var) + { + asan_poison_variable (var, poison, seq_p); + + /* Add use_after_scope_memory attribute for the variable in order + to prevent re-written into SSA. */ + DECL_ATTRIBUTES (var) + = tree_cons (get_identifier ("use_after_scope_memory"), + build_int_cst (integer_type_node, 1), + DECL_ATTRIBUTES (var)); + } } /* Gimplify a BIND_EXPR. Just voidify and recurse. */ diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c index 45e4ce05b86..0d61375462d 100644 --- a/gcc/internal-fn.c +++ b/gcc/internal-fn.c @@ -388,6 +388,14 @@ expand_ASAN_POISON (internal_fn, gcall *) gcc_unreachable (); } +/* This should get expanded in the sanopt pass. */ + +static void +expand_ASAN_POISON_USE (internal_fn, gcall *) +{ + gcc_unreachable (); +} + /* This should get expanded in the tsan pass. */ static void diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def index 7b28b6722ff..fd25a952299 100644 --- a/gcc/internal-fn.def +++ b/gcc/internal-fn.def @@ -168,6 +168,7 @@ DEF_INTERNAL_FN (BUILTIN_EXPECT, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL) DEF_INTERNAL_FN (ASAN_CHECK, ECF_TM_PURE | ECF_LEAF | ECF_NOTHROW, ".R...") DEF_INTERNAL_FN (ASAN_MARK, ECF_LEAF | ECF_NOTHROW, ".R..") DEF_INTERNAL_FN (ASAN_POISON, ECF_LEAF | ECF_NOTHROW | ECF_NOVOPS, NULL) +DEF_INTERNAL_FN (ASAN_POISON_USE, ECF_LEAF | ECF_NOTHROW | ECF_NOVOPS, NULL) DEF_INTERNAL_FN (ADD_OVERFLOW, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL) DEF_INTERNAL_FN (SUB_OVERFLOW, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL) DEF_INTERNAL_FN (MUL_OVERFLOW, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL) diff --git a/gcc/testsuite/g++.dg/asan/use-after-scope-5.C b/gcc/testsuite/g++.dg/asan/use-after-scope-5.C new file mode 100644 index 00000000000..7e28fc35e6d --- /dev/null +++ b/gcc/testsuite/g++.dg/asan/use-after-scope-5.C @@ -0,0 +1,23 @@ +// { dg-do run } + +int * +__attribute__((optimize(("-O0")))) +fn1 (int *a) +{ + return a; +} + +void +fn2 () +{ + for (int i = 0; i < 10; i++) + { + int *a; + (a) = fn1 (a); + } +} + +int main() +{ + fn2(); +} diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-10.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-10.c new file mode 100644 index 00000000000..24de8cec1ff --- /dev/null +++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-10.c @@ -0,0 +1,22 @@ +// { dg-do run } +// { dg-shouldfail "asan" } +// { dg-additional-options "-O2 -fdump-tree-asan1" } + +int +main (int argc, char **argv) +{ + int *ptr = 0; + + { + int a; + ptr = &a; + *ptr = 12345; + } + + *ptr = 12345; + return *ptr; +} + +// { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" } +// { dg-output "WRITE of size .*" } +// { dg-output ".*'a' <== Memory access at offset \[0-9\]* is inside this variable.*" } diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-11.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-11.c new file mode 100644 index 00000000000..b3c4c9ec758 --- /dev/null +++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-11.c @@ -0,0 +1,30 @@ +// { dg-do run } + +#include <string.h> + +char cc; +char ptr[] = "sparta2"; + +void get(char **x) +{ + *x = ptr; +} + +int main() +{ + char *here = &cc; + + for (;;) + { + next_line: + if (here == NULL) + __builtin_abort(); + get (&here); + if (strcmp (here, "sparta") == 0) + goto next_line; + else if (strcmp (here, "sparta2") == 0) + break; + } + + return 0; +} diff --git a/gcc/tree-into-ssa.c b/gcc/tree-into-ssa.c index c7df237d57f..22261c15dc2 100644 --- a/gcc/tree-into-ssa.c +++ b/gcc/tree-into-ssa.c @@ -38,6 +38,7 @@ along with GCC; see the file COPYING3. If not see #include "tree-ssa.h" #include "domwalk.h" #include "statistics.h" +#include "asan.h" #define PERCENT(x,y) ((float)(x) * 100.0 / (float)(y)) @@ -1807,6 +1808,26 @@ maybe_replace_use_in_debug_stmt (use_operand_p use_p) } +/* If DEF has x_5 = ASAN_POISON () as its current def, add + ASAN_POISON_USE (x_5) stmt before GSI to denote the stmt writes into + a poisoned (out of scope) variable. */ + +static void +maybe_add_asan_poison_write (tree def, gimple_stmt_iterator *gsi) +{ + tree cdef = get_current_def (def); + if (cdef != NULL + && TREE_CODE (cdef) == SSA_NAME + && gimple_call_internal_p (SSA_NAME_DEF_STMT (cdef), IFN_ASAN_POISON)) + { + gcall *call + = gimple_build_call_internal (IFN_ASAN_POISON_USE, 1, cdef); + gimple_set_location (call, gimple_location (gsi_stmt (*gsi))); + gsi_insert_before (gsi, call, GSI_SAME_STMT); + } +} + + /* If the operand pointed to by DEF_P is an SSA name in NEW_SSA_NAMES or OLD_SSA_NAMES, or if it is a symbol marked for renaming, register it as the current definition for the names replaced by @@ -1837,7 +1858,11 @@ maybe_register_def (def_operand_p def_p, gimple *stmt, def = get_or_create_ssa_default_def (cfun, sym); } else - def = make_ssa_name (def, stmt); + { + if (asan_sanitize_use_after_scope ()) + maybe_add_asan_poison_write (def, &gsi); + def = make_ssa_name (def, stmt); + } SET_DEF (def_p, def); tree tracked_var = target_for_debug_bind (sym); diff --git a/gcc/tree-ssa-dce.c b/gcc/tree-ssa-dce.c index 4e51e699d49..5ebe57b0983 100644 --- a/gcc/tree-ssa-dce.c +++ b/gcc/tree-ssa-dce.c @@ -1367,10 +1367,18 @@ eliminate_unnecessary_stmts (void) update_stmt (stmt); release_ssa_name (name); - /* GOMP_SIMD_LANE without lhs is not needed. */ - if (gimple_call_internal_p (stmt) - && gimple_call_internal_fn (stmt) == IFN_GOMP_SIMD_LANE) - remove_dead_stmt (&gsi, bb); + /* GOMP_SIMD_LANE or ASAN_POISON without lhs is not + needed. */ + if (gimple_call_internal_p (stmt)) + switch (gimple_call_internal_fn (stmt)) + { + case IFN_GOMP_SIMD_LANE: + case IFN_ASAN_POISON: + remove_dead_stmt (&gsi, bb); + break; + default: + break; + } } else if (gimple_call_internal_p (stmt)) switch (gimple_call_internal_fn (stmt)) diff --git a/gcc/tree-ssa.c b/gcc/tree-ssa.c index f1826b2c9c4..10b77895508 100644 --- a/gcc/tree-ssa.c +++ b/gcc/tree-ssa.c @@ -1590,6 +1590,10 @@ is_asan_mark_p (gimple *stmt) && VAR_P (TREE_OPERAND (addr, 0))) { tree var = TREE_OPERAND (addr, 0); + if (lookup_attribute ("use_after_scope_memory", + DECL_ATTRIBUTES (var))) + return false; + unsigned addressable = TREE_ADDRESSABLE (var); TREE_ADDRESSABLE (var) = 0; bool r = is_gimple_reg (var); @@ -1911,7 +1915,16 @@ execute_update_addresses_taken (void) gsi_replace (&gsi, call, GSI_SAME_STMT); } else - gsi_remove (&gsi, true); + { + /* In ASAN_MARK (UNPOISON, &b, ...) the variable + is uninitialized. Avoid dependencies on + previous out of scope value. */ + tree clobber + = build_constructor (TREE_TYPE (var), NULL); + TREE_THIS_VOLATILE (clobber) = 1; + gimple *g = gimple_build_assign (var, clobber); + gsi_replace (&gsi, g, GSI_SAME_STMT); + } continue; } } -- 2.11.0
diff --git a/gcc/gimplify.c b/gcc/gimplify.c index 2777a23eb93..1b076fdf45c 100644 --- a/gcc/gimplify.c +++ b/gcc/gimplify.c @@ -1206,8 +1206,19 @@ asan_poison_variables (hash_set<tree> *variables, bool poison, gimple_seq *seq_p sorted_variables.qsort (sort_by_decl_uid); - for (unsigned i = 0; i < sorted_variables.length (); i++) - asan_poison_variable (sorted_variables[i], poison, seq_p); + unsigned i; + tree var; + FOR_EACH_VEC_ELT (sorted_variables, i, var) + { + asan_poison_variable (var, poison, seq_p); + + /* Add use_after_scope_memory attribute for the variable in order + to prevent re-written into SSA. */ + DECL_ATTRIBUTES (var) + = tree_cons (get_identifier ("use_after_scope_memory"), + build_int_cst (integer_type_node, 1), + DECL_ATTRIBUTES (var)); + } } /* Gimplify a BIND_EXPR. Just voidify and recurse. */ diff --git a/gcc/tree-ssa.c b/gcc/tree-ssa.c index 5bd9004e715..2b33da93a23 100644 --- a/gcc/tree-ssa.c +++ b/gcc/tree-ssa.c @@ -1565,6 +1565,10 @@ is_asan_mark_p (gimple *stmt) && VAR_P (TREE_OPERAND (addr, 0))) { tree var = TREE_OPERAND (addr, 0); + if (lookup_attribute ("use_after_scope_memory", + DECL_ATTRIBUTES (var))) + return false; + unsigned addressable = TREE_ADDRESSABLE (var); TREE_ADDRESSABLE (var) = 0; bool r = is_gimple_reg (var);