feat(fuzz): add SharpFuzz-based fuzzing for untrusted-input parsers

New tests/PalladiumWallet.Fuzz project: one target per parser that
consumes untrusted input (block headers, Merkle proofs, peer lists,
wallet files, user-pasted mnemonics/keys/addresses/amounts), each
encoding the parser's documented error contract - any exception beyond
that contract is a finding, reported as a crash.

Three ways to run: the seed corpus (with regression inputs for every
crash found so far, including the two fixed in prior commits) replays
automatically inside dotnet test via FuzzCorpusTests, so a fixed
contract violation can't silently return; a built-in random-mutation
mode needs no external tooling (dotnet run -- <target> --random N);
and fuzz.sh drives real coverage-guided campaigns via afl++ +
SharpFuzz instrumentation for pre-release or post-parser-change runs.
This commit is contained in:
2026-07-07 22:05:08 +02:00
parent a264669151
commit cdede17683
36 changed files with 683 additions and 66 deletions
+38
View File
@@ -0,0 +1,38 @@
#!/usr/bin/env bash
# Coverage-guided fuzzing with afl++ + SharpFuzz.
#
# One-time setup:
# apt install afl++ (or build from source)
# dotnet tool install --global SharpFuzz.CommandLine
#
# Usage:
# ./fuzz.sh <target> [afl-fuzz args...]
# ./fuzz.sh header
# ./fuzz.sh peers -V 3600 # time-limited campaign (seconds)
#
# Targets: header merkle slip132 bip39 address coinamount walletdoc encfile peers
#
# Findings land in findings/<target>/crashes/: replay one with
# dotnet run -- <target> findings/<target>/crashes/<file>
# then add it to the seed corpus in Program.cs as a regression input.
set -euo pipefail
cd "$(dirname "$0")"
TARGET="${1:?usage: ./fuzz.sh <target> [afl-fuzz args...]}"
shift || true
command -v afl-fuzz >/dev/null || { echo "afl-fuzz not found: apt install afl++"; exit 1; }
command -v sharpfuzz >/dev/null || { echo "sharpfuzz not found: dotnet tool install --global SharpFuzz.CommandLine"; exit 1; }
dotnet build -c Release
BIN="bin/Release/net10.0"
# Instrument the assemblies under test (idempotent: SharpFuzz skips already-
# instrumented DLLs). Core is the real target; NBitcoin gives the fuzzer
# visibility into the library our parsers delegate to.
sharpfuzz "$BIN/PalladiumWallet.Core.dll"
sharpfuzz "$BIN/NBitcoin.dll" || true
mkdir -p "findings/$TARGET"
afl-fuzz -i "Corpus/$TARGET" -o "findings/$TARGET" -t 5000 "$@" \
-- dotnet "$BIN/PalladiumWallet.Fuzz.dll" "$TARGET" --afl