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
86 changes: 56 additions & 30 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,17 @@ jobs:
# Maybe if codecov wasn't broken we wouldn't need to do this...
./codecov --verbose upload-process --disable-search --fail-on-error -f target/codecov.json -t "f421b687-4dc2-4387-ac3d-dc3b2528af57" -F 'tests'
cargo clean
- name: Download honggfuzz corpus
uses: actions/download-artifact@v4
with:
name: hfuzz-corpus
path: fuzz/hfuzz_workspace
- name: Clone fuzzing corpus
run: git clone --depth=1 https://github.com/lightningdevkit/ldk-fuzzing-corpus.git fuzz/ldk-fuzzing-corpus
- name: Symlink corpus into hfuzz_workspace
run: |
set -eu
cd fuzz
for D in ldk-fuzzing-corpus/rust-lightning/*/; do
NAME=$(basename "$D")
mkdir -p "hfuzz_workspace/${NAME}_target"
cp -r "ldk-fuzzing-corpus/rust-lightning/${NAME}" "hfuzz_workspace/${NAME}_target/input"
done
Comment thread
TheBlueMatt marked this conversation as resolved.
- name: Run fuzz coverage generation
run: |
./contrib/generate_fuzz_coverage.sh --output-dir `pwd` --output-codecov-json
Expand Down Expand Up @@ -233,39 +239,59 @@ jobs:
- name: Install Rust ${{ env.TOOLCHAIN }} toolchain
run: |
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile=minimal --default-toolchain ${{ env.TOOLCHAIN }}
# This is read-only for PRs. It seeds the fuzzer for a more effective run.
# NOTE: The `key` is unique and will always miss, forcing a fallback to
# the `restore-keys` to find the latest global cache from the `main` branch.
- name: Restore persistent fuzz corpus (PR)
if: ${{ github.ref != 'refs/heads/main' }}
uses: actions/cache/restore@v4
with:
path: fuzz/hfuzz_workspace
key: fuzz-corpus-${{ github.ref }}-${{ github.sha }}
restore-keys: |
fuzz-corpus-refs/heads/main-
# The `restore-keys` performs a prefix search to find the most recent
# cache from a previous `main` run. We then save with a new, unique
# `key` (using the SHA) to ensure the cache is always updated,
# as caches are immutable.
- name: Restore/Save persistent honggfuzz corpus (Main)
if: ${{ github.ref == 'refs/heads/main' }}
uses: actions/cache@v4
with:
path: fuzz/hfuzz_workspace
key: fuzz-corpus-refs/heads/main-${{ github.sha }}
restore-keys: |
fuzz-corpus-refs/heads/main-
- name: Clone fuzzing corpus
run: git clone --depth=1 https://github.com/lightningdevkit/ldk-fuzzing-corpus.git fuzz/ldk-fuzzing-corpus
- name: Symlink corpus into hfuzz_workspace
run: |
set -eu
cd fuzz
for D in ldk-fuzzing-corpus/rust-lightning/*/; do
NAME=$(basename "$D")
mkdir -p "hfuzz_workspace/${NAME}_target"
ln -sfn "../../ldk-fuzzing-corpus/rust-lightning/${NAME}" \
"hfuzz_workspace/${NAME}_target/input"
done
- name: Run fuzzers
run: cd fuzz && ./ci-fuzz.sh && cd ..
env:
FUZZ_MINIMIZE: ${{ contains(github.event.pull_request.labels.*.name, 'fuzz-minimize') }}
- name: Upload honggfuzz corpus
- name: Stage new corpus entries for upload
if: success() || failure()
run: |
set -eu
WORKSPACE="$(pwd)"
rm -rf "$WORKSPACE/new-corpus"
mkdir -p "$WORKSPACE/new-corpus"

cd fuzz/ldk-fuzzing-corpus
while IFS= read -r F; do
mkdir -p "$WORKSPACE/new-corpus/$(dirname "$F")"
cp -a "$F" "$WORKSPACE/new-corpus/$F"
done < <(git ls-files --others --exclude-standard rust-lightning/)
cd "$WORKSPACE"

