FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

LLVM Sanitizerを試す

Zapccを評価するために、図らずも自作RISC-VシミュレータをLLVMに移植したのだった。

LLVMに移植したので、どうせならLLVMメモリリークチェック機能などについて試行してみたい。 TuringCompleteFM にも出ていたのだが、LLVMのSanitizerはかなり優秀らしいということで動作させてみた。 これまではValgrindしか使ってこなかったのでLLVMのSanitizerのほうが優秀なのであれば移行したい。

github.com

LLVMのSanitizerを調査した結果、大きく分けて3種類が存在するらしい。

  • AddressSanitizer (detects addressability issues) and LeakSanitizer (detects memory leaks)
  • ThreadSanitizer (detects data races and deadlocks) for C++ and Go
  • MemorySanitizer (detects use of uninitialized memory)

一つ一つ試していこう。

Memory Sanitizer (未初期化メモリをチェックする)

Memory Sanitizerを有効化するためには、LLVMコンパイルオプションに -fsanitize=memory を追加してビルドする。 ビルド後のRISC-Vシミュレータを動作させると、いきなりGFlagsの部分で落ちてしまった。あれえ?Googleのコードなのに。

==21479==WARNING: MemorySanitizer: use-of-uninitialized-value
    #0 0x70ebf6 in std::_Rb_tree<char const*, std::pair<char const* const, google::(anonymous namespace)::CommandLineFlag*>, std::_Select1st<std::pair<char const* const, google::(anonymous namespace)::CommandLineFlag*> >, google::(anonymous namespace)::StringCmp, std::allocator<std::pair<char const* const, google::(anonymous namespace)::CommandLineFlag*> > >::_M_get_insert_unique_pos(char const* const&) /usr/lib/gcc/x86_64-linux-gnu/7.3.0/../../../../include/c++/7.3.0/bits/stl_tree.h:2035:7
    #1 0x70e6f0 in std::pair<std::_Rb_tree_iterator<std::pair<char const* const, google::(anonymous namespace)::CommandLineFlag*> >, bool> std::_Rb_tree<char const*, std::pair<char const* const, google::(anonymous namespace)::CommandLineFlag*>, std::_Select1st<std::pair<char const* const, google::(anonymous namespace)::CommandLineFlag*> >, google::(anonymous namespace)::StringCmp, std::allocator<std::pair<char const* const, google::(anonymous namespace)::CommandLineFlag*> > >::_M_insert_unique<std::pair<char const*, google::(anonymous namespace)::CommandLineFlag*> >(std::pair<char const*, google::(anonymous namespace)::CommandLineFlag*>&&) /usr/lib/gcc/x86_64-linux-gnu/7.3.0/../../../../include/c++/7.3.0/bits/stl_tree.h:2091:4
    #2 0x70d7ce in std::pair<std::_Rb_tree_iterator<std::pair<char const* const, google::(anonymous namespace)::CommandLineFlag*> >, bool> std::map<char const*, google::(anonymous namespace)::CommandLineFlag*, google::(anonymous namespace)::StringCmp, std::allocator<std::pair<char const* const, google::(anonymous namespace)::CommandLineFlag*> > >::insert<std::pair<char const*, google::(anonymous namespace)::CommandLineFlag*>, void>(std::pair<char const*, google::(anonymous namespace)::CommandLineFlag*>&&) /usr/lib/gcc/x86_64-linux-gnu/7.3.0/../../../../include/c++/7.3.0/bits/stl_map.h:810:16
    #3 0x70d045 in google::(anonymous namespace)::FlagRegistry::RegisterFlag(google::(anonymous namespace)::CommandLineFlag*) /home/msyksphinz/work/forest/riscv_forest_zapcc/vendor/gflags/src/gflags.cc:750:12
    #4 0x6fd174 in google::(anonymous namespace)::RegisterCommandLineFlag(char const*, char const*, char const*, google::(anonymous namespace)::FlagValue*, google::(anonymous namespace)::FlagValue*) /home/msyksphinz/work/forest/riscv_forest_zapcc/vendor/gflags/src/gflags.cc:1472:35
    #5 0x730f93 in google::FlagRegisterer::FlagRegisterer<long>(char const*, char const*, char const*, long*, long*) /home/msyksphinz/work/forest/riscv_forest_zapcc/vendor/gflags/src/gflags.cc:1484:3
    #6 0x47f5f0 in __cxx_global_var_init.4 /home/msyksphinz/work/forest/riscv_forest_zapcc/src/swimmer_main.cpp:62:1
    #7 0x480e02 in _GLOBAL__sub_I_swimmer_main.cpp /home/msyksphinz/work/forest/riscv_forest_zapcc/src/swimmer_main.cpp
    #8 0x77695c in __libc_csu_init (/home/msyksphinz/work/forest/riscv_forest_zapcc/build_riscvforest/riscvforest+0x77695c)
    #9 0x7f085bce0b27 in __libc_start_main /build/glibc-OTsEL5/glibc-2.27/csu/../csu/libc-start.c:266
    #10 0x490b39 in _start (/home/msyksphinz/work/forest/riscv_forest_zapcc/build_riscvforest/riscvforest+0x490b39)

