From 3612a8a98258a9d9002ba8ba7f018bc30b37a051 Mon Sep 17 00:00:00 2001 From: Ivan K Date: Sun, 19 Jan 2025 13:52:59 +0300 Subject: [PATCH 1/4] Add the check workflow with sanitizers enabled --- .github/workflows/LSan.supp | 1 + .github/workflows/sanitizers.yaml | 51 +++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 .github/workflows/LSan.supp create mode 100644 .github/workflows/sanitizers.yaml diff --git a/.github/workflows/LSan.supp b/.github/workflows/LSan.supp new file mode 100644 index 000000000..6ac2d14a1 --- /dev/null +++ b/.github/workflows/LSan.supp @@ -0,0 +1 @@ +leak:libfontconfig.so diff --git a/.github/workflows/sanitizers.yaml b/.github/workflows/sanitizers.yaml new file mode 100644 index 000000000..8433f3bde --- /dev/null +++ b/.github/workflows/sanitizers.yaml @@ -0,0 +1,51 @@ +on: + push: + branches: + - master + - GHA-sanitizers # during development + workflow_dispatch: + pull_request: + +name: R-CMD-check-sanitized + +concurrency: + group: ${{ github.event.pull_request.number || github.run_id }} + cancel-in-progress: true + +jobs: + R-CMD-check-sanitized: + runs-on: ubuntu-latest + container: + image: docker://ghcr.io/r-hub/containers/clang-asan + + strategy: + fail-fast: true + + steps: + - uses: actions/checkout@v4 + + - name: "Set things up" + run: | + # setting aside the other.Rraw dependencies for now (will need cache) + Rscript -e 'pak::pak()' + # this should at least help with the build step + echo "MAKEFLAGS=-j$(nproc)" >> $GITHUB_ENV + # fontconfig is known to leak; add more suppressions as discovered + echo "LSAN_OPTIONS=suppressions=$(realpath .github/workflows/LSan.supp)" >> $GITHUB_ENV + # disabled by default, so reenable (needs suppressions above) + echo "ASAN_OPTIONS=detect_leaks=1" >> $GITHUB_ENV + # otherwise there are literal jumps to ud1 in the code with no diagnostics + sed -i 's/-fsanitize-trap\S*//g' /opt/R/devel-asan/lib/R/etc/Makeconf + + - name: "Build" + run: R CMD build . + + - name: "Check" + run: | + set +e # expect some things to fail and keep going + echo "::group::full R CMD check output" + R CMD check --no-manual data.table_*.tar.gz; res1=$? + echo "::endgroup::" + perl -nle '(print, $a=1) if /: runtime error: |ERROR: LeakSanitizer/../SUMMARY.*Sanitizer/ }{ exit $a' data.table.Rcheck/**/*.Rout*; res2=$? + # fail if R CMD check had failed or if sanitizer output found + [ $res1 -eq 0 ] && [ $res2 -eq 0 ] From 17a6b4fb8767d5167965b6470c2b1197ca32e25d Mon Sep 17 00:00:00 2001 From: Ivan K Date: Tue, 21 Jan 2025 14:03:06 +0300 Subject: [PATCH 2/4] Try a more R-hub-v2-like approach --- .github/workflows/sanitizers.yaml | 44 +++++++++++-------------------- 1 file changed, 15 insertions(+), 29 deletions(-) diff --git a/.github/workflows/sanitizers.yaml b/.github/workflows/sanitizers.yaml index 8433f3bde..b5a657dd1 100644 --- a/.github/workflows/sanitizers.yaml +++ b/.github/workflows/sanitizers.yaml @@ -1,3 +1,8 @@ +name: R-CMD-check-sanitized +concurrency: + group: ${{ github.event.pull_request.number || github.run_id }} + cancel-in-progress: true + on: push: branches: @@ -6,46 +11,27 @@ on: workflow_dispatch: pull_request: -name: R-CMD-check-sanitized - -concurrency: - group: ${{ github.event.pull_request.number || github.run_id }} - cancel-in-progress: true - jobs: - R-CMD-check-sanitized: + linux-containers: runs-on: ubuntu-latest + name: "Check with sanitizers enabled" + strategy: + fail-fast: false container: image: docker://ghcr.io/r-hub/containers/clang-asan - strategy: - fail-fast: true - steps: - - uses: actions/checkout@v4 - - - name: "Set things up" + - uses: r-hub/actions/checkout@v1 + - uses: r-hub/actions/platform-info@v1 + - uses: r-hub/actions/setup-deps@v1 + - name: "Additional setup" run: | - # setting aside the other.Rraw dependencies for now (will need cache) - Rscript -e 'pak::pak()' # this should at least help with the build step echo "MAKEFLAGS=-j$(nproc)" >> $GITHUB_ENV # fontconfig is known to leak; add more suppressions as discovered echo "LSAN_OPTIONS=suppressions=$(realpath .github/workflows/LSan.supp)" >> $GITHUB_ENV # disabled by default, so reenable (needs suppressions above) echo "ASAN_OPTIONS=detect_leaks=1" >> $GITHUB_ENV - # otherwise there are literal jumps to ud1 in the code with no diagnostics + # see r-hub/containers#84 sed -i 's/-fsanitize-trap\S*//g' /opt/R/devel-asan/lib/R/etc/Makeconf - - - name: "Build" - run: R CMD build . - - - name: "Check" - run: | - set +e # expect some things to fail and keep going - echo "::group::full R CMD check output" - R CMD check --no-manual data.table_*.tar.gz; res1=$? - echo "::endgroup::" - perl -nle '(print, $a=1) if /: runtime error: |ERROR: LeakSanitizer/../SUMMARY.*Sanitizer/ }{ exit $a' data.table.Rcheck/**/*.Rout*; res2=$? - # fail if R CMD check had failed or if sanitizer output found - [ $res1 -eq 0 ] && [ $res2 -eq 0 ] + - uses: r-hub/actions/run-check@v1 From 4a7618a847458229970d5bfa37645f77c6770def Mon Sep 17 00:00:00 2001 From: Ivan K Date: Tue, 28 Jan 2025 14:33:33 +0300 Subject: [PATCH 3/4] Rewrite action using rocker/r-devel-ubsan-clang Also: - Do not check pull requests - Try to cache installed dependencies --- .github/workflows/sanitizers.yaml | 86 +++++++++++++++++++++++-------- 1 file changed, 65 insertions(+), 21 deletions(-) diff --git a/.github/workflows/sanitizers.yaml b/.github/workflows/sanitizers.yaml index b5a657dd1..4ac80f013 100644 --- a/.github/workflows/sanitizers.yaml +++ b/.github/workflows/sanitizers.yaml @@ -1,37 +1,81 @@ -name: R-CMD-check-sanitized -concurrency: - group: ${{ github.event.pull_request.number || github.run_id }} - cancel-in-progress: true - on: push: branches: - master - GHA-sanitizers # during development workflow_dispatch: - pull_request: + +name: R-CMD-check-sanitized + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: - linux-containers: + R-CMD-check-sanitized: runs-on: ubuntu-latest - name: "Check with sanitizers enabled" - strategy: - fail-fast: false container: - image: docker://ghcr.io/r-hub/containers/clang-asan + image: docker://docker.io/rocker/r-devel-ubsan-clang + + strategy: + fail-fast: true steps: - - uses: r-hub/actions/checkout@v1 - - uses: r-hub/actions/platform-info@v1 - - uses: r-hub/actions/setup-deps@v1 - - name: "Additional setup" + - uses: actions/checkout@v4 + + - name: "Environment setup" run: | - # this should at least help with the build step + RDscript -e ' + # install the dependencies into a separate directory to cache them + dir.create(lib <- Sys.getenv("R_LIBS_USER"), recursive = TRUE) + writeLines(paste0("R_LIBS_USER=", lib), Sys.getenv("GITHUB_ENV")) + # to be used as the cache key + writeLines(R.version.string, ".R_version_string") + ' echo "MAKEFLAGS=-j$(nproc)" >> $GITHUB_ENV # fontconfig is known to leak; add more suppressions as discovered echo "LSAN_OPTIONS=suppressions=$(realpath .github/workflows/LSan.supp)" >> $GITHUB_ENV - # disabled by default, so reenable (needs suppressions above) - echo "ASAN_OPTIONS=detect_leaks=1" >> $GITHUB_ENV - # see r-hub/containers#84 - sed -i 's/-fsanitize-trap\S*//g' /opt/R/devel-asan/lib/R/etc/Makeconf - - uses: r-hub/actions/run-check@v1 + # disable for most operations just in case + echo "ASAN_OPTIONS=detect_leaks=0" >> $GITHUB_ENV + # needed by udunits2, sf + apt-get update && apt-get install -y libudunits2-dev libgdal-dev + + - name: "Restore dependency cache" + id: dependency-cache + uses: actions/cache/restore@v4 + with: + path: ${{ env.R_LIBS_USER }}/* + key: r-clang-san-deps-${{hashFiles('.R_version_string')}} + + - name: "Install dependencies" + shell: RDscript {0} + run: | + lib <- Sys.getenv('R_LIBS_USER') # why doesn't it work by itself? + if (!requireNamespace('BiocManager')) install.packages('BiocManager', lib = lib) + union( + 'DESCRIPTION' |> read.dcf('Suggests') |> tools:::.split_dependencies() |> names(), + 'inst/tests/other.Rraw' |> parse() |> _[[1]][[3]] |> eval() + ) |> setdiff(tools::standard_package_names()$base) |> + BiocManager::install(lib = lib, update = TRUE, ask = FALSE) + + - name: "Save dependency cache" + uses: actions/cache/save@v4 + with: + path: ${{ env.R_LIBS_USER }}/* + key: ${{ steps.dependency-cache.outputs.cache-primary-key }} + + - name: "Build" + run: | + rm .R_version_string # there's probably a better place for this + RD CMD build . + + - name: "Check" + run: | + set +e # expect some things to fail and keep going + echo "::group::full R CMD check output" + # only use leak checking for this run + ASAN_OPTIONS=detect_leaks=1 RD CMD check --no-manual data.table_*.tar.gz; res1=$? + echo "::endgroup::" + perl -nle '(print, $a=1) if /: runtime error: |ERROR: LeakSanitizer/../SUMMARY.*Sanitizer/ }{ exit $a' data.table.Rcheck/**/*.Rout*; res2=$? + # fail if R CMD check had failed or if sanitizer output found + [ $res1 -eq 0 ] && [ $res2 -eq 0 ] From f218563673162c8e065f578d0662a21311a9b65c Mon Sep 17 00:00:00 2001 From: Ivan K Date: Thu, 13 Feb 2025 23:48:12 +0300 Subject: [PATCH 4/4] Run the sanitizer-enabled checks on GitLab CI Also, add a temporary suppression for R's .Call() due to a minor 'bit' problem. --- .github/workflows/LSan.supp => .dev/lsan.supp | 0 .dev/ubsan.supp | 2 + .github/workflows/sanitizers.yaml | 81 ------------------- .gitlab-ci.yml | 21 ++++- 4 files changed, 22 insertions(+), 82 deletions(-) rename .github/workflows/LSan.supp => .dev/lsan.supp (100%) create mode 100644 .dev/ubsan.supp delete mode 100644 .github/workflows/sanitizers.yaml diff --git a/.github/workflows/LSan.supp b/.dev/lsan.supp similarity index 100% rename from .github/workflows/LSan.supp rename to .dev/lsan.supp diff --git a/.dev/ubsan.supp b/.dev/ubsan.supp new file mode 100644 index 000000000..bea1b67e1 --- /dev/null +++ b/.dev/ubsan.supp @@ -0,0 +1,2 @@ +# TODO(bit>4.5.0.1): remove after a new 'bit' version is on CRAN +function:R_doDotCall diff --git a/.github/workflows/sanitizers.yaml b/.github/workflows/sanitizers.yaml deleted file mode 100644 index 4ac80f013..000000000 --- a/.github/workflows/sanitizers.yaml +++ /dev/null @@ -1,81 +0,0 @@ -on: - push: - branches: - - master - - GHA-sanitizers # during development - workflow_dispatch: - -name: R-CMD-check-sanitized - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - R-CMD-check-sanitized: - runs-on: ubuntu-latest - container: - image: docker://docker.io/rocker/r-devel-ubsan-clang - - strategy: - fail-fast: true - - steps: - - uses: actions/checkout@v4 - - - name: "Environment setup" - run: | - RDscript -e ' - # install the dependencies into a separate directory to cache them - dir.create(lib <- Sys.getenv("R_LIBS_USER"), recursive = TRUE) - writeLines(paste0("R_LIBS_USER=", lib), Sys.getenv("GITHUB_ENV")) - # to be used as the cache key - writeLines(R.version.string, ".R_version_string") - ' - echo "MAKEFLAGS=-j$(nproc)" >> $GITHUB_ENV - # fontconfig is known to leak; add more suppressions as discovered - echo "LSAN_OPTIONS=suppressions=$(realpath .github/workflows/LSan.supp)" >> $GITHUB_ENV - # disable for most operations just in case - echo "ASAN_OPTIONS=detect_leaks=0" >> $GITHUB_ENV - # needed by udunits2, sf - apt-get update && apt-get install -y libudunits2-dev libgdal-dev - - - name: "Restore dependency cache" - id: dependency-cache - uses: actions/cache/restore@v4 - with: - path: ${{ env.R_LIBS_USER }}/* - key: r-clang-san-deps-${{hashFiles('.R_version_string')}} - - - name: "Install dependencies" - shell: RDscript {0} - run: | - lib <- Sys.getenv('R_LIBS_USER') # why doesn't it work by itself? - if (!requireNamespace('BiocManager')) install.packages('BiocManager', lib = lib) - union( - 'DESCRIPTION' |> read.dcf('Suggests') |> tools:::.split_dependencies() |> names(), - 'inst/tests/other.Rraw' |> parse() |> _[[1]][[3]] |> eval() - ) |> setdiff(tools::standard_package_names()$base) |> - BiocManager::install(lib = lib, update = TRUE, ask = FALSE) - - - name: "Save dependency cache" - uses: actions/cache/save@v4 - with: - path: ${{ env.R_LIBS_USER }}/* - key: ${{ steps.dependency-cache.outputs.cache-primary-key }} - - - name: "Build" - run: | - rm .R_version_string # there's probably a better place for this - RD CMD build . - - - name: "Check" - run: | - set +e # expect some things to fail and keep going - echo "::group::full R CMD check output" - # only use leak checking for this run - ASAN_OPTIONS=detect_leaks=1 RD CMD check --no-manual data.table_*.tar.gz; res1=$? - echo "::endgroup::" - perl -nle '(print, $a=1) if /: runtime error: |ERROR: LeakSanitizer/../SUMMARY.*Sanitizer/ }{ exit $a' data.table.Rcheck/**/*.Rout*; res2=$? - # fail if R CMD check had failed or if sanitizer output found - [ $res1 -eq 0 ] && [ $res2 -eq 0 ] diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 91d1a9849..793e73ccd 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -212,6 +212,25 @@ test-lin-ancient-cran: # Restore checking vignettes if upgrading our R dependency means knitr can be installed. - R CMD check --no-manual --no-build-vignettes --ignore-vignettes $(ls -1t data.table_*.tar.gz | head -n 1) +# run the main checks with Address(+Leak),UBSanitizer enabled +test-lin-san: + <<: *test-lin + image: docker.io/rocker/r-devel-ubsan-clang + variables: + # must be set for most of the process because there are pseudo-leaks everywhere + ASAN_OPTIONS: "detect_leaks=0" + # fontconfig is known to leak; add more suppressions as discovered + LSAN_OPTIONS: "suppressions=$CI_PROJECT_DIR/.dev/lsan.supp" + UBSAN_OPTIONS: "suppressions=$CI_PROJECT_DIR/.dev/ubsan.supp" + script: + - ln -svf "$(which RDscript)" "$(which Rscript)" # install-deps will run 'Rscript', we need R-devel+sanitizers + - *install-deps + - >- + ASAN_OPTIONS=detect_leaks=1 RD CMD check --no-manual $(ls -1t data.table_*.tar.gz | head -n 1); res1=$? + perl -nle '(print, $a=1) if /: runtime error: |ERROR: LeakSanitizer/../SUMMARY.*Sanitizer/ }{ exit $a' data.table.Rcheck/**/*.Rout*; res2=$? + # fail if R CMD check had failed or if sanitizer output found + [ $res1 -eq 0 ] && [ $res2 -eq 0 ] + .test-win-template: &test-win <<: *test tags: @@ -311,7 +330,7 @@ integration: - saas-linux-medium-amd64 only: - master - needs: ["mirror-packages","build","test-lin-rel","test-lin-rel-cran","test-lin-dev-gcc-strict-cran","test-lin-dev-clang-cran","test-lin-rel-vanilla","test-lin-ancient-cran","test-win-rel","test-win-dev" ,"test-win-old","test-mac-rel","test-mac-old"] + needs: ["mirror-packages","build","test-lin-rel","test-lin-rel-cran","test-lin-dev-gcc-strict-cran","test-lin-dev-clang-cran","test-lin-rel-vanilla","test-lin-ancient-cran","test-lin-san","test-win-rel","test-win-dev" ,"test-win-old","test-mac-rel","test-mac-old"] script: - R --version - *install-deps ## markdown pkg not present in r-pkgdown image