diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 89a651798..9f3215329 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -114,12 +114,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 @@ -151,6 +162,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..fbe6b9429 --- /dev/null +++ b/ci/san.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +cd "$(dirname "$0")"/.. +set -ex + +if [[ "$OSTYPE" != "linux"* ]]; then + exit 0 +fi + +rustup component add rust-src + +# Run address sanitizer +cargo clean +# https://github.com/crossbeam-rs/crossbeam/issues/614 +export ASAN_OPTIONS="detect_leaks=0" +# TODO: Once `cfg(sanitize = "..")` is stable, replace +# `cfg(crossbeam_sanitize)` with `cfg(sanitize = "..")` and remove +# `--cfg crossbeam_sanitize`. +RUSTFLAGS="-D warnings -Z sanitizer=address --cfg crossbeam_sanitize" \ +cargo test --all --release --target x86_64-unknown-linux-gnu --tests --exclude benchmarks + +RUSTFLAGS="-D warnings -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 + +# Run memory sanitizer +cargo clean +RUSTFLAGS="-D warnings -Z sanitizer=memory --cfg crossbeam_sanitize" \ +cargo test -Zbuild-std --all --release --target x86_64-unknown-linux-gnu --tests --exclude benchmarks + +# Run thread sanitizer +cargo clean +export TSAN_OPTIONS="suppressions=$(pwd)/ci/tsan" +RUSTFLAGS="-D warnings -Z sanitizer=thread --cfg crossbeam_sanitize" \ +cargo test -Zbuild-std --all --release --target x86_64-unknown-linux-gnu --tests --exclude benchmarks diff --git a/ci/test.sh b/ci/test.sh index fe14ee846..7fc76548e 100755 --- a/ci/test.sh +++ b/ci/test.sh @@ -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..2b0c2aded --- /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 + +# FIXME: ??? +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 8c582f1f1..451bda15e 100644 --- a/crossbeam-epoch/src/collector.rs +++ b/crossbeam-epoch/src/collector.rs @@ -14,9 +14,9 @@ /// ``` use core::fmt; -use crate::primitive::sync::Arc; use crate::guard::Guard; use crate::internal::{Global, Local}; +use crate::primitive::sync::Arc; /// An epoch-based garbage collector. pub struct Collector { @@ -199,6 +199,7 @@ mod tests { .unwrap(); } + #[cfg(not(crossbeam_sanitize))] // TODO: assertions failed due to tsan is slow #[test] fn incremental() { const COUNT: usize = 100_000; diff --git a/crossbeam-epoch/src/internal.rs b/crossbeam-epoch/src/internal.rs index 4da5d0268..41bc292bb 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!(