Skip to content

nix: put libstdc++ on the fuzz shell's LD_LIBRARY_PATH for Linux #2

nix: put libstdc++ on the fuzz shell's LD_LIBRARY_PATH for Linux

nix: put libstdc++ on the fuzz shell's LD_LIBRARY_PATH for Linux #2

Workflow file for this run

# SPDX-License-Identifier: AGPL-3.0-only
# Copyright (C) 2026 RS-Key contributors
# Scheduled deep checks — the slow adversarial suites that are deliberately
# not part of the merge gate (docs/testing.md):
# miri — fuzz/tests/miri.rs: every fuzz target's logic under Miri's UB
# checker. MIRIFLAGS policy comes from the .#fuzz dev shell.
# fuzz — a timed libFuzzer pass over every cargo-fuzz target. The corpus
# is carried between runs via the actions cache, so coverage
# accumulates week over week; crash artifacts are uploaded.
#
# Runs weekly, on demand (workflow_dispatch, with a per-target time knob),
# and self-tests on any push that edits this file. Local equivalents:
# nix develop .#fuzz -c cargo miri test --manifest-path fuzz/Cargo.toml
# nix develop .#fuzz -c cargo fuzz run <target> -- -max_total_time=120
name: deep-checks
on:
schedule:
- cron: "17 5 * * 1" # Mondays 05:17 UTC
workflow_dispatch:
inputs:
fuzz_seconds:
description: seconds of fuzzing per target
default: "120"
push:
branches: [main]
# The dev shell defines this workflow's runtime environment — self-test on
# changes to either.
paths: [".github/workflows/deep-checks.yml", "nix/devshells.nix"]
permissions:
contents: read
concurrency:
group: deep-checks
cancel-in-progress: true
jobs:
miri:
runs-on: ubuntu-latest
timeout-minutes: 180
steps:
- uses: actions/checkout@v4
- uses: DeterminateSystems/nix-installer-action@v16
# Own prefix (the nightly toolchain only this workflow needs), falling
# back to the ci cache for the shared store.
- uses: nix-community/cache-nix-action@v6
with:
primary-key: nix-fuzz-${{ runner.os }}-${{ hashFiles('flake.lock') }}
restore-prefixes-first-match: |
nix-fuzz-${{ runner.os }}-
nix-${{ runner.os }}-
- uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
fuzz/target
key: miri-${{ runner.os }}-${{ hashFiles('fuzz/Cargo.lock', 'flake.lock') }}
restore-keys: miri-${{ runner.os }}-
- name: miri — every fuzz target's logic under the UB checker
run: nix develop .#fuzz -c cargo miri test --manifest-path fuzz/Cargo.toml
fuzz:
runs-on: ubuntu-latest
timeout-minutes: 240
steps:
- uses: actions/checkout@v4
- uses: DeterminateSystems/nix-installer-action@v16
- uses: nix-community/cache-nix-action@v6
with:
primary-key: nix-fuzz-${{ runner.os }}-${{ hashFiles('flake.lock') }}
restore-prefixes-first-match: |
nix-fuzz-${{ runner.os }}-
nix-${{ runner.os }}-
- uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
fuzz/target
key: fuzz-${{ runner.os }}-${{ hashFiles('fuzz/Cargo.lock', 'flake.lock') }}
restore-keys: fuzz-${{ runner.os }}-
# The corpus is gitignored (fuzz/corpus) — in CI it lives in the cache:
# restore the newest, save under a fresh key even when a target crashes.
- uses: actions/cache/restore@v4
with:
path: fuzz/corpus
key: fuzz-corpus-${{ github.run_id }}
restore-keys: fuzz-corpus-
- name: build every fuzz target
run: nix develop .#fuzz -c cargo fuzz build
# One pass over every target; a crash does not stop the loop, so one
# bad target cannot mask another's findings.
- name: fuzz every target
run: |
nix develop .#fuzz -c bash -euo pipefail -c '
failed=""
for t in $(cargo fuzz list); do
echo "::group::${t} (${FUZZ_SECONDS}s)"
if ! cargo fuzz run "$t" -- -max_total_time="$FUZZ_SECONDS" -print_final_stats=1; then
failed="$failed $t"
fi
echo "::endgroup::"
done
if [ -n "$failed" ]; then
echo "::error::crashing targets:$failed"
exit 1
fi
'
env:
FUZZ_SECONDS: ${{ inputs.fuzz_seconds || '120' }}
- name: upload crash artifacts
if: failure()
uses: actions/upload-artifact@v4
with:
name: fuzz-artifacts-${{ github.run_id }}
path: fuzz/artifacts/
if-no-files-found: ignore
- uses: actions/cache/save@v4
if: always()
with:
path: fuzz/corpus
key: fuzz-corpus-${{ github.run_id }}