Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved trdelnik-tests folder structure for PoC and Fuzz Tests #109

Merged
merged 22 commits into from
Jan 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
47dfdcd
🎨 Split tests, fuzz tests within fuzz_tests folder , PoC tests within…
lukacan Dec 28, 2023
4fc0548
🚑️ Add subcommand , and update accordingly paths based on trdelnik-te…
lukacan Dec 28, 2023
c583240
🚑️ Update paths and commands accordingly
lukacan Dec 28, 2023
96ad98f
✅ Regenerate examples structure based on the new trdelnik-tests struc…
lukacan Dec 28, 2023
4024573
🎨 Modified structure based on PR change request
Jan 4, 2024
6401507
✅ modify fuzzer structure based on previous update
Jan 4, 2024
737fb35
🙈 update .gitignore paths
Jan 4, 2024
b8c4af2
🎨 cargo fmt fix
Jan 4, 2024
9516e3c
🐛 updated precedence order so that it supports HFUZZ_WORKSPACE for fu…
Jan 9, 2024
ee86950
🔥 remove cur_dir as I think it is unnecessary
Jan 9, 2024
f990789
🚑️ remove skip fuzzer condition and update init/add fuzz function so …
Jan 15, 2024
2588b5b
🐛 update commander so that it generates fuzz test files on correct lo…
Jan 15, 2024
2863bb1
🩹 rename template so it corresponds to the fuzz test filename
Jan 15, 2024
278d227
🚑️ fix build so that it works with optional function argument
Jan 15, 2024
05422c7
✅ update fuzz_example0 structure
Jan 15, 2024
30a6284
✅ update fuzz_example1 structure
Jan 15, 2024
a9b1d94
✅ update fuzz_example2 structure
Jan 15, 2024
35e314a
✅ udpate fuzz_example3 structure
Jan 15, 2024
691ff42
🔥 removed fuzzer example
Jan 15, 2024
786cd09
🙈 update gitignore
Jan 19, 2024
4caf9ca
⚰️ Removed commented code
Ikrk Jan 29, 2024
7230fa0
⏪️ Reverted gitignore
Ikrk Jan 29, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/cli/src/command/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ pub async fn build(root: String) {
commander.create_program_client_crate().await?;
commander.build_programs().await?;
commander.generate_program_client_deps().await?;
commander.generate_program_client_lib_rs().await?;
commander.generate_program_client_lib_rs(None).await?;
}
264 changes: 8 additions & 256 deletions crates/cli/src/command/fuzz.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,8 @@
use std::{
env,
path::{Path, PathBuf},
process,
};

use anyhow::{bail, Context, Error, Result};

use clap::Subcommand;
use fehler::throws;
use trdelnik_client::Commander;

pub const TESTS_WORKSPACE: &str = "trdelnik-tests";
pub const HFUZZ_WORKSPACE: &str = "hfuzz_workspace";
use trdelnik_client::{Commander, TestGenerator};

#[derive(Subcommand)]
#[allow(non_camel_case_types)]
Expand All @@ -31,6 +22,7 @@ pub enum FuzzCommand {
/// Path to the crash file
crash_file_path: String,
},
Add,
}
Ikrk marked this conversation as resolved.
Show resolved Hide resolved

#[throws]
Expand All @@ -55,26 +47,7 @@ pub async fn fuzz(root: Option<String>, subcmd: FuzzCommand) {
with_exit_code,
} => {
if with_exit_code {
// Parse the HFUZZ run arguments to find out if the crash folder and crash files extension was modified.
let hfuzz_run_args = env::var("HFUZZ_RUN_ARGS").unwrap_or_default();
let (crash_dir, ext) = get_crash_dir_and_ext(&root, &target, &hfuzz_run_args);

if let Ok(crash_files) = get_crash_files(&crash_dir, &ext) {
if !crash_files.is_empty() {
println!("Error: The crash directory {} already contains crash files from previous runs. \n\nTo run Trdelnik fuzzer with exit code, you must either (backup and) remove the old crash files or alternatively change the crash folder using for example the --crashdir option and the HFUZZ_RUN_ARGS env variable such as:\nHFUZZ_RUN_ARGS=\"--crashdir ./new_crash_dir\"", crash_dir.to_string_lossy());
process::exit(1);
}
}
commander.run_fuzzer(target).await?;
if let Ok(crash_files) = get_crash_files(&crash_dir, &ext) {
if !crash_files.is_empty() {
println!(
"The crash directory {} contains new fuzz test crashes. Exiting!",
crash_dir.to_string_lossy()
);
process::exit(1);
}
}
commander.run_fuzzer_with_exit_code(target).await?;
} else {
commander.run_fuzzer(target).await?;
}
Expand All @@ -85,6 +58,11 @@ pub async fn fuzz(root: Option<String>, subcmd: FuzzCommand) {
} => {
commander.run_fuzzer_debug(target, crash_file_path).await?;
}

FuzzCommand::Add => {
let generator = TestGenerator::new();
generator.add_new_fuzz_test().await?;
}
};
}

