Implement protection of global variables

This patch implements the protection of global variables.  See the
comments appended to the beginning of the asan.c file.

	* varasm.c: Include asan.h.
	(assemble_noswitch_variable): Grow size by asan_red_zone_size
	if decl is asan protected.
	(place_block_symbol): Likewise.
	(assemble_variable): If decl is asan protected, increase
	DECL_ALIGN if needed, and for decls emitted using
	assemble_variable_contents append padding zeros after it.
	* Makefile.in (varasm.o): Depend on asan.h.
	* asan.c: Include output.h.
	(asan_pp, asan_pp_initialized, asan_ctor_statements): New variables.
	(asan_pp_initialize, asan_pp_string): New functions.
	(asan_emit_stack_protection): Use asan_pp{,_initialized}
	instead of local pp{,_initialized} vars, use asan_pp_initialize
	and asan_pp_string helpers.
	(asan_needs_local_alias, asan_protect_global,
	asan_global_struct, asan_add_global): New functions.
	(asan_finish_file): Protect global vars that can be protected. Use
	asan_ctor_statements instead of ctor_statements
	* asan.h (asan_protect_global): New prototype.
	(asan_red_zone_size): New inline function.

Co-Authored-By: Wei Mi <wmi@google.com>

From-SVN: r193437
This commit is contained in:
Jakub Jelinek 2012-11-12 16:52:42 +01:00 committed by Dodji Seketeli
parent f3ddd6929a
commit 8240018b0c
5 changed files with 365 additions and 38 deletions

View File

@ -1,3 +1,27 @@
2012-11-12 Jakub Jelinek <jakub@redhat.com>
Wei Mi <wmi@google.com>
* varasm.c: Include asan.h.
(assemble_noswitch_variable): Grow size by asan_red_zone_size
if decl is asan protected.
(place_block_symbol): Likewise.
(assemble_variable): If decl is asan protected, increase
DECL_ALIGN if needed, and for decls emitted using
assemble_variable_contents append padding zeros after it.
* Makefile.in (varasm.o): Depend on asan.h.
* asan.c: Include output.h.
(asan_pp, asan_pp_initialized, asan_ctor_statements): New variables.
(asan_pp_initialize, asan_pp_string): New functions.
(asan_emit_stack_protection): Use asan_pp{,_initialized}
instead of local pp{,_initialized} vars, use asan_pp_initialize
and asan_pp_string helpers.
(asan_needs_local_alias, asan_protect_global,
asan_global_struct, asan_add_global): New functions.
(asan_finish_file): Protect global vars that can be protected. Use
asan_ctor_statements instead of ctor_statements
* asan.h (asan_protect_global): New prototype.
(asan_red_zone_size): New inline function.
2012-11-12 Jakub Jelinek <jakub@redhat.com>
* Makefile.in (asan.o): Depend on $(EXPR_H) $(OPTABS_H).

View File

@ -2719,7 +2719,7 @@ varasm.o : varasm.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) \
output.h $(DIAGNOSTIC_CORE_H) xcoffout.h debug.h $(GGC_H) $(TM_P_H) \
$(HASHTAB_H) $(TARGET_H) langhooks.h gt-varasm.h $(BASIC_BLOCK_H) \
$(CGRAPH_H) $(TARGET_DEF_H) tree-mudflap.h \
pointer-set.h $(COMMON_TARGET_H)
pointer-set.h $(COMMON_TARGET_H) asan.h
function.o : function.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_ERROR_H) \
$(TREE_H) $(GIMPLE_H) $(FLAGS_H) $(FUNCTION_H) $(EXPR_H) \
$(OPTABS_H) $(LIBFUNCS_H) $(REGS_H) hard-reg-set.h insn-config.h $(RECOG_H) \

View File

