This is an automated email from the ASF dual-hosted git repository.

zeroshade pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-go.git


The following commit(s) were added to refs/heads/main by this push:
     new 9e0b802a ci: use clang ASan runtime for -asan tests (#1299)
9e0b802a is described below

commit 9e0b802a81ef7796af80bde49a631f54b284e872
Author: Matt Topol <[email protected]>
AuthorDate: Fri Sep 18 10:14:35 2026 -0700

    ci: use clang ASan runtime for -asan tests (#1299)
    
    ### Rationale for this change
    
    The Ubuntu RC verification job intermittently fails with
    AddressSanitizer *internal* CHECK failures under `go test -asan`,
    striking arbitrary pure-Go packages with no failing Go assertion. Most
    recent occurrence on `main`:
    https://github.com/apache/arrow-go/actions/runs/34250658983/job/102145334765
    —
    
    ```text
    AddressSanitizer: CHECK failed: sanitizer_common.h:522 "((i)) < ((size_))"
      #2 InternalMmapVectorNoCtor<ThreadContextBase*>::operator[]
      #3 ThreadRegistry::StartThread   sanitizer_thread_registry.cpp:314
      #4 AsanThread::ThreadStart
    FAIL github.com/apache/arrow-go/v18/arrow/util 0.287s
    ```
    
    Sibling signatures over the last month include
    `sanitizer_thread_registry.cpp:161/316`,
    `sanitizer_allocator_secondary.h:297`, and plain segfaults, hitting
    `arrow/float16`, `arrow/internal/dictutils`, `arrow/util`, and
    `arrow/flight/flightsql/example`. Every crash site is GCC libasan's
    thread-registry/allocator bookkeeping at thread start or exit: the Go
    runtime creates and retires threads in ways GCC's libasan thread
    registry does not tolerate. This is the dominant recurring RC
    verification flake (~5 of the last ~11 Ubuntu failures).
    
    ### What changes are included in this PR?
    
    When `ci/scripts/test.sh` selects `-asan`, export
    `CC=clang`/`CXX=clang++` if clang is available and `CC` is not already
    set, so the test binaries link LLVM's compiler-rt ASan runtime instead
    of GCC's libasan. Environments without clang (e.g. the Debian golang
    images) keep the current behavior unchanged.
    
    `ubuntu-latest` runners ship clang, so the RC `Verify (ubuntu-latest)`
    job — the only recurring victim — picks up the LLVM runtime
    automatically.
    
    ### Are these changes tested?
    
    - `bash -n` and `shellcheck` pass on the edited script
    - This PR's own RC `Verify (ubuntu-latest)` check executes exactly this
    code path with clang
    
    ### Are there any user-facing changes?
    
    No, CI-only.
    
    ---------
    
    Signed-off-by: Matt Topol <[email protected]>
---
 .github/workflows/rc.yml       |  9 +++++++++
 ci/docker/debian-12.dockerfile |  9 +++++++++
 ci/scripts/test.sh             | 45 +++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 62 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/rc.yml b/.github/workflows/rc.yml
index e0ee7944..a20c24ab 100644
--- a/.github/workflows/rc.yml
+++ b/.github/workflows/rc.yml
@@ -96,6 +96,15 @@ jobs:
         uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # 
v6.2.0
         with:
           go-version-file: go.mod
+      - name: Install ASan runtime
+        # ci/scripts/test.sh only runs -asan against an LLVM >= 19 runtime;
+        # ubuntu-latest still defaults to clang 18.
+        if: runner.os == 'Linux'
+        run: |
+          sudo apt-get update
+          sudo apt-get install -y --no-install-recommends \
+            clang-19 \
+            libclang-rt-19-dev
       - name: Verify
         run: |
           tar_gz=$(echo apache-arrow-go-*.tar.gz)
diff --git a/ci/docker/debian-12.dockerfile b/ci/docker/debian-12.dockerfile
index 95187272..e2885812 100644
--- a/ci/docker/debian-12.dockerfile
+++ b/ci/docker/debian-12.dockerfile
@@ -19,6 +19,15 @@ ARG arch=amd64
 ARG go=1.24
 FROM ${arch}/golang:${go}-bookworm
 
+# ci/scripts/test.sh only runs -asan against an LLVM >= 19 runtime; the
+# libsanitizer shipped with the image's GCC predates the thread-registry fix.
+RUN apt-get update -y -q && \
+    apt-get install -y -q --no-install-recommends \
+        clang-19 \
+        libclang-rt-19-dev && \
+    apt-get clean && \
+    rm -rf /var/lib/apt/lists/*
+
 # Copy the go.mod and go.sum over and pre-download all the dependencies
 COPY . /arrow-go
 RUN cd /arrow-go && \
diff --git a/ci/scripts/test.sh b/ci/scripts/test.sh
index 411fd35a..45719d45 100755
--- a/ci/scripts/test.sh
+++ b/ci/scripts/test.sh
@@ -25,6 +25,22 @@ export PARQUET_TEST_DATA=${1}/parquet-testing/data
 export PARQUET_TEST_BAD_DATA=${1}/parquet-testing/bad_data
 export ARROW_TEST_DATA=${1}/arrow-testing/data
 
+# Go's -asan links whatever AddressSanitizer runtime the C toolchain ships.
+# Runtimes older than LLVM 19 allocate thread contexts from the global
+# low-level allocator, which is not thread safe, so they corrupt their own
+# thread registry when the Go runtime creates and retires threads
+# concurrently and abort test binaries at random in arbitrary packages
+# (llvm/llvm-project#87324, fixed by llvm/llvm-project#88177). GCC's
+# libsanitizer snapshot predates that fix as well, so -asan is only reliable
+# when a clang >= 19 runtime is available.
+asan_runtime_major() {
+  local version
+  version=$("${1}" --version 2>/dev/null | head -1) || return 1
+  [[ "${version}" = *"clang version "* ]] || return 1
+  version=${version##*clang version }
+  echo "${version%%.*}"
+}
+
 case "$(uname)" in
 MINGW*)
   # -race and -asan don't work on Windows currently
@@ -39,7 +55,34 @@ MINGW*)
       # -asan not supported on darwin/amd64
       test_args=("-race")
     else
-      test_args=("-asan")
+      asan_cc=${CC:-}
+      if [[ -z "${asan_cc}" ]]; then
+        # Prefer the default clang when it is new enough, otherwise the
+        # newest explicitly installed one.
+        for candidate in clang clang-21 clang-20 clang-19; do
+          command -v "${candidate}" >/dev/null 2>&1 || continue
+          major=$(asan_runtime_major "${candidate}") || continue
+          if [[ "${major}" -ge 19 ]]; then
+            asan_cc=${candidate}
+            break
+          fi
+        done
+      fi
+
+      major=$(asan_runtime_major "${asan_cc:-false}") || major=0
+      if [[ "${major}" -ge 19 ]]; then
+        test_args=("-asan")
+        if [[ -z "${CC:-}" ]]; then
+          export CC=${asan_cc}
+          if [[ -z "${CXX:-}" ]] && command -v "${asan_cc/clang/clang++}" 
>/dev/null 2>&1; then
+            export CXX=${asan_cc/clang/clang++}
+          fi
+        fi
+      else
+        # Every available ASan runtime predates the fix; -asan would abort at
+        # random, so run the race detector instead.
+        test_args=("-race")
+      fi
     fi
   fi
   ;;

Reply via email to