Expand Down Expand Up @@ -114,229 +92,3 @@ fn _discover() -> Result<Option<String>> {

Ok(None)
}

fn get_crash_dir_and_ext(root: &str, target: &str, hfuzz_run_args: &str) -> (PathBuf, String) {
// FIXME: we split by whitespace without respecting escaping or quotes - same approach as honggfuzz-rs so there is no point to fix it here before the upstream is fixed
let hfuzz_run_args = hfuzz_run_args.split_whitespace();

let extension =
get_cmd_option_value(hfuzz_run_args.clone(), "-e", "--ext").unwrap_or("fuzz".to_string());

let crash_dir = get_cmd_option_value(hfuzz_run_args.clone(), "", "--cr")
.or_else(|| get_cmd_option_value(hfuzz_run_args.clone(), "-W", "--w"));

let crash_path = if let Some(dir) = crash_dir {
Path::new(root).join(TESTS_WORKSPACE).join(dir)
} else {
Path::new(root)
.join(TESTS_WORKSPACE)
.join(HFUZZ_WORKSPACE)
.join(target)
};

(crash_path, extension)
}

fn get_cmd_option_value<'a>(
hfuzz_run_args: impl Iterator<Item = &'a str>,
short_opt: &str,
long_opt: &str,
) -> Option<String> {
let mut args_iter = hfuzz_run_args;
let mut value: Option<String> = None;

// ensure short option starts with one dash and long option with two dashes
let short_opt = format!("-{}", short_opt.trim_start_matches('-'));
let long_opt = format!("--{}", long_opt.trim_start_matches('-'));

while let Some(arg) = args_iter.next() {
match arg.strip_prefix(&short_opt) {
Some(val) if short_opt.len() > 1 => {
if !val.is_empty() {
// -ecrash for crash extension with no space
value = Some(val.to_string());
} else if let Some(next_arg) = args_iter.next() {
// -e crash for crash extension with space
value = Some(next_arg.to_string());
} else {
value = None;
}
}
_ => {
if arg.starts_with(&long_opt) && long_opt.len() > 2 {
value = args_iter.next().map(|a| a.to_string());
}
}
}
}

value
}

fn get_crash_files(
dir: &PathBuf,
extension: &str,
) -> Result<Vec<PathBuf>, Box<dyn std::error::Error>> {
let paths = std::fs::read_dir(dir)?
// Filter out all those directory entries which couldn't be read
.filter_map(|res| res.ok())
// Map the directory entries to paths
.map(|dir_entry| dir_entry.path())
// Filter out all paths with extensions other than `extension`
.filter_map(|path| {
if path.extension().map_or(false, |ext| ext == extension) {
Some(path)
} else {
None
}
})
.collect::<Vec<_>>();
Ok(paths)
}

#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cmd_options_parsing() {
let mut command = String::from("-Q -v --extension fuzz");
let args = command.split_whitespace();

let extension = get_cmd_option_value(args, "-e", "--ext");
assert_eq!(extension, Some("fuzz".to_string()));

command = String::from("-Q --extension fuzz -v");
let args = command.split_whitespace();

let extension = get_cmd_option_value(args, "-e", "--ext");
assert_eq!(extension, Some("fuzz".to_string()));

command = String::from("-Q -e fuzz -v");
let args = command.split_whitespace();

let extension = get_cmd_option_value(args, "-e", "--ext");
assert_eq!(extension, Some("fuzz".to_string()));

command = String::from("-Q --extension fuzz -v --extension ");
let args = command.split_whitespace();

let extension = get_cmd_option_value(args, "-e", "--ext");
assert_eq!(extension, None);

command = String::from("-Q --extension fuzz -v -e ");
let args = command.split_whitespace();

let extension = get_cmd_option_value(args, "-e", "--ext");
assert_eq!(extension, None);

let mut command = String::from("--extension buzz -e fuzz");
let args = command.split_whitespace();

let extension = get_cmd_option_value(args, "-e", "--ext");
assert_eq!(extension, Some("fuzz".to_string()));

command = String::from("-Q -v -e fuzz");
let args = command.split_whitespace();

let extension = get_cmd_option_value(args, "-e", "--ext");
assert_eq!(extension, Some("fuzz".to_string()));

command = String::from("-Q -v -efuzz");
let args = command.split_whitespace();

let extension = get_cmd_option_value(args, "-e", "--ext");
assert_eq!(extension, Some("fuzz".to_string()));