@ -31,6 +31,7 @@ along with GCC; see the file COPYING3. If not see
#include "target.h"
#include "expr.h"
#include "optabs.h"
#include "output.h"
/*
AddressSanitizer finds out-of-bounds and use-after-free bugs
@ -166,7 +167,43 @@ along with GCC; see the file COPYING3. If not see
non-accessible) the regions of the red zones and mark the regions of
stack variables as accessible, and emit some epilogue code to
un-poison (mark as accessible) the regions of red zones right before
the function exits. */
the function exits.
[Protection of global variables]
The basic idea is to insert a red zone between two global variables
and install a constructor function that calls the asan runtime to do
the populating of the relevant shadow memory regions at load time.
So the global variables are laid out as to insert a red zone between
them. The size of the red zones is so that each variable starts on a
32 bytes boundary.
Then a constructor function is installed so that, for each global
variable, it calls the runtime asan library function
__asan_register_globals_with an instance of this type:
struct __asan_global
{
// Address of the beginning of the global variable.
const void *__beg;
// Initial size of the global variable.
uptr __size;
// Size of the global variable + size of the red zone. This
// size is 32 bytes aligned.
uptr __size_with_redzone;
// Name of the global variable.
const void *__name;
// This is always set to NULL for now.
uptr __has_dynamic_init;
}
A destructor function that calls the runtime asan library function
_asan_unregister_globals is also installed. */
alias_set_type asan_shadow_set = -1;
@ -174,6 +211,34 @@ alias_set_type asan_shadow_set = -1;
alias set is used for all shadow memory accesses. */
static GTY(()) tree shadow_ptr_types[2];
/* Asan pretty-printer, used for buidling of the description STRING_CSTs. */
static pretty_printer asan_pp;
static bool asan_pp_initialized;
/* Initialize asan_pp. */
static void
asan_pp_initialize (void)
{
pp_construct (&asan_pp, /* prefix */NULL, /* line-width */0);
asan_pp_initialized = true;
}
/* Create ADDR_EXPR of STRING_CST with asan_pp text. */
static tree
asan_pp_string (void)
{
const char *buf = pp_base_formatted_text (&asan_pp);
size_t len = strlen (buf);
tree ret = build_string (len + 1, buf);
TREE_TYPE (ret)
= build_array_type (char_type_node, build_index_type (size_int (len)));
TREE_READONLY (ret) = 1;
TREE_STATIC (ret) = 1;
return build1 (ADDR_EXPR, build_pointer_type (char_type_node), ret);
}
/* Return a CONST_INT representing 4 subsequent shadow memory bytes. */
static rtx
@ -208,51 +273,38 @@ asan_emit_stack_protection (rtx base, HOST_WIDE_INT *offsets, tree *decls,
HOST_WIDE_INT last_offset, last_size;
int l;
unsigned char cur_shadow_byte = ASAN_STACK_MAGIC_LEFT;
static pretty_printer pp;
static bool pp_initialized;
const char *buf;
size_t len;
tree str_cst;
/* First of all, prepare the description string. */
if (!pp_initialized)
{
pp_construct (&pp, /* prefix */NULL, /* line-width */0);
pp_initialized = true;
}
pp_clear_output_area (&pp);
if (!asan_pp_initialized)
asan_pp_initialize ();
pp_clear_output_area (&asan_pp);
if (DECL_NAME (current_function_decl))
pp_base_tree_identifier (&pp, DECL_NAME (current_function_decl));
pp_base_tree_identifier (&asan_pp, DECL_NAME (current_function_decl));
else
pp_string (&pp, "<unknown>");
pp_space (&pp);
pp_decimal_int (&pp, length / 2 - 1);
pp_space (&pp);
pp_string (&asan_pp, "<unknown>");
pp_space (&asan_pp);
pp_decimal_int (&asan_pp, length / 2 - 1);
pp_space (&asan_pp);
for (l = length - 2; l; l -= 2)
{
tree decl = decls[l / 2 - 1];
pp_wide_integer (&pp, offsets[l] - base_offset);
pp_space (&pp);
pp_wide_integer (&pp, offsets[l - 1] - offsets[l]);
pp_space (&pp);
pp_wide_integer (&asan_pp, offsets[l] - base_offset);
pp_space (&asan_pp);
pp_wide_integer (&asan_pp, offsets[l - 1] - offsets[l]);
pp_space (&asan_pp);
if (DECL_P (decl) && DECL_NAME (decl))
{
pp_decimal_int (&pp, IDENTIFIER_LENGTH (DECL_NAME (decl)));
pp_space (&pp);
pp_base_tree_identifier (&pp, DECL_NAME (decl));
pp_decimal_int (&asan_pp, IDENTIFIER_LENGTH (DECL_NAME (decl)));
pp_space (&asan_pp);
pp_base_tree_identifier (&asan_pp, DECL_NAME (decl));
}
else
pp_string (&pp, "9 <unknown>");
pp_space (&pp);
pp_string (&asan_pp, "9 <unknown>");
pp_space (&asan_pp);
}
buf = pp_base_formatted_text (&pp);
len = strlen (buf);
str_cst = build_string (len + 1, buf);
TREE_TYPE (str_cst)
= build_array_type (char_type_node, build_index_type (size_int (len)));
TREE_READONLY (str_cst) = 1;
TREE_STATIC (str_cst) = 1;
str_cst = build1 (ADDR_EXPR, build_pointer_type (char_type_node), str_cst);
str_cst = asan_pp_string ();
/* Emit the prologue sequence. */
base = expand_binop (Pmode, add_optab, base, GEN_INT (base_offset),
@ -357,6 +409,75 @@ asan_emit_stack_protection (rtx base, HOST_WIDE_INT *offsets, tree *decls,
return ret;
}
/* Return true if DECL, a global var, might be overridden and needs
therefore a local alias. */
static bool
asan_needs_local_alias (tree decl)
{
return DECL_WEAK (decl) || !targetm.binds_local_p (decl);
}
/* Return true if DECL is a VAR_DECL that should be protected
by Address Sanitizer, by appending a red zone with protected
shadow memory after it and aligning it to at least
ASAN_RED_ZONE_SIZE bytes. */
bool
asan_protect_global (tree decl)
{
rtx rtl, symbol;
section *sect;
if (TREE_CODE (decl) != VAR_DECL
/* TLS vars aren't statically protectable. */
|| DECL_THREAD_LOCAL_P (decl)
/* Externs will be protected elsewhere. */
|| DECL_EXTERNAL (decl)
|| !TREE_ASM_WRITTEN (decl)
|| !DECL_RTL_SET_P (decl)
/* Comdat vars pose an ABI problem, we can't know if
the var that is selected by the linker will have
padding or not. */
|| DECL_ONE_ONLY (decl)
/* Similarly for common vars. People can use -fno-common. */
|| DECL_COMMON (decl)
/* Don't protect if using user section, often vars placed
into user section from multiple TUs are then assumed
to be an array of such vars, putting padding in there
breaks this assumption. */
|| (DECL_SECTION_NAME (decl) != NULL_TREE
&& !DECL_HAS_IMPLICIT_SECTION_NAME_P (decl))
|| DECL_SIZE (decl) == 0
|| ASAN_RED_ZONE_SIZE * BITS_PER_UNIT > MAX_OFILE_ALIGNMENT
|| !valid_constant_size_p (DECL_SIZE_UNIT (decl))
|| DECL_ALIGN_UNIT (decl) > 2 * ASAN_RED_ZONE_SIZE)
return false;
rtl = DECL_RTL (decl);
if (!MEM_P (rtl) || GET_CODE (XEXP (rtl, 0)) != SYMBOL_REF)
return false;
symbol = XEXP (rtl, 0);
if (CONSTANT_POOL_ADDRESS_P (symbol)
|| TREE_CONSTANT_POOL_ADDRESS_P (symbol))
return false;
sect = get_variable_section (decl, false);
if (sect->common.flags & SECTION_COMMON)
return false;
if (lookup_attribute ("weakref", DECL_ATTRIBUTES (decl)))
return false;
#ifndef ASM_OUTPUT_DEF
if (asan_needs_local_alias (decl))
return false;
#endif
return true;
}
/* Construct a function tree for __asan_report_{load,store}{1,2,4,8,16}.
IS_STORE is either 1 (for a store) or 0 (for a load).
SIZE_IN_BYTES is one of 1, 2, 4, 8, 16. */
@ -657,6 +778,105 @@ transform_statements (void)
}
}
/* Build
struct __asan_global
{
const void *__beg;
uptr __size;
uptr __size_with_redzone;
const void *__name;
uptr __has_dynamic_init;
} type. */
static tree
asan_global_struct (void)
{
static const char *field_names[5]
= { "__beg", "__size", "__size_with_redzone",
"__name", "__has_dynamic_init" };
tree fields[5], ret;
int i;
ret = make_node (RECORD_TYPE);
for (i = 0; i < 5; i++)
{
fields[i]
= build_decl (UNKNOWN_LOCATION, FIELD_DECL,
get_identifier (field_names[i]),
(i == 0 || i == 3) ? const_ptr_type_node
: build_nonstandard_integer_type (POINTER_SIZE, 1));
DECL_CONTEXT (fields[i]) = ret;
if (i)
DECL_CHAIN (fields[i - 1]) = fields[i];
}
TYPE_FIELDS (ret) = fields[0];
TYPE_NAME (ret) = get_identifier ("__asan_global");
layout_type (ret);
return ret;
}
/* Append description of a single global DECL into vector V.
TYPE is __asan_global struct type as returned by asan_global_struct. */
static void
asan_add_global (tree decl, tree type, VEC(constructor_elt, gc) *v)
{
tree init, uptr = TREE_TYPE (DECL_CHAIN (TYPE_FIELDS (type)));
unsigned HOST_WIDE_INT size;
tree str_cst, refdecl = decl;
VEC(constructor_elt, gc) *vinner = NULL;
if (!asan_pp_initialized)
asan_pp_initialize ();
pp_clear_output_area (&asan_pp);
if (DECL_NAME (decl))
pp_base_tree_identifier (&asan_pp, DECL_NAME (decl));
else
pp_string (&asan_pp, "<unknown>");
pp_space (&asan_pp);
pp_left_paren (&asan_pp);
pp_string (&asan_pp, main_input_filename);
pp_right_paren (&asan_pp);
str_cst = asan_pp_string ();
if (asan_needs_local_alias (decl))
{
char buf[20];
ASM_GENERATE_INTERNAL_LABEL (buf, "LASAN",
VEC_length (constructor_elt, v) + 1);
refdecl = build_decl (DECL_SOURCE_LOCATION (decl),
VAR_DECL, get_identifier (buf), TREE_TYPE (decl));
TREE_ADDRESSABLE (refdecl) = TREE_ADDRESSABLE (decl);
TREE_READONLY (refdecl) = TREE_READONLY (decl);
TREE_THIS_VOLATILE (refdecl) = TREE_THIS_VOLATILE (decl);
DECL_GIMPLE_REG_P (refdecl) = DECL_GIMPLE_REG_P (decl);
DECL_ARTIFICIAL (refdecl) = DECL_ARTIFICIAL (decl);
DECL_IGNORED_P (refdecl) = DECL_IGNORED_P (decl);
TREE_STATIC (refdecl) = 1;
TREE_PUBLIC (refdecl) = 0;
TREE_USED (refdecl) = 1;
assemble_alias (refdecl, DECL_ASSEMBLER_NAME (decl));
}
CONSTRUCTOR_APPEND_ELT (vinner, NULL_TREE,
fold_convert (const_ptr_type_node,
build_fold_addr_expr (refdecl)));
size = tree_low_cst (DECL_SIZE_UNIT (decl), 1);
CONSTRUCTOR_APPEND_ELT (vinner, NULL_TREE, build_int_cst (uptr, size));
size += asan_red_zone_size (size);
CONSTRUCTOR_APPEND_ELT (vinner, NULL_TREE, build_int_cst (uptr, size));
CONSTRUCTOR_APPEND_ELT (vinner, NULL_TREE,
fold_convert (const_ptr_type_node, str_cst));
CONSTRUCTOR_APPEND_ELT (vinner, NULL_TREE, build_int_cst (uptr, 0));
init = build_constructor (type, vinner);
CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, init);
}
/* Needs to be GTY(()), because cgraph_build_static_cdtor may
invoke ggc_collect. */
static GTY(()) tree asan_ctor_statements;
/* Module-level instrumentation.
- Insert __asan_init() into the list of CTORs.
- TODO: insert redzones around globals.
@ -665,11 +885,61 @@ transform_statements (void)
void
asan_finish_file (void)
{
tree ctor_statements = NULL_TREE;
struct varpool_node *vnode;
unsigned HOST_WIDE_INT gcount = 0;
append_to_statement_list (build_call_expr (asan_init_func (), 0),
&ctor_statements);
cgraph_build_static_cdtor ('I', ctor_statements,
MAX_RESERVED_INIT_PRIORITY - 1);
&asan_ctor_statements);
FOR_EACH_DEFINED_VARIABLE (vnode)
if (asan_protect_global (vnode->symbol.decl))
++gcount;
if (gcount)
{
tree type = asan_global_struct (), var, ctor, decl;
tree uptr = build_nonstandard_integer_type (POINTER_SIZE, 1);
tree dtor_statements = NULL_TREE;
VEC(constructor_elt, gc) *v;
char buf[20];
type = build_array_type_nelts (type, gcount);
ASM_GENERATE_INTERNAL_LABEL (buf, "LASAN", 0);
var = build_decl (UNKNOWN_LOCATION, VAR_DECL, get_identifier (buf),
type);
TREE_STATIC (var) = 1;
TREE_PUBLIC (var) = 0;
DECL_ARTIFICIAL (var) = 1;
DECL_IGNORED_P (var) = 1;
v = VEC_alloc (constructor_elt, gc, gcount);
FOR_EACH_DEFINED_VARIABLE (vnode)
if (asan_protect_global (vnode->symbol.decl))
asan_add_global (vnode->symbol.decl, TREE_TYPE (type), v);
ctor = build_constructor (type, v);
TREE_CONSTANT (ctor) = 1;
TREE_STATIC (ctor) = 1;
DECL_INITIAL (var) = ctor;
varpool_assemble_decl (varpool_node_for_decl (var));
type = build_function_type_list (void_type_node,
build_pointer_type (TREE_TYPE (type)),
uptr, NULL_TREE);
decl = build_fn_decl ("__asan_register_globals", type);
TREE_NOTHROW (decl) = 1;
append_to_statement_list (build_call_expr (decl, 2,
build_fold_addr_expr (var),
build_int_cst (uptr, gcount)),
&asan_ctor_statements);
decl = build_fn_decl ("__asan_unregister_globals", type);
TREE_NOTHROW (decl) = 1;
append_to_statement_list (build_call_expr (decl, 2,
build_fold_addr_expr (var),
build_int_cst (uptr, gcount)),
&dtor_statements);
cgraph_build_static_cdtor ('D', dtor_statements,
MAX_RESERVED_INIT_PRIORITY - 1);
}
cgraph_build_static_cdtor ('I', asan_ctor_statements,
MAX_RESERVED_INIT_PRIORITY - 1);
}
/* Initialize shadow_ptr_types array. */

