diff --git a/Cargo.toml b/Cargo.toml index 541dc1d78..84ce19d22 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,8 @@ exclude = [ # The fuzz crate can't be compiled or tested without the 'cargo fuzz' command, # so exclude it from normal builds. "fuzz", + # Same counts for the afl fuzz targets + "afl", ] [lib] diff --git a/FUZZING.md b/FUZZING.md new file mode 100644 index 000000000..d47268d69 --- /dev/null +++ b/FUZZING.md @@ -0,0 +1,27 @@ +# Fuzzing + +Prost ships a few fuzz tests, using both libfuzzer and aflfuzz. + + +## afl + +To run the afl fuzz tests, first install cargo-afl: + + cargo install -f afl + +Then build a fuzz target and run afl on it: + + cd afl// + cargo afl build --bin fuzz-target + cargo afl fuzz -i in -o out target/debug/fuzz-target + +To reproduce a crash: + + cd afl// + cargo build --bin reproduce + cargo run --bin reproduce -- out/crashes/ + + +## libfuzzer + +TODO diff --git a/afl/.gitignore b/afl/.gitignore new file mode 100644 index 000000000..1776e1323 --- /dev/null +++ b/afl/.gitignore @@ -0,0 +1,2 @@ +out/ +core.* diff --git a/afl/proto3/Cargo.toml b/afl/proto3/Cargo.toml new file mode 100644 index 000000000..d1e00d5df --- /dev/null +++ b/afl/proto3/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "fuzz-target-proto3" +version = "0.1.0" +authors = ["Prost developers"] +edition = "2018" + +[[bin]] +name = "fuzz-target" +path = "src/main.rs" + +[[bin]] +name = "reproduce" +path = "src/reproduce.rs" + +[dependencies] +afl = "0.4" +protobuf = { path = "../../protobuf/" } +tests = { path = "../../tests/" } diff --git a/afl/proto3/README.md b/afl/proto3/README.md new file mode 100644 index 000000000..064bfdb1a --- /dev/null +++ b/afl/proto3/README.md @@ -0,0 +1,30 @@ +# proto3 fuzz tests + +## Test corpus + +The test message `testmessage` was created like this: + +```rust +use prost::Message; +use protobuf::test_messages::proto3::TestAllTypesProto3; + +fn main() { + let msg = TestAllTypesProto3 { + optional_int32: 42, + optional_fixed64: 9983748923, + optional_bool: true, + recursive_message: Some( + Box::new(TestAllTypesProto3 { + repeated_int32: vec![1, 2, 99, 50, -5], + ..Default::default() + }) + ), + repeated_sfixed32: vec![1, -1, 1, -1], + repeated_float: vec![-1.0, 10.10, 1.337, std::f32::NAN], + ..Default::default() + }; + let mut buf = vec![]; + msg.encode(&mut buf).unwrap(); + std::fs::write("proto3-default.bin", buf).unwrap(); +} +``` diff --git a/afl/proto3/in/empty b/afl/proto3/in/empty new file mode 100644 index 000000000..e69de29bb diff --git a/afl/proto3/in/testmessage b/afl/proto3/in/testmessage new file mode 100644 index 000000000..a95f31062 Binary files /dev/null and b/afl/proto3/in/testmessage differ diff --git a/afl/proto3/src/main.rs b/afl/proto3/src/main.rs new file mode 100644 index 000000000..e38c843f6 --- /dev/null +++ b/afl/proto3/src/main.rs @@ -0,0 +1,10 @@ +use afl::fuzz; + +use protobuf::test_messages::proto3::TestAllTypesProto3; +use tests::roundtrip; + +fn main() { + fuzz!(|data: &[u8]| { + let _ = roundtrip::(data).unwrap_error(); + }); +} diff --git a/afl/proto3/src/reproduce.rs b/afl/proto3/src/reproduce.rs new file mode 100644 index 000000000..ab0c0f9c5 --- /dev/null +++ b/afl/proto3/src/reproduce.rs @@ -0,0 +1,13 @@ +use protobuf::test_messages::proto3::TestAllTypesProto3; +use tests::roundtrip; + +fn main() { + let args: Vec = std::env::args().collect(); + if args.len() != 2 { + println!("Usage: {} ", args[0]); + std::process::exit(1); + } + + let data = std::fs::read(&args[1]).expect(&format!("Could not open file {}", args[1])); + let _ = roundtrip::(&data).unwrap_error(); +}