for D in fuzz/hfuzz_workspace/*_target/; do
[ -d "$D" ] || continue
BASE=$(basename "$D")
NAME="${BASE%_target}"
[ -d "$WORKSPACE/new-corpus/$NAME" ] || continue
for F in "$D"/SIG*; do
FILE="$(basename "$F")"
[ -f "$F" -a ! -f "$WORKSPACE/new-corpus/$NAME/$FILE" ] &&
cp "$F" "$WORKSPACE/new-corpus/$NAME/$FILE"
done
done
Comment thread
TheBlueMatt marked this conversation as resolved.

NEW=$(find new-corpus -type f 2>/dev/null | wc -l)
echo "Staged $NEW new corpus entries (including any SIG* crashes)"
- name: Upload new corpus entries
if: success() || failure()
uses: actions/upload-artifact@v4
with:
name: hfuzz-corpus
path: fuzz/hfuzz_workspace
path: new-corpus
compression-level: 0
if-no-files-found: ignore

linting:
runs-on: ubuntu-latest
Expand Down
92 changes: 92 additions & 0 deletions .github/workflows/push-fuzz-corpus.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
name: Push fuzz corpus

# Triggered after the main CI workflow finishes. Because `workflow_run` always
# runs in the *base* repo's context (its workflow file as of `main`, with
# full secrets access) it's safe to handle the corpus push here even for fork
# PRs — none of the PR's modified code or scripts execute in this job.
#
# Caveat: GitHub only fires `workflow_run` for workflow files that live on
# the default branch, so this workflow does nothing until it's merged to
# `master`.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: main

on:
# zizmor flags `workflow_run` as a dangerous trigger because it runs with
# repo secrets in base-branch context. That's exactly why we use it here:
# this workflow never touches any PR-supplied code (no checkout, no script
# execution from the artifact — just cp/git on opaque corpus blobs), so
# the warning is a false positive.
workflow_run: # zizmor: ignore[dangerous-triggers]
workflows: ["Continuous Integration Checks"]
types: [completed]

permissions:
# download-artifact across runs requires `actions: read`.
actions: read

jobs:
push-corpus:
# Run on either success or fuzzer crash; skip on cancellation.
if: >-
github.event.workflow_run.conclusion == 'success' ||
github.event.workflow_run.conclusion == 'failure'
runs-on: ubuntu-latest
steps:
- name: Download fuzz corpus artifact
id: download
# The artifact only exists when the fuzz job got far enough to upload
# it. Don't fail this workflow if the upload was skipped.
continue-on-error: true
uses: actions/download-artifact@v4
with:
name: hfuzz-corpus
path: hfuzz-corpus
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}

- name: Clone fuzzing corpus
if: steps.download.outcome == 'success'
run: git clone --depth=1 https://github.com/lightningdevkit/ldk-fuzzing-corpus.git

- name: Copy new corpus entries into the corpus checkout
if: steps.download.outcome == 'success'
run: |
set -eu
if [ -d hfuzz-corpus/rust-lightning ]; then
cp -rn hfuzz-corpus/rust-lightning/. ldk-fuzzing-corpus/rust-lightning/
fi

- name: Open PR with new corpus entries
if: steps.download.outcome == 'success'
env:
GH_TOKEN: ${{ secrets.CORPUS_PUSH_TOKEN }}
SOURCE_SHA: ${{ github.event.workflow_run.head_sha }}
RUN_URL: ${{ github.event.workflow_run.html_url }}
RUN_ID: ${{ github.event.workflow_run.id }}
run: |
set -eu
cd ldk-fuzzing-corpus
if [ -z "$(git status --porcelain)" ]; then
echo "No new corpus entries to contribute."
exit 0
fi
if [ -z "${GH_TOKEN:-}" ]; then
echo "Found new corpus entries but CORPUS_PUSH_TOKEN is unset; skipping PR."
git status --short
exit 0
fi
BRANCH="ci/new-corpus-${RUN_ID}"
git config user.email "ldk-ci@users.noreply.github.com"
git config user.name "LDK CI"
git checkout -b "$BRANCH"
git add rust-lightning
git commit \
-m "Add corpus entries from rust-lightning CI" \
-m "Source commit: ${SOURCE_SHA}" \
-m "Run: ${RUN_URL}"
REMOTE=$(git config --get remote.origin.url)
PUSH_URL="https://x-access-token:${GH_TOKEN}@${REMOTE#https://}"
git push "$PUSH_URL" "HEAD:$BRANCH"
gh pr create \
--title "New corpus entries from rust-lightning CI run ${RUN_ID}" \
--body "Discovered while running fuzz CI against \`${SOURCE_SHA}\`. Source: ${RUN_URL}" \
--head "$BRANCH" \
--base master
Loading