+samsonov, who wrote the clang part Do you plan to add tests? We have four lit-style tests for this (Alexey, that's all, right?): init-order-atexit.cc init-order-dlopen.cc init-order-pthread-create.cc Linux/initialization-bug-any-order.cc
I think we need at least the basic one in gcc (Linux/initialization-bug-any-order.cc) As a routine reminder: I am worried about the maintenance cost of two different test and build systems, even if I don't seem to pay for the second one. We need to find a way to share tests and preferably the build infrastructure. Ideally we would share the repository as well, but let's start with something small :) --kcc On Fri, Nov 15, 2013 at 5:58 PM, Jakub Jelinek <ja...@redhat.com> wrote: > Hi! > > Here is an implementation of init-order checking (at least how I understand > it from looking at libsanitizer and eyeballing what clang emits) plus some > performance improvements. > > Previously instrument_derefs ignored stores and loads from VAR_DECLs, that > is of course now not possible generally, because it could be dynamic init > protected globals. But, as for the improvements, if get_inner_reference > tells us that the access is known to be within bounds of some VAR_DECL, > we can check if we know it must be fine. Automatic vars in current function > within that function are (at least for now) all known to be accessible, > similarly non-external vars that don't have dynamic initialization. > > I had to tweak no-redundant-instrumentation-1.c test a little bit, because > for the foo var accesses instrument_derefs can now find out the > insturmentation is unecessary, similarly for the static tab var that didn't > have dynamic initialization. Note, some comments in that file seems to be > completely wrong and also the fact that it doesn't instrument high bound > of the memset 4/5 looks like a severe bug (though, of course if memset > isn't expanded inline, but as a library call, it will still be caught by > the runtime library). > > 2013-11-15 Jakub Jelinek <ja...@redhat.com> > > * sanitizer.def (BUILT_IN_ASAN_BEFORE_DYNAMIC_INIT, > BUILT_IN_ASAN_AFTER_DYNAMIC_INIT): New. > * asan.c (instrument_derefs): Handle also VAR_DECL loads/stores. > Don't instrument accesses to VAR_DECLs which are known to fit > into their bounds and the vars are known to have shadow bytes > indicating allowed access. > (asan_dynamic_init_call): New function. > (asan_add_global): If vnode->asan_dynamically_initialized, > set __has_dynamic_init to 1 instead of 0. > (initialize_sanitizer_builtins): Add BT_FN_VOID_CONST_PTR var. > * asan.h (asan_dynamic_init_call): New prototype. > * cgraph.h (varpool_node): Add asan_dynamically_initialized bitfield. > cp/ > * decl2.c: Include asan.h. > (one_static_initialization_or_destruction): If -fsanitize=address, > init is non-NULL and guard is NULL, set > vnode->asan_dynamically_initialized. > (do_static_initialization_or_destruction): Call > __asan_{before,after}_dynamic_init around the static initialization. > testsuite/ > * c-c++-common/asan/no-redundant-instrumentation-1.c: Tweak to avoid > optimizing away some __asan_report* calls. > > --- gcc/sanitizer.def.jj 2013-11-12 11:31:12.000000000 +0100 > +++ gcc/sanitizer.def 2013-11-15 12:25:06.160748091 +0100 > @@ -60,6 +60,12 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_UNRE > DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_HANDLE_NO_RETURN, > "__asan_handle_no_return", > BT_FN_VOID, ATTR_TMPURE_NOTHROW_LEAF_LIST) > +DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_BEFORE_DYNAMIC_INIT, > + "__asan_before_dynamic_init", > + BT_FN_VOID_CONST_PTR, ATTR_NOTHROW_LEAF_LIST) > +DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_AFTER_DYNAMIC_INIT, > + "__asan_after_dynamic_init", > + BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST) > > /* Thread Sanitizer */ > DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_INIT, "__tsan_init", > --- gcc/cp/decl2.c.jj 2013-11-12 23:10:07.000000000 +0100 > +++ gcc/cp/decl2.c 2013-11-15 12:16:24.195466188 +0100 > @@ -49,6 +49,7 @@ along with GCC; see the file COPYING3. > #include "splay-tree.h" > #include "langhooks.h" > #include "c-family/c-ada-spec.h" > +#include "asan.h" > > extern cpp_reader *parse_in; > > @@ -3456,7 +3457,15 @@ one_static_initialization_or_destruction > if (initp) > { > if (init) > - finish_expr_stmt (init); > + { > + finish_expr_stmt (init); > + if ((flag_sanitize & SANITIZE_ADDRESS) && guard == NULL_TREE) > + { > + struct varpool_node *vnode = varpool_get_node (decl); > + if (vnode) > + vnode->asan_dynamically_initialized = 1; > + } > + } > > /* If we're using __cxa_atexit, register a function that calls the > destructor for the object. */ > @@ -3498,6 +3507,9 @@ do_static_initialization_or_destruction > tf_warning_or_error); > finish_if_stmt_cond (cond, init_if_stmt); > > + if (flag_sanitize & SANITIZE_ADDRESS) > + finish_expr_stmt (asan_dynamic_init_call (/*after_p=*/false)); > + > node = vars; > do { > tree decl = TREE_VALUE (node); > @@ -3546,6 +3558,9 @@ do_static_initialization_or_destruction > > } while (node); > > + if (flag_sanitize & SANITIZE_ADDRESS) > + finish_expr_stmt (asan_dynamic_init_call (/*after_p=*/true)); > + > /* Finish up the init/destruct if-stmt body. */ > finish_then_clause (init_if_stmt); > finish_if_stmt (init_if_stmt); > --- gcc/asan.c.jj 2013-11-15 12:37:07.000000000 +0100 > +++ gcc/asan.c 2013-11-15 13:26:23.648429812 +0100 > @@ -214,7 +214,7 @@ along with GCC; see the file COPYING3. > // Name of the module where the global variable is declared. > const void *__module_name; > > - // This is always set to NULL for now. > + // 1 if it has dynamic initialization, 0 otherwise. > uptr __has_dynamic_init; > } > > @@ -1571,7 +1571,9 @@ instrument_derefs (gimple_stmt_iterator > case COMPONENT_REF: > case INDIRECT_REF: > case MEM_REF: > + case VAR_DECL: > break; > + /* FALLTHRU */ > default: > return; > } > @@ -1585,8 +1587,8 @@ instrument_derefs (gimple_stmt_iterator > tree offset; > enum machine_mode mode; > int volatilep = 0, unsignedp = 0; > - get_inner_reference (t, &bitsize, &bitpos, &offset, > - &mode, &unsignedp, &volatilep, false); > + tree inner = get_inner_reference (t, &bitsize, &bitpos, &offset, > + &mode, &unsignedp, &volatilep, false); > if (bitpos % (size_in_bytes * BITS_PER_UNIT) > || bitsize != size_in_bytes * BITS_PER_UNIT) > { > @@ -1601,6 +1603,34 @@ instrument_derefs (gimple_stmt_iterator > return; > } > > + if (TREE_CODE (inner) == VAR_DECL > + && offset == NULL_TREE > + && bitpos >= 0 > + && DECL_SIZE (inner) > + && host_integerp (DECL_SIZE (inner), 0) > + && bitpos + bitsize <= tree_low_cst (DECL_SIZE (inner), 0)) > + { > + if (DECL_THREAD_LOCAL_P (inner)) > + return; > + if (!TREE_STATIC (inner)) > + { > + /* Automatic vars in the current function will be always > + accessible. */ > + if (decl_function_context (inner) == current_function_decl) > + return; > + } > + /* Always instrument external vars, they might be dynamically > + initialized. */ > + else if (!DECL_EXTERNAL (inner)) > + { > + /* For static vars if they are known not to be dynamically > + initialized, they will be always accessible. */ > + struct varpool_node *vnode = varpool_get_node (inner); > + if (vnode && !vnode->asan_dynamically_initialized) > + return; > + } > + } > + > base = build_fold_addr_expr (t); > if (!has_mem_ref_been_instrumented (base, size_in_bytes)) > { > @@ -2059,6 +2089,34 @@ transform_statements (void) > } > > /* Build > + __asan_before_dynamic_init (module_name) > + or > + __asan_after_dynamic_init () > + call. */ > + > +tree > +asan_dynamic_init_call (bool after_p) > +{ > + tree fn = builtin_decl_implicit (after_p > + ? BUILT_IN_ASAN_AFTER_DYNAMIC_INIT > + : BUILT_IN_ASAN_BEFORE_DYNAMIC_INIT); > + tree module_name_cst = NULL_TREE; > + if (!after_p) > + { > + pretty_printer module_name_pp; > + pp_string (&module_name_pp, main_input_filename); > + > + if (shadow_ptr_types[0] == NULL_TREE) > + asan_init_shadow_ptr_types (); > + module_name_cst = asan_pp_string (&module_name_pp); > + module_name_cst = fold_convert (const_ptr_type_node, > + module_name_cst); > + } > + > + return build_call_expr (fn, after_p ? 0 : 1, module_name_cst); > +} > + > +/* Build > struct __asan_global > { > const void *__beg; > @@ -2147,7 +2205,10 @@ asan_add_global (tree decl, tree type, v > fold_convert (const_ptr_type_node, str_cst)); > CONSTRUCTOR_APPEND_ELT (vinner, NULL_TREE, > fold_convert (const_ptr_type_node, > module_name_cst)); > - CONSTRUCTOR_APPEND_ELT (vinner, NULL_TREE, build_int_cst (uptr, 0)); > + struct varpool_node *vnode = varpool_get_node (decl); > + int has_dynamic_init = vnode ? vnode->asan_dynamically_initialized : 0; > + CONSTRUCTOR_APPEND_ELT (vinner, NULL_TREE, > + build_int_cst (uptr, has_dynamic_init)); > init = build_constructor (type, vinner); > CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, init); > } > @@ -2164,6 +2225,8 @@ initialize_sanitizer_builtins (void) > tree BT_FN_VOID = build_function_type_list (void_type_node, NULL_TREE); > tree BT_FN_VOID_PTR > = build_function_type_list (void_type_node, ptr_type_node, NULL_TREE); > + tree BT_FN_VOID_CONST_PTR > + = build_function_type_list (void_type_node, const_ptr_type_node, > NULL_TREE); > tree BT_FN_VOID_PTR_PTR > = build_function_type_list (void_type_node, ptr_type_node, > ptr_type_node, NULL_TREE); > --- gcc/asan.h.jj 2013-11-15 12:37:07.000000000 +0100 > +++ gcc/asan.h 2013-11-15 12:37:18.991783166 +0100 > @@ -27,6 +27,7 @@ extern rtx asan_emit_stack_protection (r > tree *, int); > extern bool asan_protect_global (tree); > extern void initialize_sanitizer_builtins (void); > +extern tree asan_dynamic_init_call (bool); > > /* Alias set for accessing the shadow memory. */ > extern alias_set_type asan_shadow_set; > --- gcc/cgraph.h.jj 2013-11-13 18:32:52.000000000 +0100 > +++ gcc/cgraph.h 2013-11-15 12:05:25.950985500 +0100 > @@ -520,6 +520,11 @@ class GTY((tag ("SYMTAB_VARIABLE"))) var > public: > /* Set when variable is scheduled to be assembled. */ > unsigned output : 1; > + /* Set if the variable is dynamically initialized. Not set for > + function local statics or variables that can be initialized in > + multiple compilation units (such as template static data members > + that need construction). */ > + unsigned asan_dynamically_initialized : 1; > }; > > /* Every top level asm statement is put into a asm_node. */ > --- gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-1.c.jj > 2013-02-18 16:39:54.000000000 +0100 > +++ gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-1.c > 2013-11-15 14:16:22.727049197 +0100 > @@ -6,7 +6,7 @@ > /* { dg-do compile } */ > /* { dg-skip-if "" { *-*-* } { "*" } { "-O0" } } */ > > -static char tab[4] = {0}; > +extern char tab[4]; > > static int > test0 () > @@ -27,12 +27,14 @@ test0 () > return t0 + t1; > } > > -static int > -test1 () > +__attribute__((noinline, noclone)) static int > +test1 (int i) > { > + char foo[4] = {}; > + > /*__builtin___asan_report_store1 called 1 time here to instrument > the initialization. */ > - char foo[4] = {1}; > + foo[i] = 1; > > /*__builtin___asan_report_store1 called 2 times here to instrument > the store to the memory region of tab. */ > @@ -45,7 +47,7 @@ test1 () > /* There are 2 calls to __builtin___asan_report_store1 and 2 calls > to __builtin___asan_report_load1 to instrument the store to > (subset of) the memory region of tab. */ > - __builtin_memcpy (&tab[1], foo, 3); > + __builtin_memcpy (&tab[1], foo + i, 3); > > /* This should not generate a __builtin___asan_report_load1 because > the reference to tab[1] has been already instrumented above. */ > @@ -58,7 +60,7 @@ test1 () > int > main () > { > - return test0 () && test1 (); > + return test0 () && test1 (0); > } > > /* { dg-final { scan-tree-dump-times "__builtin___asan_report_store1" 7 > "asan0" } } */ > > Jakub