Add a parse + validate fuzzer
authorMatt Corallo <git@bluematt.me>
Sun, 11 Feb 2024 22:49:44 +0000 (22:49 +0000)
committerMatt Corallo <git@bluematt.me>
Sun, 11 Feb 2024 22:49:44 +0000 (22:49 +0000)
fuzz/Cargo.toml
fuzz/src/parse_stream_validate.rs [new file with mode: 0644]
src/validation.rs

index c613c877f78096ebddfe2627308442003c92aee5..56ed89c7fd958b7bd5554637fd257a944b4c1a7f 100644 (file)
@@ -44,3 +44,7 @@ opt-level = 1
 [[bin]]
 name = "parse_response"
 path = "src/parse_response.rs"
+
+[[bin]]
+name = "parse_stream_validate"
+path = "src/parse_stream_validate.rs"
diff --git a/fuzz/src/parse_stream_validate.rs b/fuzz/src/parse_stream_validate.rs
new file mode 100644 (file)
index 0000000..48fc259
--- /dev/null
@@ -0,0 +1,74 @@
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
+extern crate dnssec_prover;
+use dnssec_prover::ser::parse_rr_stream;
+use dnssec_prover::validation::verify_rr_stream;
+
+#[cfg(feature = "afl")]
+#[macro_use] extern crate afl;
+#[cfg(feature = "afl")]
+fn main() {
+       fuzz!(|data| {
+               let _ = parse_rr_stream(data).as_ref()
+                       .map(|rrs| verify_rr_stream(rrs));
+       });
+}
+
+#[cfg(feature = "honggfuzz")]
+#[macro_use] extern crate honggfuzz;
+#[cfg(feature = "honggfuzz")]
+fn main() {
+       loop {
+               fuzz!(|data| {
+                       let _ = parse_rr_stream(data).as_ref()
+                               .map(|rrs| verify_rr_stream(rrs));
+               });
+       }
+}
+
+#[cfg(feature = "libfuzzer_fuzz")]
+#[macro_use] extern crate libfuzzer_sys;
+#[cfg(feature = "libfuzzer_fuzz")]
+fuzz_target!(|data: &[u8]| {
+       let _ = parse_rr_stream(data).as_ref()
+               .map(|rrs| verify_rr_stream(rrs));
+});
+
+#[cfg(feature = "stdin_fuzz")]
+fn main() {
+       use std::io::Read;
+
+       let mut data = Vec::with_capacity(8192);
+       std::io::stdin().read_to_end(&mut data).unwrap();
+       let _ = parse_rr_stream(data).as_ref()
+               .map(|rrs| verify_rr_stream(rrs));
+}
+
+#[test]
+fn run_test_cases() {
+       use std::fs;
+       use std::io::Read;
+
+       if let Ok(tests) = fs::read_dir("test_cases/parse_response") {
+               for test in tests {
+                       let mut data: Vec<u8> = Vec::new();
+                       let path = test.unwrap().path();
+                       fs::File::open(&path).unwrap().read_to_end(&mut data).unwrap();
+
+                       let _ = parse_rr_stream(data).as_ref()
+                               .map(|rrs| verify_rr_stream(rrs));
+               }
+       }
+}
index 5fee46a73e3c868f69e1728133b8d6e04b7a6d38..8b27fce3ad8e31dfbde8d67ba3f22600d0510b21 100644 (file)
@@ -111,7 +111,7 @@ where Keys: IntoIterator<Item = &'a DnsKey> {
                                record.write_u16_len_prefixed_data(&mut signed_data);
                        }
 
-                       match sig.alg {
+                       let sig_validation = match sig.alg {
                                8|10 => {
                                        let alg = if sig.alg == 8 {
                                                &signature::RSA_PKCS1_1024_8192_SHA256_FOR_LEGACY_USE_ONLY
@@ -120,7 +120,7 @@ where Keys: IntoIterator<Item = &'a DnsKey> {
                                        };
                                        bytes_to_rsa_pk(&dnskey.pubkey).map_err(|_| ValidationError::Invalid)?
                                                .verify(alg, &signed_data, &sig.signature)
-                                               .map_err(|_| ValidationError::Invalid)?;
+                                               .map_err(|_| ValidationError::Invalid)
                                },
                                13|14 => {
                                        let alg = if sig.alg == 13 {
@@ -136,15 +136,23 @@ where Keys: IntoIterator<Item = &'a DnsKey> {
 
                                        signature::UnparsedPublicKey::new(alg, &key)
                                                .verify(&signed_data, &sig.signature)
-                                               .map_err(|_| ValidationError::Invalid)?;
+                                               .map_err(|_| ValidationError::Invalid)
                                },
                                15 => {
                                        signature::UnparsedPublicKey::new(&signature::ED25519, &dnskey.pubkey)
                                                .verify(&signed_data, &sig.signature)
-                                               .map_err(|_| ValidationError::Invalid)?;
+                                               .map_err(|_| ValidationError::Invalid)
                                },
                                _ => return Err(ValidationError::UnsupportedAlgorithm),
+                       };
+                       #[cfg(fuzzing)] {
+                               // When fuzzing, treat any signature starting with a 1 as valid, but only after
+                               // parsing and checking signatures to give that code a chance to panic.
+                               if sig.signature.get(0) == Some(&1) {
+                                       return Ok(());
+                               }
                        }
+                       sig_validation?;
 
                        return Ok(());
                }