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
85 changes: 85 additions & 0 deletions .github/workflows/coverage.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
name: Fuzz Coverage

on:
push:
branches:
- main
paths:
- "corpus/**"

schedule:
- cron: "0 3 * * *" # Daily at 03:00 UTC
workflow_dispatch:

permissions:
contents: write
pages: write
id-token: write

jobs:
coverage:
runs-on: ubuntu-latest

steps:
- name: Checkout corpus repo
uses: actions/checkout@v4

- name: Checkout fuzz targets repo
uses: actions/checkout@v4
with:
repository: stratum-mining/stratum
path: stratum

- name: Install Rust
uses: dtolnay/rust-toolchain@nightly
with:
profile: minimal
toolchain: nightly
override: true
components: llvm-tools-preview

- name: Add Rust LLVM tools to PATH
run: |
LLVM_TOOLS=$(rustc +nightly --print sysroot)/lib/rustlib/$(rustc --print host-tuple)/bin
echo "$LLVM_TOOLS" >> $GITHUB_PATH
ls -al "$LLVM_TOOLS"

- name: Install cargo-fuzz
run: cargo install cargo-fuzz

- name: Copy corpus into fuzz targets repo
run: |
mkdir -p stratum/fuzz/corpus
mv corpus/* stratum/fuzz/corpus

- name: Run fuzz coverage
run: |
chmod +x ./scripts/fuzz_coverage.sh
cp ./scripts/fuzz_coverage.sh stratum/fuzz_coverage.sh
cd stratum/
ls fuzz/
./fuzz_coverage.sh

- name: Move coverage results into this repo
run: |
rm -rf coverage
mkdir -p coverage
cp -r stratum/coverage_html coverage/

- name: Upload pages artifact
uses: actions/upload-pages-artifact@v3
with:
path: coverage/coverage_html

deploy:
needs: coverage
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deploy.outputs.page_url }}

steps:
- name: Deploy to GitHub Pages
id: deploy
uses: actions/deploy-pages@v4

98 changes: 98 additions & 0 deletions scripts/fuzz_coverage.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#!/usr/bin/env bash
set -euo pipefail

############################################
# Tools must be available directly in PATH:
# llvm-profdata
# llvm-cov
############################################

PROJECT_ROOT="$(pwd)"
HOST_TARGET=$(rustc +nightly --print host-tuple)

echo "[+] Project root: $PROJECT_ROOT"
echo "[+] Host target: $HOST_TARGET"

############################################
# 1. Run coverage for all fuzz targets
############################################

echo "[+] Running fuzz coverage"
for target in $(cargo +nightly fuzz list); do
echo " → $target"
cargo +nightly fuzz coverage "$target"
done

############################################
# 2. Merge all .profdata
############################################

echo "[+] Merging coverage profiles"
llvm-profdata merge -sparse \
fuzz/coverage/*/coverage.profdata \
-o fuzz/coverage/merged.profdata

############################################
# 3. Collect fuzz target coverage binaries
############################################

echo "[+] Collecting fuzz binaries"

OBJECTS=""
FIRST_TARGET=""

for target in $(cargo fuzz list); do
BIN="target/$HOST_TARGET/coverage/$HOST_TARGET/release/$target"

if [ ! -f "$BIN" ]; then
echo "ERROR: expected binary not found: $BIN"
exit 1
fi

if [ -z "$FIRST_TARGET" ]; then
FIRST_TARGET="$BIN"
else
OBJECTS="$OBJECTS -object $BIN"
fi
done

############################################
# 4. Filtering rules:
# - ignore stdlib (/rustc/…)
# - ignore crates.io (~/.cargo/registry/…)
# - ignore everything outside the project dir
############################################

IGNORE_FLAGS=(
"-ignore-filename-regex=/.*/rustc/.*"
"-ignore-filename-regex=/.cargo/registry/.*"
"-ignore-filename-regex=^/(?!${PROJECT_ROOT}).*$"
)

############################################
# 5. Generate text summary
############################################

echo "[+] Generating text summary"
llvm-cov report \
"${IGNORE_FLAGS[@]}" \
-instr-profile=fuzz/coverage/merged.profdata \
$FIRST_TARGET $OBJECTS \
> coverage_summary.txt

############################################
# 6. Generate HTML report
############################################

echo "[+] Generating HTML report"
llvm-cov show \
"${IGNORE_FLAGS[@]}" \
-instr-profile=fuzz/coverage/merged.profdata \
$FIRST_TARGET $OBJECTS \
-format=html \
-output-dir=coverage_html

echo "[✓] Done!"
echo "HTML → coverage_html/index.html"
echo "TXT → coverage_summary.txt"

75 changes: 75 additions & 0 deletions scripts/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Fuzz Coverage Script

This repository includes a helper script, `fuzz_coverage.sh`, that lets you generate local coverage reports for our fuzz targets.

The script is meant to be copied into the **root of the [stratum](https://github.com/stratum-mining/stratum) repository**.

It also expects that the **corpus directory** of this repository to be present in `fuzz/corpus` on the [stratum](https://github.com/stratum-mining/stratum) repository.

---

## Requirements

Before running the script, the following must be available directly in your `$PATH`:

* `llvm-profdata`
* `llvm-cov`
* A nightly Rust toolchain
* `cargo-fuzz`

For details on installing LLVM coverage tools, see:
[https://doc.rust-lang.org/rustc/instrument-coverage.html#installing-llvm-coverage-tools](https://doc.rust-lang.org/rustc/instrument-coverage.html#installing-llvm-coverage-tools)

---

## Expected Project Layout

After copying `fuzz_coverage.sh` into the root of [stratum](https://github.com/stratum-mining/stratum) repository, the structure should look like:

```
stratum/
├── coverage_html (generated by the fuzz_coverage.sh after a successfull run)
├── fuzz
│ ├── artifacts
│ ├── corpus (the one copied from this repo)
│ ├── coverage (generated by cargo-fuzz)
│ ├── fuzz_targets
└── fuzz_coverage.sh (copied from this repo to the root of the stratum repo)
```

---

## Usage

Run the script from the project root:

```bash
./fuzz_coverage.sh
```

The script does the following:

1. Runs `cargo +nightly fuzz coverage` for every fuzz target.
2. Merges all generated `.profdata` files.
3. Collects the built coverage binaries.
5. Generates:
* `coverage_summary.txt` (text report)
* `coverage_html/` (HTML report)

---

## Output

After running:

* Open the HTML report:

```
coverage_html/index.html
```

* View the text summary:

```
coverage_summary.txt
```