Merge pull request 'Fuzz replay' (#19) from replay into main
Reviewed-on: https://git.ts.mattnite.net/mattnite/cairn/pulls/19
This commit is contained in:
commit
965f48a3e7
|
|
@ -1,5 +1,5 @@
|
|||
name: 'Cairn Zig Fuzz (AFL++)'
|
||||
description: 'Build and run Zig AFL++ fuzz targets, reporting crashes and corpus to Cairn. Each target is built via `zig build fuzz -Dfuzz-target=<name>`.'
|
||||
description: 'Build and run Zig AFL++ fuzz targets, reporting crashes and corpus to Cairn. Each target is built twice: `zig build fuzz -Dfuzz-target=<name>` (AFL-instrumented) and `zig build fuzz-replay -Dfuzz-target=<name>` (plain, for crash replay).'
|
||||
|
||||
inputs:
|
||||
cairn_server:
|
||||
|
|
@ -115,7 +115,30 @@ runs:
|
|||
echo "Building ${TARGET_COUNT} target(s)"
|
||||
echo "=========================================="
|
||||
|
||||
# Helper to find the single executable in a bin/ directory.
|
||||
find_bin() {
|
||||
local BIN_DIR="$1"
|
||||
local LABEL="$2"
|
||||
if [ -n "${FUZZ_BINARY}" ]; then
|
||||
echo "${BIN_DIR}/${FUZZ_BINARY}"
|
||||
return
|
||||
fi
|
||||
local BIN
|
||||
BIN=$(find "${BIN_DIR}" -maxdepth 1 -type f -executable)
|
||||
local COUNT
|
||||
COUNT=$(echo "${BIN}" | wc -l)
|
||||
if [ "${COUNT}" -eq 0 ] || [ -z "${BIN}" ]; then
|
||||
echo "ERROR: No executable found in ${BIN_DIR} (${LABEL})" >&2
|
||||
return 1
|
||||
elif [ "${COUNT}" -gt 1 ]; then
|
||||
echo "ERROR: Multiple executables in ${BIN_DIR} (${LABEL}), specify fuzz_binary input" >&2
|
||||
return 1
|
||||
fi
|
||||
echo "${BIN}"
|
||||
}
|
||||
|
||||
declare -A TARGET_BINS
|
||||
declare -A REPLAY_BINS
|
||||
for i in "${!TARGET_NAMES[@]}"; do
|
||||
FUZZ_TARGET="${TARGET_NAMES[$i]}"
|
||||
NUM=$((i + 1))
|
||||
|
|
@ -124,33 +147,32 @@ runs:
|
|||
|
||||
echo "[${NUM}/${TARGET_COUNT}] Building ${FUZZ_TARGET}..."
|
||||
|
||||
# Each target gets its own zig-out to avoid binary name collisions.
|
||||
ZIG_OUT="${WORK}/zig-out"
|
||||
rm -rf "${ZIG_OUT}"
|
||||
zig build fuzz -Dfuzz-target="${FUZZ_TARGET}" --prefix "${ZIG_OUT}"
|
||||
|
||||
# Locate fuzz binary.
|
||||
if [ -n "${FUZZ_BINARY}" ]; then
|
||||
FUZZ_BIN="${ZIG_OUT}/bin/${FUZZ_BINARY}"
|
||||
else
|
||||
FUZZ_BIN=$(find "${ZIG_OUT}/bin" -maxdepth 1 -type f -executable)
|
||||
BIN_COUNT=$(echo "${FUZZ_BIN}" | wc -l)
|
||||
if [ "${BIN_COUNT}" -eq 0 ] || [ -z "${FUZZ_BIN}" ]; then
|
||||
echo "ERROR: No executable found in ${ZIG_OUT}/bin/"
|
||||
exit 1
|
||||
elif [ "${BIN_COUNT}" -gt 1 ]; then
|
||||
echo "ERROR: Multiple executables in ${ZIG_OUT}/bin/, specify fuzz_binary input"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
# Each target gets its own output dir to avoid binary name collisions.
|
||||
# 1) AFL-instrumented binary for fuzzing.
|
||||
AFL_OUT="${WORK}/afl-out"
|
||||
rm -rf "${AFL_OUT}"
|
||||
zig build fuzz -Dfuzz-target="${FUZZ_TARGET}" --prefix "${AFL_OUT}"
|
||||
|
||||
FUZZ_BIN=$(find_bin "${AFL_OUT}/bin" "afl")
|
||||
if [ ! -x "${FUZZ_BIN}" ]; then
|
||||
echo "ERROR: Fuzz binary not found or not executable: ${FUZZ_BIN}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TARGET_BINS["${FUZZ_TARGET}"]="${FUZZ_BIN}"
|
||||
echo " Built: ${FUZZ_BIN}"
|
||||
echo " AFL binary: ${FUZZ_BIN}"
|
||||
|
||||
# 2) Plain binary for crash replay (no AFL instrumentation).
|
||||
REPLAY_OUT="${WORK}/replay-out"
|
||||
rm -rf "${REPLAY_OUT}"
|
||||
zig build fuzz-replay -Dfuzz-target="${FUZZ_TARGET}" --prefix "${REPLAY_OUT}"
|
||||
|
||||
REPLAY_BIN=$(find_bin "${REPLAY_OUT}/bin" "replay")
|
||||
if [ ! -x "${REPLAY_BIN}" ]; then
|
||||
echo "ERROR: Replay binary not found or not executable: ${REPLAY_BIN}"
|
||||
exit 1
|
||||
fi
|
||||
REPLAY_BINS["${FUZZ_TARGET}"]="${REPLAY_BIN}"
|
||||
echo " Replay binary: ${REPLAY_BIN}"
|
||||
done
|
||||
|
||||
# ── Phase 2: Prepare seeds and Cairn targets (sequential, network I/O) ──
|
||||
|
|
@ -326,6 +348,7 @@ runs:
|
|||
FUZZ_TARGET="${TARGET_NAMES[$i]}"
|
||||
NUM=$((i + 1))
|
||||
FUZZ_BIN="${TARGET_BINS[${FUZZ_TARGET}]}"
|
||||
REPLAY_BIN="${REPLAY_BINS[${FUZZ_TARGET}]}"
|
||||
RUN_ID="${RUN_IDS[${FUZZ_TARGET}]}"
|
||||
CAIRN_TARGET_ID="${CAIRN_TARGET_IDS[${FUZZ_TARGET}]}"
|
||||
FINDINGS="work/${FUZZ_TARGET}/findings"
|
||||
|
|
@ -346,16 +369,20 @@ runs:
|
|||
;;
|
||||
esac
|
||||
|
||||
# Replay the crash input to capture the stack trace.
|
||||
# Replay using the non-AFL binary to get a proper stack trace.
|
||||
STACK_TRACE=""
|
||||
CRASH_MSG="AFL++ crash (${FUZZ_TARGET}): ${CRASH_NAME}"
|
||||
REPLAY_OUTPUT=$(timeout 10 "${FUZZ_BIN}" < "${crash_file}" 2>&1 || true)
|
||||
REPLAY_OUTPUT=$(timeout 10 "${REPLAY_BIN}" < "${crash_file}" 2>&1 || true)
|
||||
if [ -n "${REPLAY_OUTPUT}" ]; then
|
||||
STACK_TRACE="${REPLAY_OUTPUT}"
|
||||
FIRST_LINE=$(echo "${REPLAY_OUTPUT}" | grep -m1 -iE 'panic|error|fault|abort|overflow|undefined|sanitizer|SUMMARY' || true)
|
||||
if [ -n "${FIRST_LINE}" ]; then
|
||||
CRASH_MSG="${FIRST_LINE}"
|
||||
fi
|
||||
echo " Replay (${CRASH_NAME}):"
|
||||
echo "${REPLAY_OUTPUT}" | head -10 | sed 's/^/ /'
|
||||
else
|
||||
echo " Replay produced no output for ${CRASH_NAME}"
|
||||
fi
|
||||
|
||||
echo " Uploading crash: ${CRASH_NAME}"
|
||||
|
|
|
|||
Loading…
Reference in New Issue