From e7e8a91d74b6d26d7fbcd50dcbe056e0d8e4c03f Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Wed, 24 Feb 2021 23:05:00 -0700 Subject: [PATCH] Fuzzing --- .gitignore | 1 + README.md | 2 + fuzz/.gitignore | 4 ++ fuzz/Cargo.toml | 27 ++++++++++++++ fuzz/fuzz_targets/fuzz_patch.rs | 55 +++++++++++++++++++++++++++ src/lib.rs | 66 +++++++++++++++++++++------------ src/main.rs | 2 +- 7 files changed, 133 insertions(+), 24 deletions(-) create mode 100644 fuzz/.gitignore create mode 100644 fuzz/Cargo.toml create mode 100644 fuzz/fuzz_targets/fuzz_patch.rs diff --git a/.gitignore b/.gitignore index 96ef6c0..46f3847 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target Cargo.lock +*.swp diff --git a/README.md b/README.md index 0a9e72b..103c010 100644 --- a/README.md +++ b/README.md @@ -35,5 +35,7 @@ but it implements a different format. + +[dependencies] +diff = "0.1.10" +~/unified-diff$ rustup override set nightly +~/unified-diff$ cargo fuzz run fuzz_patch ``` diff --git a/fuzz/.gitignore b/fuzz/.gitignore new file mode 100644 index 0000000..572e03b --- /dev/null +++ b/fuzz/.gitignore @@ -0,0 +1,4 @@ + +target +corpus +artifacts diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml new file mode 100644 index 0000000..3b9e70f --- /dev/null +++ b/fuzz/Cargo.toml @@ -0,0 +1,27 @@ + +[package] +name = "unified-diff-fuzz" +version = "0.0.0" +authors = ["Automatically generated"] +publish = false +edition = "2018" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +libfuzzer-sys = "0.3" + +[dependencies.unified-diff] +path = ".." + +# Prevent this from interfering with workspaces +[workspace] +members = ["."] + +[[bin]] +name = "fuzz_patch" +path = "fuzz_targets/fuzz_patch.rs" +test = false +doc = false + diff --git a/fuzz/fuzz_targets/fuzz_patch.rs b/fuzz/fuzz_targets/fuzz_patch.rs new file mode 100644 index 0000000..d3f3b93 --- /dev/null +++ b/fuzz/fuzz_targets/fuzz_patch.rs @@ -0,0 +1,55 @@ +#![no_main] +#[macro_use] extern crate libfuzzer_sys; +extern crate unified_diff; + +use std::fs::{self, File}; +use std::io::Write; +use std::process::Command; + +fuzz_target!(|x: (Vec, Vec, u8)| { + let (from, to, context) = x; + if let Ok(s) = String::from_utf8(from.clone()) { + if !s.is_ascii() { return } + if s.find(|x| x < ' ' && x != '\n').is_some() { return } + } else { + return + } + if let Ok(s) = String::from_utf8(to.clone()) { + if !s.is_ascii() { return } + if s.find(|x| x < ' ' && x != '\n').is_some() { return } + } else { + return + } + let diff = unified_diff::diff(&from, "a/fuzz.file", &to, "target/fuzz.file", context as usize); + File::create("target/fuzz.file.original") + .unwrap() + .write_all(&from) + .unwrap(); + File::create("target/fuzz.file.expected") + .unwrap() + .write_all(&to) + .unwrap(); + File::create("target/fuzz.file") + .unwrap() + .write_all(&from) + .unwrap(); + File::create("target/fuzz.diff") + .unwrap() + .write_all(&diff) + .unwrap(); + let output = Command::new("patch") + .arg("-p0") + .arg("--binary") + .arg("--fuzz=0") + .stdin(File::open("target/fuzz.diff").unwrap()) + .output() + .unwrap(); + if !output.status.success() { + panic!("STDOUT:\n{}\nSTDERR:\n{}", String::from_utf8_lossy(&output.stdout), String::from_utf8_lossy(&output.stderr)); + } + let result = fs::read("target/fuzz.file").unwrap(); + if result != to { + panic!("STDOUT:\n{}\nSTDERR:\n{}", String::from_utf8_lossy(&output.stdout), String::from_utf8_lossy(&output.stderr)); + } +}); + diff --git a/src/lib.rs b/src/lib.rs index 6a8a2b1..432ea03 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -112,15 +112,24 @@ fn make_diff(expected: &[u8], actual: &[u8], context_size: usize) -> Vec { + + // if one of them is missing a newline and the other isn't, then they don't actually match if (line_number_actual > actual_lines_count) - || (line_number_expected > expected_lines_count) + && (line_number_expected > expected_lines_count) { - // if one of them is missing a newline and the other isn't, then they don't actually match - if lines_since_mismatch >= context_size - && lines_since_mismatch > 0 - && (line_number_actual > actual_lines_count) - != (line_number_expected > expected_lines_count) - { + if context_queue.len() < context_size { + while let Some(line) = context_queue.pop_front() { + assert!(mismatch.lines.last() != Some(&DiffLine::MissingNL)); + mismatch.lines.push(DiffLine::Context(line.to_vec())); + } + if lines_since_mismatch < context_size { + mismatch.lines.push(DiffLine::Context(str.to_vec())); + mismatch.lines.push(DiffLine::MissingNL); + } + } + lines_since_mismatch = 0; + } else if line_number_actual > actual_lines_count { + if lines_since_mismatch >= context_size && lines_since_mismatch > 0 { results.push(mismatch); mismatch = Mismatch::new( line_number_expected - context_queue.len() as u32, @@ -131,22 +140,28 @@ fn make_diff(expected: &[u8], actual: &[u8], context_size: usize) -> Vec actual_lines_count - && line_number_expected > expected_lines_count - { - mismatch.lines.push(DiffLine::Context(str.to_vec())); - mismatch.lines.push(DiffLine::MissingNL); - } else if line_number_expected > expected_lines_count { - mismatch.lines.push(DiffLine::Expected(str.to_vec())); - mismatch.lines.push(DiffLine::MissingNL); - mismatch.lines.push(DiffLine::Actual(str.to_vec())); - } else if line_number_actual > actual_lines_count { - mismatch.lines.push(DiffLine::Expected(str.to_vec())); - mismatch.lines.push(DiffLine::Actual(str.to_vec())); - mismatch.lines.push(DiffLine::MissingNL); + } else if line_number_expected > expected_lines_count { + if lines_since_mismatch >= context_size && lines_since_mismatch > 0 { + results.push(mismatch); + mismatch = Mismatch::new( + line_number_expected - context_queue.len() as u32, + line_number_actual - context_queue.len() as u32, + ); } + while let Some(line) = context_queue.pop_front() { + assert!(mismatch.lines.last() != Some(&DiffLine::MissingNL)); + mismatch.lines.push(DiffLine::Context(line.to_vec())); + } + mismatch.lines.push(DiffLine::Expected(str.to_vec())); + mismatch.lines.push(DiffLine::MissingNL); + mismatch.lines.push(DiffLine::Actual(str.to_vec())); + lines_since_mismatch = 0; } else { + assert!(context_queue.len() <= context_size); if context_queue.len() >= context_size { let _ = context_queue.pop_front(); } @@ -157,7 +172,6 @@ fn make_diff(expected: &[u8], actual: &[u8], context_size: usize) -> Vec { expected_count += 1; + has_expected = true; } DiffLine::Context(_) => { expected_count += 1; @@ -235,10 +252,13 @@ pub fn diff( } DiffLine::Actual(_) => { actual_count += 1; + has_actual = true; } DiffLine::MissingNL => {} } } + if expected_count == 0 { line_number_expected -= 1 } + if actual_count == 0 { line_number_actual -= 1 } writeln!( output, "@@ -{},{} +{},{} @@", diff --git a/src/main.rs b/src/main.rs index d49d6c1..16ff8e2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -49,7 +49,7 @@ fn main() { &from.to_string_lossy(), &to_content, &to.to_string_lossy(), - 3, + 1, )) .unwrap(); }