command = String::from("-Q -v --ext fuzz");
let args = command.split_whitespace();

let extension = get_cmd_option_value(args, "-e", "--ext");
assert_eq!(extension, Some("fuzz".to_string()));

command = String::from("-Q -v --extfuzz");
let args = command.split_whitespace();

let extension = get_cmd_option_value(args, "-e", "--ext");
assert_eq!(extension, None);

command = String::from("-Q -v --workspace");
let args = command.split_whitespace();

let extension = get_cmd_option_value(args, "-e", "--ext");
assert_eq!(extension, None);

command = String::from("-Q -v -e");
let args = command.split_whitespace();

let extension = get_cmd_option_value(args, "", "--ext");
assert_eq!(extension, None);

command = String::from("-Q -v --ext fuzz");
let args = command.split_whitespace();

let extension = get_cmd_option_value(args, "-e", "");
assert_eq!(extension, None);
}

#[test]
fn test_get_crash_dir_and_ext() {
let root = "/home/fuzz";
let target = "target";
let default_crash_path = Path::new(root)
.join(TESTS_WORKSPACE)
.join(HFUZZ_WORKSPACE)
.join(target);

let (crash_dir, ext) = get_crash_dir_and_ext(root, target, "");

assert_eq!(crash_dir, default_crash_path);
assert_eq!(&ext, "fuzz");

let (crash_dir, ext) = get_crash_dir_and_ext(root, target, "-Q -e");

assert_eq!(crash_dir, default_crash_path);
assert_eq!(&ext, "fuzz");

let (crash_dir, ext) = get_crash_dir_and_ext(root, target, "-Q -e crash");

assert_eq!(crash_dir, default_crash_path);
assert_eq!(&ext, "crash");

// test absolute path
let (crash_dir, ext) = get_crash_dir_and_ext(root, target, "-Q -W /home/crash -e crash");

let expected_crash_path = Path::new("/home/crash");
assert_eq!(crash_dir, expected_crash_path);
assert_eq!(&ext, "crash");

// test absolute path
let (crash_dir, ext) =
get_crash_dir_and_ext(root, target, "-Q --crash /home/crash -e crash");

let expected_crash_path = Path::new("/home/crash");
assert_eq!(crash_dir, expected_crash_path);
assert_eq!(&ext, "crash");

// test relative path
let (crash_dir, ext) = get_crash_dir_and_ext(root, target, "-Q -W ../crash -e crash");

let expected_crash_path = Path::new(root).join(TESTS_WORKSPACE).join("../crash");
assert_eq!(crash_dir, expected_crash_path);
assert_eq!(&ext, "crash");

// test relative path
let (crash_dir, ext) = get_crash_dir_and_ext(root, target, "-Q --crash ../crash -e crash");

let expected_crash_path = Path::new(root).join(TESTS_WORKSPACE).join("../crash");
assert_eq!(crash_dir, expected_crash_path);
assert_eq!(&ext, "crash");

// crash directory has precedence before workspace
let (crash_dir, ext) =
get_crash_dir_and_ext(root, target, "-Q --crash ../crash -W /workspace -e crash");

let expected_crash_path = Path::new(root).join(TESTS_WORKSPACE).join("../crash");
assert_eq!(crash_dir, expected_crash_path);
assert_eq!(&ext, "crash");
}
}
1 change: 1 addition & 0 deletions crates/client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,4 @@ shellexpand = { workspace = true }
trdelnik-derive-displayix = { path = "./derive/display_ix" }
trdelnik-derive-fuzz-deserialize = { path = "./derive/fuzz_deserialize" }
trdelnik-derive-fuzz-test-executor = { path = "./derive/fuzz_test_executor" }
pathdiff="0.2.1"
6 changes: 5 additions & 1 deletion crates/client/src/cleaner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,17 @@ impl Cleaner {
async fn clean_hfuzz_target(&self, root: &PathBuf) {
let hfuzz_target_path = Path::new(root)
.join(crate::test_generator::TESTS_WORKSPACE)
.join(crate::test_generator::FUZZ_TEST_DIRECTORY)
.join(crate::test_generator::FUZZING)
.join(crate::test_generator::HFUZZ_TARGET);
if hfuzz_target_path.exists() {
fs::remove_dir_all(hfuzz_target_path).await?;
} else {
println!(
"skipping {}/{} directory: not found",
"skipping {}/{}/{}/{} directory: not found",
crate::test_generator::TESTS_WORKSPACE,
crate::test_generator::FUZZ_TEST_DIRECTORY,
crate::test_generator::FUZZING,
crate::test_generator::HFUZZ_TARGET
)
}
Expand Down
Loading
Loading