Skip to content

Commit 5005741

Browse files
committed
Add cmp utility
The utility should support all the arguments supported by GNU cmp and perform slightly better. On a "bad" scenario, ~36M files which are completely different, our version runs in ~72% of the time of the original on my M1 Max: > hyperfine --warmup 1 -i --output=pipe \ 'cmp -l huge huge.3' Benchmark 1: cmp -l huge huge.3 Time (mean ± σ): 3.237 s ± 0.014 s [User: 2.891 s, System: 0.341 s] Range (min … max): 3.221 s … 3.271 s 10 runs Warning: Ignoring non-zero exit code. > hyperfine --warmup 1 -i --output=pipe \ '../target/release/diffutils cmp -l huge huge.3' Benchmark 1: ../target/release/diffutils cmp -l huge huge.3 Time (mean ± σ): 2.392 s ± 0.009 s [User: 1.978 s, System: 0.406 s] Range (min … max): 2.378 s … 2.406 s 10 runs Warning: Ignoring non-zero exit code. Our cmp runs in ~116% of the time when comparing libxul.so to the chromium-browser binary with -l and -b. In a best case scenario of comparing 2 files which are the same except for the last byte, our tool is slightly faster.
1 parent 72c7802 commit 5005741

File tree

12 files changed

+2089
-281
lines changed

12 files changed

+2089
-281
lines changed

.github/workflows/fuzzing.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ jobs:
4141
strategy:
4242
matrix:
4343
test-target:
44+
- { name: fuzz_cmp, should_pass: true }
45+
- { name: fuzz_cmp_args, should_pass: true }
4446
- { name: fuzz_ed, should_pass: true }
4547
- { name: fuzz_normal, should_pass: true }
4648
- { name: fuzz_patch, should_pass: true }

fuzz/Cargo.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,18 @@ diffutils = { path = "../" }
1616
[workspace]
1717
members = ["."]
1818

19+
[[bin]]
20+
name = "fuzz_cmp"
21+
path = "fuzz_targets/fuzz_cmp.rs"
22+
test = false
23+
doc = false
24+
25+
[[bin]]
26+
name = "fuzz_cmp_args"
27+
path = "fuzz_targets/fuzz_cmp_args.rs"
28+
test = false
29+
doc = false
30+
1931
[[bin]]
2032
name = "fuzz_patch"
2133
path = "fuzz_targets/fuzz_patch.rs"

fuzz/dictionaries/cmp.txt

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
"-l"
2+
"--verbose"
3+
"-b"
4+
"--print-bytes"
5+
"-lb"
6+
"-bl"
7+
"-n"
8+
"--bytes"
9+
"--bytes="
10+
"--bytes=1024"
11+
"--bytes=99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"
12+
"-i"
13+
"--ignore-initial"
14+
"--ignore-initial="
15+
"--ignore-initial=1024"
16+
"--ignore-initial=99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999:9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"
17+
"-s"
18+
"-q"
19+
"--quiet"
20+
"--silent"
21+
"-"
22+
"--"
23+
"1kB"
24+
"1G"
25+
"1GB"
26+
"1T"
27+
"1TB"
28+
"1P"
29+
"1PB"
30+
"1Z"
31+
"1ZB"
32+
"1Y"
33+
"1YB"
34+
"1Y"
35+
"0"
36+
"1:2"

fuzz/fuzz_targets/fuzz_cmp.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#![no_main]
2+
#[macro_use]
3+
extern crate libfuzzer_sys;
4+
use diffutilslib::cmp::{self, Cmp};
5+
6+
use std::ffi::OsString;
7+
use std::fs::File;
8+
use std::io::Write;
9+
10+
fn os(s: &str) -> OsString {
11+
OsString::from(s)
12+
}
13+
14+
fuzz_target!(|x: (Vec<u8>, Vec<u8>)| {
15+
let args = vec!["cmp", "-l", "-b", "target/fuzz.cmp.a", "target/fuzz.cmp.b"]
16+
.into_iter()
17+
.map(|s| os(s))
18+
.peekable();
19+
20+
let (from, to) = x;
21+
22+
File::create("target/fuzz.cmp.a")
23+
.unwrap()
24+
.write_all(&from)
25+
.unwrap();
26+
27+
File::create("target/fuzz.cmp.b")
28+
.unwrap()
29+
.write_all(&to)
30+
.unwrap();
31+
32+
let params =
33+
cmp::parse_params(args).unwrap_or_else(|e| panic!("Failed to parse params: {}", e));
34+
let ret = cmp::cmp(&params);
35+
if from == to && !matches!(ret, Ok(Cmp::Equal)) {
36+
panic!(
37+
"target/fuzz.cmp.a and target/fuzz.cmp.b are equal, but cmp returned {:?}.",
38+
ret
39+
);
40+
} else if from != to && !matches!(ret, Ok(Cmp::Different)) {
41+
panic!(
42+
"target/fuzz.cmp.a and target/fuzz.cmp.b are different, but cmp returned {:?}.",
43+
ret
44+
);
45+
} else if ret.is_err() {
46+
panic!(
47+
"target/fuzz.cmp.a and target/fuzz.cmp.b caused cmp to error ({:?}).",
48+
ret
49+
);
50+
}
51+
});

fuzz/fuzz_targets/fuzz_cmp_args.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#![no_main]
2+
#[macro_use]
3+
extern crate libfuzzer_sys;
4+
use diffutilslib::cmp;
5+
6+
use libfuzzer_sys::Corpus;
7+
use std::ffi::OsString;
8+
9+
fn os(s: &str) -> OsString {
10+
OsString::from(s)
11+
}
12+
13+
fuzz_target!(|x: Vec<OsString>| -> Corpus {
14+
if x.len() > 6 {
15+
// Make sure we try to parse an option when we get longer args. x[0] will be
16+
// the executable name.
17+
if ![os("-l"), os("-b"), os("-s"), os("-n"), os("-i")].contains(&x[1]) {
18+
return Corpus::Reject;
19+
}
20+
}
21+
let _ = cmp::parse_params(x.into_iter().peekable());
22+
Corpus::Keep
23+
});

0 commit comments

Comments
 (0)