View File

@ -23,6 +23,7 @@ along with GCC; see the file COPYING3. If not see
extern void asan_finish_file (void);
extern rtx asan_emit_stack_protection (rtx, HOST_WIDE_INT *, tree *, int);
extern bool asan_protect_global (tree);
/* Alias set for accessing the shadow memory. */
extern alias_set_type asan_shadow_set;
@ -56,4 +57,14 @@ asan_protect_stack_decl (tree decl)
return DECL_P (decl) && !DECL_ARTIFICIAL (decl);
}
/* Return the size of padding needed to insert after a protected
decl of SIZE. */
static inline unsigned int
asan_red_zone_size (unsigned int size)
{
unsigned int c = size & (ASAN_RED_ZONE_SIZE - 1);
return c ? 2 * ASAN_RED_ZONE_SIZE - c : ASAN_RED_ZONE_SIZE;
}
#endif /* TREE_ASAN */

View File

@ -51,6 +51,7 @@ along with GCC; see the file COPYING3. If not see
#include "tree-mudflap.h"
#include "cgraph.h"
#include "pointer-set.h"
#include "asan.h"
#ifdef XCOFF_DEBUGGING_INFO
#include "xcoffout.h" /* Needed for external data
@ -1831,6 +1832,9 @@ assemble_noswitch_variable (tree decl, const char *name, section *sect)
size = tree_low_cst (DECL_SIZE_UNIT (decl), 1);
rounded = size;
if (flag_asan && asan_protect_global (decl))
size += asan_red_zone_size (size);
/* Don't allocate zero bytes of common,
since that means "undefined external" in the linker. */
if (size == 0)
@ -1897,6 +1901,7 @@ assemble_variable (tree decl, int top_level ATTRIBUTE_UNUSED,
const char *name;
rtx decl_rtl, symbol;
section *sect;
bool asan_protected = false;
/* This function is supposed to handle VARIABLES. Ensure we have one. */
gcc_assert (TREE_CODE (decl) == VAR_DECL);
@ -1984,6 +1989,15 @@ assemble_variable (tree decl, int top_level ATTRIBUTE_UNUSED,
/* Compute the alignment of this data. */
align_variable (decl, dont_output_data);
if (flag_asan
&& asan_protect_global (decl))
{
asan_protected = true;
DECL_ALIGN (decl) = MAX (DECL_ALIGN (decl),
ASAN_RED_ZONE_SIZE * BITS_PER_UNIT);
}
set_mem_align (decl_rtl, DECL_ALIGN (decl));
if (TREE_PUBLIC (decl))
@ -2022,6 +2036,12 @@ assemble_variable (tree decl, int top_level ATTRIBUTE_UNUSED,
if (DECL_ALIGN (decl) > BITS_PER_UNIT)
ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (DECL_ALIGN_UNIT (decl)));
assemble_variable_contents (decl, name, dont_output_data);
if (asan_protected)
{
unsigned HOST_WIDE_INT int size
= tree_low_cst (DECL_SIZE_UNIT (decl), 1);
assemble_zeros (asan_red_zone_size (size));
}
}
}
@ -6926,6 +6946,8 @@ place_block_symbol (rtx symbol)
decl = SYMBOL_REF_DECL (symbol);
alignment = DECL_ALIGN (decl);
size = tree_low_cst (DECL_SIZE_UNIT (decl), 1);
if (flag_asan && asan_protect_global (decl))
size += asan_red_zone_size (size);
}
/* Calculate the object's offset from the start of the block. */