From f91b77fa328cca4f85af2bee955100e8fd88b6cd Mon Sep 17 00:00:00 2001 From: Vincent Ulitzsch Date: Fri, 11 Sep 2020 17:42:09 +0200 Subject: [PATCH 1/4] Add fuzzer for the compact custom codec implementation introduced in PR #6720. This commit adds a fuzzing harness for the custom compact encoding/decoding introduced in PR #6720. --- primitives/npos-elections/fuzzer/Cargo.toml | 5 +++ .../npos-elections/fuzzer/src/compact.rs | 39 +++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 primitives/npos-elections/fuzzer/src/compact.rs diff --git a/primitives/npos-elections/fuzzer/Cargo.toml b/primitives/npos-elections/fuzzer/Cargo.toml index f0c9442aade5f..4d262bc50074e 100644 --- a/primitives/npos-elections/fuzzer/Cargo.toml +++ b/primitives/npos-elections/fuzzer/Cargo.toml @@ -19,6 +19,7 @@ sp-std = { version = "2.0.0-rc6", path = "../../std" } sp-runtime = { version = "2.0.0-rc6", path = "../../runtime" } honggfuzz = "0.5" rand = { version = "0.7.3", features = ["std", "small_rng"] } +codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } [[bin]] name = "reduce" @@ -27,3 +28,7 @@ path = "src/reduce.rs" [[bin]] name = "balance_solution" path = "src/balance_solution.rs" + +[[bin]] +name = "compact" +path = "src/compact.rs" diff --git a/primitives/npos-elections/fuzzer/src/compact.rs b/primitives/npos-elections/fuzzer/src/compact.rs new file mode 100644 index 0000000000000..1fb4b45afd7b3 --- /dev/null +++ b/primitives/npos-elections/fuzzer/src/compact.rs @@ -0,0 +1,39 @@ +use honggfuzz::fuzz; +use sp_npos_elections::generate_solution_type; +use sp_npos_elections::sp_arithmetic::Percent; +use sp_runtime::codec::{Encode, Error}; + +fn main() { + generate_solution_type!(#[compact] pub struct InnerTestSolutionCompact::(16)); + loop { + fuzz!(|fuzzer_data: &[u8]| { + let result_decoded: Result = + ::decode(&mut &fuzzer_data[..]); + match result_decoded { + Ok(decoded) => { + // Decoding works, let's re-encode it and compare results. + let reencoded: std::vec::Vec = decoded.encode(); + // The reencoded value may or may not be equal to the original fuzzer output. However, the + // original decoder should be optimal (in the sense that there is no shorter encoding of + // the same object). So let's see if the fuzzer can find something shorter: + if fuzzer_data.len() < reencoded.len() { + panic!("fuzzer_data.len() < reencoded.len()"); + } + // The reencoded value should definitely be decodable (if unwrap() fails that is a valid + // panic/finding for the fuzzer): + let decoded2: InnerTestSolutionCompact = + ::decode( + &mut reencoded.as_slice(), + ) + .unwrap(); + // And it should be equal to the original decoded object (resulting from directly + // decoding fuzzer_data): + assert_eq!(decoded, decoded2); + } + Err(_e) => { + // Can happen, not every random sequence of bytes an be decoded as InnerTestSolutionCompact + } + } + }); + } +} From f6d7a4869e6b6b1c0892db743c2c638c4c198c1a Mon Sep 17 00:00:00 2001 From: Vincent Ulitzsch Date: Sat, 12 Sep 2020 13:26:50 +0200 Subject: [PATCH 2/4] Update primitives/npos-elections/fuzzer/src/compact.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Köcher --- .../npos-elections/fuzzer/src/compact.rs | 41 ++++++++----------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/primitives/npos-elections/fuzzer/src/compact.rs b/primitives/npos-elections/fuzzer/src/compact.rs index 1fb4b45afd7b3..26dadc857b7f2 100644 --- a/primitives/npos-elections/fuzzer/src/compact.rs +++ b/primitives/npos-elections/fuzzer/src/compact.rs @@ -9,30 +9,25 @@ fn main() { fuzz!(|fuzzer_data: &[u8]| { let result_decoded: Result = ::decode(&mut &fuzzer_data[..]); - match result_decoded { - Ok(decoded) => { - // Decoding works, let's re-encode it and compare results. - let reencoded: std::vec::Vec = decoded.encode(); - // The reencoded value may or may not be equal to the original fuzzer output. However, the - // original decoder should be optimal (in the sense that there is no shorter encoding of - // the same object). So let's see if the fuzzer can find something shorter: - if fuzzer_data.len() < reencoded.len() { - panic!("fuzzer_data.len() < reencoded.len()"); - } - // The reencoded value should definitely be decodable (if unwrap() fails that is a valid - // panic/finding for the fuzzer): - let decoded2: InnerTestSolutionCompact = - ::decode( - &mut reencoded.as_slice(), - ) - .unwrap(); - // And it should be equal to the original decoded object (resulting from directly - // decoding fuzzer_data): - assert_eq!(decoded, decoded2); - } - Err(_e) => { - // Can happen, not every random sequence of bytes an be decoded as InnerTestSolutionCompact + // Ignore errors as not every random sequence of bytes can be decoded as InnerTestSolutionCompact + if let Ok(decoded) = result_decoded { + // Decoding works, let's re-encode it and compare results. + let reencoded: std::vec::Vec = decoded.encode(); + // The reencoded value may or may not be equal to the original fuzzer output. However, the + // original decoder should be optimal (in the sense that there is no shorter encoding of + // the same object). So let's see if the fuzzer can find something shorter: + if fuzzer_data.len() < reencoded.len() { + panic!("fuzzer_data.len() < reencoded.len()"); } + // The reencoded value should definitely be decodable (if unwrap() fails that is a valid + // panic/finding for the fuzzer): + let decoded2: InnerTestSolutionCompact = + ::decode( + &mut reencoded.as_slice(), + ).unwrap(); + // And it should be equal to the original decoded object (resulting from directly + // decoding fuzzer_data): + assert_eq!(decoded, decoded2); } }); } From 3929b7ddfab96f3ab1a90a5c40cdf1a664fb4934 Mon Sep 17 00:00:00 2001 From: Vincent Ulitzsch Date: Sat, 12 Sep 2020 13:40:17 +0200 Subject: [PATCH 3/4] Update Cargo.lock: Add changes in elections-fuzzer --- Cargo.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.lock b/Cargo.lock index 741f2ba7c48c4..81fcee59cda9d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8250,6 +8250,7 @@ name = "sp-npos-elections-fuzzer" version = "2.0.0-alpha.5" dependencies = [ "honggfuzz", + "parity-scale-codec", "rand 0.7.3", "sp-npos-elections", "sp-runtime", From 9c44f27930cf716177efa61c17d6f97d8009fbc5 Mon Sep 17 00:00:00 2001 From: Vincent Ulitzsch Date: Sat, 12 Sep 2020 13:45:06 +0200 Subject: [PATCH 4/4] Change indentation from spaces to tabs --- .../npos-elections/fuzzer/src/compact.rs | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/primitives/npos-elections/fuzzer/src/compact.rs b/primitives/npos-elections/fuzzer/src/compact.rs index 26dadc857b7f2..91f734bb5b7cb 100644 --- a/primitives/npos-elections/fuzzer/src/compact.rs +++ b/primitives/npos-elections/fuzzer/src/compact.rs @@ -4,31 +4,31 @@ use sp_npos_elections::sp_arithmetic::Percent; use sp_runtime::codec::{Encode, Error}; fn main() { - generate_solution_type!(#[compact] pub struct InnerTestSolutionCompact::(16)); - loop { - fuzz!(|fuzzer_data: &[u8]| { - let result_decoded: Result = - ::decode(&mut &fuzzer_data[..]); - // Ignore errors as not every random sequence of bytes can be decoded as InnerTestSolutionCompact - if let Ok(decoded) = result_decoded { - // Decoding works, let's re-encode it and compare results. - let reencoded: std::vec::Vec = decoded.encode(); - // The reencoded value may or may not be equal to the original fuzzer output. However, the - // original decoder should be optimal (in the sense that there is no shorter encoding of - // the same object). So let's see if the fuzzer can find something shorter: - if fuzzer_data.len() < reencoded.len() { - panic!("fuzzer_data.len() < reencoded.len()"); - } - // The reencoded value should definitely be decodable (if unwrap() fails that is a valid - // panic/finding for the fuzzer): - let decoded2: InnerTestSolutionCompact = - ::decode( - &mut reencoded.as_slice(), - ).unwrap(); - // And it should be equal to the original decoded object (resulting from directly - // decoding fuzzer_data): - assert_eq!(decoded, decoded2); - } - }); - } + generate_solution_type!(#[compact] pub struct InnerTestSolutionCompact::(16)); + loop { + fuzz!(|fuzzer_data: &[u8]| { + let result_decoded: Result = + ::decode(&mut &fuzzer_data[..]); + // Ignore errors as not every random sequence of bytes can be decoded as InnerTestSolutionCompact + if let Ok(decoded) = result_decoded { + // Decoding works, let's re-encode it and compare results. + let reencoded: std::vec::Vec = decoded.encode(); + // The reencoded value may or may not be equal to the original fuzzer output. However, the + // original decoder should be optimal (in the sense that there is no shorter encoding of + // the same object). So let's see if the fuzzer can find something shorter: + if fuzzer_data.len() < reencoded.len() { + panic!("fuzzer_data.len() < reencoded.len()"); + } + // The reencoded value should definitely be decodable (if unwrap() fails that is a valid + // panic/finding for the fuzzer): + let decoded2: InnerTestSolutionCompact = + ::decode( + &mut reencoded.as_slice(), + ).unwrap(); + // And it should be equal to the original decoded object (resulting from directly + // decoding fuzzer_data): + assert_eq!(decoded, decoded2); + } + }); + } }