diff --git a/.github/workflows/ci.yml b/.github/workflows/ci-miri.yaml similarity index 82% rename from .github/workflows/ci.yml rename to .github/workflows/ci-miri.yaml index 0d5c3a8..c8c60a4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci-miri.yaml @@ -1,4 +1,4 @@ -name: CI +name: Miri CI on: push: @@ -16,45 +16,45 @@ jobs: steps: - uses: actions/checkout@v3 - name: Setup environment - run: bash ./ci-setup.sh + run: bash ./ci-miri-setup.sh - name: Test - run: bash ./ci-test.sh core + run: bash ./ci-miri-test.sh core test-alloc: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Setup environment - run: bash ./ci-setup.sh + run: bash ./ci-miri-setup.sh - name: Test - run: bash ./ci-test.sh alloc + run: bash ./ci-miri-test.sh alloc test-std: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Setup environment - run: bash ./ci-setup.sh + run: bash ./ci-miri-setup.sh - name: Test - run: bash ./ci-test.sh std + run: bash ./ci-miri-test.sh std test-simd: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Setup environment - run: bash ./ci-setup.sh + run: bash ./ci-miri-setup.sh - name: Test - run: bash ./ci-test.sh simd + run: bash ./ci-miri-test.sh simd test-stdarch: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Setup environment - run: bash ./ci-setup.sh + run: bash ./ci-miri-setup.sh - name: Test - run: bash ./ci-test.sh stdarch + run: bash ./ci-miri-test.sh stdarch # Send a Zulip notification when a cron job fails cron-fail-notify: diff --git a/.github/workflows/ci-sanitizers.yaml b/.github/workflows/ci-sanitizers.yaml new file mode 100644 index 0000000..f54e21f --- /dev/null +++ b/.github/workflows/ci-sanitizers.yaml @@ -0,0 +1,88 @@ +name: Sanitizers CI + +on: + push: + branches: + - 'master' + pull_request: + branches: + - 'master' + schedule: + - cron: '00 2 * * *' # At 02:00 UTC every day (like rustup-components-history). + +jobs: + test-core: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Setup environment + run: bash ./ci-sanitizers-setup.sh + - name: Test ASAN + run: bash ./ci-sanitizers-test.sh core address + - name: Test MSAN + run: bash ./ci-sanitizers-test.sh core memory + - name: Test TSAN + run: bash ./ci-sanitizers-test.sh core thread + - name: Test CFISAN + run: bash ./ci-sanitizers-test.sh core cfi + + # test-alloc: + # runs-on: ubuntu-latest + # steps: + # - uses: actions/checkout@v3 + # - name: Setup environment + # run: bash ./ci-sanitizers-setup.sh + # - name: Test + # run: bash ./ci-sanitizers-test.sh alloc + + # test-std: + # runs-on: ubuntu-latest + # steps: + # - uses: actions/checkout@v3 + # - name: Setup environment + # run: bash ./ci-sanitizers-setup.sh + # - name: Test + # run: bash ./ci-sanitizers-test.sh std + + # test-simd: + # runs-on: ubuntu-latest + # steps: + # - uses: actions/checkout@v3 + # - name: Setup environment + # run: bash ./ci-sanitizers-setup.sh + # - name: Test + # run: bash ./ci-sanitizers-test.sh simd + + # test-stdarch: + # runs-on: ubuntu-latest + # steps: + # - uses: actions/checkout@v3 + # - name: Setup environment + # run: bash ./ci-sanitizers-setup.sh + # - name: Test + # run: bash ./ci-sanitizers-test.sh stdarch + + # Send a Zulip notification when a cron job fails + # cron-fail-notify: + # name: cronjob failure notification + # runs-on: ubuntu-latest + # needs: [test-core, test-alloc, test-std, test-simd] + # if: github.event_name == 'schedule' && (failure() || cancelled()) + # steps: + # - name: Install zulip-send + # run: pip3 install zulip + # - name: Send Zulip notification + # shell: bash + # env: + # ZULIP_BOT_EMAIL: ${{ secrets.ZULIP_BOT_EMAIL }} + # ZULIP_API_TOKEN: ${{ secrets.ZULIP_API_TOKEN }} + # run: | + # ~/.local/bin/zulip-send --stream miri --subject "Cron Job Failure (miri-test-libstd, $(date -u +%Y-%m))" \ + # --message 'Dear @*T-miri*, + + # The standard library test suite is [failing under Miri]('"https://github.com/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID"'). Would you mind investigating this issue? + + # Thanks in advance! + # Sincerely, + # The Miri Cronjobs Bot' \ + # --user $ZULIP_BOT_EMAIL --api-key $ZULIP_API_TOKEN --site https://rust-lang.zulipchat.com diff --git a/Cargo.toml b/Cargo.toml index acaab28..8826ef6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,8 @@ [workspace] members = [ - "core_miri_test", - "alloc_miri_test", - "std_miri_test", + "core_sanity_test", + "alloc_sanity_test", + "std_sanity_test", ] exclude = [ diff --git a/alloc_miri_test/Cargo.toml b/alloc_sanity_test/Cargo.toml similarity index 92% rename from alloc_miri_test/Cargo.toml rename to alloc_sanity_test/Cargo.toml index ac870d2..4133f8c 100644 --- a/alloc_miri_test/Cargo.toml +++ b/alloc_sanity_test/Cargo.toml @@ -1,12 +1,12 @@ [package] -name = "alloc_miri_test" +name = "alloc_sanity_test" version = "0.0.0" autotests = false autobenches = false edition = "2021" [lib] -name = "alloc_miri_test" +name = "alloc_sanity_test" path = "../library/alloc/src/lib.rs" [features] diff --git a/ci-setup.sh b/ci-miri-setup.sh similarity index 100% rename from ci-setup.sh rename to ci-miri-setup.sh diff --git a/ci-test.sh b/ci-miri-test.sh similarity index 94% rename from ci-test.sh rename to ci-miri-test.sh index 4cc514a..6c2b64f 100644 --- a/ci-test.sh +++ b/ci-miri-test.sh @@ -5,9 +5,10 @@ DEFAULTFLAGS="-Zmiri-retag-fields -Zrandomize-layout -Zmiri-strict-provenance" # apply our patch rm -rf rust-src-patched -cp -a $(rustc --print sysroot)/lib/rustlib/src/rust/ rust-src-patched +cp -a "$(rustc --print sysroot)/lib/rustlib/src/rust/" rust-src-patched ( cd rust-src-patched && patch -f -p1 < ../rust-src.diff >/dev/null ) || ( echo "Applying rust-src.diff failed!" && exit 1 ) -export MIRI_LIB_SRC=$(pwd)/rust-src-patched/library +MIRI_LIB_SRC="$(pwd)/rust-src-patched/library" +export MIRI_LIB_SRC # run the tests (some also without validation, to exercise those code paths in Miri) case "$1" in @@ -60,13 +61,13 @@ std) echo "::group::Testing std core ($CORE on $TARGET)" MIRIFLAGS="$DEFAULTFLAGS -Zmiri-disable-isolation" \ ./run-test.sh std --target $TARGET --lib --tests \ - -- $CORE \ + -- "$CORE" \ 2>&1 | ts -i '%.s ' echo "::endgroup::" echo "::group::Testing std core docs ($CORE on $TARGET, ignore leaks)" MIRIFLAGS="$DEFAULTFLAGS -Zmiri-ignore-leaks -Zmiri-disable-isolation" \ ./run-test.sh std --target $TARGET --doc \ - -- $CORE \ + -- "$CORE" \ 2>&1 | ts -i '%.s ' echo "::endgroup::" done @@ -85,7 +86,7 @@ std) echo "::endgroup::" ;; simd) - cd $MIRI_LIB_SRC/portable-simd + cd "$MIRI_LIB_SRC/portable-simd" export RUSTFLAGS="-Ainternal_features ${RUSTFLAGS:-}" export RUSTDOCFLAGS="-Ainternal_features ${RUSTDOCFLAGS:-}" @@ -108,7 +109,7 @@ stdarch) for TARGET in x86_64-unknown-linux-gnu i686-unknown-linux-gnu; do echo "::group::Testing stdarch ($TARGET)" MIRIFLAGS="$DEFAULTFLAGS" \ - ./run-stdarch-test.sh $TARGET \ + ./miri-run-stdarch-test.sh $TARGET \ 2>&1 | ts -i '%.s ' echo "::endgroup::" done diff --git a/ci-sanitizers-setup.sh b/ci-sanitizers-setup.sh new file mode 100644 index 0000000..a996424 --- /dev/null +++ b/ci-sanitizers-setup.sh @@ -0,0 +1,18 @@ +#!/bin/bash +set -eauxo pipefail + +## Shared setup code for CI jobs + +# We need 'ts' +sudo apt-get -y install moreutils +echo + +# And of course we need Rust +if [[ "$GITHUB_EVENT_NAME" == 'schedule' ]]; then + RUST_TOOLCHAIN=nightly +else + RUST_TOOLCHAIN=$(cat rust-version) +fi +echo "Installing Rust version: $RUST_TOOLCHAIN" +rustup toolchain install "$RUST_TOOLCHAIN" +rustup override set "$RUST_TOOLCHAIN" diff --git a/ci-sanitizers-test.sh b/ci-sanitizers-test.sh new file mode 100755 index 0000000..2c912db --- /dev/null +++ b/ci-sanitizers-test.sh @@ -0,0 +1,174 @@ +#!/bin/bash +set -eauxo pipefail + +DEFAULTFLAGS="-Zrandomize-layout" + +# apply our patch +rm -rf rust-src-patched +cp -a "$(rustc --print sysroot)/lib/rustlib/src/rust/" rust-src-patched +( cd rust-src-patched && patch -f -p1 < ../rust-src.diff >/dev/null ) || ( echo "Applying rust-src.diff failed!" && exit 1 ) +LIB_SRC="$(pwd)/rust-src-patched/library" +export LIB_SRC + +case "$2" in +address) + # FIXME: if on aarch64-{unknown}-linux-{android, gnu}, we can use `hwaddress` + # instead of `address` which should be faster. Unfortunately we probably + # don't have that in CI + SANITIZER=address + ;; +hwaddress) + # see above + echo "we don't have a CI target for this yet" + exit 1 + ;; +kasan) + echo "we aren't a kernel, can't use kasan" + exit 1 + ;; +memory) + SANITIZER=memory + ;; +memtag) + # FIXME: alternative to MSAN with the same target restrictions as hwaddress + SANITIZER=memtag + ecbo "we don't have a CI target for this yet" + exit 1 + ;; +cfi) + SANITIZER=cfi + # cfi needs LTO + EXTRAFLAGS="-Clto" + ;; +kcfi) + SANITIZER=kcfi + ;; +safestack) + # FIXME: aarch64-linux-android only + SANITIZER=safestack + ;; +shadow-call-stack) + SANITIZER=shadow-call-stack + echo "we don't have a CI target for this yet" + exit 1 + ;; +leak) + SANITIZER=leak + ;; +thread) + SANITIZER=thread + ;; +*) + echo "unknown sanitizer $2" + exit 1 +esac + + +# run the tests (some also without validation, to exercise those code paths in Miri) +case "$1" in +core) + # A 64bit little-endian and a 32bit big-endian target. + # (Varying the OS is totally pointless for core.) + for TARGET in x86_64-unknown-linux-gnu mips-unknown-linux-gnu; do + echo "::group::Testing core ($TARGET, $SANITIZER)" + RUSTFLAGS="$DEFAULTFLAGS -Zsanitizer=$SANITIZER $EXTRAFLAGS" \ + ./run-test.sh core --target $TARGET --lib --tests \ + -- --skip align \ + 2>&1 | ts -i '%.s ' + echo "::endgroup::" + echo "::group::Testing core ($TARGET, $SANITIZER)" + RUSTFLAGS="$DEFAULTFLAGS -Zsanitizer=$SANITIZER $EXTRAFLAGS" \ + ./run-test.sh core --target $TARGET --lib --tests \ + 2>&1 | ts -i '%.s ' + echo "::endgroup::" + echo "::group::Testing core docs ($TARGET, $SANITIZER)" && echo + RUSTFLAGS="$DEFAULTFLAGS -Zsanitizer=$SANITIZER $EXTRAFLAGS" \ + ./run-test.sh core --target $TARGET --doc \ + 2>&1 | ts -i '%.s ' + echo "::endgroup::" + done + ;; +alloc) + # A 64bit little-endian and a 32bit big-endian target. + # (Varying the OS is not really worth it for alloc.) + for TARGET in x86_64-unknown-linux-gnu mips-unknown-linux-gnu; do + echo "::group::Testing alloc ($SANITIZER, $TARGET, $SANITIZER)" + RUSTFLAGS="$DEFAULTFLAGS -Zsanitizer=$SANITIZER $EXTRAFLAGS" \ + ./run-test.sh alloc --target $TARGET --lib --tests \ + 2>&1 | ts -i '%.s ' + echo "::endgroup::" + echo "::group::Testing alloc docs ($TARGET, $SANITIZER)" + RUSTFLAGS="$DEFAULTFLAGS -Zsanitizer=$SANITIZER $EXTRAFLAGS" \ + ./run-test.sh alloc --target $TARGET --doc \ + 2>&1 | ts -i '%.s ' + echo "::endgroup::" + done + ;; +std) + # Modules that we skip entirely, because they need a lot of shims we don't support. + SKIP="fs:: net:: process:: sys:: sys_common::net::" + # Core modules, that we are testing on a bunch of targets. + # These are the most OS-specific (among the modules we do not skip). + CORE="time:: sync:: thread:: env::" + + for TARGET in x86_64-unknown-linux-gnu aarch64-apple-darwin x86_64-pc-windows-msvc i686-pc-windows-gnu; do + echo "::group::Testing std core ($CORE on $TARGET)" + RUSTFLAGS="$DEFAULTFLAGS -Zsanitizer=$SANITIZER $EXTRAFLAGS" \ + ./run-test.sh std --target $TARGET --lib --tests \ + -- $CORE \ + 2>&1 | ts -i '%.s ' + echo "::endgroup::" + echo "::group::Testing std core docs ($CORE on $TARGET, $SANITIZER)" + RUSTFLAGS="$DEFAULTFLAGS -Zsanitizer=$SANITIZER $EXTRAFLAGS" \ + ./run-test.sh std --target $TARGET --doc \ + -- $CORE \ + 2>&1 | ts -i '%.s ' + echo "::endgroup::" + done + # "sleep" has a thread leak that we have to ignore + echo "::group::Testing remaining std (all except for $SKIP, $SANITIZER)" + RUSTFLAGS="$DEFAULTFLAGS -Zsanitizer=$SANITIZER $EXTRAFLAGS" \ + ./run-test.sh std --lib --tests \ + -- $(for M in $CORE; do echo "--skip $M "; done) $(for M in $SKIP; do echo "--skip $M "; done) \ + 2>&1 | ts -i '%.s ' + echo "::endgroup::" + echo "::group::Testing remaining std docs (all except for $SKIP, $SANITIZER)" + RUSTFLAGS="$DEFAULTFLAGS -Zsanitizer=$SANITIZER $EXTRAFLAGS" \ + ./run-test.sh std --doc \ + -- $(for M in $CORE; do echo "--skip $M "; done) $(for M in $SKIP; do echo "--skip $M "; done) \ + 2>&1 | ts -i '%.s ' + echo "::endgroup::" + ;; +simd) + cd $LIB_SRC/portable-simd + export RUSTFLAGS="-Ainternal_features ${RUSTFLAGS:-}" + export RUSTDOCFLAGS="-Ainternal_features ${RUSTDOCFLAGS:-}" + + echo "::group::Testing portable-simd" + RUSTFLAGS="$DEFAULTFLAGS -Zsanitizer=$SANITIZER $EXTRAFLAGS" \ + cargo miri test --lib --tests -- --skip ptr \ + 2>&1 | ts -i '%.s ' + # This contains some pointer tests that do int/ptr casts, so we need permissive provenance. + RUSTFLAGS="$DEFAULTFLAGS -Zsanitizer=$SANITIZER $EXTRAFLAGS" \ + cargo miri test --lib --tests -- ptr \ + 2>&1 | ts -i '%.s ' + echo "::endgroup::" + echo "::group::Testing portable-simd docs" + RUSTFLAGS="$DEFAULTFLAGS -Zsanitizer=$SANITIZER $EXTRAFLAGS" \ + cargo miri test --doc \ + 2>&1 | ts -i '%.s ' + echo "::endgroup::" + ;; +stdarch) + for TARGET in x86_64-unknown-linux-gnu i686-unknown-linux-gnu; do + echo "::group::Testing stdarch ($TARGET, $SANITIZER)" + RUSTFLAGS="$DEFAULTFLAGS -Zsanitizer=$SANITIZER $EXTRAFLAGS" \ + ./run-stdarch-test.sh $TARGET \ + 2>&1 | ts -i '%.s ' + echo "::endgroup::" + done + ;; +*) + echo "Unknown command" + exit 1 +esac diff --git a/core_miri_test/Cargo.toml b/core_sanity_test/Cargo.toml similarity index 92% rename from core_miri_test/Cargo.toml rename to core_sanity_test/Cargo.toml index 3188dea..b2709fe 100644 --- a/core_miri_test/Cargo.toml +++ b/core_sanity_test/Cargo.toml @@ -1,13 +1,13 @@ [package] authors = ["The Rust Project Developers"] -name = "core_miri_test" +name = "core_sanity_test" version = "0.0.0" autotests = false autobenches = false edition = "2021" [lib] -name = "core_miri_test" +name = "core_sanity_test" path = "../library/core/src/lib.rs" test = false bench = false diff --git a/run-stdarch-test.sh b/miri-run-stdarch-test.sh similarity index 98% rename from run-stdarch-test.sh rename to miri-run-stdarch-test.sh index f978302..a22f913 100755 --- a/run-stdarch-test.sh +++ b/miri-run-stdarch-test.sh @@ -58,7 +58,7 @@ export STDARCH_TEST_EVERYTHING=1 # Needed to pass the STDARCH_TEST_EVERYTHING environment variable export MIRIFLAGS="${MIRIFLAGS:-} -Zmiri-disable-isolation" -cd $MIRI_LIB_SRC/stdarch +cd "$MIRI_LIB_SRC/stdarch" cargo miri test \ --target "$TARGET" \ --manifest-path=crates/core_arch/Cargo.toml \ diff --git a/run-test.sh b/miri-run-test.sh similarity index 100% rename from run-test.sh rename to miri-run-test.sh diff --git a/sanitizers-run-stdarch-test.sh b/sanitizers-run-stdarch-test.sh new file mode 100755 index 0000000..8777d7a --- /dev/null +++ b/sanitizers-run-stdarch-test.sh @@ -0,0 +1,63 @@ +#!/bin/bash +set -eauxo pipefail + +## Run stdarch test suite with sanitizers. +## Usage: +## ./run-test.sh TARGET +## Environment variables: +## LIB_SRC: The path to the Rust library directory (`library`). +## RUSTFLAGS: rustc flags (optional) +## MIRIFLAGS: Miri flags (optional) + +if [ $# -ne 1 ]; then + echo "Usage: $0 TARGET" + exit 1 +fi + +export TARGET="$1" + +case "$TARGET" in +i586-*|i686-*|x86_64-*) + RUSTFLAGS="$RUSTFLAGS -C target-feature=+ssse3" + # TEST_ARGS=( + # core_arch::x86::{sse,sse2,sse3,ssse3}:: + # core_arch::x86_64::{sse,sse2}:: + # # FIXME add `#[cfg_attr(miri, ignore)]` to those tests in stdarch + # # These are nontemporal stores, fences, and CSR (FP env status register) accesses + # --skip test_mm_comieq_ss_vs_ucomieq_ss + # --skip test_mm_getcsr_setcsr_1 + # --skip test_mm_getcsr_setcsr_2 + # --skip test_mm_getcsr_setcsr_underflow + # --skip test_mm_sfence + # --skip test_mm_stream_ps + # --skip test_mm_clflush + # --skip test_mm_lfence + # --skip test_mm_maskmoveu_si128 + # --skip test_mm_mfence + # --skip test_mm_stream_pd + # --skip test_mm_stream_si128 + # --skip test_mm_stream_si32 + # --skip test_mm_stream_si64 + # # FIXME fix those in stdarch + # --skip test_mm_rcp_ss # __m128(0.24997461, 13.0, 16.0, 100.0) != __m128(0.24993896, 13.0, 16.0, 100.0) + # --skip test_mm_store1_ps # attempt to subtract with overflow + # --skip test_mm_store_ps # attempt to subtract with overflow + # --skip test_mm_storer_ps # attempt to subtract with overflow + # ) + ;; +*) + echo "Unknown target $TARGET" + exit 1 +esac + +export RUSTFLAGS="$RUSTFLAGS -Ainternal_features" + +# Make sure all tested target features are enabled +export STDARCH_TEST_EVERYTHING=1 +# Needed to pass the STDARCH_TEST_EVERYTHING environment variable + +cd "$LIB_SRC/stdarch" +cargo test \ + --target "$TARGET" \ + --manifest-path=crates/core_arch/Cargo.toml \ + -- "${TEST_ARGS[@]}" diff --git a/sanitizers-run-test.sh b/sanitizers-run-test.sh new file mode 100755 index 0000000..d21d9bd --- /dev/null +++ b/sanitizers-run-test.sh @@ -0,0 +1,40 @@ +#!/bin/bash +set -eauxo pipefail + +## Run a Rust libstd test suite with Miri. +## Usage: +## ./run-test.sh CRATE_NAME CARGO_TEST_ARGS +## Environment variables: +## LIB_SRC: The path to the Rust library directory (`library`). +## Defaults to `$(rustc --print sysroot)/lib/rustlib/src/rust/library`. + +CRATE=${1:-} +if [[ -z "$CRATE" ]]; then + echo "Usage: $0 CRATE_NAME" + exit 1 +fi +shift + +# compute the library directory +LIB_SRC=${LIB_SRC:-$(rustc --print sysroot)/lib/rustlib/src/rust/library} +if ! test -d "$LIB_SRC/core"; then + echo "Rust source dir ($LIB_SRC) does not contain a 'core' subdirectory." + echo "Set LIB_SRC to the Rust source directory, or install the rust-src component." + exit 1 +fi +# macOS does not have a useful readlink/realpath so we have to use Python instead... +LIB_SRC=$(python3 -c 'import os, sys; print(os.path.realpath(sys.argv[1]))' "$LIB_SRC") +export LIB_SRC + +# update symlink +rm -f library +ln -s "$LIB_SRC" library + +# use the rust-src lockfile +cp "$LIB_SRC/../Cargo.lock" Cargo.lock + +echo "running test with RUSTFLAGS ${RUSTFLAGS}" + +# run test +cd "./${CRATE}_sanity_test" +cargo test "$@" diff --git a/std_miri_test/Cargo.toml b/std_sanity_test/Cargo.toml similarity index 96% rename from std_miri_test/Cargo.toml rename to std_sanity_test/Cargo.toml index 2240c34..31954d4 100644 --- a/std_miri_test/Cargo.toml +++ b/std_sanity_test/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "std_miri_test" +name = "std_sanity_test" version = "0.0.0" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust.git" @@ -10,7 +10,7 @@ edition = "2021" build = "../library/std/build.rs" [lib] -name = "std_miri_test" +name = "std_sanity_test" path = "../library/std/src/lib.rs" [dependencies]