From 98b3e4fa69221f3c4539c2fe521deb45658c97aa Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Tue, 3 May 2016 15:35:22 +0200
Subject: [PATCH 1/2] Introduce -fsanitize=use-after-scope
gcc/ChangeLog:
2016-05-10 Martin Liska <mliska@suse.cz>
* asan.c (enum asan_check_flags): Enum is moved to a header
file.
(asan_init_shadow_ptr_types): Add new shadow_ptr_type.
(get_shadow_memory_size): New function.
(asan_poison_stack_variables): Likewise.
(asan_emit_stack_protection): Poison and unpoison stack
variables.
(build_shadow_mem_access): New arguments is added.
(instrument_derefs): Do not skip automatic variables if we
instrument for use-after-scope.
(asan_store_shadow_bytes): New function.
(asan_expand_mark_ifn): Likewise.
* asan.h (enum asan_mark_flags): Move from the source file.
(asan_sanitize_use_after_scope): New function.
(asan_no_sanitize_address_p): Likewise.
* builtins.c: Include asan.h file.
* cfgexpand.c (asan_sanitize_stack_p): Use
asan_no_sanitize_address_p.
* flag-types.h (enum sanitize_code): Add
SANITIZE_ADDRESS_USE_AFTER_SCOPE enum value.
* gimplify.c (asan_poison_variable): New function.
(gimplify_bind_expr): Unpoison stack variables.
(gimplify_decl_expr): Poison stack variables.
(gimplify_expr): Unpoison stack variables that precede a label.
(gimplify_function_tree): Guard that all local variables
(sort_by_decl_uid): New function.
are properly unpoisoned.
* internal-fn.c (expand_ASAN_MARK): New function.
* internal-fn.def: Declare ASAN_MARK.
* opts-global.c: Include asan.h header file.
* opts.c (finish_options): Handle interaction between -fstack-reuse
and -fsanitize=use-after-scope.
* params.def: Add new param use-after-scope-direct-emission-threshold.
* params.h: Define the param.
* sancov.c: Include asan.h header file.
* sanitizer.def: Define __asan_poison_stack_memory and
__asan_unpoison_stack_memory.
* sanopt.c (pass_sanopt::execute): Expand ASAN_MARK internal
fns.
* tree-streamer-in.c: Include asan.h header file.
* tsan.c: Likewise.
* ubsan.c: Likewise.
* varasm.c: Likewise.
* hash-set.h (hash_set::empty): New function.
---
gcc/asan.c | 258 ++++++++++++++++++++++++++++++++++++++++++++-----
gcc/asan.h | 52 +++++++++-
gcc/builtins.c | 1 +
gcc/c-family/c-ubsan.c | 1 +
gcc/cfgexpand.c | 3 +-
gcc/cp/decl2.c | 1 +
gcc/flag-types.h | 5 +-
gcc/gimplify.c | 118 ++++++++++++++++++++--
gcc/hash-set.h | 7 ++
gcc/internal-fn.c | 9 ++
gcc/internal-fn.def | 1 +
gcc/opts-global.c | 1 +
gcc/opts.c | 14 +++
gcc/params.def | 6 ++
gcc/params.h | 2 +
gcc/sancov.c | 1 +
gcc/sanitizer.def | 4 +
gcc/sanopt.c | 5 +-
gcc/tree-streamer-in.c | 1 +
gcc/tsan.c | 1 +
gcc/ubsan.c | 1 +
gcc/varasm.c | 1 +
22 files changed, 449 insertions(+), 44 deletions(-)
@@ -45,6 +45,7 @@ along with GCC; see the file COPYING3. If not see
#include "varasm.h"
#include "stor-layout.h"
#include "tree-iterator.h"
+#include "params.h"
#include "asan.h"
#include "dojump.h"
#include "explow.h"
@@ -54,7 +55,6 @@ along with GCC; see the file COPYING3. If not see
#include "cfgloop.h"
#include "gimple-builder.h"
#include "ubsan.h"
-#include "params.h"
#include "builtins.h"
#include "fnmatch.h"
@@ -243,6 +243,11 @@ static unsigned HOST_WIDE_INT asan_shadow_offset_value;
static bool asan_shadow_offset_computed;
static vec<char *> sanitized_sections;
+/* Set of variable declarations that are going to be guarded by
+ use-after-scope sanitizer. */
+
+static hash_set <tree> asan_handled_variables(13);
+
/* Sets shadow offset to value in string VAL. */
bool
@@ -313,22 +318,13 @@ asan_shadow_offset ()
alias_set_type asan_shadow_set = -1;
-/* Pointer types to 1 resp. 2 byte integers in shadow memory. A separate
+/* Pointer types to 1, 2 or 4 byte integers in shadow memory. A separate
alias set is used for all shadow memory accesses. */
-static GTY(()) tree shadow_ptr_types[2];
+static GTY(()) tree shadow_ptr_types[3];
/* Decl for __asan_option_detect_stack_use_after_return. */
static GTY(()) tree asan_detect_stack_use_after_return;
-/* Various flags for Asan builtins. */
-enum asan_check_flags
-{
- ASAN_CHECK_STORE = 1 << 0,
- ASAN_CHECK_SCALAR_ACCESS = 1 << 1,
- ASAN_CHECK_NON_ZERO_LEN = 1 << 2,
- ASAN_CHECK_LAST = 1 << 3
-};
-
/* Hashtable support for memory references used by gimple
statements. */
@@ -931,12 +927,16 @@ static void
asan_init_shadow_ptr_types (void)
{
asan_shadow_set = new_alias_set ();
- shadow_ptr_types[0] = build_distinct_type_copy (signed_char_type_node);
- TYPE_ALIAS_SET (shadow_ptr_types[0]) = asan_shadow_set;
- shadow_ptr_types[0] = build_pointer_type (shadow_ptr_types[0]);
- shadow_ptr_types[1] = build_distinct_type_copy (short_integer_type_node);
- TYPE_ALIAS_SET (shadow_ptr_types[1]) = asan_shadow_set;
- shadow_ptr_types[1] = build_pointer_type (shadow_ptr_types[1]);
+ tree types[3] = { signed_char_type_node, short_integer_type_node,
+ integer_type_node };
+
+ for (unsigned i = 0; i < 3; i++)
+ {
+ shadow_ptr_types[i] = build_distinct_type_copy (types[i]);
+ TYPE_ALIAS_SET (shadow_ptr_types[i]) = asan_shadow_set;
+ shadow_ptr_types[i] = build_pointer_type (shadow_ptr_types[i]);
+ }
+
initialize_sanitizer_builtins ();
}
@@ -1020,6 +1020,91 @@ asan_function_start (void)
current_function_funcdef_no);
}
+/* Return number of shadow bytes that are occupied by a local variable
+ of SIZE bytes. */
+
+static unsigned HOST_WIDE_INT
+get_shadow_memory_size (unsigned HOST_WIDE_INT size)
+{
+ /* Round up size of object. */
+ unsigned HOST_WIDE_INT r;
+ if ((r = size % BITS_PER_UNIT) != 0)
+ size += BITS_PER_UNIT - r;
+
+ return size / BITS_PER_UNIT;
+}
+
+/* Depending on POISON flag, emit a call to poison (or unpoison) stack memory
+ allocated for local variables, localted in OFFSETS. LENGTH is number
+ of OFFSETS, BASE is the register holding the stack base,
+ against which OFFSETS array offsets are relative to. BASE_OFFSET represents
+ an offset requested by alignment and similar stuff. */
+
+static void
+asan_poison_stack_variables (rtx shadow_base, rtx base,
+ HOST_WIDE_INT base_offset,
+ HOST_WIDE_INT *offsets, int length,
+ tree *decls, bool poison)
+{
+ if (asan_sanitize_use_after_scope ())
+ for (int l = length - 2; l > 0; l -= 2)
+ {
+ tree decl = decls[l / 2 - 1];
+ HOST_WIDE_INT var_offset = offsets[l];
+ HOST_WIDE_INT var_end_offset = offsets[l - 1];
+ if (!asan_handled_variables.contains (decl))
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Skipping stack emission for variable: %s "
+ "(%ldB)\n",
+ IDENTIFIER_POINTER (DECL_NAME (decl)),
+ var_end_offset - var_offset);
+ continue;
+ }
+
+ rtx source = expand_binop (Pmode, add_optab, base,
+ gen_int_mode
+ (var_offset - base_offset, Pmode),
+ NULL_RTX, 1, OPTAB_DIRECT);
+
+ HOST_WIDE_INT size = var_end_offset - var_offset;
+ if (size <= ASAN_PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD)
+ {
+ unsigned HOST_WIDE_INT shadow_size
+ = get_shadow_memory_size (size);
+
+ rtx shadow_mem = gen_rtx_MEM (SImode, shadow_base);
+ rtx var_mem = adjust_address (shadow_mem, QImode,
+ (var_offset - base_offset)
+ >> ASAN_SHADOW_SHIFT);
+
+ char c = poison ? ASAN_STACK_MAGIC_USE_AFTER_SCOPE : 0;
+ for (unsigned i = 0; i < shadow_size; ++i)
+ {
+ emit_move_insn (var_mem, gen_int_mode (c, QImode));
+ var_mem = adjust_address (var_mem, QImode, 1);
+ }
+ }
+ else
+ {
+ rtx size_rtx = GEN_INT (size);
+ const char *fname = poison ? "__asan_poison_stack_memory"
+ :"__asan_unpoison_stack_memory";
+ rtx ret = init_one_libfunc (fname);
+ emit_library_call (ret, LCT_NORMAL, VOIDmode, 2, source, ptr_mode,
+ size_rtx, TYPE_MODE (pointer_sized_int_node));
+ }
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Emitting stack %s for variable: %s"
+ "(%ldB)\n",
+ poison ? "poisoning" : "unpoisoning",
+ IDENTIFIER_POINTER (DECL_NAME (decl)),
+ var_end_offset - var_offset);
+ }
+}
+
+
/* Insert code to protect stack vars. The prologue sequence should be emitted
directly, epilogue sequence returned. BASE is the register holding the
stack base, against which OFFSETS array offsets are relative to, OFFSETS
@@ -1228,6 +1313,11 @@ asan_emit_stack_protection (rtx base, rtx pbase, unsigned int alignb,
}
cur_shadow_byte = ASAN_STACK_MAGIC_MIDDLE;
}
+
+ /* Poison stack variables at the very beginning of a function. */
+ asan_poison_stack_variables (shadow_base, base, base_offset, offsets, length,
+ decls, true);
+
do_pending_stack_adjust ();
/* Construct epilogue sequence. */
@@ -1309,6 +1399,14 @@ asan_emit_stack_protection (rtx base, rtx pbase, unsigned int alignb,
asan_clear_shadow (shadow_mem, last_size >> ASAN_SHADOW_SHIFT);
}
+ /* Unpoison stack variables at the end of a function. As the former
+ stack memory can be reused, we have to unpoison the memory. */
+ asan_poison_stack_variables (shadow_base, base, base_offset, offsets, length,
+ decls, false);
+
+ /* Clean-up set with instrumented stack variables. */
+ asan_handled_variables.empty ();
+
do_pending_stack_adjust ();
if (lab)
emit_label (lab);
@@ -1593,7 +1691,8 @@ insert_if_then_before_iter (gcond *cond,
static tree
build_shadow_mem_access (gimple_stmt_iterator *gsi, location_t location,
- tree base_addr, tree shadow_ptr_type)
+ tree base_addr, tree shadow_ptr_type,
+ bool return_ptr = false)
{
tree t, uintptr_type = TREE_TYPE (base_addr);
tree shadow_type = TREE_TYPE (shadow_ptr_type);
@@ -1616,11 +1715,15 @@ build_shadow_mem_access (gimple_stmt_iterator *gsi, location_t location,
gimple_set_location (g, location);
gsi_insert_after (gsi, g, GSI_NEW_STMT);
- t = build2 (MEM_REF, shadow_type, gimple_assign_lhs (g),
- build_int_cst (shadow_ptr_type, 0));
- g = gimple_build_assign (make_ssa_name (shadow_type), MEM_REF, t);
- gimple_set_location (g, location);
- gsi_insert_after (gsi, g, GSI_NEW_STMT);
+ if (!return_ptr)
+ {
+ t = build2 (MEM_REF, shadow_type, gimple_assign_lhs (g),
+ build_int_cst (shadow_ptr_type, 0));
+ g = gimple_build_assign (make_ssa_name (shadow_type), MEM_REF, t);
+ gimple_set_location (g, location);
+ gsi_insert_after (gsi, g, GSI_NEW_STMT);
+ }
+
return gimple_assign_lhs (g);
}
@@ -1824,7 +1927,8 @@ instrument_derefs (gimple_stmt_iterator *iter, tree t,
{
/* Automatic vars in the current function will be always
accessible. */
- if (decl_function_context (inner) == current_function_decl)
+ if (decl_function_context (inner) == current_function_decl
+ && !asan_sanitize_use_after_scope ())
return;
}
/* Always instrument external vars, they might be dynamically
@@ -2573,6 +2677,110 @@ asan_finish_file (void)
flag_sanitize |= SANITIZE_ADDRESS;
}
+static void
+asan_store_shadow_bytes (gimple_stmt_iterator *iter, location_t loc,
+ tree shadow,
+ unsigned HOST_WIDE_INT base_addr_offset,
+ unsigned char value, unsigned bytes)
+{
+ tree shadow_ptr_type;
+
+ switch (bytes)
+ {
+ case 1:
+ shadow_ptr_type = shadow_ptr_types[0];
+ break;
+ case 2:
+ shadow_ptr_type = shadow_ptr_types[1];
+ break;
+ case 4:
+ shadow_ptr_type = shadow_ptr_types[2];
+ break;
+ default:
+ gcc_unreachable ();
+ }
+
+ unsigned HOST_WIDE_INT val = 0;
+ for (unsigned i = 0; i < bytes; ++i)
+ val |= (unsigned HOST_WIDE_INT) value << (BITS_PER_UNIT * i);
+
+ tree magic = build_int_cst (TREE_TYPE (shadow_ptr_type), val);
+
+ tree dest = build2 (MEM_REF, TREE_TYPE (shadow_ptr_type), shadow,
+ build_int_cst (shadow_ptr_type, base_addr_offset));
+
+ gimple *g = gimple_build_assign (dest, magic);
+ gimple_set_location (g, loc);
+ gsi_insert_after (iter, g, GSI_NEW_STMT);
+}
+
+/* Expand the ASAN_MARK builtins. */
+
+bool
+asan_expand_mark_ifn (gimple_stmt_iterator *iter)
+{
+ gimple *g = gsi_stmt (*iter);
+ location_t loc = gimple_location (g);
+ HOST_WIDE_INT flags = tree_to_shwi (gimple_call_arg (g, 0));
+ gcc_assert (flags < ASAN_MARK_LAST);
+ bool is_clobber = (flags & ASAN_MARK_CLOBBER) != 0;
+
+ tree base = gimple_call_arg (g, 1);
+ gcc_checking_assert (TREE_CODE (base) == ADDR_EXPR);
+ tree decl = TREE_OPERAND (base, 0);
+ gcc_checking_assert (TREE_CODE (decl) == VAR_DECL);
+ asan_handled_variables.add (decl);
+ tree len = gimple_call_arg (g, 2);
+
+ gcc_assert (tree_fits_shwi_p (len));
+ HOST_WIDE_INT size_in_bytes = tree_to_shwi (len);
+ gcc_assert (size_in_bytes);
+
+ g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
+ NOP_EXPR, base);
+ gimple_set_location (g, loc);
+ gsi_replace (iter, g, false);
+ tree base_addr = gimple_assign_lhs (g);
+
+ if (size_in_bytes <= ASAN_PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD)
+ {
+ unsigned HOST_WIDE_INT shadow_size
+ = get_shadow_memory_size (size_in_bytes);
+ char c = (char) is_clobber ? ASAN_STACK_MAGIC_USE_AFTER_SCOPE : 0;
+
+ tree shadow = build_shadow_mem_access (iter, loc, base_addr,
+ shadow_ptr_types[0], true);
+
+ for (unsigned HOST_WIDE_INT offset = 0; offset < shadow_size;)
+ {
+ unsigned size = 1;
+ if (shadow_size - offset >= 4)
+ size = 4;
+ else if (shadow_size - offset >= 2)
+ size = 2;
+
+ asan_store_shadow_bytes (iter, loc, shadow, offset, c, size);
+ offset += size;
+ }
+ }
+ else
+ {
+ g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
+ NOP_EXPR, len);
+ gimple_set_location (g, loc);
+ gsi_insert_before (iter, g, GSI_SAME_STMT);
+ tree sz_arg = gimple_assign_lhs (g);
+
+ tree fun = builtin_decl_implicit (is_clobber ? BUILT_IN_ASAN_CLOBBER_N
+ : BUILT_IN_ASAN_UNCLOBBER_N);
+ g = gimple_build_call (fun, 2, base_addr, sz_arg);
+ gimple_set_location (g, loc);
+ gsi_insert_after (iter, g, GSI_NEW_STMT);
+ }
+
+ return false;
+}
+
/* Expand the ASAN_{LOAD,STORE} builtins. */
bool
@@ -29,6 +29,7 @@ extern bool asan_protect_global (tree);
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 gimple_stmt_iterator create_cond_insert_point
(gimple_stmt_iterator *, bool, bool, bool, basic_block *, basic_block *);
@@ -36,6 +37,10 @@ extern gimple_stmt_iterator create_cond_insert_point
/* Alias set for accessing the shadow memory. */
extern alias_set_type asan_shadow_set;
+/* Hash set of inlined variables that cannot be poisoned by use-after-cope
+ sanitizer. */
+extern hash_set <tree> asan_inlined_variables;
+
/* Shadow memory is found at
(address >> ASAN_SHADOW_SHIFT) + asan_shadow_offset (). */
#define ASAN_SHADOW_SHIFT 3
@@ -50,15 +55,33 @@ extern alias_set_type asan_shadow_set;
the frame. Middle is for padding in between variables, right is
above the last protected variable and partial immediately after variables
up to ASAN_RED_ZONE_SIZE alignment. */
-#define ASAN_STACK_MAGIC_LEFT 0xf1
-#define ASAN_STACK_MAGIC_MIDDLE 0xf2
-#define ASAN_STACK_MAGIC_RIGHT 0xf3
-#define ASAN_STACK_MAGIC_PARTIAL 0xf4
-#define ASAN_STACK_MAGIC_USE_AFTER_RET 0xf5
+#define ASAN_STACK_MAGIC_LEFT 0xf1
+#define ASAN_STACK_MAGIC_MIDDLE 0xf2
+#define ASAN_STACK_MAGIC_RIGHT 0xf3
+#define ASAN_STACK_MAGIC_PARTIAL 0xf4
+#define ASAN_STACK_MAGIC_USE_AFTER_RET 0xf5
+#define ASAN_STACK_MAGIC_USE_AFTER_SCOPE 0xf8
#define ASAN_STACK_FRAME_MAGIC 0x41b58ab3
#define ASAN_STACK_RETIRED_MAGIC 0x45e0360e
+/* Various flags for Asan builtins. */
+enum asan_check_flags
+{
+ ASAN_CHECK_STORE = 1 << 0,
+ ASAN_CHECK_SCALAR_ACCESS = 1 << 1,
+ ASAN_CHECK_NON_ZERO_LEN = 1 << 2,
+ ASAN_CHECK_LAST = 1 << 3
+};
+
+/* Flags for Asan check builtins. */
+enum asan_mark_flags
+{
+ ASAN_MARK_CLOBBER = 1 << 0,
+ ASAN_MARK_UNCLOBBER = 1 << 1,
+ ASAN_MARK_LAST = 1 << 2
+};
+
/* Return true if DECL should be guarded on the stack. */
static inline bool
@@ -105,4 +128,23 @@ asan_intercepted_p (enum built_in_function fcode)
|| fcode == BUILT_IN_STRNCMP
|| fcode == BUILT_IN_STRNCPY;
}
+
+/* Return TRUE if we should instrument for use-after-scope sanity checking. */
+
+static inline bool
+asan_sanitize_use_after_scope (void)
+{
+ return ((flag_sanitize & SANITIZE_ADDRESS_USE_AFTER_SCOPE)
+ == SANITIZE_ADDRESS_USE_AFTER_SCOPE
+ && flag_stack_reuse == SR_NONE
+ && ASAN_STACK);
+}
+
+static inline bool
+asan_no_sanitize_address_p (void)
+{
+ return lookup_attribute ("no_sanitize_address",
+ DECL_ATTRIBUTES (current_function_decl));
+}
+
#endif /* TREE_ASAN */
@@ -58,6 +58,7 @@ along with GCC; see the file COPYING3. If not see
#include "langhooks.h"
#include "value-prof.h"
#include "builtins.h"
+#include "params.h"
#include "asan.h"
#include "cilk.h"
#include "tree-chkp.h"
@@ -25,6 +25,7 @@ along with GCC; see the file COPYING3. If not see
#include "c-family/c-common.h"
#include "ubsan.h"
#include "c-family/c-ubsan.h"
+#include "params.h"
#include "asan.h"
#include "stor-layout.h"
#include "builtins.h"
@@ -876,8 +876,7 @@ asan_sanitize_stack_p (void)
{
return ((flag_sanitize & SANITIZE_ADDRESS)
&& ASAN_STACK
- && !lookup_attribute ("no_sanitize_address",
- DECL_ATTRIBUTES (current_function_decl)));
+ && !asan_no_sanitize_address_p ());
}
/* A subroutine of expand_used_vars. Binpack the variables into
@@ -46,6 +46,7 @@ along with GCC; see the file COPYING3. If not see
#include "dumpfile.h"
#include "intl.h"
#include "c-family/c-ada-spec.h"
+#include "params.h"
#include "asan.h"
extern cpp_reader *parse_in;
@@ -229,6 +229,7 @@ enum sanitize_code {
SANITIZE_OBJECT_SIZE = 1UL << 20,
SANITIZE_VPTR = 1UL << 21,
SANITIZE_BOUNDS_STRICT = 1UL << 22,
+ SANITIZE_USE_AFTER_SCOPE = 1UL << 23,
SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE
| SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN
| SANITIZE_SI_OVERFLOW | SANITIZE_BOOL | SANITIZE_ENUM
@@ -237,7 +238,9 @@ enum sanitize_code {
| SANITIZE_RETURNS_NONNULL_ATTRIBUTE
| SANITIZE_OBJECT_SIZE | SANITIZE_VPTR,
SANITIZE_NONDEFAULT = SANITIZE_FLOAT_DIVIDE | SANITIZE_FLOAT_CAST
- | SANITIZE_BOUNDS_STRICT
+ | SANITIZE_BOUNDS_STRICT,
+ SANITIZE_ADDRESS_USE_AFTER_SCOPE = SANITIZE_ADDRESS
+ | SANITIZE_USE_AFTER_SCOPE
};
/* flag_vtable_verify initialization levels. */
@@ -59,6 +59,11 @@ along with GCC; see the file COPYING3. If not see
#include "gimple-walk.h"
#include "langhooks-def.h" /* FIXME: for lhd_set_decl_assembler_name */
#include "builtins.h"
+#include "params.h"
+#include "asan.h"
+
+/* Hash set of poisoned variables in a bind expr. */
+static hash_set <tree> asan_poisoned_variables (13);
enum gimplify_omp_var_data
{
@@ -1087,6 +1092,29 @@ build_stack_save_restore (gcall **save, gcall **restore)
1, tmp_var);
}
+/* Generate IFN_ASAN_MARK internal call that depending on POISON flag
+ either poisons or unpoisons a DECL. Created statement is appended
+ to SEQ_P gimple sequence. */
+
+static void
+asan_poison_variable (tree decl, bool poison, gimple_seq *seq_p)
+{
+ /* When within an OMP context, do not emit ASAN_MARK internal fns. */
+ if (gimplify_omp_ctxp)
+ return;
+
+ tree unit_size = DECL_SIZE_UNIT (decl);
+ tree base = build_fold_addr_expr (decl);
+
+ HOST_WIDE_INT flags = poison ? ASAN_MARK_CLOBBER : ASAN_MARK_UNCLOBBER;
+
+ gimple *g
+ = gimple_build_call_internal (IFN_ASAN_MARK, 3,
+ build_int_cst (integer_type_node, flags),
+ base, unit_size);
+ gimplify_seq_add_stmt (seq_p, g);
+}
+
/* Gimplify a BIND_EXPR. Just voidify and recurse. */
static enum gimplify_status
@@ -1189,6 +1217,11 @@ gimplify_bind_expr (tree *expr_p, gimple_seq *pre_p)
/* Add clobbers for all variables that go out of scope. */
for (t = BIND_EXPR_VARS (bind_expr); t ; t = DECL_CHAIN (t))
{
+ bool unpoison_var = asan_poisoned_variables.contains (t);
+ if (asan_sanitize_use_after_scope ()
+ && unpoison_var)
+ asan_poisoned_variables.remove (t);
+
if (TREE_CODE (t) == VAR_DECL
&& !is_global_var (t)
&& DECL_CONTEXT (t) == current_function_decl
@@ -1197,17 +1230,26 @@ gimplify_bind_expr (tree *expr_p, gimple_seq *pre_p)
&& !DECL_HAS_VALUE_EXPR_P (t)
/* Only care for variables that have to be in memory. Others
will be rewritten into SSA names, hence moved to the top-level. */
- && !is_gimple_reg (t)
- && flag_stack_reuse != SR_NONE)
+ && !is_gimple_reg (t))
{
- tree clobber = build_constructor (TREE_TYPE (t), NULL);
- gimple *clobber_stmt;
- TREE_THIS_VOLATILE (clobber) = 1;
- clobber_stmt = gimple_build_assign (t, clobber);
- gimple_set_location (clobber_stmt, end_locus);
- gimplify_seq_add_stmt (&cleanup, clobber_stmt);
-
- if (flag_openacc && oacc_declare_returns != NULL)
+ if (flag_stack_reuse != SR_NONE)
+ {
+ tree clobber = build_constructor (TREE_TYPE (t), NULL);
+ gimple *clobber_stmt;
+ TREE_THIS_VOLATILE (clobber) = 1;
+ clobber_stmt = gimple_build_assign (t, clobber);
+ gimple_set_location (clobber_stmt, end_locus);
+ gimplify_seq_add_stmt (&cleanup, clobber_stmt);
+ }
+
+ if (asan_sanitize_use_after_scope ()
+ && !asan_no_sanitize_address_p ()
+ && unpoison_var)
+ asan_poison_variable (t, true, &cleanup);
+
+ if (flag_stack_reuse != SR_NONE
+ && flag_openacc
+ && oacc_declare_returns != NULL)
{
tree *c = oacc_declare_returns->get (t);
if (c != NULL)
@@ -1479,6 +1521,22 @@ gimplify_decl_expr (tree *stmt_p, gimple_seq *seq_p)
STACK_CHECK_MAX_VAR_SIZE) > 0))
gimplify_vla_decl (decl, seq_p);
+ tree unit_size = DECL_SIZE_UNIT (decl);
+ if (asan_sanitize_use_after_scope ()
+ && !asan_no_sanitize_address_p ()
+ && TREE_CODE (unit_size) == INTEGER_CST
+ && needs_to_live_in_memory (decl)
+ && DECL_NAME (decl) != NULL
+ && IDENTIFIER_POINTER (DECL_NAME (decl)) != NULL
+ && !TREE_STATIC (decl))
+ {
+ TREE_ADDRESSABLE (decl) = 1;
+ DECL_GIMPLE_REG_P (decl) = 0;
+
+ asan_poisoned_variables.add (decl);
+ asan_poison_variable (decl, false, seq_p);
+ }
+
/* Some front ends do not explicitly declare all anonymous
artificial variables. We compensate here by declaring the
variables, though it would be better if the front ends would
@@ -10030,6 +10088,25 @@ gimplify_omp_ordered (tree expr, gimple_seq body)
return gimple_build_omp_ordered (body, OMP_ORDERED_CLAUSES (expr));
}
+/* Sort pair of VAR_DECLs A and B by DECL_UID. */
+
+static int
+sort_by_decl_uid (const void *a, const void *b)
+{
+ const tree *t1 = (const tree *)a;
+ const tree *t2 = (const tree *)b;
+
+ int uid1 = DECL_UID (*t1);
+ int uid2 = DECL_UID (*t2);
+
+ if (uid1 < uid2)
+ return -1;
+ else if (uid1 > uid2)
+ return 1;
+ else
+ return 0;
+}
+
/* Convert the GENERIC expression tree *EXPR_P to GIMPLE. If the
expression produces a value to be used as an operand inside a GIMPLE
statement, the value will be stored back in *EXPR_P. This value will
@@ -10540,6 +10617,23 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
== current_function_decl);
gimplify_seq_add_stmt (pre_p,
gimple_build_label (LABEL_EXPR_LABEL (*expr_p)));
+
+ if (asan_sanitize_use_after_scope ()
+ && !asan_no_sanitize_address_p ())
+ {
+ unsigned c = asan_poisoned_variables.elements ();
+ auto_vec<tree> sorted_variables (c);
+
+ for (hash_set <tree>::iterator it
+ = asan_poisoned_variables.begin ();
+ it != asan_poisoned_variables.end (); ++it)
+ sorted_variables.safe_push (*it);
+
+ sorted_variables.qsort (sort_by_decl_uid);
+
+ for (unsigned i = 0; i < sorted_variables.length (); ++i)
+ asan_poison_variable (sorted_variables[i], false, pre_p);
+ }
break;
case CASE_LABEL_EXPR:
@@ -11633,7 +11727,11 @@ gimplify_function_tree (tree fndecl)
&& !needs_to_live_in_memory (ret))
DECL_GIMPLE_REG_P (ret) = 1;
+ gcc_checking_assert (!asan_sanitize_use_after_scope ()
+ || asan_poisoned_variables.elements () == 0);
bind = gimplify_body (fndecl, true);
+ gcc_checking_assert (!asan_sanitize_use_after_scope ()
+ || asan_poisoned_variables.elements () == 0);
/* The tree body of the function is no longer needed, replace it
with the new GIMPLE body. */
@@ -65,6 +65,13 @@ public:
m_table.remove_elt_with_hash (k, Traits::hash (k));
}
+ /* Clear all elements of the hash set. */
+
+ void empty ()
+ {
+ m_table.empty ();
+ }
+
/* Call the call back on each pair of key and value with the passed in
arg. */
@@ -235,6 +235,15 @@ expand_ASAN_CHECK (internal_fn, gcall *)
gcc_unreachable ();
}
+/* This should get expanded in the sanopt pass. */
+
+static void
+expand_ASAN_MARK (internal_fn, gcall *)
+{
+ gcc_unreachable ();
+}
+
+
/* This should get expanded in the tsan pass. */
static void
@@ -158,6 +158,7 @@ DEF_INTERNAL_FN (UBSAN_OBJECT_SIZE, ECF_LEAF | ECF_NOTHROW, NULL)
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_TM_PURE | ECF_LEAF | ECF_NOTHROW, ".R..")
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)
@@ -35,6 +35,7 @@ along with GCC; see the file COPYING3. If not see
#include "plugin.h"
#include "toplev.h"
#include "context.h"
+#include "params.h"
#include "asan.h"
typedef const char *const_char_p; /* For DEF_VEC_P. */
@@ -972,6 +972,18 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
opts->x_flag_aggressive_loop_optimizations = 0;
opts->x_flag_strict_overflow = 0;
}
+
+ /* Force -fstack-reuse=none in case -fsanitize=use-after-scope is enabled. */
+ if (opts->x_flag_sanitize & SANITIZE_USE_AFTER_SCOPE)
+ {
+ if (opts->x_flag_stack_reuse != SR_NONE
+ && opts_set->x_flag_stack_reuse != SR_NONE)
+ error_at (loc,
+ "-fsanitize=use-after-scope requires "
+ "-fstack-reuse=none option");
+
+ opts->x_flag_stack_reuse = SR_NONE;
+ }
}
#define LEFT_COLUMN 27
@@ -1443,6 +1455,8 @@ const struct sanitizer_opts_s sanitizer_opts[] =
{
#define SANITIZER_OPT(name, flags) { #name, flags, sizeof #name - 1 }
SANITIZER_OPT (address, SANITIZE_ADDRESS | SANITIZE_USER_ADDRESS),
+ SANITIZER_OPT (use-after-scope, SANITIZE_ADDRESS | SANITIZE_USER_ADDRESS
+ | SANITIZE_USE_AFTER_SCOPE),
SANITIZER_OPT (kernel-address, SANITIZE_ADDRESS | SANITIZE_KERNEL_ADDRESS),
SANITIZER_OPT (thread, SANITIZE_THREAD),
SANITIZER_OPT (leak, SANITIZE_LEAK),
@@ -1144,6 +1144,12 @@ DEFPARAM (PARAM_ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD,
"in function becomes greater or equal to this number.",
7000, 0, INT_MAX)
+DEFPARAM (PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD,
+ "use-after-scope-direct-emission-threshold",
+ "Use direct poisoning/unpoisoning intructions for variables "
+ "smaller or equal to this number.",
+ 256, 0, INT_MAX)
+
DEFPARAM (PARAM_UNINIT_CONTROL_DEP_ATTEMPTS,
"uninit-control-dep-attempts",
"Maximum number of nested calls to search for control dependencies "
@@ -243,5 +243,7 @@ extern void init_param_values (int *params);
PARAM_VALUE (PARAM_ASAN_USE_AFTER_RETURN)
#define ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD \
PARAM_VALUE (PARAM_ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD)
+#define ASAN_PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD \
+ PARAM_VALUE (PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD)
#endif /* ! GCC_PARAMS_H */
@@ -32,6 +32,7 @@ along with GCC; see the file COPYING3. If not see
#include "tree-cfg.h"
#include "tree-pass.h"
#include "tree-iterator.h"
+#include "params.h"
#include "asan.h"
namespace {
@@ -165,6 +165,10 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_BEFORE_DYNAMIC_INIT,
DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_AFTER_DYNAMIC_INIT,
"__asan_after_dynamic_init",
BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_CLOBBER_N, "__asan_poison_stack_memory",
+ BT_FN_VOID_PTR_PTRMODE, 0)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_UNCLOBBER_N, "__asan_unpoison_stack_memory",
+ BT_FN_VOID_PTR_PTRMODE, 0)
/* Thread Sanitizer */
DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_INIT, "__tsan_init",
@@ -29,9 +29,9 @@ along with GCC; see the file COPYING3. If not see
#include "gimple-pretty-print.h"
#include "fold-const.h"
#include "gimple-iterator.h"
+#include "params.h"
#include "asan.h"
#include "ubsan.h"
-#include "params.h"
#include "tree-hash-traits.h"
@@ -704,6 +704,9 @@ pass_sanopt::execute (function *fun)
case IFN_ASAN_CHECK:
no_next = asan_expand_check_ifn (&gsi, use_calls);
break;
+ case IFN_ASAN_MARK:
+ no_next = asan_expand_mark_ifn (&gsi);
+ break;
default:
break;
}
@@ -32,6 +32,7 @@ along with GCC; see the file COPYING3. If not see
#include "builtins.h"
#include "ipa-chkp.h"
#include "gomp-constants.h"
+#include "params.h"
#include "asan.h"
@@ -37,6 +37,7 @@ along with GCC; see the file COPYING3. If not see
#include "tree-iterator.h"
#include "tree-ssa-propagate.h"
#include "tree-ssa-loop-ivopts.h"
+#include "params.h"
#include "tsan.h"
#include "asan.h"
#include "builtins.h"
@@ -38,6 +38,7 @@ along with GCC; see the file COPYING3. If not see
#include "cfgloop.h"
#include "ubsan.h"
#include "expr.h"
+#include "params.h"
#include "asan.h"
#include "gimplify-me.h"
#include "dfp.h"
@@ -50,6 +50,7 @@ along with GCC; see the file COPYING3. If not see
#include "langhooks.h"
#include "debug.h"
#include "common/common-target.h"
+#include "params.h"
#include "asan.h"
#include "rtl-iter.h"
--
2.8.2