Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 14 additions & 123 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,113 +8,29 @@ Planned: clang-format, code coverage policies.

## What This Provides

- **Sanitizer Bazel feature flags** — ASan, UBSan, LSan, TSan as Bazel build features
- **`//sanitizers:wrapper`** — shell script that sets all sanitizer runtime options centrally
- **`sanitizers/sanitizers.bazelrc`** — canonical config that consumers import or copy
- **Suppression files** — per-sanitizer suppression lists for known false positives (GoogleTest, etc.)
- **Constraint system** — `target_compatible_with` settings for sanitizer-incompatible targets
- **`clang_tidy/.clang-tidy`** — centralized default check set (conservative baseline, tailorable per module)
- **`clang_tidy/clang_tidy.bazelrc`** — `--config=clang-tidy` bazelrc config consumers can import

## Available Sanitizer Configurations

| Config | Sanitizers | Notes |
|--------|-----------|-------|
| `--config=asan` | AddressSanitizer | Memory errors, buffer overflows |
| `--config=ubsan` | UndefinedBehaviorSanitizer | Integer overflow, null deref |
| `--config=lsan` | LeakSanitizer | Memory leaks |
| `--config=tsan` | ThreadSanitizer | Data races, deadlocks — cannot combine with ASan/LSan |
| `--config=asan_ubsan_lsan` | ASan + UBSan + LSan | **Recommended default for CI** |
| `--config=tsan_ubsan` | TSan + UBSan | Threading + undefined behavior |

## Sanitizer Combination Compatibility

| Combination | Valid? | Notes |
|---|---|---|
| ASan + UBSan | ✅ Yes | Standard — included in `--config=asan_ubsan_lsan` |
| ASan + LSan | ✅ Yes | Included in `--config=asan_ubsan_lsan` |
| TSan + UBSan | ✅ Yes | Use `--config=tsan_ubsan` |
| ASan + TSan | ❌ No | Incompatible runtime libraries (`libasan` vs `libtsan`) |
| LSan + TSan | ❌ No | TSan has built-in leak detection; enabling both causes runtime conflicts |

Invalid combinations are enforced at the **compiler level** — Clang emits an explicit error (e.g.
`error: invalid argument '-fsanitize=address' combined with '-fsanitize=thread'`).
The `//sanitizers/flags:sanitizer_combination_check` target additionally catches these at Bazel
build time when included in the build graph (the CI test suite depends on it automatically).

## Usage

### Add Dependency

```python
bazel_dep(name = "score_cpp_policies")
```

### Configure Sanitizers
- **[`sanitizers/`](sanitizers/README.md)** — ASan/UBSan/LSan/TSan Bazel `cc_feature`s, ready-to-use `--config=` aliases, suppression files, and `target_compatible_with` constraints.
- **[`clang_tidy/`](clang_tidy/README.md)** — centralized `.clang-tidy` baseline (conservative, tailorable per module) and a `--config=clang-tidy` Bazel integration.

Copy [`sanitizers/sanitizers.bazelrc`](sanitizers/sanitizers.bazelrc) into your repository's `.bazelrc`.
## Sanitizers

### Run Tests
ASan, UBSan, LSan, and TSan as independently configurable Bazel `cc_feature`s, with
ready-to-use `--config=` aliases (`asan`, `ubsan`, `lsan`, `tsan`, `asan_ubsan_lsan`,
`tsan_ubsan`), suppression files for known false positives, and `target_compatible_with`
constraints for skipping or restricting tests under specific sanitizers.

```bash
# ASan + UBSan + LSan (recommended)
bazel test --config=asan_ubsan_lsan //...

# ThreadSanitizer (separate run — cannot combine with ASan)
bazel test --config=tsan //...
```

## Tagging Tests for Sanitizer Compatibility

```python
cc_test(
name = "my_test",
srcs = ["my_test.cpp"],
tags = ["no-tsan"], # skip when running --config=tsan
deps = ["@googletest//:gtest_main"],
)
bazel test --config=asan_ubsan_lsan //... # recommended default for CI
```

| Tag | Skipped when using |
|-----|--------------------|
| `no-tsan` | `--config=tsan` |
| `no-asan` | `--config=asan` or `asan_ubsan_lsan` |
| `no-lsan` | `--config=lsan` or `asan_ubsan_lsan` |
| `no-ubsan` | `--config=ubsan` or `asan_ubsan_lsan` |

## Suppression Files

