add --fuzz option to roc

This commit is contained in:
Brendan Hansknecht 2024-01-23 18:27:43 -08:00
parent 5652da0562
commit abc4fd18cc
No known key found for this signature in database
GPG key ID: 0EA784685083E75B
3 changed files with 46 additions and 27 deletions

View file

@ -68,6 +68,7 @@ pub const FLAG_STDIN: &str = "stdin";
pub const FLAG_STDOUT: &str = "stdout"; pub const FLAG_STDOUT: &str = "stdout";
pub const FLAG_WASM_STACK_SIZE_KB: &str = "wasm-stack-size-kb"; pub const FLAG_WASM_STACK_SIZE_KB: &str = "wasm-stack-size-kb";
pub const FLAG_OUTPUT: &str = "output"; pub const FLAG_OUTPUT: &str = "output";
pub const FLAG_FUZZ: &str = "fuzz";
pub const ROC_FILE: &str = "ROC_FILE"; pub const ROC_FILE: &str = "ROC_FILE";
pub const ROC_DIR: &str = "ROC_DIR"; pub const ROC_DIR: &str = "ROC_DIR";
pub const GLUE_DIR: &str = "GLUE_DIR"; pub const GLUE_DIR: &str = "GLUE_DIR";
@ -139,6 +140,12 @@ pub fn build_app() -> Command {
.value_parser(value_parser!(u32)) .value_parser(value_parser!(u32))
.required(false); .required(false);
let flag_fuzz = Arg::new(FLAG_FUZZ)
.long(FLAG_FUZZ)
.help("Instrument the roc binary for fuzzing with roc-fuzz")
.action(ArgAction::SetTrue)
.required(false);
let roc_file_to_run = Arg::new(ROC_FILE) let roc_file_to_run = Arg::new(ROC_FILE)
.help("The .roc file of an app to run") .help("The .roc file of an app to run")
.value_parser(value_parser!(PathBuf)) .value_parser(value_parser!(PathBuf))
@ -175,6 +182,7 @@ pub fn build_app() -> Command {
.arg(flag_time.clone()) .arg(flag_time.clone())
.arg(flag_linker.clone()) .arg(flag_linker.clone())
.arg(flag_prebuilt.clone()) .arg(flag_prebuilt.clone())
.arg(flag_fuzz.clone())
.arg(flag_wasm_stack_size_kb) .arg(flag_wasm_stack_size_kb)
.arg( .arg(
Arg::new(FLAG_TARGET) Arg::new(FLAG_TARGET)
@ -225,6 +233,7 @@ pub fn build_app() -> Command {
.arg(flag_time.clone()) .arg(flag_time.clone())
.arg(flag_linker.clone()) .arg(flag_linker.clone())
.arg(flag_prebuilt.clone()) .arg(flag_prebuilt.clone())
.arg(flag_fuzz.clone())
.arg( .arg(
Arg::new(ROC_FILE) Arg::new(ROC_FILE)
.help("The .roc file for the main module") .help("The .roc file for the main module")
@ -248,6 +257,7 @@ pub fn build_app() -> Command {
.arg(flag_time.clone()) .arg(flag_time.clone())
.arg(flag_linker.clone()) .arg(flag_linker.clone())
.arg(flag_prebuilt.clone()) .arg(flag_prebuilt.clone())
.arg(flag_fuzz.clone())
.arg(roc_file_to_run.clone()) .arg(roc_file_to_run.clone())
.arg(args_for_app.clone().last(true)) .arg(args_for_app.clone().last(true))
) )
@ -262,6 +272,7 @@ pub fn build_app() -> Command {
.arg(flag_time.clone()) .arg(flag_time.clone())
.arg(flag_linker.clone()) .arg(flag_linker.clone())
.arg(flag_prebuilt.clone()) .arg(flag_prebuilt.clone())
.arg(flag_fuzz.clone())
.arg(roc_file_to_run.clone()) .arg(roc_file_to_run.clone())
.arg(args_for_app.clone().last(true)) .arg(args_for_app.clone().last(true))
) )
@ -393,6 +404,7 @@ pub fn build_app() -> Command {
.arg(flag_time) .arg(flag_time)
.arg(flag_linker) .arg(flag_linker)
.arg(flag_prebuilt) .arg(flag_prebuilt)
.arg(flag_fuzz)
.arg(roc_file_to_run) .arg(roc_file_to_run)
.arg(args_for_app.trailing_var_arg(true)) .arg(args_for_app.trailing_var_arg(true))
} }
@ -748,6 +760,11 @@ pub fn build(
(cross_compile && !targeting_wasm) (cross_compile && !targeting_wasm)
}; };
let fuzz = matches.get_flag(FLAG_FUZZ);
if fuzz && !matches!(code_gen_backend, CodeGenBackend::Llvm(_)) {
user_error!("Cannot instrument binary for fuzzing while using a dev backend.");
}
let wasm_dev_stack_bytes: Option<u32> = matches let wasm_dev_stack_bytes: Option<u32> = matches
.try_get_one::<u32>(FLAG_WASM_STACK_SIZE_KB) .try_get_one::<u32>(FLAG_WASM_STACK_SIZE_KB)
.ok() .ok()
@ -764,6 +781,7 @@ pub fn build(
opt_level, opt_level,
emit_debug_info, emit_debug_info,
emit_llvm_ir, emit_llvm_ir,
fuzz,
}; };
let load_config = standard_load_config(&triple, build_ordering, threading); let load_config = standard_load_config(&triple, build_ordering, threading);

View file

@ -86,6 +86,7 @@ pub struct CodeGenOptions {
pub opt_level: OptLevel, pub opt_level: OptLevel,
pub emit_debug_info: bool, pub emit_debug_info: bool,
pub emit_llvm_ir: bool, pub emit_llvm_ir: bool,
pub fuzz: bool,
} }
type GenFromMono<'a> = (CodeObject, CodeGenTiming, ExpectMetadata<'a>); type GenFromMono<'a> = (CodeObject, CodeGenTiming, ExpectMetadata<'a>);
@ -103,6 +104,7 @@ pub fn gen_from_mono_module<'a>(
let path = roc_file_path; let path = roc_file_path;
let debug = code_gen_options.emit_debug_info; let debug = code_gen_options.emit_debug_info;
let emit_llvm_ir = code_gen_options.emit_llvm_ir; let emit_llvm_ir = code_gen_options.emit_llvm_ir;
let fuzz = code_gen_options.fuzz;
let opt = code_gen_options.opt_level; let opt = code_gen_options.opt_level;
match code_gen_options.backend { match code_gen_options.backend {
@ -131,6 +133,7 @@ pub fn gen_from_mono_module<'a>(
backend_mode, backend_mode,
debug, debug,
emit_llvm_ir, emit_llvm_ir,
fuzz,
), ),
} }
} }
@ -148,6 +151,7 @@ fn gen_from_mono_module_llvm<'a>(
backend_mode: LlvmBackendMode, backend_mode: LlvmBackendMode,
emit_debug_info: bool, emit_debug_info: bool,
emit_llvm_ir: bool, emit_llvm_ir: bool,
fuzz: bool,
) -> GenFromMono<'a> { ) -> GenFromMono<'a> {
use crate::target::{self, convert_opt_level}; use crate::target::{self, convert_opt_level};
use inkwell::attributes::{Attribute, AttributeLoc}; use inkwell::attributes::{Attribute, AttributeLoc};
@ -284,7 +288,8 @@ fn gen_from_mono_module_llvm<'a>(
// annotate the LLVM IR output with debug info // annotate the LLVM IR output with debug info
// so errors are reported with the line number of the LLVM source // so errors are reported with the line number of the LLVM source
let memory_buffer = if cfg!(feature = "sanitizers") && std::env::var("ROC_SANITIZERS").is_ok() { let gen_sanitizers = cfg!(feature = "sanitizers") && std::env::var("ROC_SANITIZERS").is_ok();
let memory_buffer = if fuzz || gen_sanitizers {
let dir = tempfile::tempdir().unwrap(); let dir = tempfile::tempdir().unwrap();
let dir = dir.into_path(); let dir = dir.into_path();
@ -301,33 +306,27 @@ fn gen_from_mono_module_llvm<'a>(
let mut passes = vec![]; let mut passes = vec![];
let mut extra_args = vec![]; let mut extra_args = vec![];
let mut unrecognized = vec![]; let mut unrecognized = vec![];
for sanitizer in std::env::var("ROC_SANITIZERS") if fuzz {
.unwrap() passes.push("sancov-module");
.split(',') extra_args.extend_from_slice(&[
.map(|x| x.trim()) "-sanitizer-coverage-level=4",
{ "-sanitizer-coverage-inline-8bit-counters",
match sanitizer { "-sanitizer-coverage-pc-table",
"address" => passes.push("asan-module"), "-sanitizer-coverage-trace-compares",
"memory" => passes.push("msan-module"), ]);
"thread" => passes.push("tsan-module"), }
"cargo-fuzz" => { if gen_sanitizers {
passes.push("sancov-module"); for sanitizer in std::env::var("ROC_SANITIZERS")
extra_args.extend_from_slice(&[ .unwrap()
"-sanitizer-coverage-level=3", .split(',')
"-sanitizer-coverage-prune-blocks=0", .map(|x| x.trim())
"-sanitizer-coverage-inline-8bit-counters", {
"-sanitizer-coverage-pc-table", match sanitizer {
]); "address" => passes.push("asan-module"),
"memory" => passes.push("msan-module"),
"thread" => passes.push("tsan-module"),
x => unrecognized.push(x.to_owned()),
} }
"afl.rs" => {
passes.push("sancov-module");
extra_args.extend_from_slice(&[
"-sanitizer-coverage-level=3",
"-sanitizer-coverage-prune-blocks=0",
"-sanitizer-coverage-trace-pc-guard",
]);
}
x => unrecognized.push(x.to_owned()),
} }
} }
if !unrecognized.is_empty() { if !unrecognized.is_empty() {
@ -1291,6 +1290,7 @@ pub fn build_str_test<'a>(
opt_level: OptLevel::Normal, opt_level: OptLevel::Normal,
emit_debug_info: false, emit_debug_info: false,
emit_llvm_ir: false, emit_llvm_ir: false,
fuzz: false,
}; };
let emit_timings = false; let emit_timings = false;

View file

@ -57,6 +57,7 @@ pub fn generate(
opt_level: OptLevel::Development, opt_level: OptLevel::Development,
emit_debug_info: false, emit_debug_info: false,
emit_llvm_ir: false, emit_llvm_ir: false,
fuzz: false,
}; };
let load_config = standard_load_config( let load_config = standard_load_config(