Programming

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.

1 answer 1 view

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


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

  1. Add your ignore-list file to the workspace (e.g., tools/msan/msan_ignorelist.txt).
  2. 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"],
)
  1. 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:
  1. 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.
  2. 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=memory if 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:

  1. Create /tmp/msan_ignorelist.txt with your suppressions.
  2. 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 //...
  1. 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 aquery to 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=memory but 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


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.

Authors
Verified by moderation
Moderation
Suppress MSan Errors in Bazel: Protobuf & GTest Fix