| name: Reusable Sanitizer | |
| on: | |
| workflow_call: | |
| inputs: | |
| sanitizer: | |
| required: true | |
| type: string | |
| free-threading: | |
| description: Whether to use free-threaded mode | |
| required: false | |
| type: boolean | |
| default: false | |
| permissions: | |
| contents: read | |
| env: | |
| FORCE_COLOR: 1 | |
| OPENSSL_VER: 3.5.9 | |
| jobs: | |
| build-san-reusable: | |
| name: >- | |
| ${{ inputs.sanitizer }}${{ | |
| inputs.free-threading | |
| && ' (free-threading)' | |
| || '' | |
| }} | |
| runs-on: ubuntu-26.04 | |
| timeout-minutes: 60 | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| persist-credentials: false | |
| - name: Runner image version | |
| run: echo "IMAGE_OS_VERSION=${ImageOS}-${ImageVersion}" >> "$GITHUB_ENV" | |
| - name: Install dependencies | |
| run: | | |
| sudo ./.github/workflows/posix-deps-apt.sh | |
| # On ubuntu-26.04 image, clang is clang-21 by default | |
| # NOTE: when bumping to a new version of clang, | |
| # please update the versions in `Tools/pixi-packages/variants.yaml` too. | |
| echo "CC=clang" >> "$GITHUB_ENV" | |
| echo "CXX=clang++" >> "$GITHUB_ENV" | |
| - name: ASan option setup | |
| if: inputs.sanitizer == 'ASan' | |
| run: | | |
| echo "MULTISSL_DIR=${GITHUB_WORKSPACE}/multissl" >> "$GITHUB_ENV" | |
| echo "OPENSSL_DIR=${GITHUB_WORKSPACE}/multissl/openssl/${OPENSSL_VER}" >> "$GITHUB_ENV" | |
| echo "ASAN_OPTIONS=${SAN_LOG_OPTION} detect_leaks=0 allocator_may_return_null=1 handle_segv=0" >> "$GITHUB_ENV" | |
| echo "PYTHONSTRICTEXTENSIONBUILD=1" >> "$GITHUB_ENV" | |
| env: | |
| SAN_LOG_OPTION: log_path=${{ github.workspace }}/san_log | |
| - name: TSan option setup | |
| if: inputs.sanitizer == 'TSan' | |
| run: | | |
| sudo sysctl -w vm.mmap_rnd_bits=28 # Reduce ASLR to avoid TSan crashing | |
| echo "MULTISSL_DIR=${GITHUB_WORKSPACE}/multissl" >> "$GITHUB_ENV" | |
| echo "OPENSSL_DIR=${GITHUB_WORKSPACE}/multissl/openssl/${OPENSSL_VER}" >> "$GITHUB_ENV" | |
| echo "TSAN_OPTIONS=${SAN_LOG_OPTION} suppressions=${GITHUB_WORKSPACE}/Tools/tsan/suppressions${SUPPRESSIONS_SUFFIX}.txt handle_segv=0" >> "$GITHUB_ENV" | |
| env: | |
| SAN_LOG_OPTION: log_path=${{ github.workspace }}/san_log | |
| SUPPRESSIONS_SUFFIX: >- | |
| ${{ | |
| inputs.free-threading | |
| && '_free_threading' | |
| || '' | |
| }} | |
| - name: UBSan option setup | |
| if: inputs.sanitizer == 'UBSan' | |
| run: >- | |
| echo | |
| "UBSAN_OPTIONS=${SAN_LOG_OPTION} | |
| halt_on_error=1 | |
| suppressions=${GITHUB_WORKSPACE}/Tools/ubsan/suppressions.txt" | |
| >> "$GITHUB_ENV" | |
| env: | |
| SAN_LOG_OPTION: log_path=${{ github.workspace }}/san_log | |
| - name: MSan option setup | |
| if: inputs.sanitizer == 'MSan' | |
| run: | | |
| sudo sysctl -w vm.mmap_rnd_bits=28 # Reduce ASLR to avoid MSan re-executing | |
| echo "MSAN_OPTIONS=${SAN_LOG_OPTION} allocator_may_return_null=1 handle_segv=0" >> "$GITHUB_ENV" | |
| # MSan reports false positives for memory initialized by libraries | |
| # that are not built with MSan, so disable modules that use them. | |
| # _remote_debugging links to libzstd directly, but we unpoision the memory. | |
| { | |
| echo '*disabled*' | |
| echo '_bz2 _ctypes _curses _curses_panel _dbm _decimal _gdbm _hashlib' | |
| echo '_lzma _sqlite3 _ssl _tkinter _uuid _zstd readline zlib' | |
| } > Modules/Setup.local | |
| env: | |
| SAN_LOG_OPTION: log_path=${{ github.workspace }}/san_log | |
| - name: Add ccache to PATH | |
| run: | | |
| echo "PATH=/usr/lib/ccache:$PATH" >> "$GITHUB_ENV" | |
| - name: 'Restore OpenSSL build' | |
| id: cache-openssl | |
| if: inputs.sanitizer == 'TSan' || inputs.sanitizer == 'ASan' | |
| uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| with: | |
| path: ./multissl/openssl/${{ env.OPENSSL_VER }} | |
| key: >- | |
| ${{ env.IMAGE_OS_VERSION }}-multissl-openssl-${{ | |
| inputs.sanitizer == 'TSan' && 'tsan-' || '' | |
| }}${{ env.OPENSSL_VER }} | |
| - name: Install OpenSSL | |
| if: >- | |
| (inputs.sanitizer == 'TSan' || inputs.sanitizer == 'ASan') | |
| && steps.cache-openssl.outputs.cache-hit != 'true' | |
| run: >- | |
| python3 Tools/ssl/multissltests.py | |
| --steps=library | |
| --base-directory="${MULTISSL_DIR}" | |
| --openssl="${OPENSSL_VER}" | |
| --system=Linux | |
| ${{ inputs.sanitizer == 'TSan' && '--tsan' || '' }} | |
| # gh-157958: -O2 instead of the pydebug default -Og to avoid a clang 21 | |
| # compile-time blowup on some interpreter files. | |
| # (https://github.com/llvm/llvm-project/issues/179695) | |
| # MSan uses --with-assertions instead of --with-pydebug because its | |
| # hooks on the Python memory allocators hide uninitialized reads. | |
| - name: Configure CPython | |
| run: >- | |
| ./configure | |
| --config-cache | |
| OPT="-O2 -g" | |
| ${{ | |
| inputs.sanitizer == 'ASan' | |
| && '--with-address-sanitizer --without-pymalloc' | |
| || inputs.sanitizer == 'TSan' | |
| && '--with-thread-sanitizer' | |
| || inputs.sanitizer == 'MSan' | |
| && '--with-memory-sanitizer' | |
| || '--with-undefined-behavior-sanitizer --with-strict-overflow' | |
| }} | |
| ${{ inputs.sanitizer == 'MSan' && '--with-assertions' || '--with-pydebug' }} | |
| ${{ | |
| (inputs.sanitizer == 'TSan' || inputs.sanitizer == 'ASan') | |
| && '--with-openssl="$OPENSSL_DIR" --with-openssl-rpath=auto' | |
| || '' | |
| }} | |
| ${{ inputs.free-threading && '--disable-gil' || '' }} | |
| - name: Build CPython | |
| run: make -j4 | |
| - name: Display build info | |
| run: make pythoninfo | |
| - name: Tests | |
| run: >- | |
| ./python -m test | |
| ${{ inputs.sanitizer == 'TSan' && '--tsan' || '' }} | |
| -j4 -W --timeout=900 --slowest | |
| - name: Parallel tests | |
| if: >- | |
| inputs.sanitizer == 'TSan' | |
| && inputs.free-threading | |
| run: ./python -m test --tsan-parallel --parallel-threads=4 -j4 -W --timeout=600 --slowest | |
| - name: Display logs | |
| if: always() | |
| run: find "${GITHUB_WORKSPACE}" -name 'san_log.*' | xargs head -n 1000 | |
| - name: Archive logs | |
| if: always() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: >- | |
| ${{ inputs.sanitizer }}-logs-${{ | |
| inputs.free-threading | |
| && 'free-threading' | |
| || 'default' | |
| }} | |
| path: san_log.* | |
| if-no-files-found: ignore |