Message ID | 469bf86a-e43c-c571-66e4-87db78b6fb11@suse.cz |
---|---|
State | New |
Headers | show |
On Wed, Nov 16, 2016 at 05:01:31PM +0100, Martin Liška wrote: > + 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; > + > + built_in_function b = (recover_p > + ? BUILT_IN_ASAN_REPORT_USE_AFTER_SCOPE_NOABORT > + : BUILT_IN_ASAN_REPORT_USE_AFTER_SCOPE); > + tree fun = builtin_decl_implicit (b); > + pretty_printer pp; > + pp_tree_identifier (&pp, DECL_NAME (var_decl)); > + > + gcall *call = gimple_build_call (fun, 2, asan_pp_string (&pp), > + DECL_SIZE_UNIT (var_decl)); > + gimple_set_location (call, gimple_location (use)); > + > + /* 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); No space after *. > + 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); > + gsi_insert_seq_on_edge (e, call); > + *need_commit_edge_insert = true; You clearly don't have a sufficient testsuite coverage for this, because this won't really work if you have more than one phi argument equal to poisoned_var. Inserting the same gimple stmt into multiple places can't really work. I bet you want to set call to NULL after the gsi_insert_seq_on_edge and before that call if (call == NULL) { call = gimple_build_call (...); gimple_set_location (...); } Or maybe gimple_copy for the 2nd etc. would work too, dunno. > + } > + } > + else > + { > + gimple_stmt_iterator gsi = gsi_for_stmt (use); > + gsi_insert_before (&gsi, call, GSI_NEW_STMT); > + } > + } > + > + gimple *nop = gimple_build_nop (); > + SSA_NAME_IS_DEFAULT_DEF (poisoned_var) = true; > + SSA_NAME_DEF_STMT (poisoned_var) = nop; > + gsi_replace (iter, nop, GSI_NEW_STMT); The last argument of gsi_replace is a bool, not GSI_*. But not sure how this will work anyway, I think SSA_NAME_IS_DEFAULT_DEF are supposed to have SSA_NAME_DEF_STMT a GIMPLE_NOP that doesn't have bb set, while you are putting it into the stmt sequence. Shouldn't you just gsi_remove iter instead? Otherwise LGTM, but please post the asan patch to llvm-commits or through their web review interface. Jakub
I started review process in libsanitizer: https://reviews.llvm.org/D26965 And I have a question that was asked in the review: can we distinguish between load and store in case of having usage of ASAN_POISON? Load looks as follows: int main (int argc, char **argv) { char *ptr; if (argc != 12312) { char my_char; ptr = &my_char; } return *ptr; } main (int argc, char * * argv) { char my_char; int _5; <bb 2>: if (argc_1(D) != 12312) goto <bb 3>; else goto <bb 5>; <bb 5>: goto <bb 4>; <bb 3>: my_char_8 = ASAN_POISON (); <bb 4>: # my_char_6 = PHI <my_char_7(D)(5), my_char_8(3)> _5 = (int) my_char_6; return _5; } however doing a store: int main (int argc, char **argv) { char *ptr; if (argc != 12312) { char my_char; ptr = &my_char; } *ptr = 0; return 0; } main (int argc, char * * argv) { <bb 2>: if (argc_1(D) != 12312) goto <bb 3>; else goto <bb 5>; <bb 5>: goto <bb 4>; <bb 3>: ASAN_POISON (); <bb 4>: return 0; } leads to a situation, where LHS of ASAN_POISON assignment is identified as overwritten and eventually we see just ASAN_POISON call. This is currently removed in sanopt pass, but I'm wondering whether it's valid optimization or not in this context? Thanks, Martin
On Wed, Nov 23, 2016 at 02:57:07PM +0100, Martin Liška wrote: > I started review process in libsanitizer: https://reviews.llvm.org/D26965 > And I have a question that was asked in the review: can we distinguish between load and store > in case of having usage of ASAN_POISON? I think with ASAN_POISON it is indeed just loads from after scope that can be caught, a store overwrites the variable with a new value and when turning the store after we make the var no longer addressable into SSA form, we loose information about the out of scope store. Furthermore, if there is first a store and then a read, like: if (argc != 12312) { char my_char; ptr = &my_char; } *ptr = i + 26; return *ptr; we don't notice even the read. Not sure what could be done against that though. I think we'd need to hook into the into-ssa framework, there it should know the current value of the variable at the point of the store is result of ASAN_POISON and be able to instead of turning that my_char = _23; into my_char_35 = _23; turn it into: my_char_35 = ASAN_POISON (_23); which would represent after scope store into my_char. Not really familiar with into-ssa though to know where to do it. Jakub
On 11/23/2016 03:13 PM, Jakub Jelinek wrote: > On Wed, Nov 23, 2016 at 02:57:07PM +0100, Martin Liška wrote: >> I started review process in libsanitizer: https://reviews.llvm.org/D26965 >> And I have a question that was asked in the review: can we distinguish between load and store >> in case of having usage of ASAN_POISON? > > I think with ASAN_POISON it is indeed just loads from after scope that can > be caught, a store overwrites the variable with a new value and when turning > the store after we make the var no longer addressable into SSA form, we > loose information about the out of scope store. Furthermore, if there is > first a store and then a read, like: > if (argc != 12312) > { > char my_char; > ptr = &my_char; > } > *ptr = i + 26; > return *ptr; > we don't notice even the read. Not sure what could be done against that > though. I think we'd need to hook into the into-ssa framework, there it > should know the current value of the variable at the point of the store is > result of ASAN_POISON and be able to instead of turning that > my_char = _23; > into > my_char_35 = _23; > turn it into: > my_char_35 = ASAN_POISON (_23); > which would represent after scope store into my_char. > > Not really familiar with into-ssa though to know where to do it. > > Jakub > Richi, may I ask you for help with this question? Thanks, Martin
On Thu, Dec 1, 2016 at 5:30 PM, Martin Liška <mliska@suse.cz> wrote: > On 11/23/2016 03:13 PM, Jakub Jelinek wrote: >> On Wed, Nov 23, 2016 at 02:57:07PM +0100, Martin Liška wrote: >>> I started review process in libsanitizer: https://reviews.llvm.org/D26965 >>> And I have a question that was asked in the review: can we distinguish between load and store >>> in case of having usage of ASAN_POISON? >> >> I think with ASAN_POISON it is indeed just loads from after scope that can >> be caught, a store overwrites the variable with a new value and when turning >> the store after we make the var no longer addressable into SSA form, we >> loose information about the out of scope store. Furthermore, if there is >> first a store and then a read, like: >> if (argc != 12312) >> { >> char my_char; >> ptr = &my_char; >> } >> *ptr = i + 26; >> return *ptr; >> we don't notice even the read. Not sure what could be done against that >> though. I think we'd need to hook into the into-ssa framework, there it >> should know the current value of the variable at the point of the store is >> result of ASAN_POISON and be able to instead of turning that >> my_char = _23; >> into >> my_char_35 = _23; >> turn it into: >> my_char_35 = ASAN_POISON (_23); >> which would represent after scope store into my_char. >> >> Not really familiar with into-ssa though to know where to do it. >> >> Jakub >> > > Richi, may I ask you for help with this question? Probably where we handle the CLOBBER case (rewrite_stmt, maybe_register_def), we do this for -Wuninitialized. Richard. > Thanks, > Martin >
On 12/02/2016 01:29 PM, Richard Biener wrote: > On Thu, Dec 1, 2016 at 5:30 PM, Martin Liška <mliska@suse.cz> wrote: >> On 11/23/2016 03:13 PM, Jakub Jelinek wrote: >>> On Wed, Nov 23, 2016 at 02:57:07PM +0100, Martin Liška wrote: >>>> I started review process in libsanitizer: https://reviews.llvm.org/D26965 >>>> And I have a question that was asked in the review: can we distinguish between load and store >>>> in case of having usage of ASAN_POISON? >>> >>> I think with ASAN_POISON it is indeed just loads from after scope that can >>> be caught, a store overwrites the variable with a new value and when turning >>> the store after we make the var no longer addressable into SSA form, we >>> loose information about the out of scope store. Furthermore, if there is >>> first a store and then a read, like: >>> if (argc != 12312) >>> { >>> char my_char; >>> ptr = &my_char; >>> } >>> *ptr = i + 26; >>> return *ptr; >>> we don't notice even the read. Not sure what could be done against that >>> though. I think we'd need to hook into the into-ssa framework, there it >>> should know the current value of the variable at the point of the store is >>> result of ASAN_POISON and be able to instead of turning that >>> my_char = _23; >>> into >>> my_char_35 = _23; >>> turn it into: >>> my_char_35 = ASAN_POISON (_23); >>> which would represent after scope store into my_char. >>> >>> Not really familiar with into-ssa though to know where to do it. >>> >>> Jakub >>> >> >> Richi, may I ask you for help with this question? > > Probably where we handle the CLOBBER case (rewrite_stmt, maybe_register_def), > we do this for -Wuninitialized. > > Richard. Thanks for the tip, however as the optimization of memory address store + load happens before we rewrite my_char into SSA, it would be probably hard to guess which memory stores and loads should be preserved: use-after-scope-20.c.032t.ccp1: main (int argc, char * * argv) { int my_char; int * ptr; int _1; int _11; <bb 2> [0.0%]: if (argc_4(D) != 12312) goto <bb 3>; [0.0%] else goto <bb 4>; [0.0%] <bb 3> [0.0%]: ASAN_MARK (2, &my_char, 4); ptr_8 = &my_char; ASAN_MARK (1, &my_char, 4); <bb 4> [0.0%]: # ptr_2 = PHI <ptr_5(D)(2), ptr_8(3)> _1 = argc_4(D) + 26; *ptr_2 = _1; _11 = *ptr_2; return _11; } I sent updated version of patch to LLVM phabricator: https://reviews.llvm.org/D26965 Hopefully we can cherry pick the patch very soon to our trunk. M. > >> Thanks, >> Martin >>
On Thu, Dec 8, 2016 at 1:51 PM, Martin Liška <mliska@suse.cz> wrote: > On 12/02/2016 01:29 PM, Richard Biener wrote: >> On Thu, Dec 1, 2016 at 5:30 PM, Martin Liška <mliska@suse.cz> wrote: >>> On 11/23/2016 03:13 PM, Jakub Jelinek wrote: >>>> On Wed, Nov 23, 2016 at 02:57:07PM +0100, Martin Liška wrote: >>>>> I started review process in libsanitizer: https://reviews.llvm.org/D26965 >>>>> And I have a question that was asked in the review: can we distinguish between load and store >>>>> in case of having usage of ASAN_POISON? >>>> >>>> I think with ASAN_POISON it is indeed just loads from after scope that can >>>> be caught, a store overwrites the variable with a new value and when turning >>>> the store after we make the var no longer addressable into SSA form, we >>>> loose information about the out of scope store. Furthermore, if there is >>>> first a store and then a read, like: >>>> if (argc != 12312) >>>> { >>>> char my_char; >>>> ptr = &my_char; >>>> } >>>> *ptr = i + 26; >>>> return *ptr; >>>> we don't notice even the read. Not sure what could be done against that >>>> though. I think we'd need to hook into the into-ssa framework, there it >>>> should know the current value of the variable at the point of the store is >>>> result of ASAN_POISON and be able to instead of turning that >>>> my_char = _23; >>>> into >>>> my_char_35 = _23; >>>> turn it into: >>>> my_char_35 = ASAN_POISON (_23); >>>> which would represent after scope store into my_char. >>>> >>>> Not really familiar with into-ssa though to know where to do it. >>>> >>>> Jakub >>>> >>> >>> Richi, may I ask you for help with this question? >> >> Probably where we handle the CLOBBER case (rewrite_stmt, maybe_register_def), >> we do this for -Wuninitialized. >> >> Richard. > > Thanks for the tip, however as the optimization of memory address store + load happens > before we rewrite my_char into SSA, it would be probably hard to guess which memory > stores and loads should be preserved: > > use-after-scope-20.c.032t.ccp1: > main (int argc, char * * argv) > { > int my_char; > int * ptr; > int _1; > int _11; > > <bb 2> [0.0%]: > if (argc_4(D) != 12312) > goto <bb 3>; [0.0%] > else > goto <bb 4>; [0.0%] > > <bb 3> [0.0%]: > ASAN_MARK (2, &my_char, 4); > ptr_8 = &my_char; > ASAN_MARK (1, &my_char, 4); > > <bb 4> [0.0%]: > # ptr_2 = PHI <ptr_5(D)(2), ptr_8(3)> > _1 = argc_4(D) + 26; > *ptr_2 = _1; > _11 = *ptr_2; > return _11; > > } The SSA renamer sees my_char = ASAN_MARK; ptr_8 = &my_char; my_char = ASAN_MARK; ? It does perform a DOM walk when updating the stmts so simply registering the appropriate current def should do the trick? > I sent updated version of patch to LLVM phabricator: > https://reviews.llvm.org/D26965 > > Hopefully we can cherry pick the patch very soon to our trunk. > > M. > >> >>> Thanks, >>> Martin >>> >
From 9505c31813f224b855c5b2fab6c157e99ce54e59 Mon Sep 17 00:00:00 2001 From: marxin <mliska@suse.cz> Date: Mon, 14 Nov 2016 16:49:05 +0100 Subject: [PATCH] use-after-scope: introduce ASAN_POISON internal fn gcc/ChangeLog: 2016-11-16 Martin Liska <mliska@suse.cz> * asan.c (asan_expand_poison_ifn): New function. * asan.h (asan_expand_poison_ifn): Declare the function. * internal-fn.c (expand_ASAN_POISON): New function. * internal-fn.def (ASAN_POISON): New internal fn. * sanitizer.def (BUILT_IN_ASAN_REPORT_USE_AFTER_SCOPE_NOABORT): New built-in. (BUILT_IN_ASAN_REPORT_USE_AFTER_SCOPE): Likewise. * sanopt.c (pass_sanopt::execute): Expand IFN_ASAN_POISON. * tree-ssa.c (is_asan_mark_p): New function. (execute_update_addresses_taken): Make local variables as not addressable if address of these varibles is just taken by ASAN_MARK. gcc/testsuite/ChangeLog: 2016-11-16 Martin Liska <mliska@suse.cz> * gcc.dg/asan/use-after-scope-3.c: Run just with -O0. * gcc.dg/asan/use-after-scope-9.c: Run just with -O2 and change expected output. --- gcc/asan.c | 72 ++++++++++++++++++++++++++- gcc/asan.h | 1 + gcc/internal-fn.c | 7 +++ gcc/internal-fn.def | 1 + gcc/sanitizer.def | 8 +++ gcc/sanopt.c | 9 ++++ gcc/testsuite/gcc.dg/asan/use-after-scope-3.c | 1 + gcc/testsuite/gcc.dg/asan/use-after-scope-9.c | 6 +-- gcc/tree-ssa.c | 71 ++++++++++++++++++++++---- libsanitizer/asan/asan_errors.cc | 21 ++++++++ libsanitizer/asan/asan_errors.h | 19 +++++++ libsanitizer/asan/asan_report.cc | 10 ++++ libsanitizer/asan/asan_report.h | 3 ++ libsanitizer/asan/asan_rtl.cc | 16 ++++++ 14 files changed, 231 insertions(+), 14 deletions(-) diff --git a/gcc/asan.c b/gcc/asan.c index 6e93ea3..3a8e07d 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" @@ -2979,6 +2979,76 @@ asan_expand_check_ifn (gimple_stmt_iterator *iter, bool use_calls) return true; } + +/* Expand the ASAN_POISON builtins. */ + +bool +asan_expand_poison_ifn (gimple_stmt_iterator *iter, + bool *need_commit_edge_insert) +{ + gimple *g = gsi_stmt (*iter); + tree poisoned_var = gimple_call_lhs (g); + if (!poisoned_var) + { + gsi_remove (iter, true); + return true; + } + + tree var_decl = SSA_NAME_VAR (poisoned_var); + + 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; + + 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; + + built_in_function b = (recover_p + ? BUILT_IN_ASAN_REPORT_USE_AFTER_SCOPE_NOABORT + : BUILT_IN_ASAN_REPORT_USE_AFTER_SCOPE); + tree fun = builtin_decl_implicit (b); + pretty_printer pp; + pp_tree_identifier (&pp, DECL_NAME (var_decl)); + + gcall *call = gimple_build_call (fun, 2, asan_pp_string (&pp), + DECL_SIZE_UNIT (var_decl)); + gimple_set_location (call, gimple_location (use)); + + /* 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); + gsi_insert_seq_on_edge (e, call); + *need_commit_edge_insert = true; + } + } + else + { + gimple_stmt_iterator gsi = gsi_for_stmt (use); + gsi_insert_before (&gsi, call, GSI_NEW_STMT); + } + } + + gimple *nop = gimple_build_nop (); + SSA_NAME_IS_DEFAULT_DEF (poisoned_var) = true; + SSA_NAME_DEF_STMT (poisoned_var) = nop; + gsi_replace (iter, nop, GSI_NEW_STMT); + + return false; +} + /* Instrument the current function. */ static unsigned int diff --git a/gcc/asan.h b/gcc/asan.h index 9cf5904..6c25955 100644 --- a/gcc/asan.h +++ b/gcc/asan.h @@ -30,6 +30,7 @@ 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 *); 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 ca347c5..17624e8 100644 --- a/gcc/internal-fn.c +++ b/gcc/internal-fn.c @@ -246,6 +246,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 d1cd1a5..9454afd 100644 --- a/gcc/internal-fn.def +++ b/gcc/internal-fn.def @@ -159,6 +159,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/sanitizer.def b/gcc/sanitizer.def index 3db08a7..068c55b 100644 --- a/gcc/sanitizer.def +++ b/gcc/sanitizer.def @@ -102,6 +102,14 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_REPORT_STORE_N_NOABORT, "__asan_report_store_n_noabort", BT_FN_VOID_PTR_PTRMODE, ATTR_TMPURE_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_REPORT_USE_AFTER_SCOPE, + "__asan_report_use_after_scope", + BT_FN_VOID_PTR_PTRMODE, + ATTR_TMPURE_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_REPORT_USE_AFTER_SCOPE_NOABORT, + "__asan_report_use_after_scope_noabort", + BT_FN_VOID_PTR_PTRMODE, + ATTR_TMPURE_NOTHROW_LEAF_LIST) DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_LOAD1, "__asan_load1", BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST) DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_LOAD2, "__asan_load2", diff --git a/gcc/sanopt.c b/gcc/sanopt.c index 320e14e..77307d9 100644 --- a/gcc/sanopt.c +++ b/gcc/sanopt.c @@ -698,6 +698,7 @@ pass_sanopt::execute (function *fun) bool use_calls = ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD < INT_MAX && asan_num_accesses >= ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD; + bool need_commit_edge_insert = false; FOR_EACH_BB_FN (bb, fun) { gimple_stmt_iterator gsi; @@ -735,6 +736,10 @@ 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); + break; default: break; } @@ -766,6 +771,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 9aeed51..8b11bea 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 2e30def..10d7fb5 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" } int main (int argc, char **argv) @@ -15,6 +16,5 @@ main (int argc, char **argv) return *ptr; } -// { 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.*" } +// { dg-output "ERROR: AddressSanitizer: stack-use-after-scope at pc.*(\n|\r\n|\r)" } +// { dg-output "ACCESS of size .* for variable 'a'" } diff --git a/gcc/tree-ssa.c b/gcc/tree-ssa.c index 135952b..f253d7e 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; @@ -1550,6 +1551,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 @@ -1575,17 +1600,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. */ @@ -1841,6 +1872,26 @@ 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))) + { + HOST_WIDE_INT flags + = tree_to_shwi (gimple_call_arg (stmt, 0)); + unlink_stmt_vdef (stmt); + if (flags & ASAN_MARK_CLOBBER) + { + 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); diff --git a/libsanitizer/asan/asan_errors.cc b/libsanitizer/asan/asan_errors.cc index 73c4cca..f17c9b7 100644 --- a/libsanitizer/asan/asan_errors.cc +++ b/libsanitizer/asan/asan_errors.cc @@ -279,6 +279,27 @@ void ErrorInvalidPointerPair::Print() { ReportErrorSummary(bug_type, &stack); } +void ErrorUseAfterScope::Print() { + const char *bug_type = "stack-use-after-scope"; + Decorator d; + Printf("%s", d.Warning()); + + Report("ERROR: AddressSanitizer: stack-use-after-scope at pc %p bp %p sp %p\n", + variable_name, variable_size, pc, bp, sp); + Printf("%s", d.EndWarning()); + scariness.Print(); + + char tname[128]; + Printf("ACCESS of size %zu for variable '%s' thread T%d%s%s\n", + variable_size, variable_name, tid, + ThreadNameWithParenthesis(tid, tname, sizeof(tname)), d.EndAccess()); + + GET_STACK_TRACE_FATAL(pc, bp); + stack.Print(); + ReportErrorSummary(bug_type, &stack); +} + + static bool AdjacentShadowValuesAreFullyPoisoned(u8 *s) { return s[-1] > 127 && s[1] > 127; } diff --git a/libsanitizer/asan/asan_errors.h b/libsanitizer/asan/asan_errors.h index 6262dcf..a843859 100644 --- a/libsanitizer/asan/asan_errors.h +++ b/libsanitizer/asan/asan_errors.h @@ -294,6 +294,24 @@ struct ErrorInvalidPointerPair : ErrorBase { void Print(); }; +struct ErrorUseAfterScope : ErrorBase { + uptr pc, bp, sp; + const char *variable_name; + uptr variable_size; + // VS2013 doesn't implement unrestricted unions, so we need a trivial default + // constructor + ErrorUseAfterScope() = default; + ErrorUseAfterScope(u32 tid, uptr pc_, uptr bp_, uptr sp_, + const char *variable_name_, uptr variable_size_) + : ErrorBase(tid), + pc(pc_), + bp(bp_), + sp(sp_), + variable_name(variable_name_), + variable_size(variable_size_) {} + void Print(); +}; + struct ErrorGeneric : ErrorBase { AddressDescription addr_description; uptr pc, bp, sp; @@ -324,6 +342,7 @@ struct ErrorGeneric : ErrorBase { macro(BadParamsToAnnotateContiguousContainer) \ macro(ODRViolation) \ macro(InvalidPointerPair) \ + macro(UseAfterScope) \ macro(Generic) // clang-format on diff --git a/libsanitizer/asan/asan_report.cc b/libsanitizer/asan/asan_report.cc index 84d6764..db39d42 100644 --- a/libsanitizer/asan/asan_report.cc +++ b/libsanitizer/asan/asan_report.cc @@ -353,6 +353,16 @@ static INLINE void CheckForInvalidPointerPair(void *p1, void *p2) { return ReportInvalidPointerPair(pc, bp, sp, a1, a2); } } +// ----------------------- ReportUseAfterScope ----------- {{{1 +void ReportUseAfterScope(const char *variable_name, uptr variable_size, + bool fatal) { + ScopedInErrorReport in_report (fatal); + GET_CALLER_PC_BP_SP; + ErrorUseAfterScope error(GetCurrentTidOrInvalid(), pc, bp, sp, variable_name, + variable_size); + in_report.ReportError(error); +} + // ----------------------- Mac-specific reports ----------------- {{{1 void ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr, const char *zone_name, diff --git a/libsanitizer/asan/asan_report.h b/libsanitizer/asan/asan_report.h index 111b840..2fa158a 100644 --- a/libsanitizer/asan/asan_report.h +++ b/libsanitizer/asan/asan_report.h @@ -68,6 +68,9 @@ void ReportBadParamsToAnnotateContiguousContainer(uptr beg, uptr end, void ReportODRViolation(const __asan_global *g1, u32 stack_id1, const __asan_global *g2, u32 stack_id2); +void ReportUseAfterScope(const char *variable_name, uptr variable_size, + bool fatal); + // Mac-specific errors and warnings. void ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr, const char *zone_name, diff --git a/libsanitizer/asan/asan_rtl.cc b/libsanitizer/asan/asan_rtl.cc index 38009d2..f637d71 100644 --- a/libsanitizer/asan/asan_rtl.cc +++ b/libsanitizer/asan/asan_rtl.cc @@ -253,6 +253,22 @@ void __asan_storeN_noabort(uptr addr, uptr size) { } } +#include <stdio.h> + +extern "C" +NOINLINE INTERFACE_ATTRIBUTE +void __asan_report_use_after_scope(const char *variable_name, + uptr variable_size) { + ReportUseAfterScope(variable_name, variable_size, true); +} + +extern "C" +NOINLINE INTERFACE_ATTRIBUTE +void __asan_report_use_after_scope_noabort(const char *variable_name, + uptr variable_size) { + ReportUseAfterScope(variable_name, variable_size, false); +} + // Force the linker to keep the symbols for various ASan interface functions. // We want to keep those in the executable in order to let the instrumented // dynamic libraries access the symbol even if it is not used by the executable -- 2.10.1