chore: add edge bitmap calculation to libFuzz workflow

This commit is contained in:
Roman 2026-05-26 17:05:52 +08:00
parent dbe1b82ccc
commit 4f5c8a282e
No known key found for this signature in database
GPG Key ID: 583BDF43C238B83E

View File

@ -1,6 +1,9 @@
name: Fuzzing name: Fuzzing
on: on:
push:
branches: [main, develop, feat-add-afl-fuzzing]
pull_request:
schedule: schedule:
- cron: "0 2 * * *" - cron: "0 2 * * *"
workflow_dispatch: workflow_dispatch:
@ -81,59 +84,27 @@ jobs:
mkdir -p "$CORPUS" mkdir -p "$CORPUS"
# ── Build and replay the corpus with LLVM coverage instrumentation ── # ── Build and replay the corpus with LLVM coverage instrumentation ──
# Capture output so we can parse the libFuzzer edge-bitmap lines. cargo fuzz coverage "$TARGET" "$CORPUS" 2>/dev/null || true
# cargo fuzz coverage builds into fuzz/target/<triple>/coverage/<target>
# and writes the merged profdata to fuzz/coverage/<target>/coverage.profdata
COVERAGE_LOG=$(cargo fuzz coverage "$TARGET" "$CORPUS" 2>&1 || true)
echo "$COVERAGE_LOG"
# ── Extract libFuzzer edge-bitmap metrics from the merge log ──
# Total edges: "INFO: Loaded 1 modules (N inline 8-bit counters)"
# Covered edges: "MERGE-OUTER: ... N new coverage edges"
edge_total=$(echo "$COVERAGE_LOG" \
| grep -oP '(?<=Loaded 1 modules\s{1,10}\()\d+(?= inline 8-bit counters)' \
| tail -1)
edge_covered=$(echo "$COVERAGE_LOG" \
| grep -oP '\d+(?= new coverage edges)' \
| tail -1)
if [ -n "$edge_total" ] && [ -n "$edge_covered" ] && [ "$edge_total" -gt 0 ]; then
edge_pct=$(python3 -c "print(f'{100*${edge_covered}/${edge_total}:.2f}')")
else
edge_pct="n/a"
fi
[ -z "$edge_total" ] && edge_total="n/a"
[ -z "$edge_covered" ] && edge_covered="n/a"
echo "Edge bitmap: ${edge_covered}/${edge_total} (${edge_pct}%)"
# ── Locate llvm-cov from the installed nightly toolchain ── # ── Locate llvm-cov from the installed nightly toolchain ──
SYSROOT="$(rustc --print sysroot)" SYSROOT="$(rustc --print sysroot)"
HOST_TRIPLE="$(rustc -vV | awk '/^host:/{print $2}')" HOST_TRIPLE="$(rustc -vV | awk '/^host:/{print $2}')"
LLVM_COV="${SYSROOT}/lib/rustlib/${HOST_TRIPLE}/bin/llvm-cov" LLVM_COV="${SYSROOT}/lib/rustlib/${HOST_TRIPLE}/bin/llvm-cov"
# Use deterministic paths — cargo-fuzz always places artefacts here: PROFDATA=$(find fuzz/coverage/"$TARGET" -name "coverage.profdata" 2>/dev/null | head -1)
# binary → fuzz/target/<triple>/coverage/<target> BINARY=$(find fuzz/target -name "$TARGET" -type f -perm /111 2>/dev/null | grep release | head -1)
# profdata → fuzz/coverage/<target>/coverage.profdata
PROFDATA="fuzz/coverage/${TARGET}/coverage.profdata"
BINARY="fuzz/target/${HOST_TRIPLE}/coverage/${TARGET}"
echo "llvm-cov : ${LLVM_COV}"
echo "profdata : ${PROFDATA} (exists: $([ -f "$PROFDATA" ] && echo yes || echo no))"
echo "binary : ${BINARY} (exists: $([ -f "$BINARY" ] && echo yes || echo no))"
branches_covered="n/a" branches_covered="n/a"
branches_total="n/a" branches_total="n/a"
branch_pct="n/a" pct="n/a"
if [ -f "$PROFDATA" ] && [ -f "$BINARY" ]; then if [ -n "$PROFDATA" ] && [ -f "$PROFDATA" ] && \
[ -n "$BINARY" ] && [ -f "$BINARY" ]; then
JSON=$("$LLVM_COV" export "$BINARY" \ JSON=$("$LLVM_COV" export "$BINARY" \
--instr-profile="$PROFDATA" \ --instr-profile="$PROFDATA" \
--summary-only \ --summary-only \
--ignore-filename-regex='\.cargo|rustc' 2>/dev/null || echo "{}") --ignore-filename-regex='\.cargo|rustc' 2>/dev/null || echo "{}")
echo "llvm-cov JSON (first 400 chars): $(echo "$JSON" | head -c 400)"
branches_covered=$(echo "$JSON" | python3 -c " branches_covered=$(echo "$JSON" | python3 -c "
import sys, json import sys, json
data = json.load(sys.stdin) data = json.load(sys.stdin)
@ -152,7 +123,7 @@ jobs:
except Exception: except Exception:
print('n/a') print('n/a')
") ")
branch_pct=$(echo "$JSON" | python3 -c " pct=$(echo "$JSON" | python3 -c "
import sys, json import sys, json
data = json.load(sys.stdin) data = json.load(sys.stdin)
try: try:
@ -161,31 +132,19 @@ jobs:
except Exception: except Exception:
print('n/a') print('n/a')
") ")
else
echo "WARNING: profdata or binary not found — skipping llvm-cov."
fi fi
echo "Branch coverage: ${branches_covered}/${branches_total} (${branch_pct}%)" echo "Branch coverage: ${branches_covered}/${branches_total} (${pct}%)"
# ── GitHub Step Summary ── # ── GitHub Step Summary ──
{ {
echo "## Edge Bitmap Coverage — \`${TARGET}\`" echo "## Edge Bitmap Coverage — \`${TARGET}\`"
echo "" echo ""
echo "### libFuzzer edge bitmap (inline 8-bit counters)" echo "| Method | Covered branches | Total branches | Coverage % |"
echo "|---|---|---|---|"
echo "| \`llvm-cov\` (libFuzzer corpus) | ${branches_covered} | ${branches_total} | **${pct}%** |"
echo "" echo ""
echo "| Metric | Value |" echo "> Coverage computed over \`${CORPUS}\` using LLVM source-based branch instrumentation."
echo "|---|---|"
echo "| Total edges | ${edge_total} |"
echo "| Covered edges | ${edge_covered} |"
echo "| Edge coverage | **${edge_pct}%** |"
echo ""
echo "### LLVM source-based branch coverage"
echo ""
echo "| Covered branches | Total branches | Coverage % |"
echo "|---|---|---|"
echo "| ${branches_covered} | ${branches_total} | **${branch_pct}%** |"
echo ""
echo "> Edge bitmap from libFuzzer merge; branch coverage from \`llvm-cov\` over \`${CORPUS}\`."
} >> "$GITHUB_STEP_SUMMARY" } >> "$GITHUB_STEP_SUMMARY"
- name: Upload crash artifacts - name: Upload crash artifacts