Skip to content

Commit 33b1dcf

Browse files
authored
Merge pull request scipy#23936 from FFY00/add-asan
BLD/CI: Add ASAN suppressions and CI job
1 parent fa5a334 commit 33b1dcf

File tree

5 files changed

+94
-0
lines changed

5 files changed

+94
-0
lines changed

.github/workflows/macos.yml

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,3 +138,75 @@ jobs:
138138
spin build --with-accelerate
139139
pip install -r requirements/test.txt
140140
spin test
141+
142+
143+
clang_ASAN:
144+
name: Test under AddressSanitizer
145+
runs-on: macos-15
146+
needs: get_commit_message
147+
if: >
148+
needs.get_commit_message.outputs.message == 1
149+
&& (github.repository == 'scipy/scipy' || github.repository == '')
150+
steps:
151+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
152+
with:
153+
submodules: recursive
154+
fetch-tags: true
155+
persist-credentials: false
156+
157+
- name: Set up pyenv
158+
run: |
159+
git clone https://github.com/pyenv/pyenv.git "$HOME/.pyenv"
160+
PYENV_ROOT="$HOME/.pyenv"
161+
PYENV_BIN="$PYENV_ROOT/bin"
162+
PYENV_SHIMS="$PYENV_ROOT/shims"
163+
echo "$PYENV_BIN" >> $GITHUB_PATH
164+
echo "$PYENV_SHIMS" >> $GITHUB_PATH
165+
echo "PYENV_ROOT=$PYENV_ROOT" >> $GITHUB_ENV
166+
167+
- name: Set up LLVM
168+
run: |
169+
brew install llvm@19
170+
LLVM_PREFIX=$(brew --prefix llvm@19)
171+
echo CC="$LLVM_PREFIX/bin/clang" >> $GITHUB_ENV
172+
echo CXX="$LLVM_PREFIX/bin/clang++" >> $GITHUB_ENV
173+
echo LDFLAGS="-L$LLVM_PREFIX/lib" >> $GITHUB_ENV
174+
echo CPPFLAGS="-I$LLVM_PREFIX/include" >> $GITHUB_ENV
175+
176+
- name: Build Python with AddressSanitizer
177+
run: |
178+
CONFIGURE_OPTS="--with-address-sanitizer" pyenv install 3.14t
179+
pyenv global 3.14t
180+
181+
- name: Install NumPy dependencies from PyPI
182+
run: |
183+
pip install meson-python ninja cython
184+
185+
- name: Build NumPy with ASan
186+
run:
187+
pip install numpy --no-binary numpy --no-build-isolation -Csetup-args="-Db_sanitize=address" -v
188+
189+
- name: Install SciPy dependencies from PyPI
190+
run: |
191+
pip install pybind11 pythran spin pooch hypothesis pytest pytest-timeout
192+
193+
- name: Make gfortran-13 on runner image usable
194+
run: |
195+
# Ensure we use gfortran-13 and that its runtime is on the library search path
196+
FC=$(which gfortran-13)
197+
GFORTRAN_LIB=$(dirname `$FC --print-file-name libgfortran.dylib`)
198+
echo DYLD_LIBRARY_PATH=$GFORTRAN_LIB >> "$GITHUB_ENV"
199+
echo FC=$FC >> "$GITHUB_ENV"
200+
201+
- name: Build SciPy with ASan
202+
run: |
203+
# Can't use Meson's `-Db_sanitize=address` because gfortran-13 will choke on it
204+
export CFLAGS="-fsanitize=address -fno-omit-frame-pointer -fsanitize-ignorelist=$(pwd)/tools/asan-ignore.txt -Wno-deprecated-declarations"
205+
export CXXFLAGS="-fsanitize=address -fno-omit-frame-pointer -fsanitize-ignorelist=$(pwd)/tools/asan-ignore.txt -Wno-deprecated-declarations"
206+
spin build -S-Dblas=accelerate
207+
208+
- name: Test
209+
run: |
210+
# pass -s to pytest to see ASAN errors and warnings, otherwise pytest captures them
211+
ASAN_OPTIONS=detect_leaks=0:symbolize=1:strict_init_order=true:allocator_may_return_null=1:use_sigaltstack=0 \
212+
spin test -- -v -s --timeout=600 --durations=10 -m 'not fail_asan'

meson.build

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,13 @@ if host_machine.system() == 'darwin'
122122
endif
123123
endif
124124

125+
if get_option('b_sanitize') != 'none'
126+
if cc.get_id() == 'clang'
127+
ignore_file = meson.current_source_dir() / 'tools' / 'asan-ignore.txt'
128+
add_global_arguments('-fsanitize-ignorelist=' + ignore_file, language: ['c', 'cpp'])
129+
endif
130+
endif
131+
125132
# Intel compilers default to fast-math, so disable it if we detect Intel
126133
# compilers. A word of warning: this may not work with the conda-forge
127134
# compilers, because those have the annoying habit of including lots of flags

pytest.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,4 @@ markers =
4343
parallel_threads_limit(n): run the given test function in parallel
4444
thread_unsafe: mark the test function as single-threaded
4545
iterations(n): run the given test function `n` times in each thread
46+
fail_asan: mark test as triggering the address sanitizer (and not covered by the suppressions file)

scipy/signal/tests/test_bsplines.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,7 @@ def test_sepfir2d_strided_2(self, xp):
337337

338338
@skip_xp_backends(np_only=True, reason="TODO: convert this test")
339339
@pytest.mark.xfail(reason="XXX: flaky. pointers OOB on some platforms")
340+
@pytest.mark.fail_asan
340341
@pytest.mark.parametrize('dtyp',
341342
[np.uint8, int, np.float32, float, np.complex64, complex]
342343
)

tools/asan-ignore.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[address]
2+
3+
# from scipy.optimize._minpack._lmdif
4+
fun:enorm
5+
6+
# from scipy.interpolate._dierckx.qr_reduce_periodic
7+
fun:_ZN7fitpack18qr_reduce_periodic*
8+
9+
# from scipy.ndimage._rank_filter_1d.rank_filter
10+
fun:_Z12_rank_filterIfEiPT_iiiS1_iS0_i
11+
12+
# from scipy.signal._spline.sepfir2d
13+
fun:*_fir_mirror_symmetric*

0 commit comments

Comments
 (0)