From 309e0db107449ef597d5aba36a1560cd574a437d Mon Sep 17 00:00:00 2001 From: "Leslie P. Polzer" Date: Fri, 9 Jan 2026 02:11:55 +0000 Subject: [PATCH 01/16] Add cross-platform wheel builds via cibuildwheel Add support for building wheels on: - Linux x86_64 (existing) - Linux aarch64 (new) - macOS x86_64 (new) - macOS arm64 (new) Changes: - pyproject.toml: Add [build-system] and [tool.cibuildwheel] config - .github/workflows/wheels.yaml: New workflow for building and publishing - STATUS.md: Document testing results and implementation plan Tested on macOS 15.6 ARM64 (M1) with Python 3.12 and 3.13. Key finding: macOS builds require Homebrew LLVM and must link against Homebrew's libc++ to avoid symbol mismatch with system libc++. Addresses google/atheris#52 --- .github/workflows/wheels.yaml | 112 ++++++++++++++++++++++++++++++++ STATUS.md | 117 ++++++++++++++++++++++++++++++++++ pyproject.toml | 37 +++++++++++ 3 files changed, 266 insertions(+) create mode 100644 .github/workflows/wheels.yaml create mode 100644 STATUS.md diff --git a/.github/workflows/wheels.yaml b/.github/workflows/wheels.yaml new file mode 100644 index 00000000..4c4a8ca1 --- /dev/null +++ b/.github/workflows/wheels.yaml @@ -0,0 +1,112 @@ +# Build and publish wheels for multiple platforms +# This workflow builds wheels for Linux (x86_64, aarch64) and macOS (x86_64, arm64) +# Addresses: https://github.com/google/atheris/issues/52 + +name: Build Wheels + +on: + # Build on release tags + push: + tags: + - 'v*' + # Allow manual trigger for testing + workflow_dispatch: + # Build on PRs that modify build-related files (for testing) + pull_request: + paths: + - 'setup.py' + - 'pyproject.toml' + - '.github/workflows/wheels.yaml' + +jobs: + build_wheels: + name: Build wheels on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + # Linux x86_64 + - os: ubuntu-latest + arch: x86_64 + # Linux aarch64 (uses QEMU emulation) + - os: ubuntu-latest + arch: aarch64 + # macOS Intel + - os: macos-13 + arch: x86_64 + # macOS Apple Silicon + - os: macos-14 + arch: arm64 + + steps: + - uses: actions/checkout@v4 + + # Set up QEMU for aarch64 emulation on Linux + - name: Set up QEMU + if: matrix.arch == 'aarch64' && runner.os == 'Linux' + uses: docker/setup-qemu-action@v3 + with: + platforms: arm64 + + - name: Build wheels + uses: pypa/cibuildwheel@v2.21 + env: + # Only build for the current architecture + CIBW_ARCHS: ${{ matrix.arch }} + + # macOS: Set up Homebrew LLVM environment + # Intel Macs use /usr/local, ARM Macs use /opt/homebrew + # Must link against Homebrew's libc++ to avoid symbol mismatch with system libc++ + CIBW_ENVIRONMENT_MACOS: > + CLANG_BIN="$(brew --prefix llvm)/bin/clang" + CC="$(brew --prefix llvm)/bin/clang" + CXX="$(brew --prefix llvm)/bin/clang++" + LDFLAGS="-L$(brew --prefix llvm)/lib/c++ -L$(brew --prefix llvm)/lib -Wl,-rpath,$(brew --prefix llvm)/lib/c++" + CPPFLAGS="-I$(brew --prefix llvm)/include" + + - name: Upload wheels + uses: actions/upload-artifact@v4 + with: + name: wheels-${{ matrix.os }}-${{ matrix.arch }} + path: ./wheelhouse/*.whl + + build_sdist: + name: Build source distribution + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Build sdist + run: pipx run build --sdist + + - name: Upload sdist + uses: actions/upload-artifact@v4 + with: + name: sdist + path: dist/*.tar.gz + + # Only publish on tagged releases + publish: + name: Publish to PyPI + needs: [build_wheels, build_sdist] + runs-on: ubuntu-latest + # Only run on tag pushes, not PRs or manual triggers + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') + environment: + name: pypi + url: https://pypi.org/p/atheris + permissions: + id-token: write # Required for trusted publishing + + steps: + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: dist + merge-multiple: true + + - name: Publish to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + # Uses trusted publishing - no API token needed + # Configure at: https://pypi.org/manage/project/atheris/settings/publishing/ diff --git a/STATUS.md b/STATUS.md new file mode 100644 index 00000000..18061cde --- /dev/null +++ b/STATUS.md @@ -0,0 +1,117 @@ +# Atheris ARM/macOS Wheel Support - Status + +## Objective + +Add cross-platform binary wheel builds for ARM Linux and macOS (Intel + Apple Silicon) to the Atheris fuzzing library. + +## Current State (Upstream) + +**PyPI wheels available:** +- `manylinux2014_x86_64` only (Python 3.11, 3.12, 3.13) + +**Missing:** +- `manylinux2014_aarch64` (ARM Linux) +- `macosx_x86_64` (Intel Mac) +- `macosx_arm64` (Apple Silicon) + +## Related GitHub Issues + +| Issue | Status | Summary | +|-------|--------|---------| +| [#52](https://github.com/google/atheris/issues/52) | Open (Dec 2022) | Request for cibuildwheel - assigned to 3.0.0 milestone, never completed | +| [#68](https://github.com/google/atheris/pull/68) | Merged (Aug 2023) | Added ARM64 Linux support in code | +| [#80](https://github.com/google/atheris/issues/80) | Open | macOS 14.2.1 build failures | +| [#74](https://github.com/google/atheris/issues/74) | Open | libFuzzer dependency issues across platforms | + +## Testing Results + +### M1 Mac (macOS 15.6, ARM64) + +| Test | Python 3.12 | Python 3.13 | +|------|-------------|-------------| +| Build with Homebrew LLVM | ✓ | ✓ | +| Import atheris | ✓ | ✓ | +| FuzzedDataProvider | ✓ | ✓ | +| Fuzz test execution | ✓ | ✓ | + +**Environment used:** +```bash +brew install llvm +export CLANG_BIN="$(brew --prefix llvm)/bin/clang" +export CC="$(brew --prefix llvm)/bin/clang" +export CXX="$(brew --prefix llvm)/bin/clang++" +export LDFLAGS="-L$(brew --prefix llvm)/lib/c++ -L$(brew --prefix llvm)/lib -Wl,-rpath,$(brew --prefix llvm)/lib/c++" +export CPPFLAGS="-I$(brew --prefix llvm)/include" +``` + +### Critical Finding: libc++ Linking + +**Problem:** Homebrew LLVM uses newer libc++ with symbols not in system libc++. + +**Symptom:** +``` +ImportError: symbol not found in flat namespace '__ZNSt3__113__hash_memoryEPKvm' +``` + +**Solution:** Must link against Homebrew's libc++ explicitly: +``` +LDFLAGS="-L$(brew --prefix llvm)/lib/c++ -Wl,-rpath,$(brew --prefix llvm)/lib/c++" +``` + +**Verification:** +```bash +# Correct (works): +otool -L atheris/native.*.so + /opt/homebrew/opt/llvm/lib/c++/libc++.1.dylib + +# Incorrect (fails): + /usr/lib/libc++.1.dylib +``` + +## PR Implementation + +### Files Changed + +1. **`pyproject.toml`** - Added cibuildwheel configuration: + - `[build-system]` - PEP 517 build requirements + - `[tool.cibuildwheel]` - Skip PyPy/musllinux, Python 3.11-3.13 + - `[tool.cibuildwheel.linux]` - x86_64 + aarch64, manylinux2014 + - `[tool.cibuildwheel.macos]` - x86_64 + arm64, Homebrew LLVM + +2. **`.github/workflows/wheels.yaml`** - New workflow: + - Matrix: ubuntu-latest (x86_64, aarch64), macos-13 (x86_64), macos-14 (arm64) + - QEMU for aarch64 emulation + - Homebrew LLVM setup for macOS + - PyPI trusted publishing on tagged releases + +### Wheels Produced + +| Platform | Arch | Python Versions | +|----------|------|-----------------| +| manylinux2014 | x86_64 | 3.11, 3.12, 3.13 | +| manylinux2014 | aarch64 | 3.11, 3.12, 3.13 | +| macosx | x86_64 | 3.11, 3.12, 3.13 | +| macosx | arm64 | 3.11, 3.12, 3.13 | + +**Total: 12 wheels** (up from 3) + +## Next Steps + +1. [ ] Comment on Issue #52 to check maintainer interest +2. [ ] Fork google/atheris +3. [ ] Push changes to fork +4. [ ] Open PR with test results +5. [ ] Address any CI failures + +## Known Limitations + +- **macOS requires Homebrew LLVM** - Apple Clang doesn't include libFuzzer +- **No Windows support** - Shell-based build scripts incompatible +- **ASan/UBSan optional** - Sanitizer libraries may not be bundled on all platforms +- **Python 3.14+ unsupported** - Atheris only supports 3.6-3.13 currently + +## Notes + +- Wheel size ~7.5MB each (includes libFuzzer) +- aarch64 Linux builds use QEMU emulation (slower CI) +- macOS builds require `brew install llvm` in CI diff --git a/pyproject.toml b/pyproject.toml index 5f4f3fb3..561512a3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,3 +16,40 @@ ignore-init-module-imports = true # E501 Line too long # F401 … imported but unused; consider adding to `__all__` or using a redundant alias ignore = ["E501", "F401"] + + +[build-system] +requires = ["setuptools>=45", "pybind11>=2.5.0"] +build-backend = "setuptools.build_meta" + + +[tool.cibuildwheel] +# Build for CPython only, skip PyPy and musllinux +skip = "pp* *-musllinux*" + +# Test that the built wheel can be imported +test-command = "python -c \"import atheris; print('atheris', atheris.__version__)\"" + +# Build for Python 3.11, 3.12, 3.13 +build = "cp311-* cp312-* cp313-*" + +[tool.cibuildwheel.linux] +# Build for x86_64 and aarch64 +archs = ["x86_64", "aarch64"] + +# Use manylinux2014 for glibc compatibility +manylinux-x86_64-image = "manylinux2014" +manylinux-aarch64-image = "manylinux2014" + +# Install clang/llvm for libFuzzer +before-all = "yum install -y clang llvm-devel || apt-get update && apt-get install -y clang llvm-dev" + +[tool.cibuildwheel.macos] +# Build for both Intel and Apple Silicon +archs = ["x86_64", "arm64"] + +# Install LLVM from Homebrew (Apple Clang doesn't include libFuzzer) +before-all = "brew install llvm" + +# Environment variables are set via CIBW_ENVIRONMENT_MACOS in the workflow +# This is needed because $(brew --prefix) differs between Intel and ARM Macs From ee841f06e90fc94dd3104c35392bfec72fb68569 Mon Sep 17 00:00:00 2001 From: "Leslie P. Polzer" Date: Fri, 9 Jan 2026 02:29:46 +0000 Subject: [PATCH 02/16] Fix: Use macos-latest instead of deprecated macos-13 --- .github/workflows/wheels.yaml | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/.github/workflows/wheels.yaml b/.github/workflows/wheels.yaml index 4c4a8ca1..9ddb9f85 100644 --- a/.github/workflows/wheels.yaml +++ b/.github/workflows/wheels.yaml @@ -32,12 +32,9 @@ jobs: # Linux aarch64 (uses QEMU emulation) - os: ubuntu-latest arch: aarch64 - # macOS Intel - - os: macos-13 - arch: x86_64 - # macOS Apple Silicon - - os: macos-14 - arch: arm64 + # macOS - build both archs on ARM runner (cross-compilation for x86_64) + - os: macos-latest + arch: "x86_64 arm64" steps: - uses: actions/checkout@v4 @@ -68,7 +65,7 @@ jobs: - name: Upload wheels uses: actions/upload-artifact@v4 with: - name: wheels-${{ matrix.os }}-${{ matrix.arch }} + name: wheels-${{ matrix.os }}-${{ strategy.job-index }} path: ./wheelhouse/*.whl build_sdist: From 3365b470f226a37c54cbfdeca6a4b7b8de4a39c9 Mon Sep 17 00:00:00 2001 From: "Leslie P. Polzer" Date: Fri, 9 Jan 2026 02:33:36 +0000 Subject: [PATCH 03/16] Fix: Use manylinux_2_28 with dnf, skip delocate on macOS --- .github/workflows/wheels.yaml | 4 ++++ pyproject.toml | 10 +++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/wheels.yaml b/.github/workflows/wheels.yaml index 9ddb9f85..bab9676f 100644 --- a/.github/workflows/wheels.yaml +++ b/.github/workflows/wheels.yaml @@ -62,6 +62,10 @@ jobs: LDFLAGS="-L$(brew --prefix llvm)/lib/c++ -L$(brew --prefix llvm)/lib -Wl,-rpath,$(brew --prefix llvm)/lib/c++" CPPFLAGS="-I$(brew --prefix llvm)/include" + # macOS: Skip delocate repair - it fails on .a static library files + # The libclang_rt.fuzzer_no_main_osx.a is bundled but delocate can't process it + CIBW_REPAIR_WHEEL_COMMAND_MACOS: "" + - name: Upload wheels uses: actions/upload-artifact@v4 with: diff --git a/pyproject.toml b/pyproject.toml index 561512a3..7a75904f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,12 +37,12 @@ build = "cp311-* cp312-* cp313-*" # Build for x86_64 and aarch64 archs = ["x86_64", "aarch64"] -# Use manylinux2014 for glibc compatibility -manylinux-x86_64-image = "manylinux2014" -manylinux-aarch64-image = "manylinux2014" +# Use manylinux_2_28 which has newer clang with libFuzzer +manylinux-x86_64-image = "manylinux_2_28" +manylinux-aarch64-image = "manylinux_2_28" -# Install clang/llvm for libFuzzer -before-all = "yum install -y clang llvm-devel || apt-get update && apt-get install -y clang llvm-dev" +# Install clang with libFuzzer support (manylinux_2_28 is AlmaLinux 8 based) +before-all = "dnf install -y clang compiler-rt" [tool.cibuildwheel.macos] # Build for both Intel and Apple Silicon From 9bf0ee0c9cccd4e4266e38ac948e97e5edb62448 Mon Sep 17 00:00:00 2001 From: "Leslie P. Polzer" Date: Fri, 9 Jan 2026 02:41:59 +0000 Subject: [PATCH 04/16] Simplify: macOS ARM64 only, fix test command, document Linux limitations --- .github/workflows/wheels.yaml | 19 ++++--------------- STATUS.md | 9 +++++++++ pyproject.toml | 8 ++++---- 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/.github/workflows/wheels.yaml b/.github/workflows/wheels.yaml index bab9676f..fe2308f1 100644 --- a/.github/workflows/wheels.yaml +++ b/.github/workflows/wheels.yaml @@ -26,26 +26,15 @@ jobs: fail-fast: false matrix: include: - # Linux x86_64 - - os: ubuntu-latest - arch: x86_64 - # Linux aarch64 (uses QEMU emulation) - - os: ubuntu-latest - arch: aarch64 - # macOS - build both archs on ARM runner (cross-compilation for x86_64) + # macOS ARM64 (native on macos-latest which is ARM) - os: macos-latest - arch: "x86_64 arm64" + arch: arm64 + # Note: Linux wheels require building LLVM from source (see deployment/Dockerfile) + # Note: macOS x86_64 cross-compilation doesn't work well - skip for now steps: - uses: actions/checkout@v4 - # Set up QEMU for aarch64 emulation on Linux - - name: Set up QEMU - if: matrix.arch == 'aarch64' && runner.os == 'Linux' - uses: docker/setup-qemu-action@v3 - with: - platforms: arm64 - - name: Build wheels uses: pypa/cibuildwheel@v2.21 env: diff --git a/STATUS.md b/STATUS.md index 18061cde..64ecdbad 100644 --- a/STATUS.md +++ b/STATUS.md @@ -106,10 +106,19 @@ otool -L atheris/native.*.so ## Known Limitations - **macOS requires Homebrew LLVM** - Apple Clang doesn't include libFuzzer +- **Linux requires building LLVM from source** - The manylinux containers don't have libFuzzer pre-installed. Google uses a custom Docker image that builds LLVM from source (see `deployment/Dockerfile`). This takes too long for cibuildwheel. +- **macOS x86_64 cross-compilation has issues** - Building x86_64 on ARM runner fails delocate/test steps - **No Windows support** - Shell-based build scripts incompatible - **ASan/UBSan optional** - Sanitizer libraries may not be bundled on all platforms - **Python 3.14+ unsupported** - Atheris only supports 3.6-3.13 currently +## Current PR Scope + +Due to the Linux LLVM build complexity, this PR focuses on: +- **macOS ARM64 wheels only** (native builds on Apple Silicon) + +Linux wheels should continue using the existing `deployment/Dockerfile` approach. + ## Notes - Wheel size ~7.5MB each (includes libFuzzer) diff --git a/pyproject.toml b/pyproject.toml index 7a75904f..1cd3d2b7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,8 +27,8 @@ build-backend = "setuptools.build_meta" # Build for CPython only, skip PyPy and musllinux skip = "pp* *-musllinux*" -# Test that the built wheel can be imported -test-command = "python -c \"import atheris; print('atheris', atheris.__version__)\"" +# Test that the built wheel can be imported (atheris doesn't have __version__) +test-command = "python -c \"import atheris; print('atheris loaded, FuzzedDataProvider:', hasattr(atheris, 'FuzzedDataProvider'))\"" # Build for Python 3.11, 3.12, 3.13 build = "cp311-* cp312-* cp313-*" @@ -45,8 +45,8 @@ manylinux-aarch64-image = "manylinux_2_28" before-all = "dnf install -y clang compiler-rt" [tool.cibuildwheel.macos] -# Build for both Intel and Apple Silicon -archs = ["x86_64", "arm64"] +# Build for Apple Silicon only (x86_64 cross-compilation has issues) +archs = ["arm64"] # Install LLVM from Homebrew (Apple Clang doesn't include libFuzzer) before-all = "brew install llvm" From d73e1eff2bbcce4ad8b81dae2d3614732555c006 Mon Sep 17 00:00:00 2001 From: "Leslie P. Polzer" Date: Fri, 9 Jan 2026 03:16:13 +0000 Subject: [PATCH 05/16] Update STATUS.md with successful CI build results --- STATUS.md | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/STATUS.md b/STATUS.md index 64ecdbad..93a5b8fb 100644 --- a/STATUS.md +++ b/STATUS.md @@ -95,13 +95,23 @@ otool -L atheris/native.*.so **Total: 12 wheels** (up from 3) +## CI Build Results + +**Successful build:** https://github.com/rmc-infosec/atheris/actions/runs/20839360007 + +**Wheels produced:** +- `atheris-3.0.0-cp311-cp311-macosx_11_0_arm64.whl` (7.2 MB) +- `atheris-3.0.0-cp312-cp312-macosx_11_0_arm64.whl` (7.2 MB) +- `atheris-3.0.0-cp313-cp313-macosx_11_0_arm64.whl` (7.2 MB) +- `atheris-3.0.0.tar.gz` (114 KB) + ## Next Steps -1. [ ] Comment on Issue #52 to check maintainer interest -2. [ ] Fork google/atheris -3. [ ] Push changes to fork -4. [ ] Open PR with test results -5. [ ] Address any CI failures +1. [x] Fork google/atheris to rmc-infosec +2. [x] Push changes to fork +3. [x] Test CI workflow - SUCCESS +4. [ ] Comment on Issue #52 to check maintainer interest +5. [ ] Open PR to upstream ## Known Limitations From 9dec6dc12edc01752d13dc4371321768a75a4b1d Mon Sep 17 00:00:00 2001 From: "Leslie P. Polzer" Date: Fri, 9 Jan 2026 03:17:58 +0000 Subject: [PATCH 06/16] Add Linux ARM64 support via Docker + QEMU - Add deployment/Dockerfile.aarch64 for ARM64 Linux builds - Add .github/workflows/linux-wheels.yaml for Docker-based builds - Linux builds require LLVM from source (~30-60 min x86_64, ~2-3h aarch64) - Update STATUS.md with workflow documentation --- .github/workflows/linux-wheels.yaml | 87 +++++++++++++++++++++++++++++ STATUS.md | 13 ++++- deployment/Dockerfile.aarch64 | 52 +++++++++++++++++ 3 files changed, 149 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/linux-wheels.yaml create mode 100644 deployment/Dockerfile.aarch64 diff --git a/.github/workflows/linux-wheels.yaml b/.github/workflows/linux-wheels.yaml new file mode 100644 index 00000000..2b265494 --- /dev/null +++ b/.github/workflows/linux-wheels.yaml @@ -0,0 +1,87 @@ +# Build Linux wheels using Docker (requires building LLVM from source) +# This is separate from the main wheels.yaml because it takes much longer + +name: Build Linux Wheels + +on: + # Manual trigger only (building LLVM takes ~30-60 minutes) + workflow_dispatch: + inputs: + arch: + description: 'Architecture to build' + required: true + default: 'both' + type: choice + options: + - x86_64 + - aarch64 + - both + +jobs: + build-x86_64: + name: Build Linux x86_64 wheels + runs-on: ubuntu-latest + if: inputs.arch == 'x86_64' || inputs.arch == 'both' + + steps: + - uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build Docker image + run: | + docker build -t atheris-builder-x86_64 -f deployment/Dockerfile deployment/ + + - name: Build wheels + run: | + docker run --rm \ + -v ${{ github.workspace }}:/atheris \ + -e ATHERIS_VERSION="${{ github.ref_name }}" \ + atheris-builder-x86_64 + + - name: Upload wheels + uses: actions/upload-artifact@v4 + with: + name: linux-x86_64-wheels + path: dist/*.whl + + build-aarch64: + name: Build Linux aarch64 wheels + runs-on: ubuntu-latest + if: inputs.arch == 'aarch64' || inputs.arch == 'both' + + steps: + - uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + with: + platforms: arm64 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build Docker image (this takes a while - building LLVM on QEMU) + run: | + docker buildx build \ + --platform linux/arm64 \ + -t atheris-builder-aarch64 \ + -f deployment/Dockerfile.aarch64 \ + --load \ + deployment/ + timeout-minutes: 180 # Building LLVM on QEMU is slow + + - name: Build wheels + run: | + docker run --rm \ + --platform linux/arm64 \ + -v ${{ github.workspace }}:/atheris \ + -e ATHERIS_VERSION="${{ github.ref_name }}" \ + atheris-builder-aarch64 + + - name: Upload wheels + uses: actions/upload-artifact@v4 + with: + name: linux-aarch64-wheels + path: dist/*.whl diff --git a/STATUS.md b/STATUS.md index 93a5b8fb..50c522f0 100644 --- a/STATUS.md +++ b/STATUS.md @@ -124,10 +124,17 @@ otool -L atheris/native.*.so ## Current PR Scope -Due to the Linux LLVM build complexity, this PR focuses on: -- **macOS ARM64 wheels only** (native builds on Apple Silicon) +This PR adds: +- **macOS ARM64 wheels** via cibuildwheel (fast, ~5 min) +- **Linux x86_64 wheels** via Docker (slow, ~30-60 min, builds LLVM) +- **Linux aarch64 wheels** via Docker + QEMU (very slow, ~2-3 hours, builds LLVM on emulation) -Linux wheels should continue using the existing `deployment/Dockerfile` approach. +### Workflows + +| Workflow | Platforms | Trigger | Speed | +|----------|-----------|---------|-------| +| `wheels.yaml` | macOS ARM64 | Push tags, manual | Fast (~5 min) | +| `linux-wheels.yaml` | Linux x86_64, aarch64 | Manual only | Slow (LLVM build) | ## Notes diff --git a/deployment/Dockerfile.aarch64 b/deployment/Dockerfile.aarch64 new file mode 100644 index 00000000..5ebe40db --- /dev/null +++ b/deployment/Dockerfile.aarch64 @@ -0,0 +1,52 @@ +# Copyright 2020 Google LLC +# Copyright 2024 rmc-infosec +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FROM quay.io/pypa/manylinux2014_aarch64 + +# Clang needs to be able to find python3 +RUN set -e -x -v; \ + ln -s /opt/python/cp38-cp38/bin/python /usr/bin/python3 + +RUN set -e -x -v; \ + yum install -y cmake; + +RUN set -e -x -v; \ + cd /root; \ + git clone --depth=1 https://github.com/llvm/llvm-project.git; + +RUN set -e -x -v; \ + cd /root/llvm-project; \ + cmake -S llvm -B build -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang;compiler-rt"; + +RUN set -e -x -v; \ + cd /root/llvm-project/build; \ + make -j$(nproc) compiler-rt; + +RUN set -e -x -v; \ + python3 -m pip install auditwheel; + +RUN set -e -x -v; \ + /opt/python/cp311-cp311/bin/python3 -m pip install setuptools && \ + /opt/python/cp312-cp312/bin/python3 -m pip install setuptools && \ + /opt/python/cp313-cp313/bin/python3 -m pip install setuptools; + +WORKDIR /atheris + +CMD export LIBFUZZER_LIB="/root/llvm-project/build/lib/clang/$(ls /root/llvm-project/build/lib/clang/)/lib/aarch64-unknown-linux-gnu/libclang_rt.fuzzer_no_main.a"; \ + /opt/python/cp311-cp311/bin/python3 setup.py bdist_wheel -d /tmp/dist && \ + /opt/python/cp312-cp312/bin/python3 setup.py bdist_wheel -d /tmp/dist && \ + /opt/python/cp313-cp313/bin/python3 setup.py bdist_wheel -d /tmp/dist && \ + ( cd /tmp/dist && find /tmp/dist/* | xargs -I{} auditwheel repair --plat manylinux2014_aarch64 {} ) && \ + mkdir -p /atheris/dist && cp /tmp/dist/wheelhouse/* /atheris/dist/ From 3b0f87be6f8d4ed49b5db20d0457270e51bbdaf7 Mon Sep 17 00:00:00 2001 From: "Leslie P. Polzer" Date: Fri, 9 Jan 2026 03:26:13 +0000 Subject: [PATCH 07/16] Remove STATUS.md from PR --- STATUS.md | 143 ------------------------------------------------------ 1 file changed, 143 deletions(-) delete mode 100644 STATUS.md diff --git a/STATUS.md b/STATUS.md deleted file mode 100644 index 50c522f0..00000000 --- a/STATUS.md +++ /dev/null @@ -1,143 +0,0 @@ -# Atheris ARM/macOS Wheel Support - Status - -## Objective - -Add cross-platform binary wheel builds for ARM Linux and macOS (Intel + Apple Silicon) to the Atheris fuzzing library. - -## Current State (Upstream) - -**PyPI wheels available:** -- `manylinux2014_x86_64` only (Python 3.11, 3.12, 3.13) - -**Missing:** -- `manylinux2014_aarch64` (ARM Linux) -- `macosx_x86_64` (Intel Mac) -- `macosx_arm64` (Apple Silicon) - -## Related GitHub Issues - -| Issue | Status | Summary | -|-------|--------|---------| -| [#52](https://github.com/google/atheris/issues/52) | Open (Dec 2022) | Request for cibuildwheel - assigned to 3.0.0 milestone, never completed | -| [#68](https://github.com/google/atheris/pull/68) | Merged (Aug 2023) | Added ARM64 Linux support in code | -| [#80](https://github.com/google/atheris/issues/80) | Open | macOS 14.2.1 build failures | -| [#74](https://github.com/google/atheris/issues/74) | Open | libFuzzer dependency issues across platforms | - -## Testing Results - -### M1 Mac (macOS 15.6, ARM64) - -| Test | Python 3.12 | Python 3.13 | -|------|-------------|-------------| -| Build with Homebrew LLVM | ✓ | ✓ | -| Import atheris | ✓ | ✓ | -| FuzzedDataProvider | ✓ | ✓ | -| Fuzz test execution | ✓ | ✓ | - -**Environment used:** -```bash -brew install llvm -export CLANG_BIN="$(brew --prefix llvm)/bin/clang" -export CC="$(brew --prefix llvm)/bin/clang" -export CXX="$(brew --prefix llvm)/bin/clang++" -export LDFLAGS="-L$(brew --prefix llvm)/lib/c++ -L$(brew --prefix llvm)/lib -Wl,-rpath,$(brew --prefix llvm)/lib/c++" -export CPPFLAGS="-I$(brew --prefix llvm)/include" -``` - -### Critical Finding: libc++ Linking - -**Problem:** Homebrew LLVM uses newer libc++ with symbols not in system libc++. - -**Symptom:** -``` -ImportError: symbol not found in flat namespace '__ZNSt3__113__hash_memoryEPKvm' -``` - -**Solution:** Must link against Homebrew's libc++ explicitly: -``` -LDFLAGS="-L$(brew --prefix llvm)/lib/c++ -Wl,-rpath,$(brew --prefix llvm)/lib/c++" -``` - -**Verification:** -```bash -# Correct (works): -otool -L atheris/native.*.so - /opt/homebrew/opt/llvm/lib/c++/libc++.1.dylib - -# Incorrect (fails): - /usr/lib/libc++.1.dylib -``` - -## PR Implementation - -### Files Changed - -1. **`pyproject.toml`** - Added cibuildwheel configuration: - - `[build-system]` - PEP 517 build requirements - - `[tool.cibuildwheel]` - Skip PyPy/musllinux, Python 3.11-3.13 - - `[tool.cibuildwheel.linux]` - x86_64 + aarch64, manylinux2014 - - `[tool.cibuildwheel.macos]` - x86_64 + arm64, Homebrew LLVM - -2. **`.github/workflows/wheels.yaml`** - New workflow: - - Matrix: ubuntu-latest (x86_64, aarch64), macos-13 (x86_64), macos-14 (arm64) - - QEMU for aarch64 emulation - - Homebrew LLVM setup for macOS - - PyPI trusted publishing on tagged releases - -### Wheels Produced - -| Platform | Arch | Python Versions | -|----------|------|-----------------| -| manylinux2014 | x86_64 | 3.11, 3.12, 3.13 | -| manylinux2014 | aarch64 | 3.11, 3.12, 3.13 | -| macosx | x86_64 | 3.11, 3.12, 3.13 | -| macosx | arm64 | 3.11, 3.12, 3.13 | - -**Total: 12 wheels** (up from 3) - -## CI Build Results - -**Successful build:** https://github.com/rmc-infosec/atheris/actions/runs/20839360007 - -**Wheels produced:** -- `atheris-3.0.0-cp311-cp311-macosx_11_0_arm64.whl` (7.2 MB) -- `atheris-3.0.0-cp312-cp312-macosx_11_0_arm64.whl` (7.2 MB) -- `atheris-3.0.0-cp313-cp313-macosx_11_0_arm64.whl` (7.2 MB) -- `atheris-3.0.0.tar.gz` (114 KB) - -## Next Steps - -1. [x] Fork google/atheris to rmc-infosec -2. [x] Push changes to fork -3. [x] Test CI workflow - SUCCESS -4. [ ] Comment on Issue #52 to check maintainer interest -5. [ ] Open PR to upstream - -## Known Limitations - -- **macOS requires Homebrew LLVM** - Apple Clang doesn't include libFuzzer -- **Linux requires building LLVM from source** - The manylinux containers don't have libFuzzer pre-installed. Google uses a custom Docker image that builds LLVM from source (see `deployment/Dockerfile`). This takes too long for cibuildwheel. -- **macOS x86_64 cross-compilation has issues** - Building x86_64 on ARM runner fails delocate/test steps -- **No Windows support** - Shell-based build scripts incompatible -- **ASan/UBSan optional** - Sanitizer libraries may not be bundled on all platforms -- **Python 3.14+ unsupported** - Atheris only supports 3.6-3.13 currently - -## Current PR Scope - -This PR adds: -- **macOS ARM64 wheels** via cibuildwheel (fast, ~5 min) -- **Linux x86_64 wheels** via Docker (slow, ~30-60 min, builds LLVM) -- **Linux aarch64 wheels** via Docker + QEMU (very slow, ~2-3 hours, builds LLVM on emulation) - -### Workflows - -| Workflow | Platforms | Trigger | Speed | -|----------|-----------|---------|-------| -| `wheels.yaml` | macOS ARM64 | Push tags, manual | Fast (~5 min) | -| `linux-wheels.yaml` | Linux x86_64, aarch64 | Manual only | Slow (LLVM build) | - -## Notes - -- Wheel size ~7.5MB each (includes libFuzzer) -- aarch64 Linux builds use QEMU emulation (slower CI) -- macOS builds require `brew install llvm` in CI From 6c2c918d31d5b76f5557604b5892658368010134 Mon Sep 17 00:00:00 2001 From: "Leslie P. Polzer" Date: Fri, 9 Jan 2026 03:37:23 +0000 Subject: [PATCH 08/16] Address review findings - Add 'wheel' to build-system requires - Document Homebrew runtime dependency in workflow header - Remove unused Linux cibuildwheel config (use Docker instead) - Clean up misleading comments about multi-platform support --- .github/workflows/wheels.yaml | 14 ++++++++++---- pyproject.toml | 14 +++----------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/.github/workflows/wheels.yaml b/.github/workflows/wheels.yaml index fe2308f1..d6e7bbd1 100644 --- a/.github/workflows/wheels.yaml +++ b/.github/workflows/wheels.yaml @@ -1,6 +1,11 @@ -# Build and publish wheels for multiple platforms -# This workflow builds wheels for Linux (x86_64, aarch64) and macOS (x86_64, arm64) +# Build and publish macOS ARM64 wheels # Addresses: https://github.com/google/atheris/issues/52 +# +# NOTE: This workflow builds macOS ARM64 only. Linux wheels require building +# LLVM from source - see linux-wheels.yaml for Docker-based Linux builds. +# +# KNOWN LIMITATION: macOS wheels currently require Homebrew LLVM at runtime +# (brew install llvm) due to libc++ dependency. See TODO for future fix. name: Build Wheels @@ -51,8 +56,9 @@ jobs: LDFLAGS="-L$(brew --prefix llvm)/lib/c++ -L$(brew --prefix llvm)/lib -Wl,-rpath,$(brew --prefix llvm)/lib/c++" CPPFLAGS="-I$(brew --prefix llvm)/include" - # macOS: Skip delocate repair - it fails on .a static library files - # The libclang_rt.fuzzer_no_main_osx.a is bundled but delocate can't process it + # macOS: Skip delocate - it fails on .a static library files + # TODO: The wheel currently requires Homebrew LLVM at runtime + # Future work: vendor libc++ or use static linking CIBW_REPAIR_WHEEL_COMMAND_MACOS: "" - name: Upload wheels diff --git a/pyproject.toml b/pyproject.toml index 1cd3d2b7..e7ffd1e6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,7 +19,7 @@ ignore = ["E501", "F401"] [build-system] -requires = ["setuptools>=45", "pybind11>=2.5.0"] +requires = ["setuptools>=45", "wheel", "pybind11>=2.5.0"] build-backend = "setuptools.build_meta" @@ -33,16 +33,8 @@ test-command = "python -c \"import atheris; print('atheris loaded, FuzzedDataPro # Build for Python 3.11, 3.12, 3.13 build = "cp311-* cp312-* cp313-*" -[tool.cibuildwheel.linux] -# Build for x86_64 and aarch64 -archs = ["x86_64", "aarch64"] - -# Use manylinux_2_28 which has newer clang with libFuzzer -manylinux-x86_64-image = "manylinux_2_28" -manylinux-aarch64-image = "manylinux_2_28" - -# Install clang with libFuzzer support (manylinux_2_28 is AlmaLinux 8 based) -before-all = "dnf install -y clang compiler-rt" +# Linux builds use Docker (see linux-wheels.yaml) because manylinux +# containers don't have libFuzzer pre-installed and building LLVM is slow. [tool.cibuildwheel.macos] # Build for Apple Silicon only (x86_64 cross-compilation has issues) From 69666de3f3ef80f8ab974aecb4f015db1ba30f89 Mon Sep 17 00:00:00 2001 From: "Leslie P. Polzer" Date: Fri, 9 Jan 2026 03:38:46 +0000 Subject: [PATCH 09/16] Fix: Remove invalid ATHERIS_VERSION env var from Docker builds --- .github/workflows/linux-wheels.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/linux-wheels.yaml b/.github/workflows/linux-wheels.yaml index 2b265494..7991b671 100644 --- a/.github/workflows/linux-wheels.yaml +++ b/.github/workflows/linux-wheels.yaml @@ -37,7 +37,6 @@ jobs: run: | docker run --rm \ -v ${{ github.workspace }}:/atheris \ - -e ATHERIS_VERSION="${{ github.ref_name }}" \ atheris-builder-x86_64 - name: Upload wheels @@ -77,7 +76,6 @@ jobs: docker run --rm \ --platform linux/arm64 \ -v ${{ github.workspace }}:/atheris \ - -e ATHERIS_VERSION="${{ github.ref_name }}" \ atheris-builder-aarch64 - name: Upload wheels From d5fc70aecaf5c5dc5eca4c9e2e383dab66c5ddbf Mon Sep 17 00:00:00 2001 From: "Leslie P. Polzer" Date: Fri, 9 Jan 2026 03:50:12 +0000 Subject: [PATCH 10/16] Fix copyright year: 2026 --- deployment/Dockerfile.aarch64 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment/Dockerfile.aarch64 b/deployment/Dockerfile.aarch64 index 5ebe40db..0402aca7 100644 --- a/deployment/Dockerfile.aarch64 +++ b/deployment/Dockerfile.aarch64 @@ -1,5 +1,5 @@ # Copyright 2020 Google LLC -# Copyright 2024 rmc-infosec +# Copyright 2026 rmc-infosec # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. From 57a1a524748755bba876a972d12a8da095d13ed8 Mon Sep 17 00:00:00 2001 From: "Leslie P. Polzer" Date: Fri, 9 Jan 2026 04:03:46 +0000 Subject: [PATCH 11/16] Address review comments: Unicode fix and documentation - Replace UTF-8 ellipsis character with ASCII '...' in pyproject.toml - Document macOS ARM64 Homebrew runtime dependency in README --- README.md | 8 ++++++++ pyproject.toml | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 11235353..358eea91 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,14 @@ If you don't have `clang` installed or it's too old, you'll need to download and Apple Clang doesn't come with libFuzzer, so you'll need to install a new version of LLVM from head. Follow the instructions in Installing Against New LLVM below. +**Note for macOS ARM64 (Apple Silicon) wheels:** The prebuilt wheels on PyPI for macOS ARM64 require Homebrew LLVM to be installed at runtime due to libc++ dependencies: + +```bash +brew install llvm +``` + +This requirement exists because the wheels link against Homebrew's libc++ library. Future versions may bundle this dependency to make the wheels fully self-contained. + #### Installing Against New LLVM ```bash diff --git a/pyproject.toml b/pyproject.toml index e7ffd1e6..a09428fb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,7 +14,7 @@ exclude = [ exclude = ["src/benchmark/"] ignore-init-module-imports = true # E501 Line too long -# F401 … imported but unused; consider adding to `__all__` or using a redundant alias +# F401 ... imported but unused; consider adding to `__all__` or using a redundant alias ignore = ["E501", "F401"] From 91a0b7e4572158d795a257370a1a6d9ce39b99c2 Mon Sep 17 00:00:00 2001 From: "Leslie P. Polzer" Date: Fri, 9 Jan 2026 04:39:36 +0000 Subject: [PATCH 12/16] Address review findings: LLVM pinning and Linux publish - Pin aarch64 LLVM to same commit as x86_64 (0982db188b...) for reproducible builds and consistent libFuzzer versions - Add tag-push trigger to linux-wheels.yaml so Linux wheels build on releases automatically - Add publish job to linux-wheels.yaml so Linux wheels are uploaded to PyPI alongside macOS wheels from the main workflow --- .github/workflows/linux-wheels.yaml | 40 ++++++++++++++++++++++++++--- deployment/Dockerfile.aarch64 | 3 ++- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/.github/workflows/linux-wheels.yaml b/.github/workflows/linux-wheels.yaml index 7991b671..ae8a8f6c 100644 --- a/.github/workflows/linux-wheels.yaml +++ b/.github/workflows/linux-wheels.yaml @@ -1,10 +1,17 @@ # Build Linux wheels using Docker (requires building LLVM from source) # This is separate from the main wheels.yaml because it takes much longer +# +# Publishes Linux wheels to PyPI on tag pushes. The main wheels.yaml publishes +# macOS wheels + sdist; this workflow adds Linux wheels to the same release. name: Build Linux Wheels on: - # Manual trigger only (building LLVM takes ~30-60 minutes) + # Build on release tags (same as wheels.yaml) + push: + tags: + - 'v*' + # Manual trigger for testing (building LLVM takes ~30-60 minutes) workflow_dispatch: inputs: arch: @@ -21,7 +28,8 @@ jobs: build-x86_64: name: Build Linux x86_64 wheels runs-on: ubuntu-latest - if: inputs.arch == 'x86_64' || inputs.arch == 'both' + # Build on tag pushes, or manual trigger with x86_64/both selected + if: github.event_name == 'push' || inputs.arch == 'x86_64' || inputs.arch == 'both' steps: - uses: actions/checkout@v4 @@ -48,7 +56,8 @@ jobs: build-aarch64: name: Build Linux aarch64 wheels runs-on: ubuntu-latest - if: inputs.arch == 'aarch64' || inputs.arch == 'both' + # Build on tag pushes, or manual trigger with aarch64/both selected + if: github.event_name == 'push' || inputs.arch == 'aarch64' || inputs.arch == 'both' steps: - uses: actions/checkout@v4 @@ -83,3 +92,28 @@ jobs: with: name: linux-aarch64-wheels path: dist/*.whl + + # Publish Linux wheels to PyPI on tag pushes + publish: + name: Publish Linux wheels to PyPI + needs: [build-x86_64, build-aarch64] + runs-on: ubuntu-latest + # Only run on tag pushes, not manual triggers + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') + environment: + name: pypi + url: https://pypi.org/p/atheris + permissions: + id-token: write # Required for trusted publishing + + steps: + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: dist + merge-multiple: true + + - name: Publish to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + # Uses trusted publishing - no API token needed + # Note: macOS wheels + sdist are published by the main wheels.yaml workflow diff --git a/deployment/Dockerfile.aarch64 b/deployment/Dockerfile.aarch64 index 0402aca7..62641cfd 100644 --- a/deployment/Dockerfile.aarch64 +++ b/deployment/Dockerfile.aarch64 @@ -24,10 +24,11 @@ RUN set -e -x -v; \ RUN set -e -x -v; \ cd /root; \ - git clone --depth=1 https://github.com/llvm/llvm-project.git; + git clone https://github.com/llvm/llvm-project.git; RUN set -e -x -v; \ cd /root/llvm-project; \ + git checkout 0982db188b661d6744b06244fda64d43dd80206e; \ cmake -S llvm -B build -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang;compiler-rt"; RUN set -e -x -v; \ From 4dd69b6812dd12cabcc13ac240f3721753f14a2e Mon Sep 17 00:00:00 2001 From: "Leslie P. Polzer" Date: Fri, 9 Jan 2026 05:39:55 +0000 Subject: [PATCH 13/16] Consolidate publishing into unified release workflow - Remove publish jobs from wheels.yaml and linux-wheels.yaml - Add release.yaml that waits for all build workflows to complete before downloading artifacts and publishing to PyPI - Prevents partial PyPI releases when one workflow fails On tag push, all three workflows run: 1. wheels.yaml builds macOS + sdist 2. linux-wheels.yaml builds Linux x86_64 + aarch64 3. release.yaml waits for both, then publishes everything --- .github/workflows/linux-wheels.yaml | 25 +------ .github/workflows/release.yaml | 110 ++++++++++++++++++++++++++++ .github/workflows/wheels.yaml | 33 ++------- 3 files changed, 117 insertions(+), 51 deletions(-) create mode 100644 .github/workflows/release.yaml diff --git a/.github/workflows/linux-wheels.yaml b/.github/workflows/linux-wheels.yaml index ae8a8f6c..bdf0d586 100644 --- a/.github/workflows/linux-wheels.yaml +++ b/.github/workflows/linux-wheels.yaml @@ -93,27 +93,4 @@ jobs: name: linux-aarch64-wheels path: dist/*.whl - # Publish Linux wheels to PyPI on tag pushes - publish: - name: Publish Linux wheels to PyPI - needs: [build-x86_64, build-aarch64] - runs-on: ubuntu-latest - # Only run on tag pushes, not manual triggers - if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') - environment: - name: pypi - url: https://pypi.org/p/atheris - permissions: - id-token: write # Required for trusted publishing - - steps: - - name: Download all artifacts - uses: actions/download-artifact@v4 - with: - path: dist - merge-multiple: true - - - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 - # Uses trusted publishing - no API token needed - # Note: macOS wheels + sdist are published by the main wheels.yaml workflow + # Linux wheels are published by the unified release.yaml workflow diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 00000000..260235c9 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,110 @@ +# Unified release workflow that publishes all wheels to PyPI +# +# This workflow waits for both build workflows to complete before publishing, +# ensuring atomic releases with all artifacts (macOS + Linux + sdist). +# +# Triggered by tag pushes - waits for wheels.yaml and linux-wheels.yaml to finish. + +name: Release to PyPI + +on: + push: + tags: + - 'v*' + +jobs: + wait-for-builds: + name: Wait for build workflows + runs-on: ubuntu-latest + steps: + - name: Wait for macOS wheels workflow + uses: lewagon/wait-on-check-action@v1.3.4 + with: + ref: ${{ github.ref }} + check-name: 'Build wheels on macos-latest' + repo-token: ${{ secrets.GITHUB_TOKEN }} + wait-interval: 30 + + - name: Wait for Linux x86_64 wheels workflow + uses: lewagon/wait-on-check-action@v1.3.4 + with: + ref: ${{ github.ref }} + check-name: 'Build Linux x86_64 wheels' + repo-token: ${{ secrets.GITHUB_TOKEN }} + wait-interval: 60 + + - name: Wait for Linux aarch64 wheels workflow + uses: lewagon/wait-on-check-action@v1.3.4 + with: + ref: ${{ github.ref }} + check-name: 'Build Linux aarch64 wheels' + repo-token: ${{ secrets.GITHUB_TOKEN }} + wait-interval: 60 + + - name: Wait for sdist build + uses: lewagon/wait-on-check-action@v1.3.4 + with: + ref: ${{ github.ref }} + check-name: 'Build source distribution' + repo-token: ${{ secrets.GITHUB_TOKEN }} + wait-interval: 30 + + publish: + name: Publish to PyPI + needs: wait-for-builds + runs-on: ubuntu-latest + environment: + name: pypi + url: https://pypi.org/p/atheris + permissions: + id-token: write # Required for trusted publishing + actions: read # Required to download artifacts + + steps: + - name: Get workflow run IDs + id: get-runs + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # Get the most recent successful run of each workflow for this tag + MACOS_RUN=$(gh run list --repo ${{ github.repository }} \ + --workflow "Build macOS Wheels" \ + --branch ${{ github.ref_name }} \ + --status success \ + --limit 1 \ + --json databaseId \ + --jq '.[0].databaseId') + echo "macos_run=$MACOS_RUN" >> $GITHUB_OUTPUT + + LINUX_RUN=$(gh run list --repo ${{ github.repository }} \ + --workflow "Build Linux Wheels" \ + --branch ${{ github.ref_name }} \ + --status success \ + --limit 1 \ + --json databaseId \ + --jq '.[0].databaseId') + echo "linux_run=$LINUX_RUN" >> $GITHUB_OUTPUT + + - name: Download macOS wheels + uses: actions/download-artifact@v4 + with: + run-id: ${{ steps.get-runs.outputs.macos_run }} + github-token: ${{ secrets.GITHUB_TOKEN }} + path: dist + merge-multiple: true + + - name: Download Linux wheels + uses: actions/download-artifact@v4 + with: + run-id: ${{ steps.get-runs.outputs.linux_run }} + github-token: ${{ secrets.GITHUB_TOKEN }} + path: dist + merge-multiple: true + + - name: List artifacts + run: ls -la dist/ + + - name: Publish to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + # Uses trusted publishing - no API token needed + # Configure at: https://pypi.org/manage/project/atheris/settings/publishing/ diff --git a/.github/workflows/wheels.yaml b/.github/workflows/wheels.yaml index d6e7bbd1..469ac754 100644 --- a/.github/workflows/wheels.yaml +++ b/.github/workflows/wheels.yaml @@ -1,4 +1,4 @@ -# Build and publish macOS ARM64 wheels +# Build macOS ARM64 wheels and source distribution # Addresses: https://github.com/google/atheris/issues/52 # # NOTE: This workflow builds macOS ARM64 only. Linux wheels require building @@ -6,11 +6,13 @@ # # KNOWN LIMITATION: macOS wheels currently require Homebrew LLVM at runtime # (brew install llvm) due to libc++ dependency. See TODO for future fix. +# +# Publishing is handled by release.yaml which waits for all build workflows. -name: Build Wheels +name: Build macOS Wheels on: - # Build on release tags + # Build on release tags (release.yaml handles publishing) push: tags: - 'v*' @@ -82,27 +84,4 @@ jobs: name: sdist path: dist/*.tar.gz - # Only publish on tagged releases - publish: - name: Publish to PyPI - needs: [build_wheels, build_sdist] - runs-on: ubuntu-latest - # Only run on tag pushes, not PRs or manual triggers - if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') - environment: - name: pypi - url: https://pypi.org/p/atheris - permissions: - id-token: write # Required for trusted publishing - - steps: - - name: Download all artifacts - uses: actions/download-artifact@v4 - with: - path: dist - merge-multiple: true - - - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 - # Uses trusted publishing - no API token needed - # Configure at: https://pypi.org/manage/project/atheris/settings/publishing/ + # Publishing is handled by release.yaml From e1c274db20f4af29532b5bd1deb988a93315ff94 Mon Sep 17 00:00:00 2001 From: "Leslie P. Polzer" Date: Fri, 9 Jan 2026 06:45:44 +0000 Subject: [PATCH 14/16] Fix release workflow issues - Add checks:read permission for wait-for-builds job - Use --commit instead of --branch for gh run list (--branch doesn't work for tag refs, would return wrong/no runs) - Fix stale comment in linux-wheels.yaml header --- .github/workflows/linux-wheels.yaml | 3 +-- .github/workflows/release.yaml | 9 ++++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/linux-wheels.yaml b/.github/workflows/linux-wheels.yaml index bdf0d586..33305686 100644 --- a/.github/workflows/linux-wheels.yaml +++ b/.github/workflows/linux-wheels.yaml @@ -1,8 +1,7 @@ # Build Linux wheels using Docker (requires building LLVM from source) # This is separate from the main wheels.yaml because it takes much longer # -# Publishes Linux wheels to PyPI on tag pushes. The main wheels.yaml publishes -# macOS wheels + sdist; this workflow adds Linux wheels to the same release. +# Publishing is handled by release.yaml which waits for all build workflows. name: Build Linux Wheels diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 260235c9..0abfcefe 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -16,6 +16,8 @@ jobs: wait-for-builds: name: Wait for build workflows runs-on: ubuntu-latest + permissions: + checks: read # Required by lewagon/wait-on-check-action steps: - name: Wait for macOS wheels workflow uses: lewagon/wait-on-check-action@v1.3.4 @@ -66,10 +68,11 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - # Get the most recent successful run of each workflow for this tag + # Get the most recent successful run of each workflow for this commit + # Note: --commit filters by headSha, works correctly for tag pushes MACOS_RUN=$(gh run list --repo ${{ github.repository }} \ --workflow "Build macOS Wheels" \ - --branch ${{ github.ref_name }} \ + --commit ${{ github.sha }} \ --status success \ --limit 1 \ --json databaseId \ @@ -78,7 +81,7 @@ jobs: LINUX_RUN=$(gh run list --repo ${{ github.repository }} \ --workflow "Build Linux Wheels" \ - --branch ${{ github.ref_name }} \ + --commit ${{ github.sha }} \ --status success \ --limit 1 \ --json databaseId \ From 4a9b74d51e9e44e9f2a8ca4738fdf5dec19c95e9 Mon Sep 17 00:00:00 2001 From: "Leslie P. Polzer" Date: Fri, 9 Jan 2026 08:15:13 +0000 Subject: [PATCH 15/16] Add timeout and retry support to release workflow - Add 240-minute timeout to wait-for-builds job (aarch64 build can take up to 180 min, plus buffer for other builds) - Add skip-existing: true to PyPI publish step so failed releases can be retried without manual cleanup --- .github/workflows/release.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 0abfcefe..9db0c997 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -16,6 +16,8 @@ jobs: wait-for-builds: name: Wait for build workflows runs-on: ubuntu-latest + # aarch64 build can take up to 180 min; add buffer for all builds + timeout-minutes: 240 permissions: checks: read # Required by lewagon/wait-on-check-action steps: @@ -109,5 +111,8 @@ jobs: - name: Publish to PyPI uses: pypa/gh-action-pypi-publish@release/v1 + with: + # Skip files already uploaded (allows retrying failed releases) + skip-existing: true # Uses trusted publishing - no API token needed # Configure at: https://pypi.org/manage/project/atheris/settings/publishing/ From 255c0b0587eb3841c1dca50d1dbe675c28dcb710 Mon Sep 17 00:00:00 2001 From: "Leslie P. Polzer" Date: Fri, 9 Jan 2026 09:42:13 +0000 Subject: [PATCH 16/16] Fix copyright: RMC Infosec (proper capitalization) --- deployment/Dockerfile.aarch64 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment/Dockerfile.aarch64 b/deployment/Dockerfile.aarch64 index 62641cfd..e34d9993 100644 --- a/deployment/Dockerfile.aarch64 +++ b/deployment/Dockerfile.aarch64 @@ -1,5 +1,5 @@ # Copyright 2020 Google LLC -# Copyright 2026 rmc-infosec +# Copyright 2026 RMC Infosec # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License.