1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 03:27:44 +00:00

fuzzing: add a new fuzzer for cksum

This commit is contained in:
Sylvestre Ledru 2024-07-18 23:43:24 +02:00
parent 6f43aa3739
commit 04f130ac08
4 changed files with 439 additions and 27 deletions

View file

@ -0,0 +1,164 @@
// 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 std::ffi::OsString;
use uu_cksum::uumain;
mod fuzz_common;
use crate::fuzz_common::{
compare_result, generate_and_run_uumain, generate_random_file, generate_random_string,
run_gnu_cmd, CommandResult,
};
use rand::Rng;
use std::env::temp_dir;
use std::fs::{self, File};
use std::io::Write;
use std::process::Command;
static CMD_PATH: &str = "cksum";
fn generate_cksum_args() -> Vec<String> {
let mut rng = rand::thread_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.gen_bool(0.3) {
args.push("-a".to_string());
args.push(digests[rng.gen_range(0..digests.len())].to_string());
}
if rng.gen_bool(0.2) {
args.push(digest_opts[rng.gen_range(0..digest_opts.len())].to_string());
}
if rng.gen_bool(0.15) {
args.push("-l".to_string());
args.push(rng.gen_range(8..513).to_string());
}
if rng.gen_bool(0.05) {
for _ in 0..rng.gen_range(0..3) {
args.push(format!("file_{}", generate_random_string(5)));
}
} else {
args.push("-c".to_string());
}
if rng.gen_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.gen_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.gen_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::thread_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)
{
if let Ok(content) = fs::read_to_string(&checksum_file_path) {
println!("File content: {checksum_file_path}={content}");
} else {
eprintln!("Error reading the checksum file.");
}
println!("args: {:?}", args);
let 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,
}
}
};
compare_result(
"cksum",
&format!("{:?}", &args[1..]),
None,
&rust_result,
&gnu_result,
false,
);
}
}
});