From a22aee1106efaa966797104e365b5f7a29eb0eaf Mon Sep 17 00:00:00 2001 From: Armando Martins Date: Thu, 10 Sep 2026 15:22:04 +0100 Subject: [PATCH 1/2] update documentation about sanatizer features (#25) --- sanitizers/README.md | 88 ++++++++++++++++++++++++-------------------- 1 file changed, 48 insertions(+), 40 deletions(-) diff --git a/sanitizers/README.md b/sanitizers/README.md index 16cfed9..a682e0f 100644 --- a/sanitizers/README.md +++ b/sanitizers/README.md @@ -7,8 +7,33 @@ dedicated Bazel config flag. Sanitizers can be used in isolation or combined. --- +## Policy Ownership + +`score_cpp_policies` is the canonical owner of sanitizer policy. Toolchain +packages provide compilers; the consuming workspace registers the policy's +feature labels with its toolchain. This policy includes the feature definitions, +configuration flags, constraints, test wrapper, runtime suppressions, and Bazel +configs. + +--- + ## Quick Start +Add the policy dependency to your `MODULE.bazel` without pinning a version here (if you pin a version, ensure you are using >= 0.1.0, as before that the policies were defined in "bazel_cpp_toolchains"): + +```python +bazel_dep(name = "score_cpp_policies") +``` + +Import `sanitizers.bazelrc` from your workspace `.bazelrc` using the `try-import` example: + +``` +try-import %workspace%/path/to/score_cpp_policies/sanitizers/sanitizers.bazelrc +``` + +Complete the mandatory [Toolchain Registration](#toolchain-registration) before +using a sanitizer config. + ```bash # Run tests with AddressSanitizer bazel test --config=asan //your/target/... @@ -23,12 +48,6 @@ bazel test --config=asan_ubsan_lsan //your/target/... bazel test --config=tsan_ubsan //your/target/... ``` -> **Note:** Import `sanitizers.bazelrc` from your workspace `.bazelrc` to make the -> above configs available: -> ``` -> try-import %workspace%/path/to/score_cpp_policies/sanitizers/sanitizers.bazelrc -> ``` - --- ## Architecture @@ -59,10 +78,11 @@ One `bool_flag` per sanitizer (`asan`, `ubsan`, `lsan`, `tsan`) and correspondin Flags are set automatically by the `--config=` aliases in `sanitizers.bazelrc`. Do not set them directly unless you have a non-standard composition need. -### `features/` — Compiler/linker feature definitions +### `features/` — Policy-owned compiler/linker feature definitions -One `cc_feature` per sanitizer, registered under the `score_*` namespace to -avoid collisions with toolchain built-in feature names: +The policy owns these `cc_feature` targets. Register them through your +toolchain's `extra_known_features`; their `score_*` names avoid collisions with +toolchain built-in feature names: | Target | Feature name | Toolchain | Key flags | |---|---|---|---| @@ -72,7 +92,11 @@ avoid collisions with toolchain built-in feature names: | `ubsan_clang` | `score_ubsan_clang` | Clang | implies `ubsan_base` + `-fsanitize-link-c++-runtime` (link) | | `lsan` | `score_lsan` | both | `-fsanitize=leak` | | `tsan` | `score_tsan` | both | `-fsanitize=thread`, `-O1` | -| `debug_symbols` | `debug_symbols` | both | `-g1` + `--strip=never` | +| `debug_symbols` | `debug_symbols` | both | `-g1` | + +The `with_debug_symbols` config adds `--strip=never`. Each sanitizer feature +implies `debug_symbols`; `ubsan_gcc` and `ubsan_clang` imply `ubsan_base`, which +then implies `debug_symbols`. UBSan uses a three-target composition: - `ubsan_base` carries the flags common to both toolchains (`-fsanitize=undefined` at compile and link time). @@ -154,7 +178,7 @@ Available constraints: | `only_lsan` | Only run when LSan is active | | `only_tsan` | Only run when TSan is active | -> **Note — `no_asan_ubsan_lsan` semantic:** +> **Note — `no_asan_ubsan_lsan` semantic:** > In the previous single-flag API, `no_asan_ubsan_lsan` was satisfied only when the > combined `asan_ubsan_lsan` preset was active (i.e. all three flags simultaneously). > In the current per-flag API it is satisfied when **any one** of the three flags is @@ -162,7 +186,7 @@ Available constraints: > > | Scenario | Old behaviour | New behaviour | > |---|---|---| -> | Only `--//flags:asan=True` | Target **skipped** — combo not active, so `no_asan_ubsan_lsan` was always satisfied | Target **skipped** — `any_asan_ubsan_lsan` is satisfied | +> | Only `--//flags:asan=True` | Target **built** — combo not active, so the old `match_all` constraint was satisfied | Target **skipped** — `any_asan_ubsan_lsan` is satisfied | > | `--//flags:asan + :ubsan + :lsan` | Target **skipped** | Target **skipped** | > | No sanitizer | Target **built** | Target **built** | > @@ -174,7 +198,7 @@ Available constraints: ## Valid Sanitizer Combinations -The following table shows which flag combinations are **supported**: +The following table lists the supported presets and selected combinations: | ASan | UBSan | LSan | TSan | Status | Preset | |:---:|:---:|:---:|:---:|---|---| @@ -207,8 +231,9 @@ Each env file sets sanitizer-specific runtime options (e.g. `ASAN_OPTIONS`, ## Toolchain Registration -To use sanitizer features, register them in your toolchain's `known_features` -or `extra_known_features`: +Users of `score_bazel_cpp_toolchains` pass these policy targets through +`extra_known_features`/`extra_enabled_features`. The policy is toolchain-neutral: use the matching UBSan +variant for the compiler package in use. ```python # Clang toolchain (toolchains_llvm) @@ -255,29 +280,12 @@ testing) rather than relying on `wrapper` at test-run time. ## Migration from v0.x -The `--@score_cpp_policies//sanitizers/flags:sanitizer=` string flag has been removed. -Replace any direct flag usage with the equivalent `--config=` alias: - -| Old | New | -|-----|-----| -| `--@score_cpp_policies//sanitizers/flags:sanitizer=asan_ubsan_lsan` | `--config=asan_ubsan_lsan` | -| `--@score_cpp_policies//sanitizers/flags:sanitizer=tsan` | `--config=tsan` | - -`--config=asan`, `--config=ubsan`, and `--config=lsan` now activate exactly their named -sanitizer rather than the combined `asan_ubsan_lsan` mode. - -### GCC-specific feature variants removed - -The `asan_ubsan_lsan_gcc` and `tsan_gcc` `cc_feature` targets (which omitted -`-fsanitize-link-c++-runtime`) have been removed. The new per-sanitizer features -(`score_asan`, `score_ubsan`, etc.) work with both Clang and GCC toolchains. If you were -registering GCC-specific features explicitly in your toolchain, replace them with the new -single features (e.g. `@score_cpp_policies//sanitizers/features:asan`). - -### `no_asan_ubsan_lsan` constraint semantics changed +The complete migration procedure, old-to-new mapping, toolchain registration +examples, compatibility rules, and validation commands are in the +[authoritative sanitizer migration guide](../docs/migration-sanitizers.md). -See the `constraints/` section above — in the previous -single-flag API, `no_asan_ubsan_lsan` was satisfied only when the combined -`asan_ubsan_lsan` preset was active (all three flags simultaneously). In the current -per-flag API it is satisfied when **any one** of the three flags is set. Prefer the more -granular `no_asan`, `no_ubsan`, or `no_lsan` constraints for new targets. +In short: add `score_cpp_policies`, import `sanitizers.bazelrc`, register the +per-sanitizer feature labels through `extra_known_features`, replace the +deprecated string flag and removed GCC aggregate features, and review uses of +`no_asan_ubsan_lsan` because its current meaning is "skip when any of ASan, +UBSan, or LSan is active." From 339ff2244386bfa4bd3905cdd7c809181f898a3d Mon Sep 17 00:00:00 2001 From: Armando Martins Date: Thu, 10 Sep 2026 15:22:54 +0100 Subject: [PATCH 2/2] created documentation about the migration of sanitizer policy (#26) documentated how to migrate from legacy-to-current usage of sanatizer policies --- README.md | 3 +- docs/migration-sanitizers.md | 201 +++++++++++++++++++++++++++++++++++ 2 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 docs/migration-sanitizers.md diff --git a/README.md b/README.md index 1bebee9..20ca257 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,8 @@ bazel test --config=asan_ubsan_lsan //... # recommended default for CI ``` See [`sanitizers/README.md`](sanitizers/README.md) for setup, the full config/constraint -reference, and the v0.x migration guide. +reference, and the v0.x migration guide. See the [detailed sanitizer migration guide](docs/migration-sanitizers.md) +for the complete old-to-new API procedure. ## Clang-Tidy diff --git a/docs/migration-sanitizers.md b/docs/migration-sanitizers.md new file mode 100644 index 0000000..87e6d5d --- /dev/null +++ b/docs/migration-sanitizers.md @@ -0,0 +1,201 @@ +# Migration: from the v0.x sanitizer API to the policy-owned API + +See [`sanitizers/README.md`](../sanitizers/README.md) for the full feature, +constraint, suppression, and runtime reference. This guide covers the API +migration itself. + +## Scope and version assumptions + +The current sanitizer API is owned by `score_cpp_policies`. It uses the +`extra_known_features` toolchain registration API and the policy's +`rules_cc` feature definitions. Use a C++ toolchain that supports +`extra_known_features` and the current feature API. This repository does not +claim a minimum toolchain version for that support. + +The repository tests currently use these versions: + +- `score_bazel_cpp_toolchains` 1.0.2 +- `rules_cc` 0.2.17 +- `toolchains_llvm` 1.7.0 +- GCC 12.2.0 and 15.3.0 +- Clang 19.1.7 + +These are repository test versions, not minimum version requirements. + +## Old model + +The v0.x API exposed one string flag: + +```text +--@score_cpp_policies//sanitizers/flags:sanitizer= +``` + +The recognised legacy values were `asan_ubsan_lsan` and `tsan`. The old model +also used aggregate GCC feature names such as `asan_ubsan_lsan_gcc` and +`tsan_gcc`. + +The current `sanitizers/flags:sanitizer` target remains only as a deprecated +compatibility shim so Bazel can parse those legacy values. Non-default use is +rejected by `sanitizer_deprecated_check`; it is not a migration path. + +| Legacy invocation | Current invocation | +| --- | --- | +| `--@score_cpp_policies//sanitizers/flags:sanitizer=asan_ubsan_lsan` | `--config=asan_ubsan_lsan` | +| `--@score_cpp_policies//sanitizers/flags:sanitizer=tsan` | `--config=tsan` | + +## New model + +The policy defines one bool flag per sanitizer and exposes these config +aliases through [`sanitizers.bazelrc`](../sanitizers/sanitizers.bazelrc): + +| Sanitizer | Current config | +| --- | --- | +| AddressSanitizer | `--config=asan` | +| UndefinedBehaviorSanitizer | `--config=ubsan` | +| LeakSanitizer | `--config=lsan` | +| ThreadSanitizer | `--config=tsan` | +| ASan + UBSan + LSan | `--config=asan_ubsan_lsan` | +| TSan + UBSan | `--config=tsan_ubsan` | + +The single-sanitizer configs activate exactly their named sanitizer. Composite +configs are convenience aliases that compose those single-sanitizer configs. +The aliases also configure the policy's test wrapper and debug-symbol setup. + +### Feature registration + +Register the policy labels in the toolchain's `extra_known_features`. Register +the compiler-appropriate UBSan variant: + +| Purpose | Label | Feature name | Toolchain | +| --- | --- | --- | --- | +| Debug symbols | `@score_cpp_policies//sanitizers/features:debug_symbols` | `debug_symbols` | GCC and Clang | +| AddressSanitizer | `@score_cpp_policies//sanitizers/features:asan` | `score_asan` | GCC and Clang | +| UBSan common flags | `@score_cpp_policies//sanitizers/features:ubsan_base` | `score_ubsan_base` | GCC and Clang | +| UBSan GCC variant | `@score_cpp_policies//sanitizers/features:ubsan_gcc` | `score_ubsan_gcc` | GCC | +| UBSan Clang variant | `@score_cpp_policies//sanitizers/features:ubsan_clang` | `score_ubsan_clang` | Clang | +| LeakSanitizer | `@score_cpp_policies//sanitizers/features:lsan` | `score_lsan` | GCC and Clang | +| ThreadSanitizer | `@score_cpp_policies//sanitizers/features:tsan` | `score_tsan` | GCC and Clang | + +The former aggregate GCC features `asan_ubsan_lsan_gcc` and `tsan_gcc` are +removed. Replace them with the per-sanitizer labels above. In particular, +register `asan`, `lsan`, and `tsan` directly, and register `ubsan_gcc` for GCC +or `ubsan_clang` for Clang. Do not register both UBSan variants for one +toolchain. + +Example GCC registration: + +```starlark +gcc.toolchain( + ..., + extra_known_features = [ + "@score_cpp_policies//sanitizers/features:debug_symbols", + "@score_cpp_policies//sanitizers/features:asan", + "@score_cpp_policies//sanitizers/features:ubsan_base", + "@score_cpp_policies//sanitizers/features:ubsan_gcc", + "@score_cpp_policies//sanitizers/features:lsan", + "@score_cpp_policies//sanitizers/features:tsan", + ], +) +``` + +Example Clang registration: + +```starlark +llvm.toolchain( + llvm_version = "...", + extra_known_features = [ + "@score_cpp_policies//sanitizers/features:debug_symbols", + "@score_cpp_policies//sanitizers/features:asan", + "@score_cpp_policies//sanitizers/features:ubsan_base", + "@score_cpp_policies//sanitizers/features:ubsan_clang", + "@score_cpp_policies//sanitizers/features:lsan", + "@score_cpp_policies//sanitizers/features:tsan", + ], +) +``` + +### Dependency and Bazel configuration + +Add `score_cpp_policies` to the consuming workspace: + +```starlark +bazel_dep(name = "score_cpp_policies") +``` + +Import the policy config file from the workspace `.bazelrc`: + +```text +try-import %workspace%/path/to/score_cpp_policies/sanitizers/sanitizers.bazelrc +``` + +Use the import path appropriate for how the dependency is available in the +workspace. The config file is the source of the `--config=` aliases. + +### Valid and invalid combinations + +Supported combinations are: + +| Combination | Current invocation | +| --- | --- | +| ASan | `--config=asan` | +| UBSan | `--config=ubsan` | +| LSan | `--config=lsan` | +| TSan | `--config=tsan` | +| ASan + UBSan + LSan | `--config=asan_ubsan_lsan` | +| TSan + UBSan | `--config=tsan_ubsan` | +| ASan + LSan | `--config=asan --config=lsan` | + +ASan + TSan and LSan + TSan are invalid because their runtime libraries are +incompatible; TSan also provides its own leak detection. The feature layer +declares these pairs mutually exclusive, and the flag layer provides a +secondary combination check. Do not create configs for these pairs. + +### Constraints and duplicate flags + +In the current API, `no_asan_ubsan_lsan` means no ASan, UBSan, or LSan is +active: it matches when any one of those three flags is set. This differs from +the old single-flag behavior, where it was satisfied unless the combined +`asan_ubsan_lsan` preset was active. For new targets, prefer the granular +`no_asan`, `no_ubsan`, or `no_lsan` constraints. If a target must be skipped +only when all three are active, use the `asan_ubsan_lsan` match-all flag group +in a custom `config_setting`. + +Remove duplicate custom sanitizer flags from `copts`, custom features, or +toolchain forks. The policy features already provide the compile and link +flags, while the config aliases select them. Duplicates can conflict with the +policy's feature composition and runtime checks. + +## Migration procedure + +1. Add `score_cpp_policies` as a `bazel_dep`. +2. Import `sanitizers/sanitizers.bazelrc` from the consuming workspace's + `.bazelrc`. +3. Replace legacy string-flag use with the equivalent `--config=` alias: + `asan_ubsan_lsan` maps to `--config=asan_ubsan_lsan`, and `tsan` maps to + `--config=tsan`. +4. Replace registrations of `asan_ubsan_lsan_gcc` and `tsan_gcc` with the + per-sanitizer labels, including the matching `ubsan_gcc` or `ubsan_clang`. +5. Remove duplicate sanitizer flags from project options and custom features. +6. Review `no_asan_ubsan_lsan` uses and replace them with granular constraints + where that expresses the intended compatibility rule. +7. Choose a supported config, run the validation commands below, and fix any + sanitizer or runtime issues exposed by the new policy. + +## Validation + +This repository does not provide a dedicated Markdown-link checker. Use the +Markdown diagnostics available in the editor or the documentation tooling used +by the consuming workspace to verify relative links. + +From `tests/`, run the sanitizer suites: + +```bash +bazel test --config=asan_ubsan_lsan //... +bazel test --config=tsan //... +``` + +Also validate a Clang UBSan configuration when using the Clang toolchain: + +```bash +bazel test --config=ubsan //... +```