使用地址清理程序时的g ++ 5和6的错误以及静态初始化顺序的附加asan标志

时间:2016-08-22 13:42:33

标签: c++ g++ clang address-sanitizer

我的库doctesttravis CI - x86 / x64 Debug / Release linux / osx以及各种编译器上进行了200多次构建测试 - 从gcc 4.4到6以及clang 3.4到3.8

我所有的测试都是通过valgrind和地址消毒剂(也是UB消毒剂)进行的。

我最近发现并非所有ASAN功能都默认启用 - 例如:

  • check_initialization_order=true
  • detect_stack_use_after_return=true
  • strict_init_order=true

所以我启用了它们并开始为代码添加错误,如下例所示。

int& getStatic() {
    static int data;
    return data;
}

int reg() { return getStatic() = 0; }

static int dummy = reg();

int main() { return getStatic(); }

使用g++ (Ubuntu 5.2.1-22ubuntu2) 5.2.1 20151010编译:

g++ -fsanitize=address -g -fno-omit-frame-pointer -O2 a.cpp

并且像这样跑:

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

产生以下错误:

==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)

g++-6 (Ubuntu 6.1.1-3ubuntu11~12.04.1) 6.1.1 20160511

相同

当我执行以下三项操作之一时,错误消失了:

  • 使用clang ++(任何版本)代替g ++
  • 删除-O2并使用-O0
  • 移除static
  • 前面的dummy

为什么会这样?如果它是一个错误 - 是否报告?如何避免呢?

修改

@vadikrobot说即使这样:static int data = 0; static int dummy = data; int main() { }也会产生问题。

修改

@ead的答案是正确的,但我找到了一种方法来规避静态假人的移除,并且asan不再断言:

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(); }

2 个答案:

答案 0 :(得分:4)

这是gcc使用asan的问题。我不知道这是一个错误(因为我所知道的都来自逆向工程),但gcc至少还有一些改进空间。但是,asan在处理这个案件时可能会更加强大。

出了什么问题?对于我的解释,我想看看vadikrobot示例的汇编代码,然后转到您的问题:

full-width

首先我们在没有优化的情况下进行编译:{{1​​}} (here the whole assembler code)

最重要的一点是:

- 对于100%static int data = 0; static int dummy = data; int main() { } 整数静态变量,有两个全局变量:

g++ -O0 -S

- 在data部分中注明了dummy之前调用的所有函数。在我们的例子中,这是.local _ZL4data .comm _ZL4data,4,4 .local _ZL5dummy .comm _ZL5dummy,4,4

.init_array

- 正如预期的那样,全局变量在main

中的某处初始化
_GLOBAL__sub_I_main

确定后,让我们看看optimized version

  1. .section .init_array,"aw" .align 8 .quad _GLOBAL__sub_I_main 变量是本地的,只能从这个翻译单元访问,这里不使用它们,因此根本不使用它们,因此进行了优化。
  2. _GLOBAL__sub_I_main部分中没有任何内容,因为没有任何内容可以初始化。
  3. 奇怪的是,仍然有一个未使用的_GLOBAL__sub_I_main: ... #in this function is the initialization call _Z41__static_initialization_and_destruction_0ii ... 函数,它什么也没做。我想它也应该被优化掉。
  4. 现在让我们看一下static(完整汇编代码here)的未经优化的版本:

    最重要的是:第.init_array部分现在有更多的功能需要初始化清洁剂,最后它们都会按顺序调用这些重要的功能:

    _GLOBAL__sub_I_main

    optimized version有什么不同?

    - 没有全局变量(毕竟它们已被优化),因此不会调用-fsanitize=address。这是好的。

    - 但奇怪的是,.init_array部分现在再次包含了不需要的方法call __asan_init call __asan_register_globals call __asan_before_dynamic_init call __asan_report_store4 call __asan_after_dynamic_init ,它不会初始化任何全局变量(它们被优化掉),但是调用__asan_register_globals

    .init_array

    问题:似乎不允许在没有事先调用_GLOBAL__sub_I_main的情况下调用__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 ... - 您的错误跟踪是断言失败。

    确定之后,让我们找你问题:

    1. __asan_before_dynamic_init未在此翻译单元中的任何位置使用,因此会进行优化,没有全局变量,并且您将在__asan_register_globals的{​​{1}}的情况下运行而不会NULL

    2. 没有static int dummy = reg();,变量__asan_before_dynamic_init可以在不同的翻译单元中使用,因此无法优化 - 有全局变量,因此调用__asan_register_globals

    3. 为什么5.0之前的gcc版本有效?遗憾的是,他们不会优化未使用的全局static变量。

    4. 怎么办?

      1. 您应该将此问题报告给gcc。
      2. 作为一种解决方法,我会手工进行优化。
      3. 例如:

        dummy

        并删除静态变量__asan_register_globals,也可以删除函数static,如果它不用于其他目的。

答案 1 :(得分:1)