Default suppressions for common third-party libraries are included:

| File | Sanitizer | Current Suppressions |
|------|-----------|---------------------|
| `sanitizers/suppressions/asan.supp` | ASan | *(empty)* |
| `sanitizers/suppressions/lsan.supp` | LSan | GoogleTest static initialization leaks |
| `sanitizers/suppressions/tsan.supp` | TSan | stdlib false positives, Rust test suppressions |
| `sanitizers/suppressions/ubsan.supp` | UBSan | *(empty)* |

**Adding project-specific suppressions:** Currently, the wrapper loads suppressions from this module only. For project-specific suppressions, you'll need to create a custom wrapper or extend the environment variables in your `.bazelrc`. This is a known limitation being tracked for future improvements.
See [`sanitizers/README.md`](sanitizers/README.md) for setup, the full config/constraint
reference, and the v0.x migration guide.

> **Runtime Options**: See [`sanitizers/templates/`](sanitizers/templates/) for detailed documentation of all sanitizer options configured by the wrapper.
## Clang-Tidy

## Constraint System
Centralized `.clang-tidy` check set and a `--config=clang-tidy` Bazel integration via
`aspect_rules_lint`.

Use constraints to mark targets as incompatible with specific sanitizers:

```python
cc_library(
name = "legacy_lib",
target_compatible_with = [
"@score_cpp_policies//sanitizers/constraints:no_tsan",
],
)
```

| Constraint | Effect |
|-----------|--------|
| `@score_cpp_policies//sanitizers/constraints:no_tsan` | Skip when `--config=tsan` |
| `@score_cpp_policies//sanitizers/constraints:no_asan_ubsan_lsan` | Skip when `--config=asan_ubsan_lsan` |
| `@score_cpp_policies//sanitizers/constraints:any_sanitizer` | Only builds with a sanitizer enabled |
See [`clang_tidy/README.md`](clang_tidy/README.md) for the 8-step setup guide.

## Testing This Repository

Expand All @@ -126,31 +42,6 @@ bazel test --config=tsan //...
bazel test --config=clang-tidy //...
```

## Migration from v0.x

The `--@score_cpp_policies//sanitizers/flags:sanitizer=<value>` 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 retained as compatibility alias

`//sanitizers/constraints:no_asan_ubsan_lsan` is kept as a backwards-compatible alias. It resolves to
`incompatible` if **any** of ASan, UBSan, or LSan is active. Prefer the more granular `no_asan`, `no_ubsan`,
or `no_lsan` constraints for new targets.

## Contributing

See [CONTRIBUTION.md](CONTRIBUTION.md) for guidelines. All commits must follow [Eclipse Foundation commit rules](https://www.eclipse.org/projects/handbook/#resources-commit). Contributors must sign the ECA and DCO.
Expand Down
41 changes: 35 additions & 6 deletions sanitizers/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ Available constraints:
| `no_lsan` | Skip when LSan is active |
| `no_tsan` | Skip when TSan is active |
| `no_asan_ubsan_lsan` | Skip when **any** of ASan, UBSan, or LSan is active (see note below) |
| `only_asan` | Only run when ASan is active |
| `only_ubsan` | Only run when UBSan is active |
| `only_lsan` | Only run when LSan is active |
| `only_tsan` | Only run when TSan is active |

> **Note — `no_asan_ubsan_lsan` semantic:**
> In the previous single-flag API, `no_asan_ubsan_lsan` was satisfied only when the
Expand Down Expand Up @@ -145,12 +149,6 @@ Invalid combinations are caught at **build time** by
`@score_cpp_policies//sanitizers/flags:sanitizer_combination_check` (the CI
test suite depends on this target automatically via `tests/BUILD.bazel`).


| `only_asan` | Only run when ASan is active |
| `only_ubsan` | Only run when UBSan is active |
| `only_lsan` | Only run when LSan is active |
| `only_tsan` | Only run when TSan is active |

### `wrapper.sh` — Runtime environment loader

The test runner script. It is set via `--run_under` in `sanitizers.bazelrc`
Expand Down Expand Up @@ -186,3 +184,34 @@ llvm.toolchain(
Runtime suppression files live in `suppressions/`. Add suppressions for known
false positives in your module's `.bazelrc` or by passing the suppression file
path in the `*_OPTIONS` environment variable.

---

## Migration from v0.x

The `--@score_cpp_policies//sanitizers/flags:sanitizer=<value>` 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

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.
Loading