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
64 changes: 2 additions & 62 deletions .github/workflows/fuzz.yml
Original file line number Diff line number Diff line change
Expand Up @@ -171,39 +171,9 @@ jobs:
echo "No fuzz targets found in ${{ matrix.pkg }}, skipping"
exit 0
fi
# fuzz_run wraps a single fuzz invocation and treats "context deadline exceeded"
# at the fuzz time boundary as success. When -fuzztime=30s expires, Go's internal
# fuzz coordinator cancels any in-flight iteration and emits "context deadline
# exceeded" with no file:line reference — this is not a real test failure.
# Real failures always include a file:line reference (e.g. foo_test.go:42:).
#
# We use `tee` rather than output=$(...) so that:
# 1. Progress lines appear in the Actions log in real time (output=$(...) buffers
# everything and bash -e exits the function before we ever reach echo "$output").
# 2. PIPESTATUS[0] captures go test's exit code even when the pipe itself exits 0.
fuzz_run() {
local func="$1"
local tmpfile exit_code
tmpfile=$(mktemp)
go test -fuzz="^${func}$" -fuzztime=30s ${{ matrix.pkg }} -timeout 90s 2>&1 | tee "$tmpfile" || true
exit_code=${PIPESTATUS[0]}
if [ $exit_code -ne 0 ]; then
# Check whether any line has a file:line reference (real assertion failure).
# Coordinator boundary timeouts produce " context deadline exceeded" with
# no file:line, so they will NOT match this pattern.
if grep -qE '[[:space:]]+[^[:space:]]+_test\.go:[0-9]+:' "$tmpfile"; then
rm -f "$tmpfile"
echo "FAIL: $func — test assertion failure detected" >&2
return $exit_code
fi
echo "NOTE: $func — fuzz coordinator boundary timeout (expected at fuzz time limit, not a failure)"
fi
rm -f "$tmpfile"
return 0
}
for FUNC in $FUZZ_FUNCS; do
echo "Fuzzing $FUNC..."
fuzz_run "$FUNC"
bash scripts/fuzz-run.sh ${{ matrix.pkg }} "$FUNC"
done

# Save corpus
Expand Down Expand Up @@ -255,39 +225,9 @@ jobs:
echo "No differential fuzz targets found in ${{ matrix.pkg }}, skipping"
exit 0
fi
# fuzz_run wraps a single fuzz invocation and treats "context deadline exceeded"
# at the fuzz time boundary as success. When -fuzztime=30s expires, Go's internal
# fuzz coordinator cancels any in-flight iteration and emits "context deadline
# exceeded" with no file:line reference — this is not a real test failure.
# Real failures always include a file:line reference (e.g. foo_test.go:42:).
#
# We use `tee` rather than output=$(...) so that:
# 1. Progress lines appear in the Actions log in real time (output=$(...) buffers
# everything and bash -e exits the function before we ever reach echo "$output").
# 2. PIPESTATUS[0] captures go test's exit code even when the pipe itself exits 0.
fuzz_run() {
local func="$1"
local tmpfile exit_code
tmpfile=$(mktemp)
go test -fuzz="^${func}$" -fuzztime=30s ${{ matrix.pkg }} -timeout 90s 2>&1 | tee "$tmpfile" || true
exit_code=${PIPESTATUS[0]}
if [ $exit_code -ne 0 ]; then
# Check whether any line has a file:line reference (real assertion failure).
# Coordinator boundary timeouts produce " context deadline exceeded" with
# no file:line, so they will NOT match this pattern.
if grep -qE '[[:space:]]+[^[:space:]]+_test\.go:[0-9]+:' "$tmpfile"; then
rm -f "$tmpfile"
echo "FAIL: $func — test assertion failure detected" >&2
return $exit_code
fi
echo "NOTE: $func — fuzz coordinator boundary timeout (expected at fuzz time limit, not a failure)"
fi
rm -f "$tmpfile"
return 0
}
for FUNC in $FUZZ_FUNCS; do
echo "Fuzzing $FUNC..."
fuzz_run "$FUNC"
bash scripts/fuzz-run.sh ${{ matrix.pkg }} "$FUNC"
done

- name: Save fuzz corpus
Expand Down
43 changes: 43 additions & 0 deletions scripts/fuzz-run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/usr/bin/env bash
# Run one Go fuzz target for a fixed duration, treating only Go's -fuzztime
# boundary timeout as a pass.
#
# Usage: bash scripts/fuzz-run.sh <package> <FuzzFunc> [fuzztime] [timeout]
#
# When -fuzztime expires, Go's fuzz coordinator cancels in-flight inputs and
# can fail the run with a bare "context deadline exceeded" and no failing
# input. That is not a finding. A real finding (a panic, t.Fatal, a hang the
# worker reports, ...) always makes Go write the input to testdata/fuzz and
# print "Failing input written to", so any failure that printed it, or that
# lacks the bare timeout message, still fails.
set -uo pipefail

if [ $# -lt 2 ] || [ $# -gt 4 ]; then
echo "usage: $0 <package> <FuzzFunc> [fuzztime] [timeout]" >&2
exit 2
fi

pkg="$1"
func="$2"
fuzztime="${3:-30s}"
timeout="${4:-90s}"

tmpfile=$(mktemp)
trap 'rm -f "$tmpfile"' EXIT

# tee streams progress to the CI log in real time; PIPESTATUS keeps go test's
# exit code rather than tee's.
go test -fuzz="^${func}\$" -fuzztime="$fuzztime" "$pkg" -timeout "$timeout" 2>&1 | tee "$tmpfile"
exit_code=${PIPESTATUS[0]}

if [ "$exit_code" -eq 0 ]; then
exit 0
fi

if grep -q 'context deadline exceeded' "$tmpfile" && ! grep -q 'Failing input written to' "$tmpfile"; then
echo "NOTE: $func hit Go's fuzz coordinator timeout at the -fuzztime limit; not a failure"
exit 0
fi

echo "FAIL: $func" >&2
exit "$exit_code"
Loading