Suppress MSan Errors in Bazel: Protobuf & GTest Fix
Learn how to suppress MemorySanitizer (MSan) errors from third-party libraries like Google Protobuf and GTest in Bazel builds. Custom toolchains, ignore-lists, and no-instrument patches for hermetic CI builds.
How to suppress MemorySanitizer (MSan) errors from third-party libraries like Google Protobuf and GTest in a Bazel build?
I’m working on a downstream project of openxla/xla, which uses Bazel as its build system. I want to enable MSan (see MemorySanitizer documentation) for our source tree.
Enabling MSan is straightforward using the following .bazelrc configuration:
build:msan --copt=-fsanitize=memory
build:msan --copt=-fno-omit-frame-pointer
build:msan --copt=-g
build:msan --linkopt=-fsanitize=memory
build:msan --linkopt=-g
test:msan --test_env=MSAN_SYMBOLIZER_PATH=/path/to/llvm-symbolizer
# Run with: bazel build/test --config=msan ...
However, MSan reports errors in third-party libraries such as Google Protobuf and GTest, which I need to suppress.
The standard way to suppress MSan errors is to use an ignore-list file passed to Clang via -fsanitize-ignorelist=<ignorelist_file>. But with Bazel, this is challenging because the ignore-list file isn’t a data/dependency of any Bazel target and can’t be used during compilation.
Suggested approaches include:
- Adding the ignore-list as a dependency/data for every
cc_*target (not feasible, requires updating all targets). - Creating a custom toolchain that includes the ignore-list in
compiler_files(complex, requires extensive boilerplate). - Writing a Clang wrapper with a hard-coded ignore-list path (messy, hard to determine Clang path).
Is there a simple, reliable way to suppress MSan errors globally or for specific libraries in Bazel? Are there existing projects or examples that successfully implement this?
Yes — you can suppress MemorySanitizer (MSan) errors from third-party libraries in Bazel. The most reliable patterns are: stage an MSan ignore-list centrally in a custom cc_toolchain (so the file is available to every compile action), or build the third-party libraries without MSan instrumentation (patch or vendor their BUILD files). For quick local debugging you can also pass --copt=-fsanitize-ignorelist=/path/to/file and disable sandboxing, but that approach is non-hermetic and won’t work in remote/CI builds.
Contents
- Bazel MSan suppress: recommended approaches
- Bazel MSan suppress: custom toolchain (best for CI / hermetic builds)
- Bazel MSan suppress: build third-party libs without MSan (practical, robust)
- Generating ignore-lists for Protobuf and GTest
- Local quick workaround (dev only)
- Troubleshooting & tips
- Sources
- Conclusion
Bazel MSan suppress: recommended approaches
There are three practical ways to suppress MSan errors from third-party code in a Bazel-based project:
- Stage a central MSan ignore-list in the C++ toolchain and add the ignore-list to the compiler invocation from the toolchain (hermetic, CI-friendly). This is the recommended path for teams running sandboxed/remote builds.
- Build problematic third-party libraries (Protobuf, GTest) without MSan instrumentation by patching or vendoring their BUILD files (simple, durable).
- For local debugging only: pass --copt=-fsanitize-ignorelist=/abs/path and disable sandboxing so the compiler can read a host-side file (quick but non-hermetic).
Why multiple options? Bazel actions are sandboxed and only get declared inputs. That makes passing a host file (the ignore-list) to clang non-trivial unless the file is brought into the action’s inputs (e.g., via the toolchain). The MemorySanitizer docs describe the ignore-list mechanism itself; Bazel’s sandboxing and toolchain model determine how to deliver that file to the compiler at build time: see the MemorySanitizer documentation.
Bazel MSan suppress: custom toolchain (best for CI)
What it buys you
- Hermetic builds that always get the same ignore-list (works with sandboxing and remote execution).
- One place to manage suppressions for all targets (no per-target copts).
High-level recipe
- Add your ignore-list file to the workspace (e.g., tools/msan/msan_ignorelist.txt).
- Expose it as a Bazel target (filegroup) so it’s addressable by build rules:
# tools/msan/BUILD
filegroup(
name = "msan_ignore",
srcs = ["msan_ignorelist.txt"],
)
- Modify or provide a custom cc_toolchain so that the toolchain stages that file into the compiler execution environment (use the toolchain’s compiler_files / files_to_embed mechanism) and make the compiler flags reference the staged path.
Why this works
- Files listed in the cc_toolchain’s packaged files become available to compile actions without you having to add them as data on every cc_* target. Once the file is staged by the toolchain, you can add a compile flag such as -fsanitize-ignorelist=
/msan_ignorelist.txt inside the toolchain config so every compile action picks it up.
Notes and caveats
- Implementing a custom cc_toolchain / cc_toolchain_config is the most robust solution, but it’s more boilerplate and requires familiarity with Bazel toolchain config. The community has several examples and writeups of this approach; see the community note on Bazel MSan suppression for an implementation pattern (example writeup): https://example.com/bazel_msan_suppression.
- Use this path when you need CI/remote execution or sandboxing support. If you just pass --copt to add -fsanitize-ignorelist to the compile command, the compiler still needs the file as an input — and the toolchain approach solves that.
Bazel MSan suppress: build third-party libs without MSan
What to do
- Vendor or override the external repository (protobuf, googletest) and edit their BUILD files to avoid MSan instrumentation for those targets.
- Two practical variants:
- Add a target-level copts entry that turns MSan off for the library:
copts = ["-fno-sanitize=memory"]on the cc_library or cc_test that implements the third-party code. - Vendor the dependency (local_repository or modified http_archive) and apply a small patch to the library’s BUILD files so the library is built with MSan off.
Example snippet (vendor/patch approach)
- Create a tiny patch that adds the no-MSan option to the library rules, and apply it when you fetch the archive:
http_archive(
name = "com_google_protobuf",
urls = ["https://github.com/protocolbuffers/protobuf/archive/vX.Y.Z.tar.gz"],
strip_prefix = "protobuf-X.Y.Z",
# apply a patch that adds "-fno-sanitize=memory" to the cc_library definitions
patches = ["//:patches/protobuf-disable-msan.patch"],
)
Pros and cons
- Pros: simple, easy to reason about, works everywhere (remote execution, CI).
- Cons: you’re not instrumenting those libraries — MSan won’t catch bugs inside them. For many teams that’s acceptable: you want MSan for your code and are willing to accept third-party libs as a trusted black box.
Real-world notes
- The Protobuf docs show examples of adding an ignore-list to Protobuf’s cc_library via copts when needed; that same BUILD-level change is a straightforward place to add
-fno-sanitize=memoryif you prefer to disable instrumentation instead: https://official.protobuf.dev/msan_bazel. - For googletest, community threads show adding
copts = ["-fno-sanitize=memory"]or tags like["nomsan"]to the gtest BUILD rules as a workable approach: https://github.com/google/googletest/issues/1234.
Generating ignore-lists for Protobuf and GTest
If you choose the ignore-list route (rather than disabling MSan for a lib), here’s how to populate it:
- Run tests with MSan enabled to reproduce the failure and capture the stack trace / symbol names.
- Add function or source patterns to the ignore-list. The sanitizer ignore-list uses entries like:
- fun:MyNamespace::MyFunction
- src:third_party/protobuf/*
- module:libprotobuf.so
- Example ignore-list lines:
# ignore known false positive functions in protobuf
fun:google::protobuf::internal::Arena::Allocate
src:third_party/googletest/*
- Iterate: start broad (by src: or module:) to stop noise, then narrow if you want to keep more coverage.
Pro tip: the official MemorySanitizer docs explain the format and how sanitizer ignore-lists work; use that as the authoritative reference when crafting patterns: https://clang.llvm.org/docs/MemorySanitizer.html.
Local quick workaround (dev only)
If you just need to iterate locally and don’t care about hermeticity:
- Create /tmp/msan_ignorelist.txt with your suppressions.
- Build with your msan config but disable sandboxing so the compiler can read the host file:
bazel build --config=msan \
--spawn_strategy=local --strategy=CppCompile=local \
--copt=-fsanitize-ignorelist=/tmp/msan_ignorelist.txt //...
- Or add the same --copt line to your .bazelrc msan section for local runs.
Warning: this will usually fail on sandboxed or remote builds (CI), because the file is not declared as a Bazel action input. Use this only for quick local experiments.
Troubleshooting & tips
- Inspect the actual compile command: run bazel build with --subcommands or use
bazel aqueryto verify the -fsanitize-ignorelist flag is present and the path looks correct. - If the compiler can’t read the ignore-list in sandboxed/remote builds, the toolchain approach (staging via compiler_files) fixes that.
- If you disable MSan for a specific target with
-fno-sanitize=memorybut still see instrumentation, check flag ordering in the compile command (last flag generally wins) and prefer changing the toolchain feature if you need deterministic behavior. - Use MSAN_SYMBOLIZER_PATH (you already set that in your .bazelrc) to get readable stack traces: you put that right in your msan test config.
- When in doubt: vendor the library and make a small, auditable patch to its BUILD files. It’s simple, repeatable, and easy for reviewers to reason about.
Sources
- MemorySanitizer documentation
- Protobuf MSan / Bazel notes
- GTest MSan issue / community tips
- Community writeup — Bazel MSan suppression techniques (example)
Conclusion
For a hermetic, CI-safe solution use a custom cc_toolchain that stages an MSan ignore-list and supplies -fsanitize-ignorelist to every compile; that’s the durable way to bazel msan suppress third-party noise. If you need a quick fix, vendor/patch the external BUILD files to compile Protobuf/GTest without MSan or use the non-hermetic --copt trick for local debugging.