+2017-01-23 Jakub Jelinek <jakub@redhat.com>
+ Martin Liska <mliska@suse.cz>
+
+ * asan.h: Define ASAN_USE_AFTER_SCOPE_ATTRIBUTE.
+ * 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.
+
2017-01-23 Martin Liska <mliska@suse.cz>
* asan.c (create_asan_shadow_var): New function.
return *slot;
}
+/* Expand ASAN_POISON ifn. */
+
bool
asan_expand_poison_ifn (gimple_stmt_iterator *iter,
bool *need_commit_edge_insert,
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)
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,
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);
}
}
#define ASAN_STACK_FRAME_MAGIC 0x41b58ab3
#define ASAN_STACK_RETIRED_MAGIC 0x45e0360e
+#define ASAN_USE_AFTER_SCOPE_ATTRIBUTE "use after scope memory"
+
/* Various flags for Asan builtins. */
enum asan_check_flags
{
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. */
+ if (!lookup_attribute (ASAN_USE_AFTER_SCOPE_ATTRIBUTE,
+ DECL_ATTRIBUTES (var)))
+ DECL_ATTRIBUTES (var)
+ = tree_cons (get_identifier (ASAN_USE_AFTER_SCOPE_ATTRIBUTE),
+ integer_one_node,
+ DECL_ATTRIBUTES (var));
+ }
}
/* Gimplify a BIND_EXPR. Just voidify and recurse. */
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
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)
+2017-01-23 Martin Liska <mliska@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.
+
2017-01-23 Martin Liska <mliska@suse.cz>
* gcc.dg/asan/use-after-scope-3.c: Add additional flags.
--- /dev/null
+// { 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();
+}
--- /dev/null
+// { 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.*" }
--- /dev/null
+// { 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;
+}
#include "tree-ssa.h"
#include "domwalk.h"
#include "statistics.h"
+#include "asan.h"
#define PERCENT(x,y) ((float)(x) * 100.0 / (float)(y))
}
+/* 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
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);
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))
&& VAR_P (TREE_OPERAND (addr, 0)))
{
tree var = TREE_OPERAND (addr, 0);
+ if (lookup_attribute (ASAN_USE_AFTER_SCOPE_ATTRIBUTE,
+ DECL_ATTRIBUTES (var)))
+ return false;
+
unsigned addressable = TREE_ADDRESSABLE (var);
TREE_ADDRESSABLE (var) = 0;
bool r = is_gimple_reg (var);
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;
}
}