mirror of
https://github.com/uutils/coreutils.git
synced 2025-07-07 21:45:01 +00:00

* uufuzz: create a crate with the common functions * uufuzz: move the fuzz-common functions * uufuzz: polish the crate * adjust the fuzzer to use uufuzz
170 lines
4.9 KiB
Rust
170 lines
4.9 KiB
Rust
// This file is part of the uutils coreutils package.
|
|
//
|
|
// For the full copyright and license information, please view the LICENSE
|
|
// file that was distributed with this source code.
|
|
// spell-checker:ignore chdir
|
|
|
|
#![no_main]
|
|
use libfuzzer_sys::fuzz_target;
|
|
use rand::Rng;
|
|
use std::env::temp_dir;
|
|
use std::ffi::OsString;
|
|
use std::fs::{self, File};
|
|
use std::io::Write;
|
|
use std::process::Command;
|
|
use uu_cksum::uumain;
|
|
use uufuzz::{
|
|
CommandResult, compare_result, generate_and_run_uumain, generate_random_file,
|
|
generate_random_string,
|
|
pretty_print::{print_or_empty, print_test_begin},
|
|
replace_fuzz_binary_name, run_gnu_cmd,
|
|
};
|
|
|
|
static CMD_PATH: &str = "cksum";
|
|
|
|
fn generate_cksum_args() -> Vec<String> {
|
|
let mut rng = rand::rng();
|
|
let mut args = Vec::new();
|
|
|
|
let digests = [
|
|
"sysv", "bsd", "crc", "md5", "sha1", "sha224", "sha256", "sha384", "sha512", "blake2b",
|
|
"sm3",
|
|
];
|
|
let digest_opts = [
|
|
"--base64",
|
|
"--raw",
|
|
"--tag",
|
|
"--untagged",
|
|
"--text",
|
|
"--binary",
|
|
];
|
|
|
|
if rng.random_bool(0.3) {
|
|
args.push("-a".to_string());
|
|
args.push(digests[rng.random_range(0..digests.len())].to_string());
|
|
}
|
|
|
|
if rng.random_bool(0.2) {
|
|
args.push(digest_opts[rng.random_range(0..digest_opts.len())].to_string());
|
|
}
|
|
|
|
if rng.random_bool(0.15) {
|
|
args.push("-l".to_string());
|
|
args.push(rng.random_range(8..513).to_string());
|
|
}
|
|
|
|
if rng.random_bool(0.05) {
|
|
for _ in 0..rng.random_range(0..3) {
|
|
args.push(format!("file_{}", generate_random_string(5)));
|
|
}
|
|
} else {
|
|
args.push("-c".to_string());
|
|
}
|
|
|
|
if rng.random_bool(0.25) {
|
|
if let Ok(file_path) = generate_random_file() {
|
|
args.push(file_path);
|
|
}
|
|
}
|
|
|
|
if args.is_empty() || !args.iter().any(|arg| arg.starts_with("file_")) {
|
|
args.push("-a".to_string());
|
|
args.push(digests[rng.random_range(0..digests.len())].to_string());
|
|
|
|
if let Ok(file_path) = generate_random_file() {
|
|
args.push(file_path);
|
|
}
|
|
}
|
|
|
|
args
|
|
}
|
|
|
|
fn generate_checksum_file(
|
|
algo: &str,
|
|
file_path: &str,
|
|
digest_opts: &[&str],
|
|
) -> Result<String, std::io::Error> {
|
|
let checksum_file_path = temp_dir().join("checksum_file");
|
|
let mut cmd = Command::new(CMD_PATH);
|
|
cmd.arg("-a").arg(algo);
|
|
|
|
for opt in digest_opts {
|
|
cmd.arg(opt);
|
|
}
|
|
|
|
cmd.arg(file_path);
|
|
let output = cmd.output()?;
|
|
|
|
let mut checksum_file = File::create(&checksum_file_path)?;
|
|
checksum_file.write_all(&output.stdout)?;
|
|
|
|
Ok(checksum_file_path.to_str().unwrap().to_string())
|
|
}
|
|
|
|
fn select_random_digest_opts<'a>(
|
|
rng: &mut rand::rngs::ThreadRng,
|
|
digest_opts: &'a [&'a str],
|
|
) -> Vec<&'a str> {
|
|
digest_opts
|
|
.iter()
|
|
.filter(|_| rng.random_bool(0.5))
|
|
.copied()
|
|
.collect()
|
|
}
|
|
|
|
fuzz_target!(|_data: &[u8]| {
|
|
let cksum_args = generate_cksum_args();
|
|
let mut args = vec![OsString::from("cksum")];
|
|
args.extend(cksum_args.iter().map(OsString::from));
|
|
|
|
if let Ok(file_path) = generate_random_file() {
|
|
let algo = cksum_args
|
|
.iter()
|
|
.position(|arg| arg == "-a")
|
|
.map_or("md5", |index| &cksum_args[index + 1]);
|
|
|
|
let all_digest_opts = ["--base64", "--raw", "--tag", "--untagged"];
|
|
let mut rng = rand::rng();
|
|
let selected_digest_opts = select_random_digest_opts(&mut rng, &all_digest_opts);
|
|
|
|
if let Ok(checksum_file_path) =
|
|
generate_checksum_file(algo, &file_path, &selected_digest_opts)
|
|
{
|
|
print_test_begin(format!("cksum {args:?}"));
|
|
|
|
if let Ok(content) = fs::read_to_string(&checksum_file_path) {
|
|
println!("File content ({checksum_file_path})");
|
|
print_or_empty(&content);
|
|
} else {
|
|
eprintln!("Error reading the checksum file.");
|
|
}
|
|
let mut rust_result = generate_and_run_uumain(&args, uumain, None);
|
|
|
|
let gnu_result = match run_gnu_cmd(CMD_PATH, &args[1..], false, None) {
|
|
Ok(result) => result,
|
|
Err(error_result) => {
|
|
eprintln!("Failed to run GNU command:");
|
|
eprintln!("Stderr: {}", error_result.stderr);
|
|
eprintln!("Exit Code: {}", error_result.exit_code);
|
|
CommandResult {
|
|
stdout: String::new(),
|
|
stderr: error_result.stderr,
|
|
exit_code: error_result.exit_code,
|
|
}
|
|
}
|
|
};
|
|
|
|
// Lower the number of false positives caused by binary names
|
|
replace_fuzz_binary_name("cksum", &mut rust_result);
|
|
|
|
compare_result(
|
|
"cksum",
|
|
&format!("{:?}", &args[1..]),
|
|
None,
|
|
&rust_result,
|
|
&gnu_result,
|
|
false,
|
|
);
|
|
}
|
|
}
|
|
});
|