Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 0 additions & 28 deletions .github/actions/harden-pip/action.yml

This file was deleted.

7 changes: 2 additions & 5 deletions .github/actions/lychee-links/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ outputs:
description: "Whether the current PR label explicitly enables the HTTP 429 override"
value: ${{ steps.lychee-policy.outputs.allow-429 }}

# Callers provide Python and the checked-out repository; the build of any
# rendered documentation must already be complete.
# Callers provide Python through setup-hardened-python and the checked-out
# repository; the build of any rendered documentation must already be complete.
runs:
using: composite
steps:
Expand Down Expand Up @@ -113,9 +113,6 @@ runs:
printf '[Open the cache refresh workflow](%s).\n' "$WORKFLOW_URL"
} >> "$GITHUB_STEP_SUMMARY"

- name: Harden pip
uses: ./.github/actions/harden-pip

- name: Check links through the configured pre-commit hook
shell: bash --noprofile --norc -euo pipefail {0}
env:
Expand Down
125 changes: 125 additions & 0 deletions .github/actions/setup-hardened-python/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
#
# SPDX-License-Identifier: Apache-2.0

name: Set up hardened Python
description: >-
Install CPython with a pinned uv, create a venv from it, and put the venv
first on PATH. setup-uv verifies the uv binary and uv verifies the
python-build-standalone archive against embedded checksums, so the
interpreter's integrity does not depend on TLS. The job then fails unless
the interpreter's ssl module rejects the compromised eMudhra/emSign CA keys
(CVE-2026-115557; first fixed releases: 3.11.18, 3.12.16, 3.13.18, 3.14.10,
3.15.2), and pip is updated through that ssl module. Unlike setup-python,
this provides patched Windows builds of maintenance series, for which
python.org publishes no installers. See
https://github.com/NVIDIA/cuda-python/issues/3195 and
https://discuss.python.org/t/109678.

inputs:
python-version:
description: >-
uv Python request, e.g. "3.12" or "3.14t" (free-threaded). The newest
patch known to the pinned uv is installed. Only series with a fixed
release listed in the interpreter check (3.11-3.15) are accepted.
required: true
venv:
description: Directory for the venv. Defaults to a new directory under RUNNER_TEMP.
default: ""

outputs:
python-path:
description: Absolute path of the venv's interpreter.
value: ${{ steps.venv.outputs.python-path }}
python-version:
description: Full version of the interpreter, e.g. "3.14.10".
value: ${{ steps.venv.outputs.python-version }}

runs:
using: composite
steps:
- name: Install uv
uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
with:
# The newest uv whose checksum ships inside the pinned setup-uv, so
# the binary is verified against the SHA-pinned action itself. The uv
# release determines the newest patch of each CPython series, so
# bumping the setup-uv SHA also moves the CPython patch versions.
version: "latest-known"
enable-cache: false
# Some callers (e.g. release verification) have no checkout.
ignore-empty-workdir: true

- name: Install Python and create venv
id: venv
shell: bash --noprofile --norc -euo pipefail {0}
env:
PY_REQUEST: ${{ inputs.python-version }}
VENV_DIR: ${{ inputs.venv }}
# A per-job install directory ensures the newest patch known to the
# pinned uv is used, never an older interpreter left on a persistent
# self-hosted runner.
UV_PYTHON_INSTALL_DIR: ${{ runner.temp }}/setup-hardened-python/interpreters
UV_NO_CONFIG: "1"
run: |
uv python install --no-bin --no-registry "${PY_REQUEST}"
base="$(uv python find --managed-python --no-project "${PY_REQUEST}")"
venv="${VENV_DIR}"
if [[ -z "${venv}" ]]; then
# A fresh directory per call, so a second call in the same job
# (e.g. build-wheel.yml) leaves the first venv intact.
venv="$("${base}" -c 'import sys, tempfile; print(tempfile.mkdtemp(prefix="venv-", dir=sys.argv[1]))' "${RUNNER_TEMP}")"
fi
# The stdlib venv seeds pip from the interpreter's bundled wheel, so
# nothing is downloaded before the ssl check below.
"${base}" -m venv --clear "${venv}"
if [[ "${RUNNER_OS}" == Windows ]]; then
py="${venv}/Scripts/python.exe"
else
py="${venv}/bin/python"
fi
# Let the interpreter report native absolute paths (Windows included).
python_path="$("${py}" -c 'import sys; print(sys.executable)')"
"${py}" -c 'import os, sys; print(os.path.dirname(sys.executable))' >> "${GITHUB_PATH}"
{
echo "python-path=${python_path}"
echo "python-version=$("${py}" -c 'import platform; print(platform.python_version())')"
} >> "${GITHUB_OUTPUT}"

- name: Check Python interpreter is patched (CVE-2026-115557)
# Runs the venv's python through the PATH entry added above.
shell: python {0}
env:
PYTHON: ${{ steps.venv.outputs.python-path }}
run: |
import os
import platform
import sys

