diff options
| author | Scott Gasch <[email protected]> | 2026-09-18 18:17:39 -0700 |
|---|---|---|
| committer | Scott Gasch <[email protected]> | 2026-09-18 18:17:39 -0700 |
| commit | 284113f878444ded9cd36b4a70900efd036651de (patch) | |
| tree | f527e1e96ead13a4805d52ca3072260db46ee35e /src/fuzz_test.sh | |
| parent | f698c02de213d0ed8968fb7cca5f70df51fa0763 (diff) | |
Add fuzz_test.sh: a long-running gdb-driven fuzz loop over cross_sectional_200k.ep_
Documents two build/invocation bugs hit getting this working: DEBUG builds
need MP=1 too or --cpus is silently unrecognized, and gdb's
"handle SIGTRAP nostop noprint pass" breaks thread control against the
MP/pthread binary (misdiagnosed at first as a sandboxed ptrace restriction,
but reproduces outside any harness and goes away once that directive is
dropped).
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Diffstat (limited to 'src/fuzz_test.sh')
| -rwxr-xr-x | src/fuzz_test.sh | 73 |
1 files changed, 73 insertions, 0 deletions
diff --git a/src/fuzz_test.sh b/src/fuzz_test.sh new file mode 100755 index 0000000..5e3d5f7 --- /dev/null +++ b/src/fuzz_test.sh @@ -0,0 +1,73 @@ +#!/bin/sh +# Long-running fuzz loop per TESTING_PLAN.md's "Fuzz testing" section: +# repeatedly draw a fresh random batch of setboard/go records from the full +# 200k-position tests/cross_sectional_200k.ep_ pool, run them through a +# DEBUG=1 build under gdb (so any assert/SIGTRAP/SIGSEGV leaves a full +# backtrace instead of just a bare crash), shallow-depth, small/no hash +# (setboard memsets the hash table as a side effect, so a large --hash +# wastes time on every single record), --cpus 2 to exercise split.c's +# parallel search path too. Runs until stopped (^C) or a crash is found. +# +# Usage: ./fuzz_test.sh [binary] [depth] [batch_size] [cpus] + +BIN="${1:-./_typhoon}" +DEPTH="${2:-6}" +BATCH="${3:-200}" +CPUS="${4:-2}" +SUITE=../tests/cross_sectional_200k.ep_ +LOGDIR=/usr/local/tmp/typhoon/fuzz +mkdir -p "$LOGDIR" + +if [ ! -x "$BIN" ]; then + echo "ERROR: $BIN not found/executable (build first: gmake clean; gmake DEBUG=1 MP=1)" + exit 1 +fi +if [ ! -f "$SUITE" ]; then + echo "ERROR: $SUITE not found (run from src/)" + exit 1 +fi + +TOTAL_RECORDS=$(grep -c '^go' "$SUITE") + +ITER=0 +while :; do + ITER=$((ITER + 1)) + SAMPLE="$LOGDIR/batch_${ITER}.ep_" + awk -v k="$BATCH" -v n="$TOTAL_RECORDS" 'BEGIN { srand() } + { sub(/\r$/, "") } + /^setboard/ { rec = $0; next } + /^go[ \t]*$/ { rec = rec "\n" $0; c++; recs[c] = rec; rec = ""; next } + rec != "" { rec = rec "\n" $0 } + END { + picked = 0 + while (picked < k && picked < c) { + r = int(rand() * c) + 1 + if (!(r in used)) { used[r] = 1; print recs[r]; picked++ } + } + }' "$SUITE" > "$SAMPLE" + + STAMP=$(date +%Y%m%d_%H%M%S) + LOG="$LOGDIR/run_${ITER}_${STAMP}.log" + GDBOUT="$LOGDIR/run_${ITER}_${STAMP}.gdb.txt" + + echo "=== iter $ITER: $BATCH positions from $SUITE (sd $DEPTH, cpus $CPUS) -> $GDBOUT ===" + + gdb -q -batch \ + -ex "set pagination off" \ + -ex run \ + -ex "bt full" \ + -ex "info registers" \ + --args "$BIN" --cpus "$CPUS" --hash 1m --logfile "$LOG" \ + --batch --command "force; book name /nonexistent.book.bin; sd $DEPTH; script $SAMPLE" \ + > "$GDBOUT" 2>&1 + STATUS=$? + + if grep -qE 'Program terminated with signal (SIGSEGV|SIGABRT|SIGBUS|SIGILL|SIGFPE)|Assertion failure' "$GDBOUT"; then + echo "*** CRASH/ASSERT DETECTED on iter $ITER -- see $GDBOUT ***" + echo " positions: $SAMPLE" + echo " engine log: $LOG" + exit 1 + fi + + echo " iter $ITER clean (exit=$STATUS)" +done |
