Add a memory sanitizer build to CI #66

Closed
opened 2026-07-22 20:20:50 +00:00 by andrew · 2 comments
Owner

This could be a variant of fuzz_driver built with -fsanitize=memory if supported. We'll probably need to modify the ci script to get a working memory sanitizer environment going though

This could be a variant of fuzz_driver built with -fsanitize=memory if supported. We'll probably need to modify the ci script to get a working memory sanitizer environment going though
weaselbot was assigned by andrew 2026-07-22 20:20:50 +00:00
Member

I looked into this and hit a real blocker around the sanitizer environment, so I'm pausing before opening a PR.

What I found

A msan_driver variant of fuzz_driver (built from ConflictSet.cpp + FuzzTestDriver.cpp with -fsanitize=memory) compiles and links fine, and -fsanitize=memory is supported by the clang we use in CI. The masked-but-unused reads that the code already annotates for valgrind (VALGRIND_MAKE_MEM_DEFINED in checkMaxBetweenExclusiveImpl) are not an msan problem — msan's bitwise shadow propagation correctly clears the masked-out bits, so those loops stay clean.

The actual blocker is the well-known msan requirement that the whole program, including the C++ standard library, be instrumented. With an uninstrumented libstdc++/libc++:

  • The harness itself (std::ifstream/std::stringstream in FuzzTestDriver.cpp) immediately reports a false-positive use-of-uninitialized-value from str.data(), because the file read happens in uninstrumented library code. I confirmed this locally.
  • Switching the harness to C I/O (fread, which msan intercepts) moves the report into the test driver's std::set<std::string_view, ..., ArenaAlloc<...>> usage in TestDriver::next() (Internal.h:~658) — _Rb_tree::_M_get_insert_unique_pos reading a node's _M_left. I couldn't determine whether that's a genuine uninitialized read or an msan/stdlib imprecision without an instrumented stdlib to compare against.

What a working environment needs

The standard, documented way to run msan on C++ is to build an msan-instrumented libc++/libc++abi/libunwind from the LLVM source (e.g. via runtimes with -DLLVM_USE_SANITIZER=Memory) and build msan_driver against it (-stdlib=libc++ + a sysroot/-B pointing at the instrumented libs). That's a non-trivial addition to the CI job — it needs the LLVM runtimes source and an extra build step — and I can't build/validate that instrumented-stdlib pipeline from this sandbox (no apt, can't clone llvm-project), so I don't want to ship an untested heavy CI job that would just go red.

What I'd like to confirm

  1. Is building an msan-instrumented libc++ from LLVM source in CI (with the extra runtime cost) the approach you want, or do you have a preferred prebuilt msan sysroot / environment in mind?
  2. Once that environment is in place, msan does surface a report in the test driver's std::set (Internal.h ~658). Are you expecting that to be a real bug to fix, or should I treat test-driver-only findings as out of scope and annotate/suppress them?

Once you point me at the preferred environment, I'll add the USE_MSAN CMake target + the CI job and follow up with a PR.

I looked into this and hit a real blocker around the sanitizer environment, so I'm pausing before opening a PR. ## What I found A `msan_driver` variant of `fuzz_driver` (built from `ConflictSet.cpp` + `FuzzTestDriver.cpp` with `-fsanitize=memory`) compiles and links fine, and `-fsanitize=memory` is supported by the clang we use in CI. The masked-but-unused reads that the code already annotates for valgrind (`VALGRIND_MAKE_MEM_DEFINED` in `checkMaxBetweenExclusiveImpl`) are **not** an msan problem — msan's bitwise shadow propagation correctly clears the masked-out bits, so those loops stay clean. The actual blocker is the well-known msan requirement that **the whole program, including the C++ standard library, be instrumented**. With an uninstrumented libstdc++/libc++: - The harness itself (`std::ifstream`/`std::stringstream` in `FuzzTestDriver.cpp`) immediately reports a false-positive use-of-uninitialized-value from `str.data()`, because the file read happens in uninstrumented library code. I confirmed this locally. - Switching the harness to C I/O (`fread`, which msan intercepts) moves the report into the test driver's `std::set<std::string_view, ..., ArenaAlloc<...>>` usage in `TestDriver::next()` (Internal.h:~658) — `_Rb_tree::_M_get_insert_unique_pos` reading a node's `_M_left`. I couldn't determine whether that's a genuine uninitialized read or an msan/stdlib imprecision without an instrumented stdlib to compare against. ## What a working environment needs The standard, documented way to run msan on C++ is to build an msan-instrumented `libc++`/`libc++abi`/`libunwind` from the LLVM source (e.g. via `runtimes` with `-DLLVM_USE_SANITIZER=Memory`) and build `msan_driver` against it (`-stdlib=libc++` + a sysroot/`-B` pointing at the instrumented libs). That's a non-trivial addition to the CI job — it needs the LLVM runtimes source and an extra build step — and I can't build/validate that instrumented-stdlib pipeline from this sandbox (no apt, can't clone llvm-project), so I don't want to ship an untested heavy CI job that would just go red. ## What I'd like to confirm 1. Is building an msan-instrumented `libc++` from LLVM source in CI (with the extra runtime cost) the approach you want, or do you have a preferred prebuilt msan sysroot / environment in mind? 2. Once that environment is in place, msan does surface a report in the test driver's `std::set` (Internal.h ~658). Are you expecting that to be a real bug to fix, or should I treat test-driver-only findings as out of scope and annotate/suppress them? Once you point me at the preferred environment, I'll add the `USE_MSAN` CMake target + the CI job and follow up with a PR.
Author
Owner

Use the msan-ci branch as a starting point

Use the [msan-ci](https://git.weaselab.dev/weaselab/conflict-set/src/branch/msan-ci) branch as a starting point
weaselbot was unassigned by andrew 2026-07-22 23:13:28 +00:00
andrew referenced this issue from a commit 2026-07-23 19:17:39 +00:00
Sign in to join this conversation.
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: weaselab/conflict-set#66