if os.path.normcase(sys.executable) != os.path.normcase(os.environ["PYTHON"]):
print(f"::error::PATH resolves python to {sys.executable}, not the venv interpreter {os.environ['PYTHON']}.")
sys.exit(1)

FIXED = {(3, 11): 18, (3, 12): 16, (3, 13): 18, (3, 14): 10, (3, 15): 2}
v = sys.version_info
series = (v.major, v.minor)
name = f"{v.major}.{v.minor}"
full = platform.python_version()
print("sys.version:", sys.version)
if series in FIXED:
floor = f"{name}.{FIXED[series]}"
if v.micro < FIXED[series]:
print(f"::error::Python {full} is older than {floor}, the first {name} release whose ssl module rejects the compromised eMudhra CA keys (CVE-2026-115557).")
sys.exit(1)
print(f"Python {full}: ssl module carries the fix (first fixed: {floor}).")
else:
# Fail closed: a series is only accepted once its fixed release is listed.
print(f"::error::Python {full}: no {name} release is known to reject the compromised eMudhra CA keys (CVE-2026-115557).")
sys.exit(1)

- name: Update pip
shell: bash --noprofile --norc -xeuo pipefail {0}
env:
PYTHON: ${{ steps.venv.outputs.python-path }}
run: |
"${PYTHON}" -m pip install -U pip
2 changes: 1 addition & 1 deletion .github/workflows/build-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ jobs:
# interpreter instead of the conda environment used above.
- name: Set up Python for link checks
if: ${{ !inputs.is-release && startsWith(github.ref_name, 'pull-request/') }}
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
uses: ./.github/actions/setup-hardened-python
with:
python-version: "3.14"

Expand Down
28 changes: 11 additions & 17 deletions .github/workflows/build-wheel.yml
Original file line number Diff line number Diff line change
Expand Up @@ -107,16 +107,12 @@ jobs:
enable-apt: true

- name: Set up Python
id: setup-python1
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
id: host-python
uses: ./.github/actions/setup-hardened-python
with:
# WAR: setup-python is not relocatable, and cibuildwheel hard-wires to 3.12...
# see https://github.com/actions/setup-python/issues/871
# Also installs cibuildwheel (via python-path below), so the action
# does not fall back on its own unchecked setup-python interpreter.
python-version: "3.12"
architecture: ${{ ((inputs.host-platform == 'linux-aarch64' || inputs.host-platform == 'win-arm64') && 'arm64') || 'x64' }}

- name: Harden pip
uses: ./.github/actions/harden-pip

- name: Set up MSVC
if: ${{ startsWith(inputs.host-platform, 'win') && (env.BUILD_BINDINGS == 'true' || env.BUILD_CORE == 'true' || env.TEST_BINDINGS == 'true' || env.TEST_CORE == 'true') }}
Expand Down Expand Up @@ -235,6 +231,7 @@ jobs:
package-dir: ./cuda_bindings_13/
output-dir: ${{ env.CUDA_BINDINGS_ARTIFACTS_DIR }}
extras: uv
python-path: ${{ steps.host-python.outputs.python-path }}
env:
CIBW_BUILD: ${{ env.CIBW_BUILD }}
# build[uv]: same PEP 517 orchestration, with uv instead of pip.
Expand Down Expand Up @@ -332,6 +329,7 @@ jobs:
package-dir: ./cuda_core/
output-dir: ${{ env.CUDA_CORE_ARTIFACTS_DIR }}
extras: uv
python-path: ${{ steps.host-python.outputs.python-path }}
env:
CIBW_BUILD: ${{ env.CIBW_BUILD }}
# See the cuda.bindings step above for UV_* vs PIP_*.
Expand Down Expand Up @@ -442,16 +440,10 @@ jobs:
if-no-files-found: error

- name: Set up Python
id: setup-python2
if: ${{ env.TEST_BINDINGS == 'true' || env.TEST_CORE == 'true' }}
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
uses: ./.github/actions/setup-hardened-python
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: ${{ startsWith(matrix.python-version, '3.16') }}

- name: Harden pip
if: ${{ env.TEST_BINDINGS == 'true' || env.TEST_CORE == 'true' }}
uses: ./.github/actions/harden-pip

- name: verify free-threaded build
if: ${{ (env.TEST_BINDINGS == 'true' || env.TEST_CORE == 'true') && endsWith(matrix.python-version, 't') }}
Expand All @@ -460,10 +452,11 @@ jobs:
- name: Set up Python include paths
if: ${{ env.TEST_BINDINGS == 'true' || env.TEST_CORE == 'true' }}
run: |
include="$(python -c "import sysconfig; print(sysconfig.get_paths()['include'])")"
if [[ "${{ inputs.host-platform }}" == linux* ]]; then
echo "CPLUS_INCLUDE_PATH=${Python3_ROOT_DIR}/include/python${{ matrix.python-version }}" >> $GITHUB_ENV
echo "CPLUS_INCLUDE_PATH=${include}" >> $GITHUB_ENV
elif [[ "${{ inputs.host-platform }}" == win* ]]; then
echo "CL=/I\"${Python3_ROOT_DIR}\include\python${{ matrix.python-version }}\"" >> $GITHUB_ENV
echo "CL=/I\"${include}\"" >> $GITHUB_ENV
fi
# For caching
echo "PY_EXT_SUFFIX=$(python -c "import sysconfig; print(sysconfig.get_config_var('EXT_SUFFIX'))")" >> $GITHUB_ENV
Expand Down Expand Up @@ -614,6 +607,7 @@ jobs:
package-dir: ./cuda_core/
output-dir: ${{ env.CUDA_CORE_ARTIFACTS_DIR }}
extras: uv
python-path: ${{ steps.host-python.outputs.python-path }}
env:
CIBW_BUILD: ${{ env.CIBW_BUILD }}
# See the cuda.bindings step above for UV_* vs PIP_*.
Expand Down
6 changes: 4 additions & 2 deletions .github/workflows/ci-nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,10 @@ jobs:
with:
fetch-depth: 1

