Skip to content

Commit

Permalink
✨ added hfuzz_workspace and cargo_target_dir options to trdelnik.toml
Browse files Browse the repository at this point in the history
  • Loading branch information
lukacan committed Feb 7, 2024
1 parent cd6f808 commit abedef0
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 34 deletions.
42 changes: 27 additions & 15 deletions crates/client/src/commander.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ use tokio::{
};

pub const PROGRAM_CLIENT_DIRECTORY: &str = ".program_client";
pub const CARGO_TARGET_DIR_DEFAULT: &str = "trdelnik-tests/fuzz_tests/fuzzing/hfuzz_target";
pub const HFUZZ_WORKSPACE_DEFAULT: &str = "trdelnik-tests/fuzz_tests/fuzzing/hfuzz_workspace";

#[derive(Error, Debug)]
pub enum Error {
Expand Down Expand Up @@ -164,17 +162,27 @@ impl Commander {
pub async fn run_fuzzer_with_exit_code(&self, target: String) {
let config = Config::new();

// obtain hfuzz_run_args from env variable, this variable can contain multiple
// arguments so we need to parse the variable content.
let hfuzz_run_args = std::env::var("HFUZZ_RUN_ARGS").unwrap_or_default();

let cargo_target_dir =
std::env::var("CARGO_TARGET_DIR").unwrap_or(CARGO_TARGET_DIR_DEFAULT.to_string());
let fuzz_args = config.get_fuzz_args(hfuzz_run_args);

// we read the workspace from the env variable , if not explicitly set, set it to
// the default directory
let hfuzz_workspace =
std::env::var("HFUZZ_WORKSPACE").unwrap_or(HFUZZ_WORKSPACE_DEFAULT.to_string());
// let cargo_target_dir = std::env::var("CARGO_TARGET_DIR").unwrap_or_default();

let fuzz_args = config.get_fuzz_args(hfuzz_run_args);
// obtain cargo_target_dir, as this variable contains only 1 string
// which corresponds to desired path, we can compare it to the Config
// the default/desired value is set inside Config, however variable entered
// form CLI has always precedence
let cargo_target_dir = std::env::var("CARGO_TARGET_DIR")
.unwrap_or_else(|_| config.get_env_arg("CARGO_TARGET_DIR"));

// obtain hfuzz_workspace, as this variable contains only 1 string
// which corresponds to desired path, we can compare it to the Config
// the default/desired value is set inside Config, however variable entered
// form CLI has always precedence
let hfuzz_workspace = std::env::var("HFUZZ_WORKSPACE")
.unwrap_or_else(|_| config.get_env_arg("HFUZZ_WORKSPACE"));

let (crash_dir, ext) =
get_crash_dir_and_ext(&self.root, &target, &fuzz_args, &hfuzz_workspace);
Expand Down Expand Up @@ -225,10 +233,11 @@ impl Commander {
let config = Config::new();

let hfuzz_run_args = std::env::var("HFUZZ_RUN_ARGS").unwrap_or_default();
let cargo_target_dir =
std::env::var("CARGO_TARGET_DIR").unwrap_or(CARGO_TARGET_DIR_DEFAULT.to_string());
let hfuzz_workspace =
std::env::var("HFUZZ_WORKSPACE").unwrap_or(HFUZZ_WORKSPACE_DEFAULT.to_string());

let cargo_target_dir = std::env::var("CARGO_TARGET_DIR")
.unwrap_or_else(|_| config.get_env_arg("CARGO_TARGET_DIR"));
let hfuzz_workspace = std::env::var("HFUZZ_WORKSPACE")
.unwrap_or_else(|_| config.get_env_arg("HFUZZ_WORKSPACE"));

let fuzz_args = config.get_fuzz_args(hfuzz_run_args);

Expand Down Expand Up @@ -258,15 +267,17 @@ impl Commander {
/// Runs fuzzer on the given target.
#[throws]
pub async fn run_fuzzer_debug(&self, target: String, crash_file_path: String) {
let config = Config::new();

let crash_file = std::path::Path::new(&self.root as &str).join(crash_file_path);

if !crash_file.try_exists()? {
println!("The crash file {:?} not found!", crash_file);
throw!(Error::CrashFileNotFound);
}

let cargo_target_dir =
std::env::var("CARGO_TARGET_DIR").unwrap_or(CARGO_TARGET_DIR_DEFAULT.to_string());
let cargo_target_dir = std::env::var("CARGO_TARGET_DIR")
.unwrap_or_else(|_| config.get_env_arg("CARGO_TARGET_DIR"));

// using exec rather than spawn and replacing current process to avoid unflushed terminal output after ctrl+c signal
std::process::Command::new("cargo")
Expand Down Expand Up @@ -629,6 +640,7 @@ fn get_crash_files(
#[cfg(test)]
mod tests {
use super::*;
pub const HFUZZ_WORKSPACE_DEFAULT: &str = "trdelnik-tests/fuzz_tests/fuzzing/hfuzz_workspace";
#[test]
fn test_cmd_options_parsing() {
let mut command = String::from("-Q -v --extension fuzz");
Expand Down
132 changes: 114 additions & 18 deletions crates/client/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
use anyhow::Context;
use fehler::throw;
use serde::Deserialize;
use std::{env, fs, io, path::PathBuf};
use std::{collections::HashMap, env, fs, io, path::PathBuf};
use thiserror::Error;

pub const CARGO_TOML: &str = "Cargo.toml";
pub const TRDELNIK_TOML: &str = "Trdelnik.toml";
pub const ANCHOR_TOML: &str = "Anchor.toml";
pub const CARGO_TARGET_DIR_DEFAULT: &str = "trdelnik-tests/fuzz_tests/fuzzing/hfuzz_target";
pub const HFUZZ_WORKSPACE_DEFAULT: &str = "trdelnik-tests/fuzz_tests/fuzzing/hfuzz_workspace";

pub const CARGO_TARGET_DIR_ENV: &str = "CARGO_TARGET_DIR";
pub const HFUZZ_WORKSPACE_ENV: &str = "HFUZZ_WORKSPACE";

#[derive(Error, Debug)]
pub enum Error {
Expand Down Expand Up @@ -52,6 +57,7 @@ pub struct FuzzArg {
#[derive(Debug, Deserialize, Clone)]
pub struct Fuzz {
pub fuzz_args: Vec<FuzzArg>,
pub env_variables: HashMap<String, String>,
}
#[derive(Default, Debug, Deserialize, Clone)]
struct _Fuzz {
Expand Down Expand Up @@ -90,6 +96,14 @@ struct _Fuzz {
/// --mutations_per_run
pub mutations_per_run: Option<u16>,
#[serde(default)]
/// Target compilation directory, defaults to "trdelnik-tests/fuzz_tests/fuzzing/hfuzz_target" to not clash with cargo build's default target directory.
/// CARGO_TARGET_DIR env variable
pub cargo_target_dir: Option<String>,
#[serde(default)]
/// Honggfuzz working directory, defaults to "trdelnik-tests/fuzz_tests/fuzzing/hfuzz_workspace".
/// HFUZZ_WORKSPACE env variable
pub hfuzz_workspace: Option<String>,
#[serde(default)]
/// Directory where crashes are saved to (default: workspace directory)
/// --crashdir
pub crashdir: Option<String>,
Expand All @@ -113,23 +127,13 @@ struct _Fuzz {
/// --save_all
pub save_all: Option<bool>,
}
impl Default for Fuzz {
fn default() -> Self {
Self {
fuzz_args: vec![
FuzzArg::new("-t", "--timeout", &10.to_string()),
FuzzArg::new("-N", "--iterations", &0.to_string()),
FuzzArg::new("-r", "--mutations_per_run", &6.to_string()),
FuzzArg::new("-e", "--extension", "fuzz"),
FuzzArg::new("", "--run_time", &0.to_string()),
FuzzArg::new("-F", "--max_file_size", &1_048_576.to_string()),
],
}
}
}

impl From<_Fuzz> for Fuzz {
fn from(_f: _Fuzz) -> Self {
let mut _self = Self { fuzz_args: vec![] };
let mut _self = Self {
fuzz_args: vec![],
env_variables: HashMap::default(),
};

// timeout
let timeout = _f.timeout.unwrap_or(10);
Expand Down Expand Up @@ -158,7 +162,6 @@ impl From<_Fuzz> for Fuzz {
.fuzz_args
.push(FuzzArg::new("-Q", "--keep_output", ""));
}

// verbose
let verbose = _f.verbose.unwrap_or(false);
if verbose {
Expand All @@ -179,6 +182,30 @@ impl From<_Fuzz> for Fuzz {
"--mutations_per_run",
&mutations_per_run.to_string(),
));
// cargo_target_dir
let cargo_target_dir = _f.cargo_target_dir.unwrap_or_default();
if !cargo_target_dir.is_empty() {
_self
.env_variables
.insert(CARGO_TARGET_DIR_ENV.to_owned(), cargo_target_dir);
} else {
_self.env_variables.insert(
CARGO_TARGET_DIR_ENV.to_owned(),
CARGO_TARGET_DIR_DEFAULT.to_owned(),
);
}
// hfuzz_workspace
let hfuzz_workspace = _f.hfuzz_workspace.unwrap_or_default();
if !hfuzz_workspace.is_empty() {
_self
.env_variables
.insert(HFUZZ_WORKSPACE_ENV.to_owned(), hfuzz_workspace);
} else {
_self.env_variables.insert(
HFUZZ_WORKSPACE_ENV.to_owned(),
HFUZZ_WORKSPACE_DEFAULT.to_owned(),
);
}
// crashdir
let crash_dir = _f.crashdir.unwrap_or_default();
if !crash_dir.is_empty() {
Expand Down Expand Up @@ -297,7 +324,7 @@ impl Config {
}
throw!(Error::BadWorkspace)
}
pub fn get_fuzz_args(self, cli_input: String) -> String {
pub fn get_fuzz_args(&self, cli_input: String) -> String {
// Tested on a few examples, HFUZZ_RUN_ARGS give precedence to the later arguments.
// so if HFUZZ_RUN_ARGS="-t 10 -t 15" -> timeout 15s is applied.
// That means we do not need to parse the arguments from the CLI;
Expand All @@ -321,10 +348,43 @@ impl Config {
args.push(cli_input);
args.join(" ")
}
pub fn get_env_arg(&self, env_variable: &str) -> String {
let expect = format!("{env_variable} not found");
self.fuzz
.env_variables
.get(env_variable)
.expect(&expect)
.to_string()
}
}

#[cfg(test)]
mod tests {
impl Default for Fuzz {
fn default() -> Self {
let mut env_variables: HashMap<String, String> = HashMap::default();
env_variables.insert(
HFUZZ_WORKSPACE_ENV.to_owned(),
HFUZZ_WORKSPACE_DEFAULT.to_owned(),
);
env_variables.insert(
CARGO_TARGET_DIR_ENV.to_owned(),
CARGO_TARGET_DIR_DEFAULT.to_owned(),
);
Self {
fuzz_args: vec![
FuzzArg::new("-t", "--timeout", &10.to_string()),
FuzzArg::new("-N", "--iterations", &0.to_string()),
FuzzArg::new("-r", "--mutations_per_run", &6.to_string()),
FuzzArg::new("-e", "--extension", "fuzz"),
FuzzArg::new("", "--run_time", &0.to_string()),
FuzzArg::new("-F", "--max_file_size", &1_048_576.to_string()),
],
env_variables,
}
}
}

use super::*;
#[test]
fn test_merge_and_precedence1() {
Expand Down Expand Up @@ -392,4 +452,40 @@ mod tests {
"-t 10 -N 0 -r 6 -e fuzz --run_time 0 -F 1048576 -t 10 -N 500 -Q -v --exit_upon_crash -n 15 --verifier -W random_dir --crashdir random_dir5 --run_time 666"
);
}
#[test]
fn test_obtain_env_variables() {
let config = Config {
test: Test::default(),
fuzz: Fuzz::default(),
};

let cargo_target_dir = config.get_env_arg(CARGO_TARGET_DIR_ENV);

assert_eq!(cargo_target_dir, CARGO_TARGET_DIR_DEFAULT);
let hfuzz_workspace = config.get_env_arg(HFUZZ_WORKSPACE_ENV);
assert_eq!(hfuzz_workspace, HFUZZ_WORKSPACE_DEFAULT);
}
#[test]
fn test_obtain_env_variables2() {
let mut config = Config {
test: Test::default(),
fuzz: Fuzz::default(),
};

config
.fuzz
.env_variables
.insert(CARGO_TARGET_DIR_ENV.to_owned(), "new_value_x".to_owned());

config
.fuzz
.env_variables
.insert(HFUZZ_WORKSPACE_ENV.to_owned(), "new_value_y".to_owned());

let cargo_target_dir = config.get_env_arg(CARGO_TARGET_DIR_ENV);

assert_eq!(cargo_target_dir, "new_value_x");
let hfuzz_workspace = config.get_env_arg(HFUZZ_WORKSPACE_ENV);
assert_eq!(hfuzz_workspace, "new_value_y");
}
}
6 changes: 5 additions & 1 deletion crates/client/src/templates/Trdelnik.toml.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
validator_startup_timeout = 15000


# contains default values
[fuzz]
# Timeout in seconds (default: 10)
timeout = 10
Expand All @@ -18,6 +17,11 @@ verbose = false
exit_upon_crash = false
# Maximal number of mutations per one run (default: 6)
mutations_per_run = 6
# Target compilation directory, (default: "" ["trdelnik-tests/fuzz_tests/fuzzing/hfuzz_target"]).
# To not clash with cargo build's default target directory.
cargo_target_dir = ""
# Honggfuzz working directory, (default: "" ["trdelnik-tests/fuzz_tests/fuzzing/hfuzz_workspace"]).
hfuzz_workspace = ""
# Directory where crashes are saved to (default: "" [workspace directory])
crashdir = ""
# Input file extension (e.g. 'swf'), (default: "" ['fuzz'])
Expand Down

0 comments on commit abedef0

Please sign in to comment.