coreutils/fuzz/fuzz_targets/fuzz_cksum.rs
Sylvestre Ledru eff2cd997e
Create an uufuzz crate for common functions and use it (#7954)
* uufuzz: create a crate with the common functions

* uufuzz: move the fuzz-common functions

* uufuzz: polish the crate

* adjust the fuzzer to use uufuzz
2025-05-21 09:26:59 +02:00

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,
);
}
}
});