diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 932f5a211..724d858a3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -108,12 +108,23 @@ jobs: - name: clippy run: ./ci/clippy.sh + # Run sanitizers. + san: + name: san + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install Rust + run: rustup update nightly && rustup default nightly + - name: Run sanitizers + run: ./ci/san.sh + # Run loom tests. loom: name: loom runs-on: ubuntu-latest steps: - - uses: actions/checkout@master + - uses: actions/checkout@v2 - name: Install Rust run: rustup update stable && rustup default stable - name: loom @@ -145,6 +156,7 @@ jobs: - dependencies - rustfmt - clippy + - san - loom - docs runs-on: ubuntu-latest diff --git a/ci/san.sh b/ci/san.sh new file mode 100755 index 000000000..3bb081e5e --- /dev/null +++ b/ci/san.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +cd "$(dirname "$0")"/.. +set -ex + +if [[ "$OSTYPE" != "linux"* ]]; then + exit 0 +fi + +rustup component add rust-src + +# Run address sanitizer +# https://github.com/crossbeam-rs/crossbeam/issues/614 +export ASAN_OPTIONS="detect_leaks=0" +cargo clean +# TODO: Once `cfg(sanitize = "..")` is stable, replace +# `cfg(crossbeam_sanitize)` with `cfg(sanitize = "..")` and remove +# `--cfg crossbeam_sanitize`. +RUSTFLAGS="-Dwarnings -Zsanitizer=address --cfg crossbeam_sanitize" \ +cargo test --all --release --target x86_64-unknown-linux-gnu --tests --exclude benchmarks -- --test-threads=1 + +cargo clean +RUSTFLAGS="-Dwarnings -Zsanitizer=address --cfg crossbeam_sanitize" \ +cargo run \ + --release \ + --target x86_64-unknown-linux-gnu \ + --features nightly \ + --example sanitize \ + --manifest-path crossbeam-epoch/Cargo.toml + +# Run memory sanitizer +cargo clean +RUSTFLAGS="-Dwarnings -Zsanitizer=memory --cfg crossbeam_sanitize" \ +cargo test -Zbuild-std --all --release --target x86_64-unknown-linux-gnu --tests --exclude benchmarks -- --test-threads=1 + +# Run thread sanitizer +export TSAN_OPTIONS="suppressions=$(pwd)/ci/tsan" +cargo clean +RUSTFLAGS="-Dwarnings -Zsanitizer=thread --cfg crossbeam_sanitize" \ +cargo test -Zbuild-std --all --release --target x86_64-unknown-linux-gnu --tests --exclude benchmarks -- --test-threads=1 diff --git a/ci/test.sh b/ci/test.sh index fe14ee846..c0c633d30 100755 --- a/ci/test.sh +++ b/ci/test.sh @@ -8,7 +8,7 @@ export RUSTFLAGS="-D warnings" if [[ -n "$TARGET" ]]; then # If TARGET is specified, use cross for testing. cargo install cross - cross test --all --target "$TARGET" --exclude benchmarks + cross test --all --target "$TARGET" --exclude benchmarks -- --test-threads=1 # For now, the non-host target only runs tests. exit 0 @@ -25,22 +25,4 @@ if [[ "$RUST_VERSION" == "nightly"* ]]; then # Benchmarks are only checked on nightly because depending on unstable features. cargo check --all --benches cargo check --bins --manifest-path crossbeam-channel/benchmarks/Cargo.toml - - # Run address sanitizer on crossbeam-epoch - # Note: this will be significantly rewritten by https://github.com/crossbeam-rs/crossbeam/pull/591. - if [[ "$OSTYPE" == "linux"* ]]; then - cargo clean - - # TODO: Once `cfg(sanitize = "..")` is stable, replace - # `cfg(crossbeam_sanitize)` with `cfg(sanitize = "..")` and remove - # `--cfg crossbeam_sanitize`. - ASAN_OPTIONS="detect_odr_violation=0 detect_leaks=0" \ - RUSTFLAGS="-Z sanitizer=address --cfg crossbeam_sanitize" \ - cargo run \ - --release \ - --target x86_64-unknown-linux-gnu \ - --features nightly \ - --example sanitize \ - --manifest-path crossbeam-epoch/Cargo.toml - fi fi diff --git a/ci/tsan b/ci/tsan new file mode 100644 index 000000000..2114a4419 --- /dev/null +++ b/ci/tsan @@ -0,0 +1,13 @@ +# TSAN suppressions file for crossbeam + +# The epoch-based GC uses fences. +race:crossbeam_epoch + +# Push and steal operations in crossbeam-deque may cause data races, but such +# data races are safe. If a data race happens, the value read by `steal` is +# forgotten and the steal operation is then retried. +race:crossbeam_deque*push +race:crossbeam_deque*steal + +# Non-lock-free AtomicCell uses SeqLock which uses fences. +race:crossbeam_utils::atomic::atomic_cell::AtomicCell::compare_exchange diff --git a/crossbeam-channel/tests/tick.rs b/crossbeam-channel/tests/tick.rs index 5dc87306f..1273f64b5 100644 --- a/crossbeam-channel/tests/tick.rs +++ b/crossbeam-channel/tests/tick.rs @@ -127,6 +127,7 @@ fn recv() { assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); } +#[cfg(not(crossbeam_sanitize))] // TODO: assertions failed due to tsan is slow #[test] fn recv_timeout() { let start = Instant::now(); @@ -251,6 +252,7 @@ fn select() { assert_eq!(hits.load(Ordering::SeqCst), 8); } +#[cfg(not(crossbeam_sanitize))] // TODO: assertions failed due to tsan is slow #[test] fn ready() { const THREADS: usize = 4; diff --git a/crossbeam-epoch/src/collector.rs b/crossbeam-epoch/src/collector.rs index c94244b84..7cfb8192a 100644 --- a/crossbeam-epoch/src/collector.rs +++ b/crossbeam-epoch/src/collector.rs @@ -199,6 +199,7 @@ mod tests { .unwrap(); } + #[cfg(not(crossbeam_sanitize))] // TODO: assertions failed due to `cfg(crossbeam_sanitize)` reduce `internal::MAX_OBJECTS` #[test] fn incremental() { const COUNT: usize = 100_000; diff --git a/crossbeam-epoch/src/internal.rs b/crossbeam-epoch/src/internal.rs index 2f11a7a90..966bceb2e 100644 --- a/crossbeam-epoch/src/internal.rs +++ b/crossbeam-epoch/src/internal.rs @@ -375,6 +375,7 @@ pub(crate) struct Local { // Make sure `Local` is less than or equal to 2048 bytes. // https://github.com/crossbeam-rs/crossbeam/issues/551 +#[cfg(not(crossbeam_sanitize))] // `crossbeam_sanitize` reduces the size of `Local` #[test] fn local_size() { assert!(