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
52 changes: 48 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,54 @@ Planned: clang-format, code coverage policies.

## Sanitizers

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.
| 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 three layers, strongest first:

1. **Feature level (primary)** — the sanitizer `cc_feature`s declare
`mutually_exclusive` categories (`asan_tsan`, `lsan_tsan`), so
enabling `score_asan`+`score_tsan` or `score_lsan`+`score_tsan` through the
toolchain fails at **analysis time** with an explicit error
(`Symbol ...:asan_tsan is provided by all of the following features: score_asan score_tsan`).
This protection is intrinsic to feature resolution and applies to every
consumer automatically — no extra build-graph dependency required.
2. **Bazel build target (secondary)** — the
`//sanitizers/flags:sanitizer_combination_check` genrule catches the
flag-driven path and prints an actionable message. The CI test suite depends
on it automatically.
3. **Compiler level (backstop)** — Clang emits an explicit error (e.g.
`error: invalid argument '-fsanitize=address' combined with '-fsanitize=thread'`).
Comment thread
nradakovic marked this conversation as resolved.

## Usage

### Add Dependency

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

### Configure Sanitizers

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

### Run Tests

```bash
bazel test --config=asan_ubsan_lsan //... # recommended default for CI
Expand Down
49 changes: 46 additions & 3 deletions sanitizers/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,44 @@ library (`libclang_rt.ubsan_cxx`) is linked, which is required for C++ ABI
error handlers. GCC does not support this flag but also does not need it (GCC
links UBSan runtime automatically).

#### Mutually exclusive runtimes (primary enforcement)

TSan uses a different runtime library than ASan/LSan, so those pairs cannot be
enabled together. This incompatibility is modeled directly in the feature layer
via `cc_mutually_exclusive_category` targets:
Comment thread
nradakovic marked this conversation as resolved.

| Category | Members |
|---|---|
| `asan_tsan` | `score_asan`, `score_tsan` |
| `lsan_tsan` | `score_lsan`, `score_tsan` |

When a toolchain enables both features in a mutually exclusive category, Bazel
fails at **analysis time** with an explicit error, e.g.:

```
Error in configure_features: Symbol
@score_cpp_policies//sanitizers/features:asan_tsan is provided by all of
the following features: score_asan score_tsan
```

Because this lives in the feature definitions themselves, it protects every
consumer automatically — no dependency on a separate check target is required.

> **Note — two activation surfaces:** Sanitizers are driven through two
> independent surfaces that this repo keeps in sync via the `--config=` aliases
> in `sanitizers.bazelrc`:
>
> - **Toolchain features** (`score_*`) — carry the actual compiler/linker flags.
> The `mutually_exclusive` categories guard this surface: enabling an invalid
> pair fails at analysis time with the error shown above.
> - **`//sanitizers/flags:*` bool_flags** — drive `config_setting`,
> `config_setting_group`, and `target_compatible_with` wiring. The
> `//sanitizers/flags:sanitizer_combination_check` genrule (see below) guards
> this surface as a **secondary check**.
>
> Enable sanitizers through the `--config=` aliases, which set both surfaces
> together, rather than toggling `--features=score_*` or the bool_flags directly.

### `constraints/` — `target_compatible_with` aliases

Use these in `BUILD` files to skip tests that are incompatible with a sanitizer:
Expand Down Expand Up @@ -145,9 +183,14 @@ The following table shows which flag combinations are **supported**:
| ✓ | | | ✓ | ❌ **Invalid** | ASan+TSan: incompatible runtime libraries |
| | | ✓ | ✓ | ❌ **Invalid** | LSan+TSan: TSan has built-in leak detection |

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`).
Invalid combinations are enforced primarily at the **feature level**: the
`score_asan`/`score_lsan` and `score_tsan` features declare mutually exclusive
runtime categories, so enabling an invalid pair fails at Bazel **analysis time**
(see [`features/`](#features--compilerlinker-feature-definitions) above). As a
secondary guard for the flag-driven path,
`@score_cpp_policies//sanitizers/flags:sanitizer_combination_check` also catches
these at build time (the CI test suite depends on this target automatically via
`tests/BUILD.bazel`).

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

Expand Down
Loading