💡

MSAN(MemorySanitizer)で C++ コードをコンパイルするメモ

に公開

MSAN(MemorySanitizer)有効で C++ コードをコンパイルする場合, libc 以外のライブラリをすべてリコンパイルする必要がある(STL library 含む).

したがってまずは MSAN 有効で libc++ をビルドする.

MSAN 有効な libc++ のビルド

https://github.com/google/sanitizers/wiki/MemorySanitizerLibcxxHowTo

ただし情報はやや古い.

以下は Linux のみ + llvm 19.1.0(執筆時点での最新) を対象とする.

まず, llvm 19.1.0 の libc++ は clang-17 以上でないとコンパイルできないので注意.

libcxx 関連は llvm-project/runtimes を source dir に指定してビルドできる.
LLVM_ENABLE_RUNTIMES でどのモジュールをビルドするか指定する.
libcxx が欲しい場合, どうも libcxxabi と libunwind も一緒にビルドしないといけないっぽい.

LLVM_USE_SANITIZER には Memory or MemoryWithOrigins を指定する. MemoriWithOrigin だとどこで実際に原因が発生したかもレポートしてくれるので基本は MemoriWithOrigin がよいでしょう. ただし実行時メモリは Memory より多く使う.

CMAKE_BUILD_TYPE はお好みで.

$ CXX=clang++-18 CC=clang-18 cmake \
  -G Ninja -DCMAKE_INSTALL_PREFIX=${dist_dir} \
  -B ${libcxx_build_dir} -S llvm-project/runtimes/ \
  -DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi;libunwind" \
  -DLLVM_USE_SANITIZER=MemoryWithOrigins \
  -DCMAKE_BUILD_TYPE=Release

C++ コードを MSAN 有効でビルド

あとは -fsanitize=memory をつけ, msan 有効な libc++ を使うようにして C++ コードをビルドする.

-fsanitize=memory -fsanitize-memory-track-origins -fno-omit-frame-pointer 

-fsanitize-memory-track-origins をつけると, どこで原因が発生したかも報告してくれる.
メモリは消費するようになる.

include path は

-isystem ${dist_dir}/include -isystem ${dist_dir}/include/c++/v1 -nostdinc++

として, 既存の C++ include path は無効(-nostdinc++), カスタムビルト libcxx は -isystem で指定(そうしないと clang の warning level を上げていると reserved keyword エラーがでる)

リンクは -L ${LIBCXX_MSAN_DIR}/lib -lc++ -lc++abi -lunwind だが, これだとシステムに libcxx がすでにインストールされている場合そちらとリンクしてしまう.
実行時に LD_LIBRARY_PATH で調整してもよいですが, コンパイル時(リンク時)に固定したいときは

-Wl,-rpath,${LIBCXX_MSAN_DIR}/lib -lc++ -lc++abi -lunwind

-Wl,rpath, を使う.

llvmlibc?

llvmlibc(C lib)もせっかくなので MSAN 有効でビルドしたいところであるが, llvmlibc は一部に C++ コード(libc++ 由来)を使っているため, MSAN 有効な libc++ でコンパイルする必要がありめんどい.

C lib のいくつかは MSAN で instrumented されて suppress されているようなので, 特段セキュアな検証が必要無い限りは libc まわりは既存のもの(glibc)でよいでしょう.

Discussion