My library doctest is tested with 200+ builds on travis CI - x86/x64 Debug/Release linux/osx and with a wide range of compilers - from gcc 4.4 to 6 and clang 3.4 to 3.8
All my tests are ran through valgrind and the address sanitizer (also UB sanitizer).
I recently discovered that not all features of ASAN are on by default - for example:
check_initialization_order=true
detect_stack_use_after_return=true
strict_init_order=true
so I enabled them and started getting errors for code like the example below.
int& getStatic() {
static int data;
return data;
}
int reg() { return getStatic() = 0; }
static int dummy = reg();
int main() { return getStatic(); }
compiled with g++ (Ubuntu 5.2.1-22ubuntu2) 5.2.1 20151010
:
g++ -fsanitize=address -g -fno-omit-frame-pointer -O2 a.cpp
and ran like this:
ASAN_OPTIONS=verbosity=0:strict_string_checks=true:detect_odr_violation=2:check_initialization_order=true:detect_stack_use_after_return=true:strict_init_order=true ./a.out
produces the following error:
==23425==AddressSanitizer CHECK failed: ../../../../src/libsanitizer/asan/asan_globals.cc:255 "((dynamic_init_globals)) != (0)" (0x0, 0x0)
#0 0x7f699bd699c1 (/usr/lib/x86_64-linux-gnu/libasan.so.2+0xa09c1)
#1 0x7f699bd6e973 in __sanitizer::CheckFailed(char const*, int, char const*, unsigned long long, unsigned long long) (/usr/lib/x86_64-linux-gnu/libasan.so.2+0xa5973)
#2 0x7f699bcf2f5c in __asan_before_dynamic_init (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x29f5c)
#3 0x40075d in __static_initialization_and_destruction_0 /home/onqtam/a.cpp:10
#4 0x40075d in _GLOBAL__sub_I__Z9getStaticv /home/onqtam/a.cpp:10
#5 0x40090c in __libc_csu_init (/home/onqtam/a.out+0x40090c)
#6 0x7f699b91fa4e in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x20a4e)
#7 0x4007b8 in _start (/home/onqtam/a.out+0x4007b8)
The same is with g++-6 (Ubuntu 6.1.1-3ubuntu11~12.04.1) 6.1.1 20160511
The error disappears when I do one of these 3 things:
-O2
and use -O0
static
in front of dummy
Why is this happening? If it is a bug - is it reported? How to avoid it?
EDIT:
@vadikrobot said that even this: static int data = 0; static int dummy = data; int main() { }
produces the problem.
EDIT:
the answer of @ead is correct, however I found a way to circumvent the removal of the static dummy and asan doesn't assert anymore:
int& getStatic() {
static int data = 0;
return data;
}
int __attribute__((noinline)) reg(int* dummy_ptr) { *dummy_ptr = 5; return getStatic() = 0; }
static int __attribute__((unused)) dummy = reg(&dummy);
int main(int argc, char** argv) { return getStatic(); }
This is a problem with the usage of asan by gcc. I don't know enough to say that this is a bug (because all I know comes from reverse engineering), but there is at least some room for improvement for gcc. But also asan could be more robust in its handling of this case.
What goes wrong? For my explanation I would like to take a look at the assembler code of vadikrobot's example and later move to your problem:
static int data = 0;
static int dummy = data;
int main() { }
First we compile without optimization: g++ -O0 -S
(here the whole assembler code)
The most important points are:
-There are two globals, for data
and dummy
integer static variables:
.local _ZL4data
.comm _ZL4data,4,4
.local _ZL5dummy
.comm _ZL5dummy,4,4
-In the section .init_array
are noted all functions which are called prior to main
. In our case this is _GLOBAL__sub_I_main
:
.section .init_array,"aw"
.align 8
.quad _GLOBAL__sub_I_main
-As expected, the global variables are initialized somewhere in _GLOBAL__sub_I_main
:
_GLOBAL__sub_I_main:
...
#in this function is the initialization
call _Z41__static_initialization_and_destruction_0ii
...
After establishing that, let's take a look at the optimized version:
static
variables are local and can only be accessed from this translation unit, they are not used here, so they are not used at all and thus are optimized..init_array
, because there is nothing to initialize._GLOBAL__sub_I_main
function, which does just nothing. I guess it should be optimized away as well.Now let's take a look at unoptimized version with -fsanitize=address
(full assembler code here):
The most important thing: section .init_array
has now more functions which are needed for initialization of the sanitizer, in the end it all results in these important functions being called in this order:
call __asan_init
call __asan_register_globals
call __asan_before_dynamic_init
call __asan_report_store4
call __asan_after_dynamic_init
What is different for optimized version?
-There are no globals (they are optimized away after all), so __asan_register_globals
is not called. This is Ok.
-But strangely the section .init_array
contains now again the not needed method _GLOBAL__sub_I_main
which does not initialize any globals (they are optimized away), but calls __asan_before_dynamic_init
:
_GLOBAL__sub_I_main:
.cfi_startproc
subq $8, %rsp
.cfi_def_cfa_offset 16
movl $.LC0, %edi
call __asan_before_dynamic_init
...
The problem with this: It seems as if it were not allowed to call __asan_before_dynamic_init
without a prior call to __asan_register_globals
because some pointer seems to be NULL
- your error trace is a failed assertion.
After having established that, let's go to you problem:
static int dummy = reg();
is not used anywhere in this translation unit and thus is optimized away, there are no globals and you will run in the bad case of __asan_before_dynamic_init
without __asan_register_globals
.
without static
, the variable dummy
could be used from a different translation unit and thus cannot be optimized away - there are globals and thus __asan_register_globals
is called.
why does gcc version prior to 5.0 work? Sadly, they would not optimize unused global static
variables away.
What to do?
For example:
int& getStatic() {
static int data=0;
return data;
}
and remove the static variable dummy
and maybe also the function reg()
, if it is not used for other purposes.
This should have been fixed in GCC recently: https://gcc.gnu.org/bugzilla/show_bug.cgi?format=multiple&id=77396
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With