-
Notifications
You must be signed in to change notification settings - Fork 42
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
WIP: redesign arg parsing with structopt #52
Closed
Closed
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,304 +1,16 @@ | ||
use std::fs; | ||
use std::env; | ||
use std::process::{self, Command}; | ||
use std::os::unix::process::CommandExt; | ||
use std::path::{Path, PathBuf}; | ||
|
||
const VERSION: &str = env!("CARGO_PKG_VERSION"); | ||
const HONGGFUZZ_TARGET: &str = "hfuzz_target"; | ||
const HONGGFUZZ_WORKSPACE: &str = "hfuzz_workspace"; | ||
|
||
#[cfg(target_family="windows")] | ||
compile_error!("honggfuzz-rs does not currently support Windows but works well under WSL (Windows Subsystem for Linux)"); | ||
|
||
#[derive(PartialEq)] | ||
enum BuildType { | ||
ReleaseInstrumented, | ||
ReleaseNotInstrumented, | ||
ProfileWithGrcov, | ||
Debug | ||
} | ||
|
||
// TODO: maybe use `rustc_version` crate | ||
fn target_triple() -> String { | ||
let output = Command::new("rustc").args(&["-v", "-V"]).output().unwrap(); | ||
let stdout = String::from_utf8(output.stdout).unwrap(); | ||
let triple = stdout.lines().filter(|l|{l.starts_with("host: ")}).next().unwrap().get(6..).unwrap(); | ||
triple.into() | ||
} | ||
|
||
fn find_crate_root() -> Option<PathBuf> { | ||
let mut path = env::current_dir().unwrap(); | ||
|
||
while !path.join("Cargo.toml").is_file() { | ||
// move to parent path | ||
path = match path.parent() { | ||
Some(parent) => parent.into(), | ||
None => return None, // early return | ||
}; | ||
} | ||
|
||
Some(path) | ||
} | ||
|
||
fn debugger_command(target: &str) -> Command { | ||
let debugger = env::var("HFUZZ_DEBUGGER").unwrap_or_else(|_| "rust-lldb".into()); | ||
let honggfuzz_target = env::var("CARGO_TARGET_DIR").unwrap_or_else(|_| HONGGFUZZ_TARGET.into()); | ||
|
||
let mut cmd = Command::new(&debugger); | ||
|
||
match Path::new(&debugger).file_name().map(|f| f.to_string_lossy().contains("lldb")) { | ||
Some(true) => { | ||
cmd.args(&["-o", "b rust_panic", "-o", "r", "-o", "bt", "-f", &format!("{}/{}/debug/{}", &honggfuzz_target, target_triple(), target), "--"]); | ||
} | ||
_ => { | ||
cmd.args(&["-ex", "b rust_panic", "-ex", "r", "-ex", "bt", "--args", &format!("{}/{}/debug/{}", &honggfuzz_target, target_triple(), target)]); | ||
} | ||
}; | ||
|
||
cmd | ||
} | ||
|
||
fn hfuzz_version() { | ||
println!("cargo-hfuzz {}", VERSION); | ||
} | ||
|
||
fn hfuzz_run<T>(mut args: T, crate_root: &Path, build_type: &BuildType) where T: std::iter::Iterator<Item=String> { | ||
let target = args.next().unwrap_or_else(||{ | ||
eprintln!("please specify the name of the target like this \"cargo hfuzz run[-debug|-no-instr] TARGET [ ARGS ... ]\""); | ||
process::exit(1); | ||
}); | ||
|
||
let honggfuzz_target = env::var("CARGO_TARGET_DIR").unwrap_or_else(|_| HONGGFUZZ_TARGET.into()); | ||
let honggfuzz_workspace = env::var("HFUZZ_WORKSPACE").unwrap_or_else(|_| HONGGFUZZ_WORKSPACE.into()); | ||
let honggfuzz_input = env::var("HFUZZ_INPUT").unwrap_or_else(|_| format!("{}/{}/input", honggfuzz_workspace, target)); | ||
|
||
hfuzz_build(vec!["--bin".to_string(), target.clone()].into_iter(), crate_root, build_type); | ||
|
||
match *build_type { | ||
BuildType::Debug => { | ||
let crash_filename = args.next().unwrap_or_else(||{ | ||
eprintln!("please specify the crash filename like this \"cargo hfuzz run-debug TARGET CRASH_FILENAME [ ARGS ... ]\""); | ||
process::exit(1); | ||
}); | ||
|
||
let status = debugger_command(&target) | ||
.args(args) | ||
.env("CARGO_HONGGFUZZ_CRASH_FILENAME", crash_filename) | ||
.env("RUST_BACKTRACE", env::var("RUST_BACKTRACE").unwrap_or_else(|_| "1".into())) | ||
.status() | ||
.unwrap(); | ||
if !status.success() { | ||
process::exit(status.code().unwrap_or(1)); | ||
} | ||
} | ||
_ => { | ||
// add some flags to sanitizers to make them work with Rust code | ||
let asan_options = env::var("ASAN_OPTIONS").unwrap_or_default(); | ||
let asan_options = format!("detect_odr_violation=0:{}", asan_options); | ||
|
||
let tsan_options = env::var("TSAN_OPTIONS").unwrap_or_default(); | ||
let tsan_options = format!("report_signal_unsafe=0:{}", tsan_options); | ||
|
||
// get user-defined args for honggfuzz | ||
let hfuzz_run_args = env::var("HFUZZ_RUN_ARGS").unwrap_or_default(); | ||
// FIXME: we split by whitespace without respecting escaping or quotes | ||
let hfuzz_run_args = hfuzz_run_args.split_whitespace(); | ||
|
||
fs::create_dir_all(&format!("{}/{}/input", &honggfuzz_workspace, target)).unwrap_or_else(|_| { | ||
println!("error: failed to create \"{}/{}/input\"", &honggfuzz_workspace, target); | ||
}); | ||
|
||
let command = format!("{}/honggfuzz", &honggfuzz_target); | ||
Command::new(&command) // exec honggfuzz replacing current process | ||
.args(&["-W", &format!("{}/{}", &honggfuzz_workspace, target), "-f", &honggfuzz_input, "-P"]) | ||
.args(hfuzz_run_args) // allows user-specified arguments to be given to honggfuzz | ||
.args(&["--", &format!("{}/{}/release/{}", &honggfuzz_target, target_triple(), target)]) | ||
.args(args) | ||
.env("ASAN_OPTIONS", asan_options) | ||
.env("TSAN_OPTIONS", tsan_options) | ||
.exec(); | ||
|
||
// code flow will only reach here if honggfuzz failed to execute | ||
eprintln!("cannot execute {}, try to execute \"cargo hfuzz build\" from fuzzed project directory", &command); | ||
process::exit(1); | ||
} | ||
} | ||
} | ||
|
||
fn hfuzz_build<T>(args: T, crate_root: &Path, build_type: &BuildType) where T: std::iter::Iterator<Item=String> { | ||
let honggfuzz_target = env::var("CARGO_TARGET_DIR").unwrap_or_else(|_| HONGGFUZZ_TARGET.into()); | ||
|
||
// HACK: temporary fix, see https://github.com/rust-lang/rust/issues/53945#issuecomment-426824324 | ||
let use_gold_linker: bool = match Command::new("which") // check if the gold linker is available | ||
.args(&["ld.gold"]) | ||
.status() { | ||
Err(_) => false, | ||
Ok(status) => { | ||
match status.code() { | ||
Some(0) => true, | ||
_ => false | ||
} | ||
} | ||
}; | ||
|
||
let mut rustflags = "\ | ||
--cfg fuzzing \ | ||
-C debug-assertions \ | ||
-C overflow_checks \ | ||
".to_string(); | ||
|
||
let mut cargo_incremental = "1"; | ||
match *build_type { | ||
BuildType::Debug => { | ||
rustflags.push_str("\ | ||
--cfg fuzzing_debug \ | ||
-C opt-level=0 \ | ||
-C debuginfo=2 \ | ||
"); | ||
} | ||
|
||
BuildType::ProfileWithGrcov => { | ||
rustflags.push_str("\ | ||
--cfg fuzzing_debug \ | ||
-Zprofile \ | ||
-Cpanic=abort \ | ||
-C opt-level=0 \ | ||
-C debuginfo=2 \ | ||
-Ccodegen-units=1 \ | ||
-Cinline-threshold=0 \ | ||
-Clink-dead-code \ | ||
"); | ||
//-Coverflow-checks=off \ | ||
cargo_incremental = "0"; | ||
} | ||
|
||
_ => { | ||
rustflags.push_str("\ | ||
-C opt-level=3 \ | ||
-C target-cpu=native \ | ||
-C debuginfo=0 \ | ||
"); | ||
|
||
if *build_type == BuildType::ReleaseInstrumented { | ||
rustflags.push_str("\ | ||
-C passes=sancov \ | ||
-C llvm-args=-sanitizer-coverage-level=4 \ | ||
-C llvm-args=-sanitizer-coverage-trace-pc-guard \ | ||
-C llvm-args=-sanitizer-coverage-trace-divs \ | ||
"); | ||
|
||
// trace-compares doesn't work on macOS without a sanitizer | ||
if cfg!(not(target_os="macos")) { | ||
rustflags.push_str("\ | ||
-C llvm-args=-sanitizer-coverage-trace-compares \ | ||
"); | ||
} | ||
|
||
// HACK: temporary fix, see https://github.com/rust-lang/rust/issues/53945#issuecomment-426824324 | ||
if use_gold_linker { | ||
rustflags.push_str("-Clink-arg=-fuse-ld=gold "); | ||
} | ||
} | ||
} | ||
} | ||
|
||
// add user provided flags | ||
rustflags.push_str(&env::var("RUSTFLAGS").unwrap_or_default()); | ||
|
||
// get user-defined args for building | ||
let hfuzz_build_args = env::var("HFUZZ_BUILD_ARGS").unwrap_or_default(); | ||
// FIXME: we split by whitespace without respecting escaping or quotes | ||
let hfuzz_build_args = hfuzz_build_args.split_whitespace(); | ||
|
||
let cargo_bin = env::var("CARGO").unwrap(); | ||
let mut command = Command::new(cargo_bin); | ||
command.args(&["build", "--target", &target_triple()]) // HACK to avoid building build scripts with rustflags | ||
.args(args) | ||
.args(hfuzz_build_args) // allows user-specified arguments to be given to cargo build | ||
.env("RUSTFLAGS", rustflags) | ||
.env("CARGO_INCREMENTAL", cargo_incremental) | ||
.env("CARGO_TARGET_DIR", &honggfuzz_target) // change target_dir to not clash with regular builds | ||
.env("CRATE_ROOT", &crate_root); | ||
|
||
if *build_type == BuildType::ProfileWithGrcov { | ||
command.env("CARGO_HONGGFUZZ_BUILD_VERSION", VERSION) // used by build.rs to check that versions are in sync | ||
.env("CARGO_HONGGFUZZ_TARGET_DIR", &honggfuzz_target); // env variable to be read by build.rs script | ||
} // to place honggfuzz executable at a known location | ||
else if *build_type != BuildType::Debug { | ||
command.arg("--release") | ||
.env("CARGO_HONGGFUZZ_BUILD_VERSION", VERSION) // used by build.rs to check that versions are in sync | ||
.env("CARGO_HONGGFUZZ_TARGET_DIR", &honggfuzz_target); // env variable to be read by build.rs script | ||
} // to place honggfuzz executable at a known location | ||
|
||
let status = command.status().unwrap(); | ||
if !status.success() { | ||
process::exit(status.code().unwrap_or(1)); | ||
} | ||
} | ||
|
||
fn hfuzz_clean<T>(args: T) where T: std::iter::Iterator<Item=String> { | ||
let honggfuzz_target = env::var("CARGO_TARGET_DIR").unwrap_or_else(|_| HONGGFUZZ_TARGET.into()); | ||
let cargo_bin = env::var("CARGO").unwrap(); | ||
let status = Command::new(cargo_bin) | ||
.args(&["clean"]) | ||
.args(args) | ||
.env("CARGO_TARGET_DIR", &honggfuzz_target) // change target_dir to not clash with regular builds | ||
.status() | ||
.unwrap(); | ||
if !status.success() { | ||
process::exit(status.code().unwrap_or(1)); | ||
#[cfg(feature = "cli")] | ||
mod hfuzz; | ||
#[cfg(not(feature = "cli"))] | ||
mod hfuzz { | ||
pub fn main() { | ||
compile_error!("to build the `cargo-hfuzz` command-line tool, enable the `cli` feature with `--features=cli`"); | ||
} | ||
} | ||
|
||
fn main() { | ||
// TODO: maybe use `clap` crate | ||
|
||
let mut args = env::args().skip(1); | ||
if args.next() != Some("hfuzz".to_string()) { | ||
eprintln!("please launch as a cargo subcommand: \"cargo hfuzz ...\""); | ||
process::exit(1); | ||
} | ||
|
||
// change to crate root to have the same behavior as cargo build/run | ||
let crate_root = find_crate_root().unwrap_or_else(|| { | ||
eprintln!("error: could not find `Cargo.toml` in current directory or any parent directory"); | ||
process::exit(1); | ||
}); | ||
env::set_current_dir(&crate_root).unwrap(); | ||
|
||
match args.next() { | ||
Some(ref s) if s == "build" => { | ||
hfuzz_build(args, &crate_root, &BuildType::ReleaseInstrumented); | ||
} | ||
Some(ref s) if s == "build-no-instr" => { | ||
hfuzz_build(args, &crate_root, &BuildType::ReleaseNotInstrumented); | ||
} | ||
Some(ref s) if s == "build-debug" => { | ||
hfuzz_build(args, &crate_root, &BuildType::Debug); | ||
} | ||
Some(ref s) if s == "build-grcov" => { | ||
hfuzz_build(args, &crate_root, &BuildType::ProfileWithGrcov); | ||
} | ||
Some(ref s) if s == "run" => { | ||
hfuzz_run(args, &crate_root, &BuildType::ReleaseInstrumented); | ||
} | ||
Some(ref s) if s == "run-no-instr" => { | ||
hfuzz_run(args, &crate_root, &BuildType::ReleaseNotInstrumented); | ||
} | ||
|
||
Some(ref s) if s == "run-debug" => { | ||
hfuzz_run(args, &crate_root, &BuildType::Debug); | ||
} | ||
Some(ref s) if s == "clean" => { | ||
hfuzz_clean(args); | ||
} | ||
Some(ref s) if s == "version" => { | ||
hfuzz_version(); | ||
} | ||
_ => { | ||
eprintln!("possible commands are: run, run-no-instr, run-debug, build, build-no-instr, build-grcov, build-debug, clean, version"); | ||
process::exit(1); | ||
} | ||
} | ||
} | ||
hfuzz::main(); | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure I would want to go this way, I'd prefer to split it into two crates and avoid the end user chores.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IDK, maintaining two crates, their synchronization, and making their doc reference each other in a non confusing way seems difficult too.
It would make sense to name the binary crate
hfuzz
though..But the feature flag is not so bad, the user will try
cargo install honggfuzz
, will get a clear error stating that the flag is missing, and try again withcargo install honggfuzz --features=cli
.In the docs, the later command will be directly shown.