SUMMARY: MemorySanitizer: use-of-uninitialized-value /usr/lib/gcc/x86_64-linux-gnu/7.3.0/../../../../include/c++/7.3.0/bits/stl_tree.h:2035:7 in std::_Rb_tree<char const*, std::pair<char const* const, google::(anonymous namespace)::CommandLineFlag*>, std::_Select1st<std::pair<char const* const, google::(anonymous namespace)::CommandLineFlag*> >, google::(anonymous namespace)::StringCmp, std::allocator<std::pair<char const* const, google::(anonymous namespace)::CommandLineFlag*> > >::_M_get_insert_unique_pos(char const* const&)
Exiting

というわけで Memory Sanitizerはいったんスキップする。

Address Sanitizer (メモリリークをチェックする)

Memory Sanitizerを有効化するためには、LLVMコンパイルオプションに -fsanitize=address を追加してビルドする。

ビルドしてバイナリを実行すると、エラーで落ちた。確かに、アドレスオーバフローを起こしている部分が存在した。 修正して再ビルドすると、エラー無く実行できるようになった。

==4691==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffc68865b80 at pc 0x000000552502 bp 0x7ffc68865b30 sp 0x7ffc688652e0
READ of size 4 at 0x7ffc68865b80 thread T0
    #0 0x552501 in __asan_memcpy /home/msyksphinz/software/llvm/llvm/projects/compiler-rt/lib/asan/asan_interceptors_memintrinsics.cc:23
    #1 0x673654 in ModuleRom::ModuleRom(EnvBase*, _IO_FILE*) /home/msyksphinz/work/forest/riscv_forest_zapcc/src/module_rom.cpp:62:5
    #2 0x5a4efd in RiscvPeThread::RiscvPeThread(_IO_FILE*, PrivMode, bool, bool, _IO_FILE*) /home/msyksphinz/work/forest/riscv_forest_zapcc/src/riscv_pe_thread.cpp:71:45
    #3 0x59970f in main /home/msyksphinz/work/forest/riscv_forest_zapcc/src/swimmer_main.cpp:170:33
    #4 0x7f0fc95ecb96 in __libc_start_main /build/glibc-OTsEL5/glibc-2.27/csu/../csu/libc-start.c:310
    #5 0x489479 in _start (/home/msyksphinz/work/forest/riscv_forest_zapcc/build_riscvforest/riscvforest+0x489479)

Address 0x7ffc68865b80 is located in stack of thread T0 at offset 64 in frame
    #0 0x6733cf in ModuleRom::ModuleRom(EnvBase*, _IO_FILE*) /home/msyksphinz/work/forest/riscv_forest_zapcc/src/module_rom.cpp:50

  This frame has 3 object(s):
    [32, 64) 'init' (line 53) <== Memory access at offset 64 overflows this variable
    [96, 488) 's' (line 65)
    [560, 592) 'config_string' (line 90)
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow /home/msyksphinz/software/llvm/llvm/projects/compiler-rt/lib/asan/asan_interceptors_memintrinsics.cc:23 in __asan_memcpy
Shadow bytes around the buggy address:
  0x10000d104b20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10000d104b30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10000d104b40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10000d104b50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10000d104b60: 00 00 00 00 00 00 00 00 f1 f1 f1 f1 00 00 00 00
=>0x10000d104b70:[f2]f2 f2 f2 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8
  0x10000d104b80: f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8
  0x10000d104b90: f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8
  0x10000d104ba0: f8 f8 f8 f8 f8 f2 f2 f2 f2 f2 f2 f2 f2 f2 f8 f8
  0x10000d104bb0: f8 f8 f3 f3 f3 f3 f3 f3 00 00 00 00 00 00 00 00
  0x10000d104bc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==4691==ABORTING

Thread Sanitizer (スレッドのレースコンディションをチェックする)

Thread Sanitizerを有効化するためには、 LLVMコンパイルオプションに -fsanitize=thread を追加してビルドする。

ビルドしてバイナリを実行しても、特にエラーは発生しなかった。まあ、確かにマルチスレッドは使用していないのでエラーは発生しないだろう。

実行速度

それぞれ実行速度を比較すると以下のようになった。まあValgrindは--leak-check=fullなのでもしかしたら正確な比較ではないかもしれないが、LLVMのSanitizer、結構速いね。

  • Normal : 2m43.460s
  • Thread Sanitizer : 16m37.715s
  • Address Sanitizer : 7m53.666s
  • Valgrind --leak-check=full : 3時間程度...
f:id:msyksphinz:20180708020453p:plain