From 1641775dd83ab3ceff7745fd41ceea9b9ae2cc03 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 13 May 2026 10:43:40 +0800 Subject: [PATCH] fix: improve detection of malformed input --- fuzz/fuzz_targets/fuzz_encoding_roundtrip.rs | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/fuzz/fuzz_targets/fuzz_encoding_roundtrip.rs b/fuzz/fuzz_targets/fuzz_encoding_roundtrip.rs index 3f13302..cc9268f 100644 --- a/fuzz/fuzz_targets/fuzz_encoding_roundtrip.rs +++ b/fuzz/fuzz_targets/fuzz_encoding_roundtrip.rs @@ -4,9 +4,14 @@ //! Invariants exercised: //! //! 1. **Encode/decode stability** — `encode(decode(encode(tx))) == encode(tx)`. -//! 2. **Canonical encoding** — if raw fuzzer bytes decode successfully, re-encoding must +//! 2. **No-panic on adversarial bytes** — `from_bytes(raw_fuzz_data)` must never panic, +//! whether it returns `Ok` or `Err`. (Tests 3 & 4.) +//! 3. **Canonical encoding** — if raw fuzzer bytes decode successfully, re-encoding must //! reproduce those exact bytes (`encode(decode(data)) == data`). This catches non-canonical //! encodings that are accepted by the decoder but silently normalised on the way out. +//! Because `borsh::from_slice` (used by `from_bytes`) consumes *all* bytes and errors on +//! trailing data, `Ok` implies every input byte was semantically meaningful, and the output +//! must be identical. (Tests 3 & 4, `Ok` branch.) //! //! `PrivacyPreservingTransaction` is excluded because its ZK receipt cannot be //! reconstructed in a fuzzing loop. @@ -47,8 +52,11 @@ fuzz_target!(|data: &[u8]| { ); } - // ── Test 3: Canonical encoding — raw bytes that decode must re-encode identically ── - if let Ok(tx) = PublicTransaction::from_bytes(data) { + // ── Test 3: No-panic decode + canonical encoding (PublicTransaction) ───────────── + // Invariant 2: from_bytes must never panic on any input — Ok or Err both valid, no panic. + // Invariant 3: on Ok, re-encoding must reproduce the original bytes exactly. + let pub_raw_result = PublicTransaction::from_bytes(data); // ← no-panic check (invariant 2) + if let Ok(tx) = pub_raw_result { let re_encoded = tx.to_bytes(); assert_eq!( data, @@ -58,8 +66,10 @@ fuzz_target!(|data: &[u8]| { ); } - // ── Test 4: Canonical encoding for ProgramDeploymentTransaction ────────────────── - if let Ok(tx) = ProgramDeploymentTransaction::from_bytes(data) { + // ── Test 4: No-panic decode + canonical encoding (ProgramDeploymentTransaction) ── + // Same two invariants as Test 3, applied to ProgramDeploymentTransaction. + let prog_raw_result = ProgramDeploymentTransaction::from_bytes(data); // ← no-panic check + if let Ok(tx) = prog_raw_result { let re_encoded = tx.to_bytes(); assert_eq!( data,