- name: Harden pip
uses: ./.github/actions/harden-pip
- name: Set up Python
uses: ./.github/actions/setup-hardened-python
with:
python-version: "3.12"

- name: Run CI tools unit tests
run: |
Expand Down
44 changes: 7 additions & 37 deletions .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -102,12 +102,10 @@ jobs:
echo "CUDA_PYTHON_COVERAGE=1" >> $GITHUB_ENV

- name: Set up Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
uses: ./.github/actions/setup-hardened-python
with:
python-version: ${{ env.PY_VER }}
env:
# we use self-hosted runners on which setup-python behaves weirdly...
AGENT_TOOLSDIRECTORY: "/opt/hostedtoolcache"
venv: .venv

- name: Set up mini CTK
if: ${{ env.LOCAL_CTK == '1' }}
Expand All @@ -117,15 +115,6 @@ jobs:
host-platform: ${{ env.HOST_PLATFORM }}
cuda-version: ${{ env.CUDA_VER }}

- name: Create venv
run: |
python -m venv .venv

- name: Harden pip
uses: ./.github/actions/harden-pip
with:
python: .venv/bin/python

- name: Build and install cuda-pathfinder wheel
run: |
.venv/bin/pip wheel -v --no-deps ./cuda_pathfinder -w ./wheels/
Expand Down Expand Up @@ -253,9 +242,10 @@ jobs:
submodules: true

- name: Set up Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
uses: ./.github/actions/setup-hardened-python
with:
python-version: ${{ env.PY_VER }}
venv: .venv

- name: Set up MSVC
uses: step-security/msvc-dev-cmd@22c98154b708dbd743e6f27a933cf6ceba3305c4 # v1.13.1
Expand All @@ -267,15 +257,6 @@ jobs:
host-platform: win-64
cuda-version: ${{ env.CUDA_VER }}

- name: Create venv
run: |
python -m venv .venv

- name: Harden pip
uses: ./.github/actions/harden-pip
with:
python: .venv/Scripts/python

- name: Build cuda.pathfinder wheel
run: |
.venv/Scripts/python -m pip install wheel setuptools Cython
Expand Down Expand Up @@ -392,9 +373,10 @@ jobs:
nvidia-smi

- name: Set up Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
uses: ./.github/actions/setup-hardened-python
with:
python-version: ${{ env.PY_VER }}
venv: .venv

- name: Set up mini CTK
uses: ./.github/actions/fetch_ctk
Expand All @@ -414,15 +396,6 @@ jobs:
echo "=== Downloaded wheel artifacts ==="
ls -lahR ./wheels/

- name: Create venv
run: |
python -m venv .venv

- name: Harden pip
uses: ./.github/actions/harden-pip
with:
python: .venv/Scripts/python

- name: Install wheels from build job
run: |
.venv/Scripts/pip install ./wheels/cuda_pathfinder*.whl
Expand Down Expand Up @@ -502,13 +475,10 @@ jobs:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Set up Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
uses: ./.github/actions/setup-hardened-python
with:
python-version: ${{ env.PY_VER }}

- name: Harden pip
uses: ./.github/actions/harden-pip

- name: Install coverage
run: |
# .coveragerc enables Cython.Coverage plugin; ensure it's available here too.
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/lychee.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ jobs:

- name: Set up Python
id: python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
uses: ./.github/actions/setup-hardened-python
with:
python-version: ${{ env.PYTHON_VERSION }}

Expand All @@ -79,8 +79,8 @@ jobs:
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ${{ env.PRE_COMMIT_HOME }}
key: pre-commit-${{ runner.os }}-py${{ steps.python.outputs.python-version }}-${{ hashFiles('.pre-commit-config.yaml') }}
restore-keys: pre-commit-${{ runner.os }}-py${{ env.PYTHON_VERSION }}
key: pre-commit-${{ runner.os }}-uv-py${{ steps.python.outputs.python-version }}-${{ hashFiles('.pre-commit-config.yaml') }}
restore-keys: pre-commit-${{ runner.os }}-uv-py${{ env.PYTHON_VERSION }}

- name: Setup pixi
if: ${{ matrix.kind == 'rendered' }}
Expand Down
Loading
Loading