Add native security fuzz targets and decoder guards

Security-sensitive parsing and message handling need reusable fuzz corpora. This change adds five native Go fuzz targets, runs them in CI, and closes two decoder edge cases.

  • Fuzz targets: FuzzParseMemo, FuzzNewAddress, FuzzRPCVersion (Solana RPC version decoding), FuzzFrostEvidenceDecode and FuzzFrostSignedPayload (tampering with FROST signed statements). Inputs are size-capped. The seed cases also run under ordinary go test. FuzzRPCVersion adds the empty and null reproducers under testdata/fuzz.
  • Decoder fixes: UnmarshalFrostEquivocationEvidence now rejects any input that doesn't re-marshal to the same bytes. RPCVersion.UnmarshalJSON now rejects empty and null input, which it previously accepted as version zero.
  • Memo panic classification: recovered panics in ParseMemo / ParseMemoWithTHORNames now wrap an errMemoPanic sentinel. The fuzz target uses errors.Is to tell a real panic from a validation error that echoes the memo text back. The error text is unchanged. reference: memos are exempt: they check their embedded memo through a keeper, and keeperless ParseMemo has none, so the panic there is known and recovered. A new test checks that a validation error quoting the panic text isn't classified as a panic.
  • Runner: scripts/test-security-fuzz.sh runs the targets, each in its own go test call. FUZZ_TARGET picks one target and FUZZ_TIME sets the budget. It persists only this module's fuzz corpus via FUZZ_CORPUS_DIR and leaves the Go build cache alone. Before fuzzing, it clears this module's subtree of $GOCACHE/fuzz, copies the corpus in, and copies it back out on exit. It holds a flock on the shared cache volume while doing this, so concurrent jobs wait their turn instead of swapping the subtree out from under each other. Corpus entries that Go replays first are added to an Nx budget, so N mutated runs still happen. Usage is in docs/security-fuzz.md.
  • CI: security-fuzz-fast (1000x) runs on merge requests. security-fuzz-deep (20s per target) runs on scheduled pipelines. Each branch caches its own corpus, falling back to the default branch's, so a crasher saved on one branch is never replayed on another. The jobs install util-linux for flock. The fuzz log and every package's testdata/fuzz reproducers are kept as artifacts.
  • Solana test fix: TestReportSolvencyConcurrentNoRace no longer assumes the concurrent burst reaches every solvency boundary, since TryLock drops overlapping attempts. It now checks that later passes at the same height catch up to the final boundary without going backwards.

Validation: Each target ran 1,000 local iterations; the integrated security-fuzz-fast CI job passed. This topic is also included in draft !5181 (closed). This MR will run its own CI against develop; review and merge coordination should avoid landing the same change twice.

Edited by Huginn Ai

Merge request reports

Loading
Loading