├── .gitignore ├── .gitmodules ├── Cargo.toml ├── README.md ├── cmplog ├── Cargo.toml └── src │ ├── bin │ ├── cmplog_cc.rs │ └── cmplog_cxx.rs │ └── lib.rs ├── coe ├── Cargo.toml └── src │ ├── bin │ ├── coe_cc.rs │ └── coe_cxx.rs │ └── lib.rs ├── cov_accounting ├── Cargo.toml └── src │ ├── bin │ ├── cov_accounting_cc.rs │ └── cov_accounting_cxx.rs │ └── lib.rs ├── explore ├── Cargo.toml └── src │ ├── bin │ ├── explore_cc.rs │ └── explore_cxx.rs │ └── lib.rs ├── fast ├── Cargo.toml └── src │ ├── bin │ ├── fast_cc.rs │ └── fast_cxx.rs │ └── lib.rs ├── fast_ctx ├── Cargo.toml └── src │ ├── bin │ ├── fast_ctx_cc.rs │ └── fast_ctx_cxx.rs │ └── lib.rs ├── fast_ngram4 ├── Cargo.toml └── src │ ├── bin │ ├── fast_ngram4_cc.rs │ └── fast_ngram4_cxx.rs │ └── lib.rs ├── fast_value_profile ├── Cargo.toml └── src │ ├── bin │ ├── fast_value_profile_cc.rs │ └── fast_value_profile_cxx.rs │ └── lib.rs ├── generic ├── Cargo.toml └── src │ ├── bin │ ├── generic_cc.rs │ └── generic_cxx.rs │ └── lib.rs ├── gramatron ├── Cargo.toml └── src │ ├── bin │ ├── gramatron_cc.rs │ └── gramatron_cxx.rs │ └── lib.rs ├── grammars ├── js_automata.json.gz ├── js_automata_large.json.gz ├── js_gramatron.json ├── js_gramatron_gnf.json ├── js_nautilus.json ├── php_automata.json.gz ├── php_gramatron.json ├── php_nautilus.json ├── ruby_automata.json.gz ├── ruby_gramatron.json └── ruby_nautilus.json ├── grimoire ├── Cargo.toml └── src │ ├── bin │ ├── grimoire_cc.rs │ └── grimoire_cxx.rs │ └── lib.rs ├── mopt ├── Cargo.toml └── src │ ├── bin │ ├── mopt_cc.rs │ └── mopt_cxx.rs │ └── lib.rs ├── naive ├── Cargo.toml └── src │ ├── bin │ ├── naive_cc.rs │ └── naive_cxx.rs │ └── lib.rs ├── naive_ctx ├── Cargo.toml └── src │ ├── bin │ ├── naive_ctx_cc.rs │ └── naive_ctx_cxx.rs │ └── lib.rs ├── naive_ngram4 ├── Cargo.toml └── src │ ├── bin │ ├── naive_ngram4_cc.rs │ └── naive_ngram4_cxx.rs │ └── lib.rs ├── naive_ngram8 ├── Cargo.toml └── src │ ├── bin │ ├── naive_ngram8_cc.rs │ └── naive_ngram8_cxx.rs │ └── lib.rs ├── nautilus ├── Cargo.toml └── src │ ├── bin │ ├── nautilus_cc.rs │ └── nautilus_cxx.rs │ └── lib.rs ├── nautilus_mopt ├── Cargo.toml └── src │ ├── bin │ ├── nautilus_mopt_cc.rs │ └── nautilus_mopt_cxx.rs │ └── lib.rs ├── rand_scheduler ├── Cargo.toml └── src │ ├── bin │ ├── rand_scheduler_cc.rs │ └── rand_scheduler_cxx.rs │ └── lib.rs ├── rust-toolchain ├── stub_rt.c ├── text ├── Cargo.toml └── src │ ├── bin │ ├── text_cc.rs │ └── text_cxx.rs │ └── lib.rs ├── token_level ├── Cargo.toml └── src │ ├── bin │ ├── token_level_cc.rs │ └── token_level_cxx.rs │ └── lib.rs ├── value_profile ├── Cargo.toml └── src │ ├── bin │ ├── value_profile_cc.rs │ └── value_profile_cxx.rs │ └── lib.rs ├── value_profile_cmplog ├── Cargo.toml └── src │ ├── bin │ ├── value_profile_cmplog_cc.rs │ └── value_profile_cmplog_cxx.rs │ └── lib.rs └── weighted ├── Cargo.toml └── src ├── bin ├── weighted_cc.rs └── weighted_cxx.rs └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "LibAFL"] 2 | path = LibAFL 3 | url = https://github.com/AFLplusplus/LibAFL.git 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [profile.release] 2 | lto = true 3 | codegen-units = 1 4 | opt-level = 3 5 | debug = true 6 | 7 | [workspace] 8 | members = [ 9 | "generic", 10 | "text", 11 | "naive", # indexlenmin scheduler 12 | "naive_ctx", 13 | "naive_ngram4", 14 | "naive_ngram8", 15 | "fast", 16 | "fast_ctx", 17 | "fast_ngram4", 18 | "fast_value_profile", 19 | "coe", 20 | "explore", 21 | "cmplog", 22 | "value_profile", 23 | "value_profile_cmplog", 24 | "rand_scheduler", 25 | "cov_accounting", 26 | "mopt", 27 | "gramatron", 28 | "grimoire", 29 | "nautilus", 30 | "nautilus_mopt", 31 | "token_level", 32 | "weighted", 33 | ] 34 | exclude = [ 35 | "LibAFL", 36 | ] 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libafl_fuzzbench 2 | Fuzzers implemented with libafl to evaluate several techniques on fuzzbench 3 | -------------------------------------------------------------------------------- /cmplog/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cmplog" 3 | version = "0.0.1" 4 | authors = ["Andrea Fioraldi ", "Dominik Maier "] 5 | edition = "2021" 6 | 7 | [features] 8 | no_link_main = ["libafl_targets/libfuzzer_no_link_main"] 9 | 10 | 11 | [dependencies] 12 | libafl = { path = "../LibAFL/libafl/" } 13 | libafl_bolts = { path = "../LibAFL/libafl_bolts/" } 14 | libafl_targets = { path = "../LibAFL/libafl_targets/", features = ["sancov_pcguard_hitcounts", "sancov_cmplog", "libfuzzer"] } 15 | # TODO Include it only when building cc 16 | libafl_cc = { path = "../LibAFL/libafl_cc/" } 17 | clap = { version = "~4.2", features = ["default"] } 18 | nix = { version = "0.29", features = ["fs"] } 19 | mimalloc = { version = "*", default-features = false } 20 | 21 | [lib] 22 | crate-type = ["staticlib"] 23 | -------------------------------------------------------------------------------- /cmplog/src/bin/cmplog_cc.rs: -------------------------------------------------------------------------------- 1 | use libafl_cc::ToolWrapper; 2 | use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses}; 3 | use std::env; 4 | pub fn main() { 5 | let args: Vec = env::args().collect(); 6 | if args.len() > 1 { 7 | let mut dir = env::current_exe().unwrap(); 8 | let wrapper_name = dir.file_name().unwrap().to_str().unwrap(); 9 | 10 | let is_cpp = match wrapper_name[wrapper_name.len()-2..].to_lowercase().as_str() { 11 | "cc" => false, 12 | "++" | "pp" | "xx" => true, 13 | _ => panic!("Could not figure out if c or c++ warpper was called. Expected {:?} to end with c or cxx", dir), 14 | }; 15 | 16 | dir.pop(); 17 | 18 | let mut cc = ClangWrapper::new(); 19 | 20 | #[cfg(target_os = "linux")] 21 | cc.add_pass(LLVMPasses::AutoTokens); 22 | 23 | if let Some(code) = cc 24 | .cpp(is_cpp) 25 | // silence the compiler wrapper output, needed for some configure scripts. 26 | .silence(true) 27 | // add arguments only if --libafl or --libafl-no-link are present 28 | .need_libafl_arg(true) 29 | .parse_args(&args) 30 | .expect("Failed to parse the command line") 31 | .link_staticlib(&dir, env!("CARGO_PKG_NAME")) 32 | .add_arg("-fsanitize-coverage=trace-pc-guard,trace-cmp") 33 | .add_pass(LLVMPasses::CmpLogRtn) 34 | .run() 35 | .expect("Failed to run the wrapped compiler") 36 | { 37 | std::process::exit(code); 38 | } 39 | } else { 40 | panic!("LibAFL CC: No Arguments given"); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /cmplog/src/bin/cmplog_cxx.rs: -------------------------------------------------------------------------------- 1 | pub mod cmplog_cc; 2 | 3 | fn main() { 4 | cmplog_cc::main() 5 | } 6 | -------------------------------------------------------------------------------- /coe/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "coe" 3 | version = "0.0.1" 4 | authors = ["Andrea Fioraldi ", "Dominik Maier "] 5 | edition = "2021" 6 | 7 | [features] 8 | no_link_main = ["libafl_targets/libfuzzer_no_link_main"] 9 | 10 | [dependencies] 11 | libafl = { path = "../LibAFL/libafl/" } 12 | libafl_bolts = { path = "../LibAFL/libafl_bolts/" } 13 | libafl_targets = { path = "../LibAFL/libafl_targets/", features = ["sancov_pcguard_hitcounts", "libfuzzer"] } 14 | # TODO Include it only when building cc 15 | libafl_cc = { path = "../LibAFL/libafl_cc/" } 16 | clap = { version = "~4.2", features = ["default"] } 17 | nix = { version = "0.29", features = ["fs"] } 18 | mimalloc = { version = "*", default-features = false } 19 | 20 | [lib] 21 | crate-type = ["staticlib"] 22 | -------------------------------------------------------------------------------- /coe/src/bin/coe_cc.rs: -------------------------------------------------------------------------------- 1 | use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses, ToolWrapper}; 2 | use std::env; 3 | 4 | pub fn main() { 5 | let args: Vec = env::args().collect(); 6 | if args.len() > 1 { 7 | let mut dir = env::current_exe().unwrap(); 8 | let wrapper_name = dir.file_name().unwrap().to_str().unwrap(); 9 | 10 | let is_cpp = match wrapper_name[wrapper_name.len()-2..].to_lowercase().as_str() { 11 | "cc" => false, 12 | "++" | "pp" | "xx" => true, 13 | _ => panic!("Could not figure out if c or c++ warpper was called. Expected {:?} to end with c or cxx", dir), 14 | }; 15 | 16 | dir.pop(); 17 | 18 | let mut cc = ClangWrapper::new(); 19 | 20 | #[cfg(target_os = "linux")] 21 | cc.add_pass(LLVMPasses::AutoTokens); 22 | 23 | if let Some(code) = cc 24 | .cpp(is_cpp) 25 | // silence the compiler wrapper output, needed for some configure scripts. 26 | .silence(true) 27 | // add arguments only if --libafl or --libafl-no-link are present 28 | .need_libafl_arg(true) 29 | .parse_args(&args) 30 | .expect("Failed to parse the command line") 31 | .link_staticlib(&dir, env!("CARGO_PKG_NAME")) 32 | .add_arg("-fsanitize-coverage=trace-pc-guard") 33 | .run() 34 | .expect("Failed to run the wrapped compiler") 35 | { 36 | std::process::exit(code); 37 | } 38 | } else { 39 | panic!("LibAFL CC: No Arguments given"); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /coe/src/bin/coe_cxx.rs: -------------------------------------------------------------------------------- 1 | pub mod coe_cc; 2 | 3 | fn main() { 4 | coe_cc::main() 5 | } 6 | -------------------------------------------------------------------------------- /coe/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A singlethreaded libfuzzer-like fuzzer that can auto-restart. 2 | use mimalloc::MiMalloc; 3 | #[global_allocator] 4 | static GLOBAL: MiMalloc = MiMalloc; 5 | use libafl::observers::CanTrack; 6 | use libafl::HasMetadata; 7 | use libafl_bolts::{ 8 | current_nanos, 9 | os::dup2, 10 | rands::StdRand, 11 | shmem::{ShMemProvider, StdShMemProvider}, 12 | tuples::{tuple_list, Merge}, 13 | AsSlice, 14 | }; 15 | 16 | use clap::{Arg, Command}; 17 | use core::time::Duration; 18 | #[cfg(unix)] 19 | use nix::{self, unistd::dup}; 20 | #[cfg(unix)] 21 | use std::os::unix::io::{AsRawFd, FromRawFd}; 22 | use std::{ 23 | env, 24 | fs::{self, File}, 25 | io::{self, Read, Write}, 26 | path::PathBuf, 27 | process, 28 | }; 29 | 30 | use libafl::{ 31 | corpus::{Corpus, InMemoryOnDiskCorpus, OnDiskCorpus}, 32 | events::SimpleRestartingEventManager, 33 | executors::{inprocess::InProcessExecutor, ExitKind}, 34 | feedback_or, 35 | feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback}, 36 | fuzzer::{Fuzzer, StdFuzzer}, 37 | inputs::{BytesInput, HasTargetBytes}, 38 | monitors::SimpleMonitor, 39 | mutators::{havoc_mutations::havoc_mutations, tokens_mutations, StdScheduledMutator, Tokens}, 40 | observers::{HitcountsMapObserver, TimeObserver}, 41 | schedulers::{ 42 | powersched::PowerSchedule, IndexesLenTimeMinimizerScheduler, PowerQueueScheduler, 43 | }, 44 | stages::{calibrate::CalibrationStage, power::StdPowerMutationalStage}, 45 | state::{HasCorpus, StdState}, 46 | Error, 47 | }; 48 | use libafl_targets::{libfuzzer_initialize, libfuzzer_test_one_input, std_edges_map_observer}; 49 | 50 | #[cfg(target_os = "linux")] 51 | use libafl_targets::autotokens; 52 | 53 | /// The fuzzer main (as `no_mangle` C function) 54 | #[no_mangle] 55 | pub fn libafl_main() { 56 | // Registry the metadata types used in this fuzzer 57 | // Needed only on no_std 58 | //RegistryBuilder::register::(); 59 | 60 | let res = match Command::new(env!("CARGO_PKG_NAME")) 61 | .version(env!("CARGO_PKG_VERSION")) 62 | .author("AFLplusplus team") 63 | .about("LibAFL-based fuzzer for Fuzzbench") 64 | .arg( 65 | Arg::new("out") 66 | .short('o') 67 | .long("output") 68 | .help("The directory to place finds in ('corpus')"), 69 | ) 70 | .arg( 71 | Arg::new("in") 72 | .short('i') 73 | .long("input") 74 | .help("The directory to read initial inputs from ('seeds')"), 75 | ) 76 | .arg( 77 | Arg::new("tokens") 78 | .short('x') 79 | .long("tokens") 80 | .help("A file to read tokens from, to be used during fuzzing"), 81 | ) 82 | .arg( 83 | Arg::new("timeout") 84 | .short('t') 85 | .long("timeout") 86 | .help("Timeout for each individual execution, in milliseconds") 87 | .default_value("1200"), 88 | ) 89 | .arg(Arg::new("remaining")) 90 | .try_get_matches() 91 | { 92 | Ok(res) => res, 93 | Err(err) => { 94 | println!( 95 | "Syntax: {}, [-x dictionary] -o corpus_dir -i seed_dir\n{:?}", 96 | env::current_exe() 97 | .unwrap_or_else(|_| "fuzzer".into()) 98 | .to_string_lossy(), 99 | err, 100 | ); 101 | return; 102 | } 103 | }; 104 | 105 | println!( 106 | "Workdir: {:?}", 107 | env::current_dir().unwrap().to_string_lossy().to_string() 108 | ); 109 | 110 | if let Some(filenames) = res.get_many::("remaining") { 111 | let filenames: Vec<&str> = filenames.map(String::as_str).collect(); 112 | if !filenames.is_empty() { 113 | run_testcases(&filenames); 114 | return; 115 | } 116 | } 117 | 118 | // For fuzzbench, crashes and finds are inside the same `corpus` directory, in the "queue" and "crashes" subdir. 119 | let mut out_dir = PathBuf::from( 120 | res.get_one::("out") 121 | .expect("The --output parameter is missing") 122 | .to_string(), 123 | ); 124 | if fs::create_dir(&out_dir).is_err() { 125 | println!("Out dir at {:?} already exists.", &out_dir); 126 | if !out_dir.is_dir() { 127 | println!("Out dir at {:?} is not a valid directory!", &out_dir); 128 | return; 129 | } 130 | } 131 | let mut crashes = out_dir.clone(); 132 | crashes.push("crashes"); 133 | out_dir.push("queue"); 134 | 135 | let in_dir = PathBuf::from( 136 | res.get_one::("in") 137 | .expect("The --input parameter is missing") 138 | .to_string(), 139 | ); 140 | if !in_dir.is_dir() { 141 | println!("In dir at {:?} is not a valid directory!", &in_dir); 142 | return; 143 | } 144 | 145 | let tokens = res.get_one::("tokens").map(PathBuf::from); 146 | 147 | let timeout = Duration::from_millis( 148 | res.get_one::("timeout") 149 | .unwrap() 150 | .to_string() 151 | .parse() 152 | .expect("Could not parse timeout in milliseconds"), 153 | ); 154 | 155 | fuzz(out_dir, crashes, in_dir, tokens, timeout).expect("An error occurred while fuzzing"); 156 | } 157 | 158 | fn run_testcases(filenames: &[&str]) { 159 | // The actual target run starts here. 160 | // Call LLVMFUzzerInitialize() if present. 161 | let args: Vec = env::args().collect(); 162 | if unsafe { libfuzzer_initialize(&args) } == -1 { 163 | println!("Warning: LLVMFuzzerInitialize failed with -1") 164 | } 165 | 166 | println!( 167 | "You are not fuzzing, just executing {} testcases", 168 | filenames.len() 169 | ); 170 | for fname in filenames { 171 | println!("Executing {}", fname); 172 | 173 | let mut file = File::open(fname).expect("No file found"); 174 | let mut buffer = vec![]; 175 | file.read_to_end(&mut buffer).expect("Buffer overflow"); 176 | 177 | unsafe { libfuzzer_test_one_input(&buffer) }; 178 | } 179 | } 180 | 181 | /// The actual fuzzer 182 | fn fuzz( 183 | corpus_dir: PathBuf, 184 | objective_dir: PathBuf, 185 | seed_dir: PathBuf, 186 | tokenfile: Option, 187 | timeout: Duration, 188 | ) -> Result<(), Error> { 189 | #[cfg(unix)] 190 | let mut stdout_cpy = unsafe { 191 | let new_fd = dup(io::stdout().as_raw_fd())?; 192 | File::from_raw_fd(new_fd) 193 | }; 194 | #[cfg(unix)] 195 | let file_null = File::open("/dev/null")?; 196 | 197 | // 'While the monitor are state, they are usually used in the broker - which is likely never restarted 198 | let monitor = SimpleMonitor::new(|s| { 199 | #[cfg(unix)] 200 | writeln!(&mut stdout_cpy, "{}", s).unwrap(); 201 | #[cfg(windows)] 202 | println!("{}", s); 203 | }); 204 | 205 | // We need a shared map to store our state before a crash. 206 | // This way, we are able to continue fuzzing afterwards. 207 | let mut shmem_provider = StdShMemProvider::new()?; 208 | 209 | let (state, mut mgr) = match SimpleRestartingEventManager::launch(monitor, &mut shmem_provider) 210 | { 211 | // The restarting state will spawn the same process again as child, then restarted it each time it crashes. 212 | Ok(res) => res, 213 | Err(err) => match err { 214 | Error::ShuttingDown => { 215 | return Ok(()); 216 | } 217 | _ => { 218 | panic!("Failed to setup the restarter: {}", err); 219 | } 220 | }, 221 | }; 222 | 223 | // Create an observation channel using the coverage map 224 | // We don't use the hitcounts (see the Cargo.toml, we use pcguard_edges) 225 | let edges_observer = 226 | HitcountsMapObserver::new(unsafe { std_edges_map_observer("edges") }).track_indices(); 227 | 228 | // Create an observation channel to keep track of the execution time 229 | let time_observer = TimeObserver::new("time"); 230 | 231 | let map_feedback = MaxMapFeedback::new(&edges_observer); 232 | 233 | let calibration = CalibrationStage::new(&map_feedback); 234 | 235 | // Feedback to rate the interestingness of an input 236 | // This one is composed by two Feedbacks in OR 237 | let mut feedback = feedback_or!( 238 | // New maximization map feedback linked to the edges observer and the feedback state 239 | map_feedback, 240 | // Time feedback, this one does not need a feedback state 241 | TimeFeedback::new(&time_observer) 242 | ); 243 | 244 | // A feedback to choose if an input is a solution or not 245 | let mut objective = CrashFeedback::new(); 246 | 247 | // If not restarting, create a State from scratch 248 | let mut state = state.unwrap_or_else(|| { 249 | StdState::new( 250 | // RNG 251 | StdRand::with_seed(current_nanos()), 252 | // Corpus that will be evolved, we keep it in memory for performance 253 | InMemoryOnDiskCorpus::new(corpus_dir).unwrap(), 254 | // Corpus in which we store solutions (crashes in this example), 255 | // on disk so the user can get them after stopping the fuzzer 256 | OnDiskCorpus::new(objective_dir).unwrap(), 257 | // States of the feedbacks. 258 | // They are the data related to the feedbacks that you want to persist in the State. 259 | &mut feedback, 260 | &mut objective, 261 | ) 262 | .unwrap() 263 | }); 264 | 265 | println!("Let's fuzz :)"); 266 | 267 | // The actual target run starts here. 268 | // Call LLVMFUzzerInitialize() if present. 269 | let args: Vec = env::args().collect(); 270 | if unsafe { libfuzzer_initialize(&args) } == -1 { 271 | println!("Warning: LLVMFuzzerInitialize failed with -1") 272 | } 273 | 274 | let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations())); 275 | let power: StdPowerMutationalStage<_, _, BytesInput, _, _> = 276 | StdPowerMutationalStage::new(mutator); 277 | 278 | // A minimization+queue policy to get testcasess from the corpus 279 | let scheduler = PowerQueueScheduler::new(&mut state, &edges_observer, PowerSchedule::coe()); 280 | let scheduler = IndexesLenTimeMinimizerScheduler::new(&edges_observer, scheduler); 281 | 282 | // A fuzzer with feedbacks and a corpus scheduler 283 | let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); 284 | 285 | // The wrapped harness function, calling out to the LLVM-style harness 286 | let mut harness = |input: &BytesInput| { 287 | let target = input.target_bytes(); 288 | let buf = target.as_slice(); 289 | unsafe { libfuzzer_test_one_input(buf) }; 290 | ExitKind::Ok 291 | }; 292 | 293 | // Create the executor for an in-process function with one observer for edge coverage and one for the execution time 294 | let mut executor = InProcessExecutor::with_timeout( 295 | &mut harness, 296 | tuple_list!(edges_observer, time_observer), 297 | &mut fuzzer, 298 | &mut state, 299 | &mut mgr, 300 | timeout, 301 | )?; 302 | 303 | // The order of the stages matter! 304 | let mut stages = tuple_list!(calibration, power); 305 | 306 | // Read tokens 307 | if state.metadata_map().get::().is_none() { 308 | let mut toks = Tokens::default(); 309 | if let Some(tokenfile) = tokenfile { 310 | toks.add_from_file(tokenfile)?; 311 | } 312 | #[cfg(target_os = "linux")] 313 | { 314 | toks += autotokens()?; 315 | } 316 | 317 | if !toks.is_empty() { 318 | state.add_metadata(toks); 319 | } 320 | } 321 | 322 | // In case the corpus is empty (on first run), reset 323 | if state.corpus().count() < 1 { 324 | state 325 | .load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &[seed_dir.clone()]) 326 | .unwrap_or_else(|_| { 327 | println!("Failed to load initial corpus at {:?}", &seed_dir); 328 | process::exit(0); 329 | }); 330 | println!("We imported {} inputs from disk.", state.corpus().count()); 331 | } 332 | 333 | // Remove target ouput (logs still survive) 334 | #[cfg(unix)] 335 | { 336 | let null_fd = file_null.as_raw_fd(); 337 | dup2(null_fd, io::stdout().as_raw_fd())?; 338 | dup2(null_fd, io::stderr().as_raw_fd())?; 339 | } 340 | 341 | fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)?; 342 | 343 | // Never reached 344 | Ok(()) 345 | } 346 | -------------------------------------------------------------------------------- /cov_accounting/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cov_accounting" 3 | version = "0.0.1" 4 | authors = ["Andrea Fioraldi ", "Dominik Maier "] 5 | edition = "2021" 6 | 7 | [features] 8 | no_link_main = ["libafl_targets/libfuzzer_no_link_main"] 9 | 10 | [dependencies] 11 | libafl = { path = "../LibAFL/libafl/" } 12 | libafl_bolts = { path = "../LibAFL/libafl_bolts/" } 13 | libafl_targets = { path = "../LibAFL/libafl_targets/", features = ["sancov_pcguard_hitcounts", "libfuzzer"] } 14 | # TODO Include it only when building cc 15 | libafl_cc = { path = "../LibAFL/libafl_cc/" } 16 | clap = { version = "~4.2", features = ["default"] } 17 | nix = { version = "0.29", features = ["fs"] } 18 | mimalloc = { version = "*", default-features = false } 19 | 20 | [lib] 21 | crate-type = ["staticlib"] 22 | -------------------------------------------------------------------------------- /cov_accounting/src/bin/cov_accounting_cc.rs: -------------------------------------------------------------------------------- 1 | use libafl_cc::ToolWrapper; 2 | use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses}; 3 | use std::env; 4 | const GRANULARITY: &str = "FUNC"; 5 | 6 | pub fn main() { 7 | let args: Vec = env::args().collect(); 8 | if args.len() > 1 { 9 | let mut dir = env::current_exe().unwrap(); 10 | let wrapper_name = dir.file_name().unwrap().to_str().unwrap(); 11 | 12 | let is_cpp = match wrapper_name[wrapper_name.len()-2..].to_lowercase().as_str() { 13 | "cc" => false, 14 | "++" | "pp" | "xx" => true, 15 | _ => panic!("Could not figure out if c or c++ warpper was called. Expected {:?} to end with c or cxx", dir), 16 | }; 17 | 18 | dir.pop(); 19 | 20 | let mut cc = ClangWrapper::new(); 21 | 22 | #[cfg(target_os = "linux")] 23 | cc.add_pass(LLVMPasses::AutoTokens); 24 | 25 | if let Some(code) = cc 26 | .cpp(is_cpp) 27 | // silence the compiler wrapper output, needed for some configure scripts. 28 | .silence(true) 29 | // add arguments only if --libafl or --libafl-no-link are present 30 | .need_libafl_arg(true) 31 | .parse_args(&args) 32 | .expect("Failed to parse the command line") 33 | .link_staticlib(&dir, env!("CARGO_PKG_NAME")) 34 | .add_arg("-fsanitize-coverage=trace-pc-guard") 35 | .add_pass(LLVMPasses::CoverageAccounting) 36 | .add_passes_arg(format!("-granularity={}", GRANULARITY)) 37 | .run() 38 | .expect("Failed to run the wrapped compiler") 39 | { 40 | std::process::exit(code); 41 | } 42 | } else { 43 | panic!("LibAFL CC: No Arguments given"); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /cov_accounting/src/bin/cov_accounting_cxx.rs: -------------------------------------------------------------------------------- 1 | pub mod cov_accounting_cc; 2 | 3 | fn main() { 4 | cov_accounting_cc::main() 5 | } 6 | -------------------------------------------------------------------------------- /cov_accounting/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A singlethreaded libfuzzer-like fuzzer that can auto-restart. 2 | use mimalloc::MiMalloc; 3 | #[global_allocator] 4 | static GLOBAL: MiMalloc = MiMalloc; 5 | use libafl::observers::CanTrack; 6 | use libafl::HasMetadata; 7 | use libafl_bolts::{ 8 | current_nanos, 9 | os::dup2, 10 | rands::StdRand, 11 | shmem::{ShMemProvider, StdShMemProvider}, 12 | tuples::{tuple_list, Merge}, 13 | AsSlice, 14 | }; 15 | 16 | use clap::{Arg, Command}; 17 | use core::time::Duration; 18 | #[cfg(unix)] 19 | use nix::{self, unistd::dup}; 20 | #[cfg(unix)] 21 | use std::os::unix::io::{AsRawFd, FromRawFd}; 22 | use std::{ 23 | env, 24 | fs::{self, File}, 25 | io::{self, Read, Write}, 26 | path::PathBuf, 27 | process, 28 | }; 29 | 30 | use libafl::{ 31 | corpus::{Corpus, InMemoryOnDiskCorpus, OnDiskCorpus}, 32 | events::SimpleRestartingEventManager, 33 | executors::{inprocess::InProcessExecutor, ExitKind}, 34 | feedback_or, 35 | feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback}, 36 | fuzzer::{Fuzzer, StdFuzzer}, 37 | inputs::{BytesInput, HasTargetBytes}, 38 | monitors::SimpleMonitor, 39 | mutators::{havoc_mutations::havoc_mutations, tokens_mutations, StdScheduledMutator, Tokens}, 40 | observers::{HitcountsMapObserver, TimeObserver}, 41 | schedulers::{CoverageAccountingScheduler, QueueScheduler}, 42 | stages::StdMutationalStage, 43 | state::{HasCorpus, StdState}, 44 | Error, 45 | }; 46 | use libafl_targets::{ 47 | libfuzzer_initialize, libfuzzer_test_one_input, std_edges_map_observer, ACCOUNTING_MEMOP_MAP, 48 | }; 49 | 50 | #[cfg(target_os = "linux")] 51 | use libafl_targets::autotokens; 52 | 53 | /// The fuzzer main (as `no_mangle` C function) 54 | #[no_mangle] 55 | pub fn libafl_main() { 56 | // Registry the metadata types used in this fuzzer 57 | // Needed only on no_std 58 | //RegistryBuilder::register::(); 59 | 60 | let res = match Command::new(env!("CARGO_PKG_NAME")) 61 | .version(env!("CARGO_PKG_VERSION")) 62 | .author("AFLplusplus team") 63 | .about("LibAFL-based fuzzer for Fuzzbench") 64 | .arg( 65 | Arg::new("out") 66 | .short('o') 67 | .long("output") 68 | .help("The directory to place finds in ('corpus')"), 69 | ) 70 | .arg( 71 | Arg::new("in") 72 | .short('i') 73 | .long("input") 74 | .help("The directory to read initial inputs from ('seeds')"), 75 | ) 76 | .arg( 77 | Arg::new("tokens") 78 | .short('x') 79 | .long("tokens") 80 | .help("A file to read tokens from, to be used during fuzzing"), 81 | ) 82 | .arg( 83 | Arg::new("timeout") 84 | .short('t') 85 | .long("timeout") 86 | .help("Timeout for each individual execution, in milliseconds") 87 | .default_value("1200"), 88 | ) 89 | .arg(Arg::new("remaining")) 90 | .try_get_matches() 91 | { 92 | Ok(res) => res, 93 | Err(err) => { 94 | println!( 95 | "Syntax: {}, [-x dictionary] -o corpus_dir -i seed_dir\n{:?}", 96 | env::current_exe() 97 | .unwrap_or_else(|_| "fuzzer".into()) 98 | .to_string_lossy(), 99 | err, 100 | ); 101 | return; 102 | } 103 | }; 104 | 105 | println!( 106 | "Workdir: {:?}", 107 | env::current_dir().unwrap().to_string_lossy().to_string() 108 | ); 109 | 110 | if let Some(filenames) = res.get_many::("remaining") { 111 | let filenames: Vec<&str> = filenames.map(String::as_str).collect(); 112 | if !filenames.is_empty() { 113 | run_testcases(&filenames); 114 | return; 115 | } 116 | } 117 | 118 | // For fuzzbench, crashes and finds are inside the same `corpus` directory, in the "queue" and "crashes" subdir. 119 | let mut out_dir = PathBuf::from( 120 | res.get_one::("out") 121 | .expect("The --output parameter is missing") 122 | .to_string(), 123 | ); 124 | if fs::create_dir(&out_dir).is_err() { 125 | println!("Out dir at {:?} already exists.", &out_dir); 126 | if !out_dir.is_dir() { 127 | println!("Out dir at {:?} is not a valid directory!", &out_dir); 128 | return; 129 | } 130 | } 131 | let mut crashes = out_dir.clone(); 132 | crashes.push("crashes"); 133 | out_dir.push("queue"); 134 | 135 | let in_dir = PathBuf::from( 136 | res.get_one::("in") 137 | .expect("The --input parameter is missing") 138 | .to_string(), 139 | ); 140 | if !in_dir.is_dir() { 141 | println!("In dir at {:?} is not a valid directory!", &in_dir); 142 | return; 143 | } 144 | 145 | let tokens = res.get_one::("tokens").map(PathBuf::from); 146 | 147 | let timeout = Duration::from_millis( 148 | res.get_one::("timeout") 149 | .unwrap() 150 | .to_string() 151 | .parse() 152 | .expect("Could not parse timeout in milliseconds"), 153 | ); 154 | 155 | fuzz(out_dir, crashes, in_dir, tokens, timeout).expect("An error occurred while fuzzing"); 156 | } 157 | 158 | fn run_testcases(filenames: &[&str]) { 159 | // The actual target run starts here. 160 | // Call LLVMFUzzerInitialize() if present. 161 | let args: Vec = env::args().collect(); 162 | if unsafe { libfuzzer_initialize(&args) } == -1 { 163 | println!("Warning: LLVMFuzzerInitialize failed with -1") 164 | } 165 | 166 | println!( 167 | "You are not fuzzing, just executing {} testcases", 168 | filenames.len() 169 | ); 170 | for fname in filenames { 171 | println!("Executing {}", fname); 172 | 173 | let mut file = File::open(fname).expect("No file found"); 174 | let mut buffer = vec![]; 175 | file.read_to_end(&mut buffer).expect("Buffer overflow"); 176 | 177 | unsafe { libfuzzer_test_one_input(&buffer) }; 178 | } 179 | } 180 | 181 | /// The actual fuzzer 182 | fn fuzz( 183 | corpus_dir: PathBuf, 184 | objective_dir: PathBuf, 185 | seed_dir: PathBuf, 186 | tokenfile: Option, 187 | timeout: Duration, 188 | ) -> Result<(), Error> { 189 | #[cfg(unix)] 190 | let mut stdout_cpy = unsafe { 191 | let new_fd = dup(io::stdout().as_raw_fd())?; 192 | File::from_raw_fd(new_fd) 193 | }; 194 | #[cfg(unix)] 195 | let file_null = File::open("/dev/null")?; 196 | 197 | // 'While the monitor are state, they are usually used in the broker - which is likely never restarted 198 | let monitor = SimpleMonitor::new(|s| { 199 | #[cfg(unix)] 200 | writeln!(&mut stdout_cpy, "{}", s).unwrap(); 201 | #[cfg(windows)] 202 | println!("{}", s); 203 | }); 204 | 205 | // We need a shared map to store our state before a crash. 206 | // This way, we are able to continue fuzzing afterwards. 207 | let mut shmem_provider = StdShMemProvider::new()?; 208 | 209 | let (state, mut mgr) = match SimpleRestartingEventManager::launch(monitor, &mut shmem_provider) 210 | { 211 | // The restarting state will spawn the same process again as child, then restarted it each time it crashes. 212 | Ok(res) => res, 213 | Err(err) => match err { 214 | Error::ShuttingDown => { 215 | return Ok(()); 216 | } 217 | _ => { 218 | panic!("Failed to setup the restarter: {}", err); 219 | } 220 | }, 221 | }; 222 | 223 | // Create an observation channel using the coverage map 224 | // We don't use the hitcounts (see the Cargo.toml, we use pcguard_edges) 225 | let edges_observer = 226 | HitcountsMapObserver::new(unsafe { std_edges_map_observer("edges") }).track_indices(); 227 | 228 | // Create an observation channel to keep track of the execution time 229 | let time_observer = TimeObserver::new("time"); 230 | 231 | // Feedback to rate the interestingness of an input 232 | // This one is composed by two Feedbacks in OR 233 | let mut feedback = feedback_or!( 234 | // New maximization map feedback linked to the edges observer and the feedback state 235 | MaxMapFeedback::new(&edges_observer), 236 | // Time feedback, this one does not need a feedback state 237 | TimeFeedback::new(&time_observer) 238 | ); 239 | 240 | // A feedback to choose if an input is a solution or not 241 | let mut objective = CrashFeedback::new(); 242 | 243 | // If not restarting, create a State from scratch 244 | let mut state = state.unwrap_or_else(|| { 245 | StdState::new( 246 | // RNG 247 | StdRand::with_seed(current_nanos()), 248 | // Corpus that will be evolved, we keep it in memory for performance 249 | InMemoryOnDiskCorpus::new(corpus_dir).unwrap(), 250 | // Corpus in which we store solutions (crashes in this example), 251 | // on disk so the user can get them after stopping the fuzzer 252 | OnDiskCorpus::new(objective_dir).unwrap(), 253 | // States of the feedbacks. 254 | // They are the data related to the feedbacks that you want to persist in the State. 255 | &mut feedback, 256 | &mut objective, 257 | ) 258 | .unwrap() 259 | }); 260 | 261 | println!("Let's fuzz :)"); 262 | 263 | // The actual target run starts here. 264 | // Call LLVMFUzzerInitialize() if present. 265 | let args: Vec = env::args().collect(); 266 | if unsafe { libfuzzer_initialize(&args) } == -1 { 267 | println!("Warning: LLVMFuzzerInitialize failed with -1") 268 | } 269 | 270 | let mutator = StdMutationalStage::new(StdScheduledMutator::new( 271 | havoc_mutations().merge(tokens_mutations()), 272 | )); 273 | 274 | // A minimization+queue policy to get testcasess from the corpus 275 | let scheduler = CoverageAccountingScheduler::new( 276 | &edges_observer, 277 | &mut state, 278 | QueueScheduler::new(), 279 | unsafe { &*core::ptr::addr_of_mut!(ACCOUNTING_MEMOP_MAP) }, 280 | ); 281 | 282 | // A fuzzer with feedbacks and a corpus scheduler 283 | let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); 284 | 285 | // The wrapped harness function, calling out to the LLVM-style harness 286 | let mut harness = |input: &BytesInput| { 287 | let target = input.target_bytes(); 288 | let buf = target.as_slice(); 289 | unsafe { libfuzzer_test_one_input(buf) }; 290 | ExitKind::Ok 291 | }; 292 | 293 | // Create the executor for an in-process function with one observer for edge coverage and one for the execution time 294 | let mut executor = InProcessExecutor::with_timeout( 295 | &mut harness, 296 | tuple_list!(edges_observer, time_observer), 297 | &mut fuzzer, 298 | &mut state, 299 | &mut mgr, 300 | timeout, 301 | )?; 302 | 303 | // The order of the stages matter! 304 | let mut stages = tuple_list!(mutator); 305 | 306 | // Read tokens 307 | if state.metadata_map().get::().is_none() { 308 | let mut toks = Tokens::default(); 309 | if let Some(tokenfile) = tokenfile { 310 | toks.add_from_file(tokenfile)?; 311 | } 312 | #[cfg(target_os = "linux")] 313 | { 314 | toks += autotokens()?; 315 | } 316 | 317 | if !toks.is_empty() { 318 | state.add_metadata(toks); 319 | } 320 | } 321 | 322 | // In case the corpus is empty (on first run), reset 323 | if state.corpus().count() < 1 { 324 | state 325 | .load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &[seed_dir.clone()]) 326 | .unwrap_or_else(|_| { 327 | println!("Failed to load initial corpus at {:?}", &seed_dir); 328 | process::exit(0); 329 | }); 330 | println!("We imported {} inputs from disk.", state.corpus().count()); 331 | } 332 | 333 | // Remove target ouput (logs still survive) 334 | #[cfg(unix)] 335 | { 336 | let null_fd = file_null.as_raw_fd(); 337 | dup2(null_fd, io::stdout().as_raw_fd())?; 338 | dup2(null_fd, io::stderr().as_raw_fd())?; 339 | } 340 | 341 | fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)?; 342 | 343 | // Never reached 344 | Ok(()) 345 | } 346 | -------------------------------------------------------------------------------- /explore/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "explore" 3 | version = "0.0.1" 4 | authors = ["Andrea Fioraldi ", "Dominik Maier "] 5 | edition = "2021" 6 | 7 | [features] 8 | no_link_main = ["libafl_targets/libfuzzer_no_link_main"] 9 | 10 | [dependencies] 11 | libafl = { path = "../LibAFL/libafl/" } 12 | libafl_bolts = { path = "../LibAFL/libafl_bolts/" } 13 | libafl_targets = { path = "../LibAFL/libafl_targets/", features = ["sancov_pcguard_hitcounts", "libfuzzer"] } 14 | # TODO Include it only when building cc 15 | libafl_cc = { path = "../LibAFL/libafl_cc/" } 16 | clap = { version = "~4.2", features = ["default"] } 17 | nix = { version = "0.29", features = ["fs"] } 18 | mimalloc = { version = "*", default-features = false } 19 | 20 | [lib] 21 | crate-type = ["staticlib"] 22 | -------------------------------------------------------------------------------- /explore/src/bin/explore_cc.rs: -------------------------------------------------------------------------------- 1 | use libafl_cc::ToolWrapper; 2 | use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses}; 3 | use std::env; 4 | pub fn main() { 5 | let args: Vec = env::args().collect(); 6 | if args.len() > 1 { 7 | let mut dir = env::current_exe().unwrap(); 8 | let wrapper_name = dir.file_name().unwrap().to_str().unwrap(); 9 | 10 | let is_cpp = match wrapper_name[wrapper_name.len()-2..].to_lowercase().as_str() { 11 | "cc" => false, 12 | "++" | "pp" | "xx" => true, 13 | _ => panic!("Could not figure out if c or c++ warpper was called. Expected {:?} to end with c or cxx", dir), 14 | }; 15 | 16 | dir.pop(); 17 | 18 | let mut cc = ClangWrapper::new(); 19 | 20 | #[cfg(target_os = "linux")] 21 | cc.add_pass(LLVMPasses::AutoTokens); 22 | 23 | if let Some(code) = cc 24 | .cpp(is_cpp) 25 | // silence the compiler wrapper output, needed for some configure scripts. 26 | .silence(true) 27 | // add arguments only if --libafl or --libafl-no-link are present 28 | .need_libafl_arg(true) 29 | .parse_args(&args) 30 | .expect("Failed to parse the command line") 31 | .link_staticlib(&dir, env!("CARGO_PKG_NAME")) 32 | .add_arg("-fsanitize-coverage=trace-pc-guard") 33 | .run() 34 | .expect("Failed to run the wrapped compiler") 35 | { 36 | std::process::exit(code); 37 | } 38 | } else { 39 | panic!("LibAFL CC: No Arguments given"); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /explore/src/bin/explore_cxx.rs: -------------------------------------------------------------------------------- 1 | pub mod explore_cc; 2 | 3 | fn main() { 4 | explore_cc::main() 5 | } 6 | -------------------------------------------------------------------------------- /explore/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A singlethreaded libfuzzer-like fuzzer that can auto-restart. 2 | use mimalloc::MiMalloc; 3 | #[global_allocator] 4 | static GLOBAL: MiMalloc = MiMalloc; 5 | use libafl::observers::CanTrack; 6 | use libafl::HasMetadata; 7 | use libafl_bolts::{ 8 | current_nanos, 9 | os::dup2, 10 | rands::StdRand, 11 | shmem::{ShMemProvider, StdShMemProvider}, 12 | tuples::{tuple_list, Merge}, 13 | AsSlice, 14 | }; 15 | 16 | use clap::{Arg, Command}; 17 | use core::time::Duration; 18 | #[cfg(unix)] 19 | use nix::{self, unistd::dup}; 20 | #[cfg(unix)] 21 | use std::os::unix::io::{AsRawFd, FromRawFd}; 22 | use std::{ 23 | env, 24 | fs::{self, File}, 25 | io::{self, Read, Write}, 26 | path::PathBuf, 27 | process, 28 | }; 29 | 30 | use libafl::{ 31 | corpus::{Corpus, InMemoryOnDiskCorpus, OnDiskCorpus}, 32 | events::SimpleRestartingEventManager, 33 | executors::{inprocess::InProcessExecutor, ExitKind}, 34 | feedback_or, 35 | feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback}, 36 | fuzzer::{Fuzzer, StdFuzzer}, 37 | inputs::{BytesInput, HasTargetBytes}, 38 | monitors::SimpleMonitor, 39 | mutators::{havoc_mutations::havoc_mutations, tokens_mutations, StdScheduledMutator, Tokens}, 40 | observers::{HitcountsMapObserver, TimeObserver}, 41 | schedulers::{ 42 | powersched::PowerSchedule, IndexesLenTimeMinimizerScheduler, PowerQueueScheduler, 43 | }, 44 | stages::{calibrate::CalibrationStage, power::StdPowerMutationalStage}, 45 | state::{HasCorpus, StdState}, 46 | Error, 47 | }; 48 | use libafl_targets::{libfuzzer_initialize, libfuzzer_test_one_input, std_edges_map_observer}; 49 | 50 | #[cfg(target_os = "linux")] 51 | use libafl_targets::autotokens; 52 | 53 | /// The fuzzer main (as `no_mangle` C function) 54 | #[no_mangle] 55 | pub fn libafl_main() { 56 | // Registry the metadata types used in this fuzzer 57 | // Needed only on no_std 58 | //RegistryBuilder::register::(); 59 | 60 | let res = match Command::new(env!("CARGO_PKG_NAME")) 61 | .version(env!("CARGO_PKG_VERSION")) 62 | .author("AFLplusplus team") 63 | .about("LibAFL-based fuzzer for Fuzzbench") 64 | .arg( 65 | Arg::new("out") 66 | .short('o') 67 | .long("output") 68 | .help("The directory to place finds in ('corpus')"), 69 | ) 70 | .arg( 71 | Arg::new("in") 72 | .short('i') 73 | .long("input") 74 | .help("The directory to read initial inputs from ('seeds')"), 75 | ) 76 | .arg( 77 | Arg::new("tokens") 78 | .short('x') 79 | .long("tokens") 80 | .help("A file to read tokens from, to be used during fuzzing"), 81 | ) 82 | .arg( 83 | Arg::new("timeout") 84 | .short('t') 85 | .long("timeout") 86 | .help("Timeout for each individual execution, in milliseconds") 87 | .default_value("1200"), 88 | ) 89 | .arg(Arg::new("remaining")) 90 | .try_get_matches() 91 | { 92 | Ok(res) => res, 93 | Err(err) => { 94 | println!( 95 | "Syntax: {}, [-x dictionary] -o corpus_dir -i seed_dir\n{:?}", 96 | env::current_exe() 97 | .unwrap_or_else(|_| "fuzzer".into()) 98 | .to_string_lossy(), 99 | err, 100 | ); 101 | return; 102 | } 103 | }; 104 | 105 | println!( 106 | "Workdir: {:?}", 107 | env::current_dir().unwrap().to_string_lossy().to_string() 108 | ); 109 | 110 | if let Some(filenames) = res.get_many::("remaining") { 111 | let filenames: Vec<&str> = filenames.map(String::as_str).collect(); 112 | if !filenames.is_empty() { 113 | run_testcases(&filenames); 114 | return; 115 | } 116 | } 117 | 118 | // For fuzzbench, crashes and finds are inside the same `corpus` directory, in the "queue" and "crashes" subdir. 119 | let mut out_dir = PathBuf::from( 120 | res.get_one::("out") 121 | .expect("The --output parameter is missing") 122 | .to_string(), 123 | ); 124 | if fs::create_dir(&out_dir).is_err() { 125 | println!("Out dir at {:?} already exists.", &out_dir); 126 | if !out_dir.is_dir() { 127 | println!("Out dir at {:?} is not a valid directory!", &out_dir); 128 | return; 129 | } 130 | } 131 | let mut crashes = out_dir.clone(); 132 | crashes.push("crashes"); 133 | out_dir.push("queue"); 134 | 135 | let in_dir = PathBuf::from( 136 | res.get_one::("in") 137 | .expect("The --input parameter is missing") 138 | .to_string(), 139 | ); 140 | if !in_dir.is_dir() { 141 | println!("In dir at {:?} is not a valid directory!", &in_dir); 142 | return; 143 | } 144 | 145 | let tokens = res.get_one::("tokens").map(PathBuf::from); 146 | 147 | let timeout = Duration::from_millis( 148 | res.get_one::("timeout") 149 | .unwrap() 150 | .to_string() 151 | .parse() 152 | .expect("Could not parse timeout in milliseconds"), 153 | ); 154 | 155 | fuzz(out_dir, crashes, in_dir, tokens, timeout).expect("An error occurred while fuzzing"); 156 | } 157 | 158 | fn run_testcases(filenames: &[&str]) { 159 | // The actual target run starts here. 160 | // Call LLVMFUzzerInitialize() if present. 161 | let args: Vec = env::args().collect(); 162 | if unsafe { libfuzzer_initialize(&args) } == -1 { 163 | println!("Warning: LLVMFuzzerInitialize failed with -1") 164 | } 165 | 166 | println!( 167 | "You are not fuzzing, just executing {} testcases", 168 | filenames.len() 169 | ); 170 | for fname in filenames { 171 | println!("Executing {}", fname); 172 | 173 | let mut file = File::open(fname).expect("No file found"); 174 | let mut buffer = vec![]; 175 | file.read_to_end(&mut buffer).expect("Buffer overflow"); 176 | 177 | unsafe { libfuzzer_test_one_input(&buffer) }; 178 | } 179 | } 180 | 181 | /// The actual fuzzer 182 | fn fuzz( 183 | corpus_dir: PathBuf, 184 | objective_dir: PathBuf, 185 | seed_dir: PathBuf, 186 | tokenfile: Option, 187 | timeout: Duration, 188 | ) -> Result<(), Error> { 189 | #[cfg(unix)] 190 | let mut stdout_cpy = unsafe { 191 | let new_fd = dup(io::stdout().as_raw_fd())?; 192 | File::from_raw_fd(new_fd) 193 | }; 194 | #[cfg(unix)] 195 | let file_null = File::open("/dev/null")?; 196 | 197 | // 'While the monitor are state, they are usually used in the broker - which is likely never restarted 198 | let monitor = SimpleMonitor::new(|s| { 199 | #[cfg(unix)] 200 | writeln!(&mut stdout_cpy, "{}", s).unwrap(); 201 | #[cfg(windows)] 202 | println!("{}", s); 203 | }); 204 | 205 | // We need a shared map to store our state before a crash. 206 | // This way, we are able to continue fuzzing afterwards. 207 | let mut shmem_provider = StdShMemProvider::new()?; 208 | 209 | let (state, mut mgr) = match SimpleRestartingEventManager::launch(monitor, &mut shmem_provider) 210 | { 211 | // The restarting state will spawn the same process again as child, then restarted it each time it crashes. 212 | Ok(res) => res, 213 | Err(err) => match err { 214 | Error::ShuttingDown => { 215 | return Ok(()); 216 | } 217 | _ => { 218 | panic!("Failed to setup the restarter: {}", err); 219 | } 220 | }, 221 | }; 222 | 223 | // Create an observation channel using the coverage map 224 | // We don't use the hitcounts (see the Cargo.toml, we use pcguard_edges) 225 | let edges_observer = 226 | HitcountsMapObserver::new(unsafe { std_edges_map_observer("edges") }).track_indices(); 227 | 228 | // Create an observation channel to keep track of the execution time 229 | let time_observer = TimeObserver::new("time"); 230 | 231 | let map_feedback = MaxMapFeedback::new(&edges_observer); 232 | 233 | let calibration = CalibrationStage::new(&map_feedback); 234 | 235 | // Feedback to rate the interestingness of an input 236 | // This one is composed by two Feedbacks in OR 237 | let mut feedback = feedback_or!( 238 | // New maximization map feedback linked to the edges observer and the feedback state 239 | map_feedback, 240 | // Time feedback, this one does not need a feedback state 241 | TimeFeedback::new(&time_observer) 242 | ); 243 | 244 | // A feedback to choose if an input is a solution or not 245 | let mut objective = CrashFeedback::new(); 246 | 247 | // If not restarting, create a State from scratch 248 | let mut state = state.unwrap_or_else(|| { 249 | StdState::new( 250 | // RNG 251 | StdRand::with_seed(current_nanos()), 252 | // Corpus that will be evolved, we keep it in memory for performance 253 | InMemoryOnDiskCorpus::new(corpus_dir).unwrap(), 254 | // Corpus in which we store solutions (crashes in this example), 255 | // on disk so the user can get them after stopping the fuzzer 256 | OnDiskCorpus::new(objective_dir).unwrap(), 257 | // States of the feedbacks. 258 | // They are the data related to the feedbacks that you want to persist in the State. 259 | &mut feedback, 260 | &mut objective, 261 | ) 262 | .unwrap() 263 | }); 264 | 265 | println!("Let's fuzz :)"); 266 | 267 | // The actual target run starts here. 268 | // Call LLVMFUzzerInitialize() if present. 269 | let args: Vec = env::args().collect(); 270 | if unsafe { libfuzzer_initialize(&args) } == -1 { 271 | println!("Warning: LLVMFuzzerInitialize failed with -1") 272 | } 273 | 274 | let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations())); 275 | let power: StdPowerMutationalStage<_, _, BytesInput, _, _> = 276 | StdPowerMutationalStage::new(mutator); 277 | 278 | // A minimization+queue policy to get testcasess from the corpus 279 | let scheduler = IndexesLenTimeMinimizerScheduler::new( 280 | &edges_observer, 281 | PowerQueueScheduler::new(&mut state, &edges_observer, PowerSchedule::explore()), 282 | ); 283 | 284 | // A fuzzer with feedbacks and a corpus scheduler 285 | let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); 286 | 287 | // The wrapped harness function, calling out to the LLVM-style harness 288 | let mut harness = |input: &BytesInput| { 289 | let target = input.target_bytes(); 290 | let buf = target.as_slice(); 291 | unsafe { libfuzzer_test_one_input(buf) }; 292 | ExitKind::Ok 293 | }; 294 | 295 | // Create the executor for an in-process function with one observer for edge coverage and one for the execution time 296 | let mut executor = InProcessExecutor::with_timeout( 297 | &mut harness, 298 | tuple_list!(edges_observer, time_observer), 299 | &mut fuzzer, 300 | &mut state, 301 | &mut mgr, 302 | timeout, 303 | )?; 304 | 305 | // The order of the stages matter! 306 | let mut stages = tuple_list!(calibration, power); 307 | 308 | // Read tokens 309 | if state.metadata_map().get::().is_none() { 310 | let mut toks = Tokens::default(); 311 | if let Some(tokenfile) = tokenfile { 312 | toks.add_from_file(tokenfile)?; 313 | } 314 | #[cfg(target_os = "linux")] 315 | { 316 | toks += autotokens()?; 317 | } 318 | 319 | if !toks.is_empty() { 320 | state.add_metadata(toks); 321 | } 322 | } 323 | 324 | // In case the corpus is empty (on first run), reset 325 | if state.corpus().count() < 1 { 326 | state 327 | .load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &[seed_dir.clone()]) 328 | .unwrap_or_else(|_| { 329 | println!("Failed to load initial corpus at {:?}", &seed_dir); 330 | process::exit(0); 331 | }); 332 | println!("We imported {} inputs from disk.", state.corpus().count()); 333 | } 334 | 335 | // Remove target ouput (logs still survive) 336 | #[cfg(unix)] 337 | { 338 | let null_fd = file_null.as_raw_fd(); 339 | dup2(null_fd, io::stdout().as_raw_fd())?; 340 | dup2(null_fd, io::stderr().as_raw_fd())?; 341 | } 342 | 343 | fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)?; 344 | 345 | // Never reached 346 | Ok(()) 347 | } 348 | -------------------------------------------------------------------------------- /fast/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fast" 3 | version = "0.0.1" 4 | authors = ["Andrea Fioraldi ", "Dominik Maier "] 5 | edition = "2021" 6 | 7 | [features] 8 | no_link_main = ["libafl_targets/libfuzzer_no_link_main"] 9 | 10 | [dependencies] 11 | libafl = { path = "../LibAFL/libafl/" } 12 | libafl_bolts = { path = "../LibAFL/libafl_bolts/" } 13 | libafl_targets = { path = "../LibAFL/libafl_targets/", features = ["sancov_pcguard_hitcounts", "libfuzzer"] } 14 | # TODO Include it only when building cc 15 | libafl_cc = { path = "../LibAFL/libafl_cc/" } 16 | clap = { version = "~4.2", features = ["default"] } 17 | nix = { version = "0.29", features = ["fs"] } 18 | mimalloc = { version = "*", default-features = false } 19 | 20 | [lib] 21 | crate-type = ["staticlib"] 22 | -------------------------------------------------------------------------------- /fast/src/bin/fast_cc.rs: -------------------------------------------------------------------------------- 1 | use libafl_cc::ToolWrapper; 2 | use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses}; 3 | use std::env; 4 | pub fn main() { 5 | let args: Vec = env::args().collect(); 6 | if args.len() > 1 { 7 | let mut dir = env::current_exe().unwrap(); 8 | let wrapper_name = dir.file_name().unwrap().to_str().unwrap(); 9 | 10 | let is_cpp = match wrapper_name[wrapper_name.len()-2..].to_lowercase().as_str() { 11 | "cc" => false, 12 | "++" | "pp" | "xx" => true, 13 | _ => panic!("Could not figure out if c or c++ warpper was called. Expected {:?} to end with c or cxx", dir), 14 | }; 15 | 16 | dir.pop(); 17 | 18 | let mut cc = ClangWrapper::new(); 19 | 20 | #[cfg(target_os = "linux")] 21 | cc.add_pass(LLVMPasses::AutoTokens); 22 | 23 | if let Some(code) = cc 24 | .cpp(is_cpp) 25 | // silence the compiler wrapper output, needed for some configure scripts. 26 | .silence(true) 27 | // add arguments only if --libafl or --libafl-no-link are present 28 | .need_libafl_arg(true) 29 | .parse_args(&args) 30 | .expect("Failed to parse the command line") 31 | .link_staticlib(&dir, env!("CARGO_PKG_NAME")) 32 | .add_arg("-fsanitize-coverage=trace-pc-guard") 33 | .run() 34 | .expect("Failed to run the wrapped compiler") 35 | { 36 | std::process::exit(code); 37 | } 38 | } else { 39 | panic!("LibAFL CC: No Arguments given"); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /fast/src/bin/fast_cxx.rs: -------------------------------------------------------------------------------- 1 | pub mod fast_cc; 2 | 3 | fn main() { 4 | fast_cc::main() 5 | } 6 | -------------------------------------------------------------------------------- /fast_ctx/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fast_ctx" 3 | version = "0.0.1" 4 | authors = ["Andrea Fioraldi ", "Dominik Maier "] 5 | edition = "2021" 6 | 7 | [features] 8 | no_link_main = ["libafl_targets/libfuzzer_no_link_main"] 9 | 10 | [dependencies] 11 | libafl = { path = "../LibAFL/libafl/" } 12 | libafl_bolts = { path = "../LibAFL/libafl_bolts/" } 13 | libafl_targets = { path = "../LibAFL/libafl_targets/", features = ["libfuzzer"] } 14 | # TODO Include it only when building cc 15 | libafl_cc = { path = "../LibAFL/libafl_cc/" } 16 | clap = { version = "~4.2", features = ["default"] } 17 | nix = { version = "0.29", features = ["fs"] } 18 | mimalloc = { version = "*", default-features = false } 19 | 20 | [lib] 21 | crate-type = ["staticlib"] 22 | -------------------------------------------------------------------------------- /fast_ctx/src/bin/fast_ctx_cc.rs: -------------------------------------------------------------------------------- 1 | use libafl_cc::ToolWrapper; 2 | use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses}; 3 | use std::env; 4 | pub fn main() { 5 | let args: Vec = env::args().collect(); 6 | if args.len() > 1 { 7 | let mut dir = env::current_exe().unwrap(); 8 | let wrapper_name = dir.file_name().unwrap().to_str().unwrap(); 9 | 10 | let is_cpp = match wrapper_name[wrapper_name.len()-2..].to_lowercase().as_str() { 11 | "cc" => false, 12 | "++" | "pp" | "xx" => true, 13 | _ => panic!("Could not figure out if c or c++ warpper was called. Expected {:?} to end with c or cxx", dir), 14 | }; 15 | 16 | dir.pop(); 17 | 18 | let mut cc = ClangWrapper::new(); 19 | 20 | #[cfg(target_os = "linux")] 21 | cc.add_pass(LLVMPasses::AutoTokens); 22 | 23 | if let Some(code) = cc 24 | .cpp(is_cpp) 25 | // silence the compiler wrapper output, needed for some configure scripts. 26 | .silence(true) 27 | // add arguments only if --libafl or --libafl-no-link are present 28 | .need_libafl_arg(true) 29 | .parse_args(&args) 30 | .expect("Failed to parse the command line") 31 | .link_staticlib(&dir, env!("CARGO_PKG_NAME")) 32 | .add_passes_arg("-ctx") // Context sensitive coverage 33 | .run() 34 | .expect("Failed to run the wrapped compiler") 35 | { 36 | std::process::exit(code); 37 | } 38 | } else { 39 | panic!("LibAFL CC: No Arguments given"); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /fast_ctx/src/bin/fast_ctx_cxx.rs: -------------------------------------------------------------------------------- 1 | pub mod fast_ctx_cc; 2 | 3 | fn main() { 4 | fast_ctx_cc::main() 5 | } 6 | -------------------------------------------------------------------------------- /fast_ngram4/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fast_ngram4" 3 | version = "0.0.1" 4 | authors = ["Andrea Fioraldi ", "Dominik Maier "] 5 | edition = "2021" 6 | 7 | [features] 8 | no_link_main = ["libafl_targets/libfuzzer_no_link_main"] 9 | 10 | [dependencies] 11 | libafl = { path = "../LibAFL/libafl/" } 12 | libafl_bolts = { path = "../LibAFL/libafl_bolts/" } 13 | libafl_targets = { path = "../LibAFL/libafl_targets/", features = ["sancov_pcguard_hitcounts", "libfuzzer"] } 14 | # TODO Include it only when building cc 15 | libafl_cc = { path = "../LibAFL/libafl_cc/" } 16 | clap = { version = "~4.2", features = ["default"] } 17 | nix = { version = "0.29", features = ["fs"] } 18 | mimalloc = { version = "*", default-features = false } 19 | 20 | [lib] 21 | crate-type = ["staticlib"] 22 | -------------------------------------------------------------------------------- /fast_ngram4/src/bin/fast_ngram4_cc.rs: -------------------------------------------------------------------------------- 1 | use libafl_cc::ToolWrapper; 2 | use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses}; 3 | use std::env; 4 | pub fn main() { 5 | let args: Vec = env::args().collect(); 6 | if args.len() > 1 { 7 | let mut dir = env::current_exe().unwrap(); 8 | let wrapper_name = dir.file_name().unwrap().to_str().unwrap(); 9 | 10 | let is_cpp = match wrapper_name[wrapper_name.len()-2..].to_lowercase().as_str() { 11 | "cc" => false, 12 | "++" | "pp" | "xx" => true, 13 | _ => panic!("Could not figure out if c or c++ warpper was called. Expected {:?} to end with c or cxx", dir), 14 | }; 15 | 16 | dir.pop(); 17 | 18 | let mut cc = ClangWrapper::new(); 19 | 20 | #[cfg(target_os = "linux")] 21 | cc.add_pass(LLVMPasses::AutoTokens); 22 | 23 | if let Some(code) = cc 24 | .cpp(is_cpp) 25 | // silence the compiler wrapper output, needed for some configure scripts. 26 | .silence(true) 27 | // add arguments only if --libafl or --libafl-no-link are present 28 | .need_libafl_arg(true) 29 | .parse_args(&args) 30 | .expect("Failed to parse the command line") 31 | .link_staticlib(&dir, env!("CARGO_PKG_NAME")) 32 | .add_passes_arg("-ngram") 33 | .add_passes_arg("4") 34 | .add_passes_linking_arg("-lm") 35 | .run() 36 | .expect("Failed to run the wrapped compiler") 37 | { 38 | std::process::exit(code); 39 | } 40 | } else { 41 | panic!("LibAFL CC: No Arguments given"); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /fast_ngram4/src/bin/fast_ngram4_cxx.rs: -------------------------------------------------------------------------------- 1 | pub mod fast_ngram4_cc; 2 | 3 | fn main() { 4 | fast_ngram4_cc::main() 5 | } 6 | -------------------------------------------------------------------------------- /fast_ngram4/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A singlethreaded libfuzzer-like fuzzer that can auto-restart. 2 | use mimalloc::MiMalloc; 3 | #[global_allocator] 4 | static GLOBAL: MiMalloc = MiMalloc; 5 | use libafl::observers::CanTrack; 6 | use libafl::HasMetadata; 7 | use libafl_bolts::{ 8 | current_nanos, 9 | os::dup2, 10 | rands::StdRand, 11 | shmem::{ShMemProvider, StdShMemProvider}, 12 | tuples::{tuple_list, Merge}, 13 | AsSlice, 14 | }; 15 | 16 | use clap::{Arg, Command}; 17 | use core::time::Duration; 18 | #[cfg(unix)] 19 | use nix::{self, unistd::dup}; 20 | #[cfg(unix)] 21 | use std::os::unix::io::{AsRawFd, FromRawFd}; 22 | use std::{ 23 | env, 24 | fs::{self, File}, 25 | io::{self, Read, Write}, 26 | path::PathBuf, 27 | process, 28 | }; 29 | 30 | use libafl::{ 31 | corpus::{Corpus, InMemoryOnDiskCorpus, OnDiskCorpus}, 32 | events::SimpleRestartingEventManager, 33 | executors::{inprocess::InProcessExecutor, ExitKind}, 34 | feedback_or, 35 | feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback}, 36 | fuzzer::{Fuzzer, StdFuzzer}, 37 | inputs::{BytesInput, HasTargetBytes}, 38 | monitors::SimpleMonitor, 39 | mutators::{havoc_mutations::havoc_mutations, tokens_mutations, StdScheduledMutator, Tokens}, 40 | observers::{HitcountsMapObserver, TimeObserver}, 41 | schedulers::{ 42 | powersched::PowerSchedule, IndexesLenTimeMinimizerScheduler, PowerQueueScheduler, 43 | }, 44 | stages::{calibrate::CalibrationStage, power::StdPowerMutationalStage}, 45 | state::{HasCorpus, StdState}, 46 | Error, 47 | }; 48 | use libafl_targets::{libfuzzer_initialize, libfuzzer_test_one_input, std_edges_map_observer}; 49 | 50 | #[cfg(target_os = "linux")] 51 | use libafl_targets::autotokens; 52 | 53 | /// The fuzzer main (as `no_mangle` C function) 54 | #[no_mangle] 55 | pub fn libafl_main() { 56 | // Registry the metadata types used in this fuzzer 57 | // Needed only on no_std 58 | //RegistryBuilder::register::(); 59 | 60 | let res = match Command::new(env!("CARGO_PKG_NAME")) 61 | .version(env!("CARGO_PKG_VERSION")) 62 | .author("AFLplusplus team") 63 | .about("LibAFL-based fuzzer for Fuzzbench") 64 | .arg( 65 | Arg::new("out") 66 | .short('o') 67 | .long("output") 68 | .help("The directory to place finds in ('corpus')"), 69 | ) 70 | .arg( 71 | Arg::new("in") 72 | .short('i') 73 | .long("input") 74 | .help("The directory to read initial inputs from ('seeds')"), 75 | ) 76 | .arg( 77 | Arg::new("tokens") 78 | .short('x') 79 | .long("tokens") 80 | .help("A file to read tokens from, to be used during fuzzing"), 81 | ) 82 | .arg( 83 | Arg::new("timeout") 84 | .short('t') 85 | .long("timeout") 86 | .help("Timeout for each individual execution, in milliseconds") 87 | .default_value("1200"), 88 | ) 89 | .arg(Arg::new("remaining")) 90 | .try_get_matches() 91 | { 92 | Ok(res) => res, 93 | Err(err) => { 94 | println!( 95 | "Syntax: {}, [-x dictionary] -o corpus_dir -i seed_dir\n{:?}", 96 | env::current_exe() 97 | .unwrap_or_else(|_| "fuzzer".into()) 98 | .to_string_lossy(), 99 | err, 100 | ); 101 | return; 102 | } 103 | }; 104 | 105 | println!( 106 | "Workdir: {:?}", 107 | env::current_dir().unwrap().to_string_lossy().to_string() 108 | ); 109 | 110 | if let Some(filenames) = res.get_many::("remaining") { 111 | let filenames: Vec<&str> = filenames.map(String::as_str).collect(); 112 | if !filenames.is_empty() { 113 | run_testcases(&filenames); 114 | return; 115 | } 116 | } 117 | 118 | // For fuzzbench, crashes and finds are inside the same `corpus` directory, in the "queue" and "crashes" subdir. 119 | let mut out_dir = PathBuf::from( 120 | res.get_one::("out") 121 | .expect("The --output parameter is missing") 122 | .to_string(), 123 | ); 124 | if fs::create_dir(&out_dir).is_err() { 125 | println!("Out dir at {:?} already exists.", &out_dir); 126 | if !out_dir.is_dir() { 127 | println!("Out dir at {:?} is not a valid directory!", &out_dir); 128 | return; 129 | } 130 | } 131 | let mut crashes = out_dir.clone(); 132 | crashes.push("crashes"); 133 | out_dir.push("queue"); 134 | 135 | let in_dir = PathBuf::from( 136 | res.get_one::("in") 137 | .expect("The --input parameter is missing") 138 | .to_string(), 139 | ); 140 | if !in_dir.is_dir() { 141 | println!("In dir at {:?} is not a valid directory!", &in_dir); 142 | return; 143 | } 144 | 145 | let tokens = res.get_one::("tokens").map(PathBuf::from); 146 | 147 | let timeout = Duration::from_millis( 148 | res.get_one::("timeout") 149 | .unwrap() 150 | .to_string() 151 | .parse() 152 | .expect("Could not parse timeout in milliseconds"), 153 | ); 154 | 155 | fuzz(out_dir, crashes, in_dir, tokens, timeout).expect("An error occurred while fuzzing"); 156 | } 157 | 158 | fn run_testcases(filenames: &[&str]) { 159 | // The actual target run starts here. 160 | // Call LLVMFUzzerInitialize() if present. 161 | let args: Vec = env::args().collect(); 162 | if unsafe { libfuzzer_initialize(&args) } == -1 { 163 | println!("Warning: LLVMFuzzerInitialize failed with -1") 164 | } 165 | 166 | println!( 167 | "You are not fuzzing, just executing {} testcases", 168 | filenames.len() 169 | ); 170 | for fname in filenames { 171 | println!("Executing {}", fname); 172 | 173 | let mut file = File::open(fname).expect("No file found"); 174 | let mut buffer = vec![]; 175 | file.read_to_end(&mut buffer).expect("Buffer overflow"); 176 | 177 | unsafe { libfuzzer_test_one_input(&buffer) }; 178 | } 179 | } 180 | 181 | /// The actual fuzzer 182 | fn fuzz( 183 | corpus_dir: PathBuf, 184 | objective_dir: PathBuf, 185 | seed_dir: PathBuf, 186 | tokenfile: Option, 187 | timeout: Duration, 188 | ) -> Result<(), Error> { 189 | #[cfg(unix)] 190 | let mut stdout_cpy = unsafe { 191 | let new_fd = dup(io::stdout().as_raw_fd())?; 192 | File::from_raw_fd(new_fd) 193 | }; 194 | #[cfg(unix)] 195 | let file_null = File::open("/dev/null")?; 196 | 197 | // 'While the monitor are state, they are usually used in the broker - which is likely never restarted 198 | let monitor = SimpleMonitor::new(|s| { 199 | #[cfg(unix)] 200 | writeln!(&mut stdout_cpy, "{}", s).unwrap(); 201 | #[cfg(windows)] 202 | println!("{}", s); 203 | }); 204 | 205 | // We need a shared map to store our state before a crash. 206 | // This way, we are able to continue fuzzing afterwards. 207 | let mut shmem_provider = StdShMemProvider::new()?; 208 | 209 | let (state, mut mgr) = match SimpleRestartingEventManager::launch(monitor, &mut shmem_provider) 210 | { 211 | // The restarting state will spawn the same process again as child, then restarted it each time it crashes. 212 | Ok(res) => res, 213 | Err(err) => match err { 214 | Error::ShuttingDown => { 215 | return Ok(()); 216 | } 217 | _ => { 218 | panic!("Failed to setup the restarter: {}", err); 219 | } 220 | }, 221 | }; 222 | 223 | // Create an observation channel using the coverage map 224 | // We don't use the hitcounts (see the Cargo.toml, we use pcguard_edges) 225 | let edges_observer = 226 | HitcountsMapObserver::new(unsafe { std_edges_map_observer("edges") }).track_indices(); 227 | 228 | let map_feedback = MaxMapFeedback::new(&edges_observer); 229 | 230 | let calibration = CalibrationStage::new(&map_feedback); 231 | 232 | // Create an observation channel to keep track of the execution time 233 | let time_observer = TimeObserver::new("time"); 234 | 235 | // Feedback to rate the interestingness of an input 236 | // This one is composed by two Feedbacks in OR 237 | let mut feedback = feedback_or!( 238 | // New maximization map feedback linked to the edges observer and the feedback state 239 | map_feedback, 240 | // Time feedback, this one does not need a feedback state 241 | TimeFeedback::new(&time_observer) 242 | ); 243 | 244 | // A feedback to choose if an input is a solution or not 245 | let mut objective = CrashFeedback::new(); 246 | 247 | // If not restarting, create a State from scratch 248 | let mut state = state.unwrap_or_else(|| { 249 | StdState::new( 250 | // RNG 251 | StdRand::with_seed(current_nanos()), 252 | // Corpus that will be evolved, we keep it in memory for performance 253 | InMemoryOnDiskCorpus::new(corpus_dir).unwrap(), 254 | // Corpus in which we store solutions (crashes in this example), 255 | // on disk so the user can get them after stopping the fuzzer 256 | OnDiskCorpus::new(objective_dir).unwrap(), 257 | // States of the feedbacks. 258 | // They are the data related to the feedbacks that you want to persist in the State. 259 | &mut feedback, 260 | &mut objective, 261 | ) 262 | .unwrap() 263 | }); 264 | 265 | println!("Let's fuzz :)"); 266 | 267 | // The actual target run starts here. 268 | // Call LLVMFUzzerInitialize() if present. 269 | let args: Vec = env::args().collect(); 270 | if unsafe { libfuzzer_initialize(&args) } == -1 { 271 | println!("Warning: LLVMFuzzerInitialize failed with -1") 272 | } 273 | let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations())); 274 | 275 | let power: StdPowerMutationalStage<_, _, BytesInput, _, _> = 276 | StdPowerMutationalStage::new(mutator); 277 | let scheduler = IndexesLenTimeMinimizerScheduler::new( 278 | &edges_observer, 279 | PowerQueueScheduler::new(&mut state, &edges_observer, PowerSchedule::fast()), 280 | ); 281 | 282 | // A fuzzer with feedbacks and a corpus scheduler 283 | let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); 284 | 285 | // The wrapped harness function, calling out to the LLVM-style harness 286 | let mut harness = |input: &BytesInput| { 287 | let target = input.target_bytes(); 288 | let buf = target.as_slice(); 289 | unsafe { libfuzzer_test_one_input(buf) }; 290 | ExitKind::Ok 291 | }; 292 | 293 | // Create the executor for an in-process function with one observer for edge coverage and one for the execution time 294 | let mut executor = InProcessExecutor::with_timeout( 295 | &mut harness, 296 | tuple_list!(edges_observer, time_observer), 297 | &mut fuzzer, 298 | &mut state, 299 | &mut mgr, 300 | timeout, 301 | )?; 302 | 303 | // The order of the stages matter! 304 | let mut stages = tuple_list!(calibration, power); 305 | 306 | // Read tokens 307 | if state.metadata_map().get::().is_none() { 308 | let mut toks = Tokens::default(); 309 | if let Some(tokenfile) = tokenfile { 310 | toks.add_from_file(tokenfile)?; 311 | } 312 | #[cfg(target_os = "linux")] 313 | { 314 | toks += autotokens()?; 315 | } 316 | 317 | if !toks.is_empty() { 318 | state.add_metadata(toks); 319 | } 320 | } 321 | 322 | // In case the corpus is empty (on first run), reset 323 | if state.corpus().count() < 1 { 324 | state 325 | .load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &[seed_dir.clone()]) 326 | .unwrap_or_else(|_| { 327 | println!("Failed to load initial corpus at {:?}", &seed_dir); 328 | process::exit(0); 329 | }); 330 | println!("We imported {} inputs from disk.", state.corpus().count()); 331 | } 332 | 333 | // Remove target ouput (logs still survive) 334 | #[cfg(unix)] 335 | { 336 | let null_fd = file_null.as_raw_fd(); 337 | dup2(null_fd, io::stdout().as_raw_fd())?; 338 | dup2(null_fd, io::stderr().as_raw_fd())?; 339 | } 340 | 341 | fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)?; 342 | 343 | // Never reached 344 | Ok(()) 345 | } 346 | -------------------------------------------------------------------------------- /fast_value_profile/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fast_value_profile" 3 | version = "0.0.1" 4 | authors = ["Andrea Fioraldi ", "Dominik Maier "] 5 | edition = "2021" 6 | 7 | [features] 8 | no_link_main = ["libafl_targets/libfuzzer_no_link_main"] 9 | 10 | [dependencies] 11 | libafl = { path = "../LibAFL/libafl/" } 12 | libafl_bolts = { path = "../LibAFL/libafl_bolts/" } 13 | libafl_targets = { path = "../LibAFL/libafl_targets/", features = ["sancov_pcguard_hitcounts", "sancov_value_profile", "libfuzzer"] } 14 | # TODO Include it only when building cc 15 | libafl_cc = { path = "../LibAFL/libafl_cc/" } 16 | clap = { version = "~4.2", features = ["default"] } 17 | nix = { version = "0.29", features = ["fs"] } 18 | mimalloc = { version = "*", default-features = false } 19 | 20 | [lib] 21 | crate-type = ["staticlib"] 22 | -------------------------------------------------------------------------------- /fast_value_profile/src/bin/fast_value_profile_cc.rs: -------------------------------------------------------------------------------- 1 | use libafl_cc::ToolWrapper; 2 | use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses}; 3 | use std::env; 4 | pub fn main() { 5 | let args: Vec = env::args().collect(); 6 | if args.len() > 1 { 7 | let mut dir = env::current_exe().unwrap(); 8 | let wrapper_name = dir.file_name().unwrap().to_str().unwrap(); 9 | 10 | let is_cpp = match wrapper_name[wrapper_name.len()-2..].to_lowercase().as_str() { 11 | "cc" => false, 12 | "++" | "pp" | "xx" => true, 13 | _ => panic!("Could not figure out if c or c++ warpper was called. Expected {:?} to end with c or cxx", dir), 14 | }; 15 | 16 | dir.pop(); 17 | 18 | let mut cc = ClangWrapper::new(); 19 | 20 | #[cfg(target_os = "linux")] 21 | cc.add_pass(LLVMPasses::AutoTokens); 22 | 23 | if let Some(code) = cc 24 | .cpp(is_cpp) 25 | // silence the compiler wrapper output, needed for some configure scripts. 26 | .silence(true) 27 | // add arguments only if --libafl or --libafl-no-link are present 28 | .need_libafl_arg(true) 29 | .parse_args(&args) 30 | .expect("Failed to parse the command line") 31 | .link_staticlib(&dir, env!("CARGO_PKG_NAME")) 32 | .add_arg("-fsanitize-coverage=trace-pc-guard,trace-cmp") 33 | .run() 34 | .expect("Failed to run the wrapped compiler") 35 | { 36 | std::process::exit(code); 37 | } 38 | } else { 39 | panic!("LibAFL CC: No Arguments given"); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /fast_value_profile/src/bin/fast_value_profile_cxx.rs: -------------------------------------------------------------------------------- 1 | pub mod fast_value_profile_cc; 2 | 3 | fn main() { 4 | fast_value_profile_cc::main() 5 | } 6 | -------------------------------------------------------------------------------- /generic/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "generic" 3 | version = "0.0.1" 4 | authors = ["Andrea Fioraldi ", "Dominik Maier "] 5 | edition = "2021" 6 | 7 | [features] 8 | no_link_main = ["libafl_targets/libfuzzer_no_link_main"] 9 | 10 | [dependencies] 11 | libafl = { path = "../LibAFL/libafl/" } 12 | libafl_bolts = { path = "../LibAFL/libafl_bolts/" } 13 | libafl_targets = { path = "../LibAFL/libafl_targets/", features = ["sancov_pcguard_hitcounts", "sancov_cmplog", "libfuzzer"] } 14 | # TODO Include it only when building cc 15 | libafl_cc = { path = "../LibAFL/libafl_cc/" } 16 | clap = { version = "4.0", features = ["default"] } 17 | nix = { version = "0.29", features = ["fs"] } 18 | mimalloc = { version = "*", default-features = false } 19 | 20 | [lib] 21 | crate-type = ["staticlib"] 22 | -------------------------------------------------------------------------------- /generic/src/bin/generic_cc.rs: -------------------------------------------------------------------------------- 1 | use libafl_cc::ToolWrapper; 2 | use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses}; 3 | use std::env; 4 | pub fn main() { 5 | let args: Vec = env::args().collect(); 6 | if args.len() > 1 { 7 | let mut dir = env::current_exe().unwrap(); 8 | let wrapper_name = dir.file_name().unwrap().to_str().unwrap(); 9 | 10 | let is_cpp = match wrapper_name[wrapper_name.len()-2..].to_lowercase().as_str() { 11 | "cc" => false, 12 | "++" | "pp" | "xx" => true, 13 | _ => panic!("Could not figure out if c or c++ warpper was called. Expected {:?} to end with c or cxx", dir), 14 | }; 15 | 16 | dir.pop(); 17 | 18 | let mut cc = ClangWrapper::new(); 19 | 20 | #[cfg(target_os = "linux")] 21 | cc.add_pass(LLVMPasses::AutoTokens); 22 | 23 | if let Some(code) = cc 24 | .cpp(is_cpp) 25 | // silence the compiler wrapper output, needed for some configure scripts. 26 | .silence(true) 27 | // add arguments only if --libafl or --libafl-no-link are present 28 | .need_libafl_arg(true) 29 | .parse_args(&args) 30 | .expect("Failed to parse the command line") 31 | .link_staticlib(&dir, env!("CARGO_PKG_NAME")) 32 | .add_arg("-fsanitize-coverage=trace-pc-guard,trace-cmp") 33 | .add_pass(LLVMPasses::CmpLogRtn) 34 | .run() 35 | .expect("Failed to run the wrapped compiler") 36 | { 37 | std::process::exit(code); 38 | } 39 | } else { 40 | panic!("LibAFL CC: No Arguments given"); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /generic/src/bin/generic_cxx.rs: -------------------------------------------------------------------------------- 1 | pub mod generic_cc; 2 | 3 | fn main() { 4 | generic_cc::main() 5 | } 6 | -------------------------------------------------------------------------------- /gramatron/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gramatron" 3 | version = "0.0.1" 4 | authors = ["Andrea Fioraldi ", "Dominik Maier "] 5 | edition = "2021" 6 | 7 | [features] 8 | no_link_main = ["libafl_targets/libfuzzer_no_link_main"] 9 | 10 | [dependencies] 11 | libafl = { path = "../LibAFL/libafl/", features = ["default", "nautilus"] } 12 | libafl_bolts = { path = "../LibAFL/libafl_bolts/" } 13 | libafl_targets = { path = "../LibAFL/libafl_targets/", features = ["sancov_pcguard_hitcounts", "libfuzzer"] } 14 | serde_json = "1.0.68" 15 | # TODO Include it only when building cc 16 | libafl_cc = { path = "../LibAFL/libafl_cc/" } 17 | nix = { version = "0.29", features = ["fs"] } 18 | clap = { version = "4.0", features = ["derive"] } 19 | mimalloc = { version = "*", default-features = false } 20 | postcard = { version = "0.7", features = ["alloc"] } # no_std compatible serde serialization fromat 21 | 22 | [lib] 23 | crate-type = ["staticlib"] 24 | -------------------------------------------------------------------------------- /gramatron/src/bin/gramatron_cc.rs: -------------------------------------------------------------------------------- 1 | use libafl_cc::ToolWrapper; 2 | use libafl_cc::{ClangWrapper, CompilerWrapper}; 3 | use std::env; 4 | pub fn main() { 5 | let args: Vec = env::args().collect(); 6 | if args.len() > 1 { 7 | let mut dir = env::current_exe().unwrap(); 8 | let wrapper_name = dir.file_name().unwrap().to_str().unwrap(); 9 | 10 | let is_cpp = match wrapper_name[wrapper_name.len()-2..].to_lowercase().as_str() { 11 | "cc" => false, 12 | "++" | "pp" | "xx" => true, 13 | _ => panic!("Could not figure out if c or c++ warpper was called. Expected {:?} to end with c or cxx", dir), 14 | }; 15 | 16 | dir.pop(); 17 | 18 | let mut cc = ClangWrapper::new(); 19 | if let Some(code) = cc 20 | .cpp(is_cpp) 21 | // silence the compiler wrapper output, needed for some configure scripts. 22 | .silence(true) 23 | // add arguments only if --libafl or --libafl-no-link are present 24 | .need_libafl_arg(true) 25 | .parse_args(&args) 26 | .expect("Failed to parse the command line") 27 | .link_staticlib(&dir, env!("CARGO_PKG_NAME")) 28 | .add_arg("-fsanitize-coverage=trace-pc-guard") 29 | .run() 30 | .expect("Failed to run the wrapped compiler") 31 | { 32 | std::process::exit(code); 33 | } 34 | } else { 35 | panic!("LibAFL CC: No Arguments given"); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /gramatron/src/bin/gramatron_cxx.rs: -------------------------------------------------------------------------------- 1 | pub mod gramatron_cc; 2 | 3 | fn main() { 4 | gramatron_cc::main() 5 | } 6 | -------------------------------------------------------------------------------- /grammars/js_automata.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AFLplusplus/libafl_fuzzbench/605ce5df98d0ad816074578a28a161edde74c74c/grammars/js_automata.json.gz -------------------------------------------------------------------------------- /grammars/js_automata_large.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AFLplusplus/libafl_fuzzbench/605ce5df98d0ad816074578a28a161edde74c74c/grammars/js_automata_large.json.gz -------------------------------------------------------------------------------- /grammars/php_automata.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AFLplusplus/libafl_fuzzbench/605ce5df98d0ad816074578a28a161edde74c74c/grammars/php_automata.json.gz -------------------------------------------------------------------------------- /grammars/ruby_automata.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AFLplusplus/libafl_fuzzbench/605ce5df98d0ad816074578a28a161edde74c74c/grammars/ruby_automata.json.gz -------------------------------------------------------------------------------- /grimoire/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "grimoire" 3 | version = "0.0.1" 4 | authors = ["Andrea Fioraldi ", "Dominik Maier "] 5 | edition = "2021" 6 | 7 | [features] 8 | no_link_main = ["libafl_targets/libfuzzer_no_link_main"] 9 | 10 | [dependencies] 11 | libafl = { path = "../LibAFL/libafl/", features = ["default", "nautilus"] } 12 | libafl_bolts = { path = "../LibAFL/libafl_bolts/" } 13 | libafl_targets = { path = "../LibAFL/libafl_targets/", features = ["sancov_pcguard_hitcounts", "sancov_cmplog", "libfuzzer"] } 14 | serde_json = "1.0.68" 15 | # TODO Include it only when building cc 16 | libafl_cc = { path = "../LibAFL/libafl_cc/" } 17 | nix = { version = "0.29", features = ["fs"] } 18 | clap = { version = "~4.2", features = ["derive"] } 19 | mimalloc = { version = "*", default-features = false } 20 | postcard = { version = "0.7", features = ["alloc"] } # no_std compatible serde serialization fromat 21 | 22 | [lib] 23 | crate-type = ["staticlib"] 24 | -------------------------------------------------------------------------------- /grimoire/src/bin/grimoire_cc.rs: -------------------------------------------------------------------------------- 1 | use libafl_cc::ToolWrapper; 2 | use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses}; 3 | use std::env; 4 | pub fn main() { 5 | let args: Vec = env::args().collect(); 6 | if args.len() > 1 { 7 | let mut dir = env::current_exe().unwrap(); 8 | let wrapper_name = dir.file_name().unwrap().to_str().unwrap(); 9 | 10 | let is_cpp = match wrapper_name[wrapper_name.len()-2..].to_lowercase().as_str() { 11 | "cc" => false, 12 | "++" | "pp" | "xx" => true, 13 | _ => panic!("Could not figure out if c or c++ warpper was called. Expected {:?} to end with c or cxx", dir), 14 | }; 15 | 16 | dir.pop(); 17 | 18 | let mut cc = ClangWrapper::new(); 19 | 20 | #[cfg(target_os = "linux")] 21 | cc.add_pass(LLVMPasses::AutoTokens); 22 | 23 | if let Some(code) = cc 24 | .cpp(is_cpp) 25 | // silence the compiler wrapper output, needed for some configure scripts. 26 | .silence(true) 27 | // add arguments only if --libafl or --libafl-no-link are present 28 | .need_libafl_arg(true) 29 | .parse_args(&args) 30 | .expect("Failed to parse the command line") 31 | .link_staticlib(&dir, env!("CARGO_PKG_NAME")) 32 | .add_arg("-fsanitize-coverage=trace-pc-guard,trace-cmp") 33 | .add_pass(LLVMPasses::CmpLogRtn) 34 | // needed by Nautilus 35 | // .add_link_arg("-Wl,--push-state,-Bstatic") 36 | // .add_link_arg("-L/usr/local/lib/python3.8/config-3.8-x86_64-linux-gnu/") 37 | // .add_link_arg("-L/usr/lib/python3.8/config-3.8-x86_64-linux-gnu/") 38 | // .add_link_arg("-lpython3.8") 39 | // .add_link_arg("-Wl,--pop-state") 40 | // .add_link_arg("-lutil") 41 | .run() 42 | .expect("Failed to run the wrapped compiler") 43 | { 44 | std::process::exit(code); 45 | } 46 | } else { 47 | panic!("LibAFL CC: No Arguments given"); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /grimoire/src/bin/grimoire_cxx.rs: -------------------------------------------------------------------------------- 1 | pub mod grimoire_cc; 2 | 3 | fn main() { 4 | grimoire_cc::main() 5 | } 6 | -------------------------------------------------------------------------------- /mopt/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mopt" 3 | version = "0.0.1" 4 | authors = ["Andrea Fioraldi ", "Dominik Maier "] 5 | edition = "2021" 6 | 7 | [features] 8 | no_link_main = ["libafl_targets/libfuzzer_no_link_main"] 9 | 10 | [dependencies] 11 | libafl = { path = "../LibAFL/libafl/" } 12 | libafl_bolts = { path = "../LibAFL/libafl_bolts/" } 13 | libafl_targets = { path = "../LibAFL/libafl_targets/", features = ["sancov_pcguard_hitcounts", "libfuzzer"] } 14 | # TODO Include it only when building cc 15 | libafl_cc = { path = "../LibAFL/libafl_cc/" } 16 | clap = { version = "~4.2", features = ["default"] } 17 | nix = { version = "0.29", features = ["fs"] } 18 | mimalloc = { version = "*", default-features = false } 19 | 20 | [lib] 21 | crate-type = ["staticlib"] 22 | -------------------------------------------------------------------------------- /mopt/src/bin/mopt_cc.rs: -------------------------------------------------------------------------------- 1 | use libafl_cc::ToolWrapper; 2 | use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses}; 3 | use std::env; 4 | pub fn main() { 5 | let args: Vec = env::args().collect(); 6 | if args.len() > 1 { 7 | let mut dir = env::current_exe().unwrap(); 8 | let wrapper_name = dir.file_name().unwrap().to_str().unwrap(); 9 | 10 | let is_cpp = match wrapper_name[wrapper_name.len()-2..].to_lowercase().as_str() { 11 | "cc" => false, 12 | "++" | "pp" | "xx" => true, 13 | _ => panic!("Could not figure out if c or c++ warpper was called. Expected {:?} to end with c or cxx", dir), 14 | }; 15 | 16 | dir.pop(); 17 | 18 | let mut cc = ClangWrapper::new(); 19 | 20 | #[cfg(target_os = "linux")] 21 | cc.add_pass(LLVMPasses::AutoTokens); 22 | 23 | if let Some(code) = cc 24 | .cpp(is_cpp) 25 | // silence the compiler wrapper output, needed for some configure scripts. 26 | .silence(true) 27 | // add arguments only if --libafl or --libafl-no-link are present 28 | .need_libafl_arg(true) 29 | .parse_args(&args) 30 | .expect("Failed to parse the command line") 31 | .link_staticlib(&dir, env!("CARGO_PKG_NAME")) 32 | .add_arg("-fsanitize-coverage=trace-pc-guard") 33 | .run() 34 | .expect("Failed to run the wrapped compiler") 35 | { 36 | std::process::exit(code); 37 | } 38 | } else { 39 | panic!("LibAFL CC: No Arguments given"); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /mopt/src/bin/mopt_cxx.rs: -------------------------------------------------------------------------------- 1 | pub mod mopt_cc; 2 | 3 | fn main() { 4 | mopt_cc::main() 5 | } 6 | -------------------------------------------------------------------------------- /mopt/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A singlethreaded libfuzzer-like fuzzer that can auto-restart. 2 | use mimalloc::MiMalloc; 3 | #[global_allocator] 4 | static GLOBAL: MiMalloc = MiMalloc; 5 | use libafl::observers::CanTrack; 6 | use libafl::HasMetadata; 7 | use libafl_bolts::{ 8 | current_nanos, 9 | os::dup2, 10 | rands::StdRand, 11 | shmem::{ShMemProvider, StdShMemProvider}, 12 | tuples::{tuple_list, Merge}, 13 | AsSlice, 14 | }; 15 | 16 | use clap::{Arg, Command}; 17 | use core::time::Duration; 18 | #[cfg(unix)] 19 | use nix::{self, unistd::dup}; 20 | #[cfg(unix)] 21 | use std::os::unix::io::{AsRawFd, FromRawFd}; 22 | use std::{ 23 | env, 24 | fs::{self, File}, 25 | io::{self, Read, Write}, 26 | path::PathBuf, 27 | process, 28 | }; 29 | 30 | use libafl::{ 31 | corpus::{Corpus, InMemoryOnDiskCorpus, OnDiskCorpus}, 32 | events::SimpleRestartingEventManager, 33 | executors::{inprocess::InProcessExecutor, ExitKind}, 34 | feedback_or, 35 | feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback}, 36 | fuzzer::{Fuzzer, StdFuzzer}, 37 | inputs::{BytesInput, HasTargetBytes}, 38 | monitors::SimpleMonitor, 39 | mutators::{havoc_mutations::havoc_mutations, tokens_mutations, StdMOptMutator, Tokens}, 40 | observers::{HitcountsMapObserver, TimeObserver}, 41 | schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler}, 42 | stages::StdMutationalStage, 43 | state::{HasCorpus, StdState}, 44 | Error, 45 | }; 46 | use libafl_targets::{libfuzzer_initialize, libfuzzer_test_one_input, std_edges_map_observer}; 47 | 48 | #[cfg(target_os = "linux")] 49 | use libafl_targets::autotokens; 50 | 51 | /// The fuzzer main (as `no_mangle` C function) 52 | #[no_mangle] 53 | pub fn libafl_main() { 54 | // Registry the metadata types used in this fuzzer 55 | // Needed only on no_std 56 | //RegistryBuilder::register::(); 57 | 58 | let res = match Command::new(env!("CARGO_PKG_NAME")) 59 | .version(env!("CARGO_PKG_VERSION")) 60 | .author("AFLplusplus team") 61 | .about("LibAFL-based fuzzer for Fuzzbench") 62 | .arg( 63 | Arg::new("out") 64 | .short('o') 65 | .long("output") 66 | .help("The directory to place finds in ('corpus')"), 67 | ) 68 | .arg( 69 | Arg::new("in") 70 | .short('i') 71 | .long("input") 72 | .help("The directory to read initial inputs from ('seeds')"), 73 | ) 74 | .arg( 75 | Arg::new("tokens") 76 | .short('x') 77 | .long("tokens") 78 | .help("A file to read tokens from, to be used during fuzzing"), 79 | ) 80 | .arg( 81 | Arg::new("timeout") 82 | .short('t') 83 | .long("timeout") 84 | .help("Timeout for each individual execution, in milliseconds") 85 | .default_value("1200"), 86 | ) 87 | .arg(Arg::new("remaining")) 88 | .try_get_matches() 89 | { 90 | Ok(res) => res, 91 | Err(err) => { 92 | println!( 93 | "Syntax: {}, [-x dictionary] -o corpus_dir -i seed_dir\n{:?}", 94 | env::current_exe() 95 | .unwrap_or_else(|_| "fuzzer".into()) 96 | .to_string_lossy(), 97 | err, 98 | ); 99 | return; 100 | } 101 | }; 102 | 103 | println!( 104 | "Workdir: {:?}", 105 | env::current_dir().unwrap().to_string_lossy().to_string() 106 | ); 107 | 108 | if let Some(filenames) = res.get_many::("remaining") { 109 | let filenames: Vec<&str> = filenames.map(String::as_str).collect(); 110 | if !filenames.is_empty() { 111 | run_testcases(&filenames); 112 | return; 113 | } 114 | } 115 | 116 | // For fuzzbench, crashes and finds are inside the same `corpus` directory, in the "queue" and "crashes" subdir. 117 | let mut out_dir = PathBuf::from( 118 | res.get_one::("out") 119 | .expect("The --output parameter is missing") 120 | .to_string(), 121 | ); 122 | if fs::create_dir(&out_dir).is_err() { 123 | println!("Out dir at {:?} already exists.", &out_dir); 124 | if !out_dir.is_dir() { 125 | println!("Out dir at {:?} is not a valid directory!", &out_dir); 126 | return; 127 | } 128 | } 129 | let mut crashes = out_dir.clone(); 130 | crashes.push("crashes"); 131 | out_dir.push("queue"); 132 | 133 | let in_dir = PathBuf::from( 134 | res.get_one::("in") 135 | .expect("The --input parameter is missing") 136 | .to_string(), 137 | ); 138 | if !in_dir.is_dir() { 139 | println!("In dir at {:?} is not a valid directory!", &in_dir); 140 | return; 141 | } 142 | 143 | let tokens = res.get_one::("tokens").map(PathBuf::from); 144 | 145 | let timeout = Duration::from_millis( 146 | res.get_one::("timeout") 147 | .unwrap() 148 | .to_string() 149 | .parse() 150 | .expect("Could not parse timeout in milliseconds"), 151 | ); 152 | 153 | fuzz(out_dir, crashes, in_dir, tokens, timeout).expect("An error occurred while fuzzing"); 154 | } 155 | 156 | fn run_testcases(filenames: &[&str]) { 157 | // The actual target run starts here. 158 | // Call LLVMFUzzerInitialize() if present. 159 | let args: Vec = env::args().collect(); 160 | if unsafe { libfuzzer_initialize(&args) } == -1 { 161 | println!("Warning: LLVMFuzzerInitialize failed with -1") 162 | } 163 | 164 | println!( 165 | "You are not fuzzing, just executing {} testcases", 166 | filenames.len() 167 | ); 168 | for fname in filenames { 169 | println!("Executing {}", fname); 170 | 171 | let mut file = File::open(fname).expect("No file found"); 172 | let mut buffer = vec![]; 173 | file.read_to_end(&mut buffer).expect("Buffer overflow"); 174 | 175 | unsafe { libfuzzer_test_one_input(&buffer) }; 176 | } 177 | } 178 | 179 | /// The actual fuzzer 180 | fn fuzz( 181 | corpus_dir: PathBuf, 182 | objective_dir: PathBuf, 183 | seed_dir: PathBuf, 184 | tokenfile: Option, 185 | timeout: Duration, 186 | ) -> Result<(), Error> { 187 | #[cfg(unix)] 188 | let mut stdout_cpy = unsafe { 189 | let new_fd = dup(io::stdout().as_raw_fd())?; 190 | File::from_raw_fd(new_fd) 191 | }; 192 | #[cfg(unix)] 193 | let file_null = File::open("/dev/null")?; 194 | 195 | // 'While the monitor are state, they are usually used in the broker - which is likely never restarted 196 | let monitor = SimpleMonitor::new(|s| { 197 | #[cfg(unix)] 198 | writeln!(&mut stdout_cpy, "{}", s).unwrap(); 199 | #[cfg(windows)] 200 | println!("{}", s); 201 | }); 202 | 203 | // We need a shared map to store our state before a crash. 204 | // This way, we are able to continue fuzzing afterwards. 205 | let mut shmem_provider = StdShMemProvider::new()?; 206 | 207 | let (state, mut mgr) = match SimpleRestartingEventManager::launch(monitor, &mut shmem_provider) 208 | { 209 | // The restarting state will spawn the same process again as child, then restarted it each time it crashes. 210 | Ok(res) => res, 211 | Err(err) => match err { 212 | Error::ShuttingDown => { 213 | return Ok(()); 214 | } 215 | _ => { 216 | panic!("Failed to setup the restarter: {}", err); 217 | } 218 | }, 219 | }; 220 | 221 | // Create an observation channel using the coverage map 222 | // We don't use the hitcounts (see the Cargo.toml, we use pcguard_edges) 223 | let edges_observer = 224 | HitcountsMapObserver::new(unsafe { std_edges_map_observer("edges") }).track_indices(); 225 | 226 | // Create an observation channel to keep track of the execution time 227 | let time_observer = TimeObserver::new("time"); 228 | 229 | // Feedback to rate the interestingness of an input 230 | // This one is composed by two Feedbacks in OR 231 | let mut feedback = feedback_or!( 232 | // New maximization map feedback linked to the edges observer and the feedback state 233 | MaxMapFeedback::new(&edges_observer), 234 | // Time feedback, this one does not need a feedback state 235 | TimeFeedback::new(&time_observer) 236 | ); 237 | 238 | // A feedback to choose if an input is a solution or not 239 | let mut objective = CrashFeedback::new(); 240 | 241 | // If not restarting, create a State from scratch 242 | let mut state = state.unwrap_or_else(|| { 243 | StdState::new( 244 | // RNG 245 | StdRand::with_seed(current_nanos()), 246 | // Corpus that will be evolved, we keep it in memory for performance 247 | InMemoryOnDiskCorpus::new(corpus_dir).unwrap(), 248 | // Corpus in which we store solutions (crashes in this example), 249 | // on disk so the user can get them after stopping the fuzzer 250 | OnDiskCorpus::new(objective_dir).unwrap(), 251 | // States of the feedbacks. 252 | // They are the data related to the feedbacks that you want to persist in the State. 253 | &mut feedback, 254 | &mut objective, 255 | ) 256 | .unwrap() 257 | }); 258 | 259 | println!("Let's fuzz :)"); 260 | 261 | // The actual target run starts here. 262 | // Call LLVMFUzzerInitialize() if present. 263 | let args: Vec = env::args().collect(); 264 | if unsafe { libfuzzer_initialize(&args) } == -1 { 265 | println!("Warning: LLVMFuzzerInitialize failed with -1") 266 | } 267 | 268 | // Setup a MOPT mutator 269 | let mutator = StdMutationalStage::new(StdMOptMutator::new::( 270 | &mut state, 271 | havoc_mutations().merge(tokens_mutations()), 272 | 7, 273 | 5, 274 | )?); 275 | 276 | // A minimization+queue policy to get testcasess from the corpus 277 | let scheduler = IndexesLenTimeMinimizerScheduler::new(&edges_observer, QueueScheduler::new()); 278 | 279 | // A fuzzer with feedbacks and a corpus scheduler 280 | let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); 281 | 282 | // The wrapped harness function, calling out to the LLVM-style harness 283 | let mut harness = |input: &BytesInput| { 284 | let target = input.target_bytes(); 285 | let buf = target.as_slice(); 286 | unsafe { libfuzzer_test_one_input(buf) }; 287 | ExitKind::Ok 288 | }; 289 | 290 | // Create the executor for an in-process function with one observer for edge coverage and one for the execution time 291 | let mut executor = InProcessExecutor::with_timeout( 292 | &mut harness, 293 | tuple_list!(edges_observer, time_observer), 294 | &mut fuzzer, 295 | &mut state, 296 | &mut mgr, 297 | timeout, 298 | )?; 299 | 300 | // The order of the stages matter! 301 | let mut stages = tuple_list!(mutator); 302 | 303 | // Read tokens 304 | if state.metadata_map().get::().is_none() { 305 | let mut toks = Tokens::default(); 306 | if let Some(tokenfile) = tokenfile { 307 | toks.add_from_file(tokenfile)?; 308 | } 309 | #[cfg(target_os = "linux")] 310 | { 311 | toks += autotokens()?; 312 | } 313 | 314 | if !toks.is_empty() { 315 | state.add_metadata(toks); 316 | } 317 | } 318 | 319 | // In case the corpus is empty (on first run), reset 320 | if state.corpus().count() < 1 { 321 | state 322 | .load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &[seed_dir.clone()]) 323 | .unwrap_or_else(|_| { 324 | println!("Failed to load initial corpus at {:?}", &seed_dir); 325 | process::exit(0); 326 | }); 327 | println!("We imported {} inputs from disk.", state.corpus().count()); 328 | } 329 | 330 | // Remove target ouput (logs still survive) 331 | #[cfg(unix)] 332 | { 333 | let null_fd = file_null.as_raw_fd(); 334 | dup2(null_fd, io::stdout().as_raw_fd())?; 335 | dup2(null_fd, io::stderr().as_raw_fd())?; 336 | } 337 | 338 | fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)?; 339 | 340 | // Never reached 341 | Ok(()) 342 | } 343 | -------------------------------------------------------------------------------- /naive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "naive" 3 | version = "0.0.1" 4 | authors = ["Andrea Fioraldi ", "Dominik Maier "] 5 | edition = "2021" 6 | 7 | [features] 8 | no_link_main = ["libafl_targets/libfuzzer_no_link_main"] 9 | 10 | [dependencies] 11 | libafl = { path = "../LibAFL/libafl/" } 12 | libafl_bolts = { path = "../LibAFL/libafl_bolts/" } 13 | libafl_targets = { path = "../LibAFL/libafl_targets/", features = ["sancov_pcguard_hitcounts", "libfuzzer"] } 14 | # TODO Include it only when building cc 15 | libafl_cc = { path = "../LibAFL/libafl_cc/" } 16 | clap = { version = "~4.2", features = ["default"] } 17 | nix = { version = "0.29", features = ["fs"] } 18 | mimalloc = { version = "*", default-features = false } 19 | 20 | [lib] 21 | crate-type = ["staticlib"] 22 | -------------------------------------------------------------------------------- /naive/src/bin/naive_cc.rs: -------------------------------------------------------------------------------- 1 | use libafl_cc::ToolWrapper; 2 | use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses}; 3 | use std::env; 4 | pub fn main() { 5 | let args: Vec = env::args().collect(); 6 | if args.len() > 1 { 7 | let mut dir = env::current_exe().unwrap(); 8 | let wrapper_name = dir.file_name().unwrap().to_str().unwrap(); 9 | 10 | let is_cpp = match wrapper_name[wrapper_name.len()-2..].to_lowercase().as_str() { 11 | "cc" => false, 12 | "++" | "pp" | "xx" => true, 13 | _ => panic!("Could not figure out if c or c++ warpper was called. Expected {:?} to end with c or cxx", dir), 14 | }; 15 | 16 | dir.pop(); 17 | 18 | let mut cc = ClangWrapper::new(); 19 | 20 | #[cfg(target_os = "linux")] 21 | cc.add_pass(LLVMPasses::AutoTokens); 22 | 23 | if let Some(code) = cc 24 | .cpp(is_cpp) 25 | // silence the compiler wrapper output, needed for some configure scripts. 26 | .silence(true) 27 | // add arguments only if --libafl or --libafl-no-link are present 28 | .need_libafl_arg(true) 29 | .parse_args(&args) 30 | .expect("Failed to parse the command line") 31 | .link_staticlib(&dir, env!("CARGO_PKG_NAME")) 32 | .add_arg("-fsanitize-coverage=trace-pc-guard") 33 | .run() 34 | .expect("Failed to run the wrapped compiler") 35 | { 36 | std::process::exit(code); 37 | } 38 | } else { 39 | panic!("LibAFL CC: No Arguments given"); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /naive/src/bin/naive_cxx.rs: -------------------------------------------------------------------------------- 1 | pub mod naive_cc; 2 | 3 | fn main() { 4 | naive_cc::main() 5 | } 6 | -------------------------------------------------------------------------------- /naive/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A singlethreaded libfuzzer-like fuzzer that can auto-restart. 2 | use mimalloc::MiMalloc; 3 | #[global_allocator] 4 | static GLOBAL: MiMalloc = MiMalloc; 5 | use libafl::observers::CanTrack; 6 | use libafl::HasMetadata; 7 | use libafl_bolts::{ 8 | current_nanos, 9 | os::dup2, 10 | rands::StdRand, 11 | shmem::{ShMemProvider, StdShMemProvider}, 12 | tuples::{tuple_list, Merge}, 13 | AsSlice, 14 | }; 15 | 16 | use clap::{Arg, Command}; 17 | use core::time::Duration; 18 | #[cfg(unix)] 19 | use nix::{self, unistd::dup}; 20 | #[cfg(unix)] 21 | use std::os::unix::io::{AsRawFd, FromRawFd}; 22 | use std::{ 23 | env, 24 | fs::{self, File}, 25 | io::{self, Read, Write}, 26 | path::PathBuf, 27 | process, 28 | }; 29 | 30 | use libafl::{ 31 | corpus::{Corpus, InMemoryOnDiskCorpus, OnDiskCorpus}, 32 | events::SimpleRestartingEventManager, 33 | executors::{inprocess::InProcessExecutor, ExitKind}, 34 | feedback_or, 35 | feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback}, 36 | fuzzer::{Fuzzer, StdFuzzer}, 37 | inputs::{BytesInput, HasTargetBytes}, 38 | monitors::SimpleMonitor, 39 | mutators::{havoc_mutations::havoc_mutations, tokens_mutations, StdScheduledMutator, Tokens}, 40 | observers::{HitcountsMapObserver, TimeObserver}, 41 | schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler}, 42 | stages::StdMutationalStage, 43 | state::{HasCorpus, StdState}, 44 | Error, 45 | }; 46 | use libafl_targets::{libfuzzer_initialize, libfuzzer_test_one_input, std_edges_map_observer}; 47 | 48 | #[cfg(target_os = "linux")] 49 | use libafl_targets::autotokens; 50 | 51 | /// The fuzzer main (as `no_mangle` C function) 52 | #[no_mangle] 53 | pub fn libafl_main() { 54 | // Registry the metadata types used in this fuzzer 55 | // Needed only on no_std 56 | //RegistryBuilder::register::(); 57 | 58 | let res = match Command::new(env!("CARGO_PKG_NAME")) 59 | .version(env!("CARGO_PKG_VERSION")) 60 | .author("AFLplusplus team") 61 | .about("LibAFL-based fuzzer for Fuzzbench") 62 | .arg( 63 | Arg::new("out") 64 | .short('o') 65 | .long("output") 66 | .help("The directory to place finds in ('corpus')"), 67 | ) 68 | .arg( 69 | Arg::new("in") 70 | .short('i') 71 | .long("input") 72 | .help("The directory to read initial inputs from ('seeds')"), 73 | ) 74 | .arg( 75 | Arg::new("tokens") 76 | .short('x') 77 | .long("tokens") 78 | .help("A file to read tokens from, to be used during fuzzing"), 79 | ) 80 | .arg( 81 | Arg::new("timeout") 82 | .short('t') 83 | .long("timeout") 84 | .help("Timeout for each individual execution, in milliseconds") 85 | .default_value("1200"), 86 | ) 87 | .arg(Arg::new("remaining")) 88 | .try_get_matches() 89 | { 90 | Ok(res) => res, 91 | Err(err) => { 92 | println!( 93 | "Syntax: {}, [-x dictionary] -o corpus_dir -i seed_dir\n{:?}", 94 | env::current_exe() 95 | .unwrap_or_else(|_| "fuzzer".into()) 96 | .to_string_lossy(), 97 | err, 98 | ); 99 | return; 100 | } 101 | }; 102 | 103 | println!( 104 | "Workdir: {:?}", 105 | env::current_dir().unwrap().to_string_lossy().to_string() 106 | ); 107 | 108 | if let Some(filenames) = res.get_many::("remaining") { 109 | let filenames: Vec<&str> = filenames.map(String::as_str).collect(); 110 | if !filenames.is_empty() { 111 | run_testcases(&filenames); 112 | return; 113 | } 114 | } 115 | 116 | // For fuzzbench, crashes and finds are inside the same `corpus` directory, in the "queue" and "crashes" subdir. 117 | let mut out_dir = PathBuf::from( 118 | res.get_one::("out") 119 | .expect("The --output parameter is missing") 120 | .to_string(), 121 | ); 122 | if fs::create_dir(&out_dir).is_err() { 123 | println!("Out dir at {:?} already exists.", &out_dir); 124 | if !out_dir.is_dir() { 125 | println!("Out dir at {:?} is not a valid directory!", &out_dir); 126 | return; 127 | } 128 | } 129 | let mut crashes = out_dir.clone(); 130 | crashes.push("crashes"); 131 | out_dir.push("queue"); 132 | 133 | let in_dir = PathBuf::from( 134 | res.get_one::("in") 135 | .expect("The --input parameter is missing") 136 | .to_string(), 137 | ); 138 | if !in_dir.is_dir() { 139 | println!("In dir at {:?} is not a valid directory!", &in_dir); 140 | return; 141 | } 142 | 143 | let tokens = res.get_one::("tokens").map(PathBuf::from); 144 | 145 | let timeout = Duration::from_millis( 146 | res.get_one::("timeout") 147 | .unwrap() 148 | .to_string() 149 | .parse() 150 | .expect("Could not parse timeout in milliseconds"), 151 | ); 152 | 153 | fuzz(out_dir, crashes, in_dir, tokens, timeout).expect("An error occurred while fuzzing"); 154 | } 155 | 156 | fn run_testcases(filenames: &[&str]) { 157 | // The actual target run starts here. 158 | // Call LLVMFUzzerInitialize() if present. 159 | let args: Vec = env::args().collect(); 160 | if unsafe { libfuzzer_initialize(&args) } == -1 { 161 | println!("Warning: LLVMFuzzerInitialize failed with -1") 162 | } 163 | 164 | println!( 165 | "You are not fuzzing, just executing {} testcases", 166 | filenames.len() 167 | ); 168 | for fname in filenames { 169 | println!("Executing {}", fname); 170 | 171 | let mut file = File::open(fname).expect("No file found"); 172 | let mut buffer = vec![]; 173 | file.read_to_end(&mut buffer).expect("Buffer overflow"); 174 | 175 | unsafe { libfuzzer_test_one_input(&buffer) }; 176 | } 177 | } 178 | 179 | /// The actual fuzzer 180 | fn fuzz( 181 | corpus_dir: PathBuf, 182 | objective_dir: PathBuf, 183 | seed_dir: PathBuf, 184 | tokenfile: Option, 185 | timeout: Duration, 186 | ) -> Result<(), Error> { 187 | #[cfg(unix)] 188 | let mut stdout_cpy = unsafe { 189 | let new_fd = dup(io::stdout().as_raw_fd())?; 190 | File::from_raw_fd(new_fd) 191 | }; 192 | #[cfg(unix)] 193 | let file_null = File::open("/dev/null")?; 194 | 195 | // 'While the monitor are state, they are usually used in the broker - which is likely never restarted 196 | let monitor = SimpleMonitor::new(|s| { 197 | #[cfg(unix)] 198 | writeln!(&mut stdout_cpy, "{}", s).unwrap(); 199 | #[cfg(windows)] 200 | println!("{}", s); 201 | }); 202 | 203 | // We need a shared map to store our state before a crash. 204 | // This way, we are able to continue fuzzing afterwards. 205 | let mut shmem_provider = StdShMemProvider::new()?; 206 | 207 | let (state, mut mgr) = match SimpleRestartingEventManager::launch(monitor, &mut shmem_provider) 208 | { 209 | // The restarting state will spawn the same process again as child, then restarted it each time it crashes. 210 | Ok(res) => res, 211 | Err(err) => match err { 212 | Error::ShuttingDown => { 213 | return Ok(()); 214 | } 215 | _ => { 216 | panic!("Failed to setup the restarter: {}", err); 217 | } 218 | }, 219 | }; 220 | 221 | // Create an observation channel using the coverage map 222 | // We don't use the hitcounts (see the Cargo.toml, we use pcguard_edges) 223 | let edges_observer = 224 | HitcountsMapObserver::new(unsafe { std_edges_map_observer("edges") }).track_indices(); 225 | 226 | // Create an observation channel to keep track of the execution time 227 | let time_observer = TimeObserver::new("time"); 228 | 229 | // Feedback to rate the interestingness of an input 230 | // This one is composed by two Feedbacks in OR 231 | let mut feedback = feedback_or!( 232 | // New maximization map feedback linked to the edges observer and the feedback state 233 | MaxMapFeedback::new(&edges_observer), 234 | // Time feedback, this one does not need a feedback state 235 | TimeFeedback::new(&time_observer) 236 | ); 237 | 238 | // A feedback to choose if an input is a solution or not 239 | let mut objective = CrashFeedback::new(); 240 | 241 | // If not restarting, create a State from scratch 242 | let mut state = state.unwrap_or_else(|| { 243 | StdState::new( 244 | // RNG 245 | StdRand::with_seed(current_nanos()), 246 | // Corpus that will be evolved, we keep it in memory for performance 247 | InMemoryOnDiskCorpus::new(corpus_dir).unwrap(), 248 | // Corpus in which we store solutions (crashes in this example), 249 | // on disk so the user can get them after stopping the fuzzer 250 | OnDiskCorpus::new(objective_dir).unwrap(), 251 | // States of the feedbacks. 252 | // They are the data related to the feedbacks that you want to persist in the State. 253 | &mut feedback, 254 | &mut objective, 255 | ) 256 | .unwrap() 257 | }); 258 | 259 | println!("Let's fuzz :)"); 260 | 261 | // The actual target run starts here. 262 | // Call LLVMFUzzerInitialize() if present. 263 | let args: Vec = env::args().collect(); 264 | if unsafe { libfuzzer_initialize(&args) } == -1 { 265 | println!("Warning: LLVMFuzzerInitialize failed with -1") 266 | } 267 | 268 | let mutator = StdMutationalStage::new(StdScheduledMutator::new( 269 | havoc_mutations().merge(tokens_mutations()), 270 | )); 271 | 272 | // A minimization+queue policy to get testcasess from the corpus 273 | let scheduler = IndexesLenTimeMinimizerScheduler::new(&edges_observer, QueueScheduler::new()); 274 | 275 | // A fuzzer with feedbacks and a corpus scheduler 276 | let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); 277 | 278 | // The wrapped harness function, calling out to the LLVM-style harness 279 | let mut harness = |input: &BytesInput| { 280 | let target = input.target_bytes(); 281 | let buf = target.as_slice(); 282 | unsafe { libfuzzer_test_one_input(buf) }; 283 | ExitKind::Ok 284 | }; 285 | 286 | // Create the executor for an in-process function with one observer for edge coverage and one for the execution time 287 | let mut executor = InProcessExecutor::with_timeout( 288 | &mut harness, 289 | tuple_list!(edges_observer, time_observer), 290 | &mut fuzzer, 291 | &mut state, 292 | &mut mgr, 293 | timeout, 294 | )?; 295 | 296 | // The order of the stages matter! 297 | let mut stages = tuple_list!(mutator); 298 | 299 | // Read tokens 300 | if state.metadata_map().get::().is_none() { 301 | let mut toks = Tokens::default(); 302 | if let Some(tokenfile) = tokenfile { 303 | toks.add_from_file(tokenfile)?; 304 | } 305 | #[cfg(target_os = "linux")] 306 | { 307 | toks += autotokens()?; 308 | } 309 | 310 | if !toks.is_empty() { 311 | state.add_metadata(toks); 312 | } 313 | } 314 | 315 | // In case the corpus is empty (on first run), reset 316 | if state.corpus().count() < 1 { 317 | state 318 | .load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &[seed_dir.clone()]) 319 | .unwrap_or_else(|_| { 320 | println!("Failed to load initial corpus at {:?}", &seed_dir); 321 | process::exit(0); 322 | }); 323 | println!("We imported {} inputs from disk.", state.corpus().count()); 324 | } 325 | 326 | // Remove target ouput (logs still survive) 327 | #[cfg(unix)] 328 | { 329 | let null_fd = file_null.as_raw_fd(); 330 | dup2(null_fd, io::stdout().as_raw_fd())?; 331 | dup2(null_fd, io::stderr().as_raw_fd())?; 332 | } 333 | 334 | fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)?; 335 | 336 | // Never reached 337 | Ok(()) 338 | } 339 | -------------------------------------------------------------------------------- /naive_ctx/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "naive_ctx" 3 | version = "0.0.1" 4 | authors = ["Andrea Fioraldi ", "Dominik Maier "] 5 | edition = "2021" 6 | 7 | [features] 8 | no_link_main = ["libafl_targets/libfuzzer_no_link_main"] 9 | 10 | [dependencies] 11 | libafl = { path = "../LibAFL/libafl/" } 12 | libafl_targets = { path = "../LibAFL/libafl_targets/", features = ["libfuzzer", "sancov_pcguard_hitcounts", "sancov_ctx"] } 13 | libafl_bolts = { path = "../LibAFL/libafl_bolts/" } 14 | # TODO Include it only when building cc 15 | libafl_cc = { path = "../LibAFL/libafl_cc/" } 16 | clap = { version = "~4.2", features = ["default"] } 17 | nix = { version = "0.29", features = ["fs"] } 18 | mimalloc = { version = "*", default-features = false } 19 | 20 | [lib] 21 | crate-type = ["staticlib"] 22 | -------------------------------------------------------------------------------- /naive_ctx/src/bin/naive_ctx_cc.rs: -------------------------------------------------------------------------------- 1 | use libafl_cc::ToolWrapper; 2 | use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses}; 3 | use std::env; 4 | pub fn main() { 5 | let args: Vec = env::args().collect(); 6 | if args.len() > 1 { 7 | let mut dir = env::current_exe().unwrap(); 8 | let wrapper_name = dir.file_name().unwrap().to_str().unwrap(); 9 | 10 | let is_cpp = match wrapper_name[wrapper_name.len()-2..].to_lowercase().as_str() { 11 | "cc" => false, 12 | "++" | "pp" | "xx" => true, 13 | _ => panic!("Could not figure out if c or c++ warpper was called. Expected {:?} to end with c or cxx", dir), 14 | }; 15 | 16 | dir.pop(); 17 | 18 | let mut cc = ClangWrapper::new(); 19 | 20 | #[cfg(target_os = "linux")] 21 | cc.add_pass(LLVMPasses::AutoTokens); 22 | 23 | if let Some(code) = cc 24 | .cpp(is_cpp) 25 | // silence the compiler wrapper output, needed for some configure scripts. 26 | .silence(true) 27 | // add arguments only if --libafl or --libafl-no-link are present 28 | .need_libafl_arg(true) 29 | .parse_args(&args) 30 | .expect("Failed to parse the command line") 31 | .link_staticlib(&dir, env!("CARGO_PKG_NAME")) 32 | .add_passes_linking_arg("-lm") 33 | .add_pass(LLVMPasses::Ctx) 34 | .add_arg("-fsanitize-coverage=trace-pc-guard") 35 | .run() 36 | .expect("Failed to run the wrapped compiler") 37 | { 38 | std::process::exit(code); 39 | } 40 | } else { 41 | panic!("LibAFL CC: No Arguments given"); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /naive_ctx/src/bin/naive_ctx_cxx.rs: -------------------------------------------------------------------------------- 1 | pub mod naive_ctx_cc; 2 | 3 | fn main() { 4 | naive_ctx_cc::main() 5 | } 6 | -------------------------------------------------------------------------------- /naive_ctx/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A singlethreaded libfuzzer-like fuzzer that can auto-restart. 2 | use mimalloc::MiMalloc; 3 | #[global_allocator] 4 | static GLOBAL: MiMalloc = MiMalloc; 5 | use libafl::observers::CanTrack; 6 | use libafl::HasMetadata; 7 | use libafl_bolts::{ 8 | current_nanos, 9 | os::dup2, 10 | rands::StdRand, 11 | shmem::{ShMemProvider, StdShMemProvider}, 12 | tuples::{tuple_list, Merge}, 13 | AsSlice, 14 | }; 15 | 16 | use clap::{Arg, Command}; 17 | use core::time::Duration; 18 | #[cfg(unix)] 19 | use nix::{self, unistd::dup}; 20 | #[cfg(unix)] 21 | use std::os::unix::io::{AsRawFd, FromRawFd}; 22 | use std::{ 23 | env, 24 | fs::{self, File}, 25 | io::{self, Read, Write}, 26 | path::PathBuf, 27 | process, 28 | }; 29 | 30 | use libafl::{ 31 | corpus::{Corpus, InMemoryOnDiskCorpus, OnDiskCorpus}, 32 | events::SimpleRestartingEventManager, 33 | executors::{inprocess::HookableInProcessExecutor, ExitKind}, 34 | feedback_or, 35 | feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback}, 36 | fuzzer::{Fuzzer, StdFuzzer}, 37 | inputs::{BytesInput, HasTargetBytes}, 38 | monitors::SimpleMonitor, 39 | mutators::{havoc_mutations::havoc_mutations, tokens_mutations, StdScheduledMutator, Tokens}, 40 | observers::{HitcountsMapObserver, TimeObserver}, 41 | schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler}, 42 | stages::StdMutationalStage, 43 | state::{HasCorpus, StdState}, 44 | Error, 45 | }; 46 | use libafl_targets::{ 47 | libfuzzer_initialize, libfuzzer_test_one_input, std_edges_map_observer, CtxHook, 48 | }; 49 | 50 | #[cfg(target_os = "linux")] 51 | use libafl_targets::autotokens; 52 | 53 | /// The fuzzer main (as `no_mangle` C function) 54 | #[no_mangle] 55 | pub fn libafl_main() { 56 | // Registry the metadata types used in this fuzzer 57 | // Needed only on no_std 58 | //RegistryBuilder::register::(); 59 | 60 | let res = match Command::new(env!("CARGO_PKG_NAME")) 61 | .version(env!("CARGO_PKG_VERSION")) 62 | .author("AFLplusplus team") 63 | .about("LibAFL-based fuzzer for Fuzzbench") 64 | .arg( 65 | Arg::new("out") 66 | .short('o') 67 | .long("output") 68 | .help("The directory to place finds in ('corpus')"), 69 | ) 70 | .arg( 71 | Arg::new("in") 72 | .short('i') 73 | .long("input") 74 | .help("The directory to read initial inputs from ('seeds')"), 75 | ) 76 | .arg( 77 | Arg::new("tokens") 78 | .short('x') 79 | .long("tokens") 80 | .help("A file to read tokens from, to be used during fuzzing"), 81 | ) 82 | .arg( 83 | Arg::new("timeout") 84 | .short('t') 85 | .long("timeout") 86 | .help("Timeout for each individual execution, in milliseconds") 87 | .default_value("1200"), 88 | ) 89 | .arg(Arg::new("remaining")) 90 | .try_get_matches() 91 | { 92 | Ok(res) => res, 93 | Err(err) => { 94 | println!( 95 | "Syntax: {}, [-x dictionary] -o corpus_dir -i seed_dir\n{:?}", 96 | env::current_exe() 97 | .unwrap_or_else(|_| "fuzzer".into()) 98 | .to_string_lossy(), 99 | err, 100 | ); 101 | return; 102 | } 103 | }; 104 | 105 | println!( 106 | "Workdir: {:?}", 107 | env::current_dir().unwrap().to_string_lossy().to_string() 108 | ); 109 | 110 | if let Some(filenames) = res.get_many::("remaining") { 111 | let filenames: Vec<&str> = filenames.map(String::as_str).collect(); 112 | if !filenames.is_empty() { 113 | run_testcases(&filenames); 114 | return; 115 | } 116 | } 117 | 118 | // For fuzzbench, crashes and finds are inside the same `corpus` directory, in the "queue" and "crashes" subdir. 119 | let mut out_dir = PathBuf::from( 120 | res.get_one::("out") 121 | .expect("The --output parameter is missing") 122 | .to_string(), 123 | ); 124 | if fs::create_dir(&out_dir).is_err() { 125 | println!("Out dir at {:?} already exists.", &out_dir); 126 | if !out_dir.is_dir() { 127 | println!("Out dir at {:?} is not a valid directory!", &out_dir); 128 | return; 129 | } 130 | } 131 | let mut crashes = out_dir.clone(); 132 | crashes.push("crashes"); 133 | out_dir.push("queue"); 134 | 135 | let in_dir = PathBuf::from( 136 | res.get_one::("in") 137 | .expect("The --input parameter is missing") 138 | .to_string(), 139 | ); 140 | if !in_dir.is_dir() { 141 | println!("In dir at {:?} is not a valid directory!", &in_dir); 142 | return; 143 | } 144 | 145 | let tokens = res.get_one::("tokens").map(PathBuf::from); 146 | 147 | let timeout = Duration::from_millis( 148 | res.get_one::("timeout") 149 | .unwrap() 150 | .to_string() 151 | .parse() 152 | .expect("Could not parse timeout in milliseconds"), 153 | ); 154 | 155 | fuzz(out_dir, crashes, in_dir, tokens, timeout).expect("An error occurred while fuzzing"); 156 | } 157 | 158 | fn run_testcases(filenames: &[&str]) { 159 | // The actual target run starts here. 160 | // Call LLVMFUzzerInitialize() if present. 161 | let args: Vec = env::args().collect(); 162 | if unsafe { libfuzzer_initialize(&args) } == -1 { 163 | println!("Warning: LLVMFuzzerInitialize failed with -1") 164 | } 165 | 166 | println!( 167 | "You are not fuzzing, just executing {} testcases", 168 | filenames.len() 169 | ); 170 | for fname in filenames { 171 | println!("Executing {}", fname); 172 | 173 | let mut file = File::open(fname).expect("No file found"); 174 | let mut buffer = vec![]; 175 | file.read_to_end(&mut buffer).expect("Buffer overflow"); 176 | 177 | unsafe { libfuzzer_test_one_input(&buffer) }; 178 | } 179 | } 180 | 181 | /// The actual fuzzer 182 | fn fuzz( 183 | corpus_dir: PathBuf, 184 | objective_dir: PathBuf, 185 | seed_dir: PathBuf, 186 | tokenfile: Option, 187 | timeout: Duration, 188 | ) -> Result<(), Error> { 189 | #[cfg(unix)] 190 | let mut stdout_cpy = unsafe { 191 | let new_fd = dup(io::stdout().as_raw_fd())?; 192 | File::from_raw_fd(new_fd) 193 | }; 194 | #[cfg(unix)] 195 | let file_null = File::open("/dev/null")?; 196 | 197 | // 'While the monitor are state, they are usually used in the broker - which is likely never restarted 198 | let monitor = SimpleMonitor::new(|s| { 199 | #[cfg(unix)] 200 | writeln!(&mut stdout_cpy, "{}", s).unwrap(); 201 | #[cfg(windows)] 202 | println!("{}", s); 203 | }); 204 | 205 | // We need a shared map to store our state before a crash. 206 | // This way, we are able to continue fuzzing afterwards. 207 | let mut shmem_provider = StdShMemProvider::new()?; 208 | 209 | let (state, mut mgr) = match SimpleRestartingEventManager::launch(monitor, &mut shmem_provider) 210 | { 211 | // The restarting state will spawn the same process again as child, then restarted it each time it crashes. 212 | Ok(res) => res, 213 | Err(err) => match err { 214 | Error::ShuttingDown => { 215 | return Ok(()); 216 | } 217 | _ => { 218 | panic!("Failed to setup the restarter: {}", err); 219 | } 220 | }, 221 | }; 222 | // Create an observation channel using the coverage map 223 | // We don't use the hitcounts (see the Cargo.toml, we use pcguard_edges) 224 | let edges_observer = 225 | HitcountsMapObserver::new(unsafe { std_edges_map_observer("edges") }).track_indices(); 226 | 227 | // Create an observation channel to keep track of the execution time 228 | let time_observer = TimeObserver::new("time"); 229 | 230 | // Feedback to rate the interestingness of an input 231 | // This one is composed by two Feedbacks in OR 232 | let mut feedback = feedback_or!( 233 | // New maximization map feedback linked to the edges observer and the feedback state 234 | MaxMapFeedback::new(&edges_observer), 235 | // Time feedback, this one does not need a feedback state 236 | TimeFeedback::new(&time_observer) 237 | ); 238 | 239 | // A feedback to choose if an input is a solution or not 240 | let mut objective = CrashFeedback::new(); 241 | 242 | // If not restarting, create a State from scratch 243 | let mut state = state.unwrap_or_else(|| { 244 | StdState::new( 245 | // RNG 246 | StdRand::with_seed(current_nanos()), 247 | // Corpus that will be evolved, we keep it in memory for performance 248 | InMemoryOnDiskCorpus::new(corpus_dir).unwrap(), 249 | // Corpus in which we store solutions (crashes in this example), 250 | // on disk so the user can get them after stopping the fuzzer 251 | OnDiskCorpus::new(objective_dir).unwrap(), 252 | // States of the feedbacks. 253 | // They are the data related to the feedbacks that you want to persist in the State. 254 | &mut feedback, 255 | &mut objective, 256 | ) 257 | .unwrap() 258 | }); 259 | 260 | println!("Let's fuzz :)"); 261 | 262 | // The actual target run starts here. 263 | // Call LLVMFUzzerInitialize() if present. 264 | let args: Vec = env::args().collect(); 265 | if unsafe { libfuzzer_initialize(&args) } == -1 { 266 | println!("Warning: LLVMFuzzerInitialize failed with -1") 267 | } 268 | 269 | let mutator = StdMutationalStage::new(StdScheduledMutator::new( 270 | havoc_mutations().merge(tokens_mutations()), 271 | )); 272 | 273 | // A minimization+queue policy to get testcasess from the corpus 274 | let scheduler = IndexesLenTimeMinimizerScheduler::new(&edges_observer, QueueScheduler::new()); 275 | 276 | // A fuzzer with feedbacks and a corpus scheduler 277 | let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); 278 | 279 | // The wrapped harness function, calling out to the LLVM-style harness 280 | let mut harness = |input: &BytesInput| { 281 | let target = input.target_bytes(); 282 | let buf = target.as_slice(); 283 | unsafe { libfuzzer_test_one_input(buf) }; 284 | ExitKind::Ok 285 | }; 286 | let ctx_hook = CtxHook::new(); 287 | // Create the executor for an in-process function with one observer for edge coverage and one for the execution time 288 | let mut executor = HookableInProcessExecutor::with_timeout_generic( 289 | tuple_list!(ctx_hook), 290 | &mut harness, 291 | tuple_list!(edges_observer, time_observer), 292 | &mut fuzzer, 293 | &mut state, 294 | &mut mgr, 295 | timeout, 296 | )?; 297 | 298 | // The order of the stages matter! 299 | let mut stages = tuple_list!(mutator); 300 | 301 | // Read tokens 302 | if state.metadata_map().get::().is_none() { 303 | let mut toks = Tokens::default(); 304 | if let Some(tokenfile) = tokenfile { 305 | toks.add_from_file(tokenfile)?; 306 | } 307 | #[cfg(target_os = "linux")] 308 | { 309 | toks += autotokens()?; 310 | } 311 | 312 | if !toks.is_empty() { 313 | state.add_metadata(toks); 314 | } 315 | } 316 | 317 | // In case the corpus is empty (on first run), reset 318 | if state.corpus().count() < 1 { 319 | state 320 | .load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &[seed_dir.clone()]) 321 | .unwrap_or_else(|_| { 322 | println!("Failed to load initial corpus at {:?}", &seed_dir); 323 | process::exit(0); 324 | }); 325 | println!("We imported {} inputs from disk.", state.corpus().count()); 326 | } 327 | 328 | // Remove target ouput (logs still survive) 329 | #[cfg(unix)] 330 | { 331 | let null_fd = file_null.as_raw_fd(); 332 | dup2(null_fd, io::stdout().as_raw_fd())?; 333 | dup2(null_fd, io::stderr().as_raw_fd())?; 334 | } 335 | 336 | fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)?; 337 | 338 | // Never reached 339 | Ok(()) 340 | } 341 | -------------------------------------------------------------------------------- /naive_ngram4/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "naive_ngram4" 3 | version = "0.0.1" 4 | authors = ["Andrea Fioraldi ", "Dominik Maier "] 5 | edition = "2021" 6 | 7 | [features] 8 | no_link_main = ["libafl_targets/libfuzzer_no_link_main"] 9 | 10 | [dependencies] 11 | libafl = { path = "../LibAFL/libafl/" } 12 | libafl_targets = { path = "../LibAFL/libafl_targets/", features = ["sancov_pcguard_hitcounts", "libfuzzer", "sancov_ngram4"] } 13 | libafl_bolts = { path = "../LibAFL/libafl_bolts/" } 14 | # TODO Include it only when building cc 15 | libafl_cc = { path = "../LibAFL/libafl_cc/" } 16 | clap = { version = "~4.2", features = ["default"] } 17 | nix = { version = "0.29", features = ["fs"] } 18 | mimalloc = { version = "*", default-features = false } 19 | 20 | [lib] 21 | crate-type = ["staticlib"] 22 | -------------------------------------------------------------------------------- /naive_ngram4/src/bin/naive_ngram4_cc.rs: -------------------------------------------------------------------------------- 1 | use libafl_cc::ToolWrapper; 2 | use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses}; 3 | use std::env; 4 | pub fn main() { 5 | let args: Vec = env::args().collect(); 6 | if args.len() > 1 { 7 | let mut dir = env::current_exe().unwrap(); 8 | let wrapper_name = dir.file_name().unwrap().to_str().unwrap(); 9 | 10 | let is_cpp = match wrapper_name[wrapper_name.len()-2..].to_lowercase().as_str() { 11 | "cc" => false, 12 | "++" | "pp" | "xx" => true, 13 | _ => panic!("Could not figure out if c or c++ warpper was called. Expected {:?} to end with c or cxx", dir), 14 | }; 15 | 16 | dir.pop(); 17 | 18 | let mut cc = ClangWrapper::new(); 19 | 20 | #[cfg(target_os = "linux")] 21 | cc.add_pass(LLVMPasses::AutoTokens); 22 | 23 | if let Some(code) = cc 24 | .cpp(is_cpp) 25 | // silence the compiler wrapper output, needed for some configure scripts. 26 | .silence(true) 27 | // add arguments only if --libafl or --libafl-no-link are present 28 | .need_libafl_arg(true) 29 | .parse_args(&args) 30 | .expect("Failed to parse the command line") 31 | .link_staticlib(&dir, env!("CARGO_PKG_NAME")) 32 | .add_arg("-fsanitize-coverage=trace-pc-guard") 33 | .add_passes_linking_arg("-lm") 34 | .run() 35 | .expect("Failed to run the wrapped compiler") 36 | { 37 | std::process::exit(code); 38 | } 39 | } else { 40 | panic!("LibAFL CC: No Arguments given"); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /naive_ngram4/src/bin/naive_ngram4_cxx.rs: -------------------------------------------------------------------------------- 1 | pub mod naive_ngram4_cc; 2 | 3 | fn main() { 4 | naive_ngram4_cc::main() 5 | } 6 | -------------------------------------------------------------------------------- /naive_ngram4/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A singlethreaded libfuzzer-like fuzzer that can auto-restart. 2 | use mimalloc::MiMalloc; 3 | #[global_allocator] 4 | static GLOBAL: MiMalloc = MiMalloc; 5 | use libafl::observers::CanTrack; 6 | use libafl::HasMetadata; 7 | use libafl_bolts::{ 8 | current_nanos, 9 | os::dup2, 10 | rands::StdRand, 11 | shmem::{ShMemProvider, StdShMemProvider}, 12 | tuples::{tuple_list, Merge}, 13 | AsSlice, 14 | }; 15 | 16 | use clap::{Arg, Command}; 17 | use core::time::Duration; 18 | #[cfg(unix)] 19 | use nix::{self, unistd::dup}; 20 | #[cfg(unix)] 21 | use std::os::unix::io::{AsRawFd, FromRawFd}; 22 | use std::{ 23 | env, 24 | fs::{self, File}, 25 | io::{self, Read, Write}, 26 | path::PathBuf, 27 | process, 28 | }; 29 | 30 | use libafl::{ 31 | corpus::{Corpus, InMemoryOnDiskCorpus, OnDiskCorpus}, 32 | events::SimpleRestartingEventManager, 33 | executors::{inprocess::HookableInProcessExecutor, ExitKind}, 34 | feedback_or, 35 | feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback}, 36 | fuzzer::{Fuzzer, StdFuzzer}, 37 | inputs::{BytesInput, HasTargetBytes}, 38 | monitors::SimpleMonitor, 39 | mutators::{havoc_mutations::havoc_mutations, tokens_mutations, StdScheduledMutator, Tokens}, 40 | observers::{HitcountsMapObserver, TimeObserver}, 41 | schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler}, 42 | stages::StdMutationalStage, 43 | state::{HasCorpus, StdState}, 44 | Error, 45 | }; 46 | use libafl_targets::{ 47 | libfuzzer_initialize, libfuzzer_test_one_input, std_edges_map_observer, NgramHook, 48 | }; 49 | 50 | #[cfg(target_os = "linux")] 51 | use libafl_targets::autotokens; 52 | 53 | /// The fuzzer main (as `no_mangle` C function) 54 | #[no_mangle] 55 | pub fn libafl_main() { 56 | // Registry the metadata types used in this fuzzer 57 | // Needed only on no_std 58 | //RegistryBuilder::register::(); 59 | 60 | let res = match Command::new(env!("CARGO_PKG_NAME")) 61 | .version(env!("CARGO_PKG_VERSION")) 62 | .author("AFLplusplus team") 63 | .about("LibAFL-based fuzzer for Fuzzbench") 64 | .arg( 65 | Arg::new("out") 66 | .short('o') 67 | .long("output") 68 | .help("The directory to place finds in ('corpus')"), 69 | ) 70 | .arg( 71 | Arg::new("in") 72 | .short('i') 73 | .long("input") 74 | .help("The directory to read initial inputs from ('seeds')"), 75 | ) 76 | .arg( 77 | Arg::new("tokens") 78 | .short('x') 79 | .long("tokens") 80 | .help("A file to read tokens from, to be used during fuzzing"), 81 | ) 82 | .arg( 83 | Arg::new("timeout") 84 | .short('t') 85 | .long("timeout") 86 | .help("Timeout for each individual execution, in milliseconds") 87 | .default_value("1200"), 88 | ) 89 | .arg(Arg::new("remaining")) 90 | .try_get_matches() 91 | { 92 | Ok(res) => res, 93 | Err(err) => { 94 | println!( 95 | "Syntax: {}, [-x dictionary] -o corpus_dir -i seed_dir\n{:?}", 96 | env::current_exe() 97 | .unwrap_or_else(|_| "fuzzer".into()) 98 | .to_string_lossy(), 99 | err, 100 | ); 101 | return; 102 | } 103 | }; 104 | 105 | println!( 106 | "Workdir: {:?}", 107 | env::current_dir().unwrap().to_string_lossy().to_string() 108 | ); 109 | 110 | if let Some(filenames) = res.get_many::("remaining") { 111 | let filenames: Vec<&str> = filenames.map(String::as_str).collect(); 112 | if !filenames.is_empty() { 113 | run_testcases(&filenames); 114 | return; 115 | } 116 | } 117 | 118 | // For fuzzbench, crashes and finds are inside the same `corpus` directory, in the "queue" and "crashes" subdir. 119 | let mut out_dir = PathBuf::from( 120 | res.get_one::("out") 121 | .expect("The --output parameter is missing") 122 | .to_string(), 123 | ); 124 | if fs::create_dir(&out_dir).is_err() { 125 | println!("Out dir at {:?} already exists.", &out_dir); 126 | if !out_dir.is_dir() { 127 | println!("Out dir at {:?} is not a valid directory!", &out_dir); 128 | return; 129 | } 130 | } 131 | let mut crashes = out_dir.clone(); 132 | crashes.push("crashes"); 133 | out_dir.push("queue"); 134 | 135 | let in_dir = PathBuf::from( 136 | res.get_one::("in") 137 | .expect("The --input parameter is missing") 138 | .to_string(), 139 | ); 140 | if !in_dir.is_dir() { 141 | println!("In dir at {:?} is not a valid directory!", &in_dir); 142 | return; 143 | } 144 | 145 | let tokens = res.get_one::("tokens").map(PathBuf::from); 146 | 147 | let timeout = Duration::from_millis( 148 | res.get_one::("timeout") 149 | .unwrap() 150 | .to_string() 151 | .parse() 152 | .expect("Could not parse timeout in milliseconds"), 153 | ); 154 | 155 | fuzz(out_dir, crashes, in_dir, tokens, timeout).expect("An error occurred while fuzzing"); 156 | } 157 | 158 | fn run_testcases(filenames: &[&str]) { 159 | // The actual target run starts here. 160 | // Call LLVMFUzzerInitialize() if present. 161 | let args: Vec = env::args().collect(); 162 | if unsafe { libfuzzer_initialize(&args) } == -1 { 163 | println!("Warning: LLVMFuzzerInitialize failed with -1") 164 | } 165 | 166 | println!( 167 | "You are not fuzzing, just executing {} testcases", 168 | filenames.len() 169 | ); 170 | for fname in filenames { 171 | println!("Executing {}", fname); 172 | 173 | let mut file = File::open(fname).expect("No file found"); 174 | let mut buffer = vec![]; 175 | file.read_to_end(&mut buffer).expect("Buffer overflow"); 176 | 177 | unsafe { libfuzzer_test_one_input(&buffer) }; 178 | } 179 | } 180 | 181 | /// The actual fuzzer 182 | fn fuzz( 183 | corpus_dir: PathBuf, 184 | objective_dir: PathBuf, 185 | seed_dir: PathBuf, 186 | tokenfile: Option, 187 | timeout: Duration, 188 | ) -> Result<(), Error> { 189 | #[cfg(unix)] 190 | let mut stdout_cpy = unsafe { 191 | let new_fd = dup(io::stdout().as_raw_fd())?; 192 | File::from_raw_fd(new_fd) 193 | }; 194 | #[cfg(unix)] 195 | let file_null = File::open("/dev/null")?; 196 | 197 | // 'While the monitor are state, they are usually used in the broker - which is likely never restarted 198 | let monitor = SimpleMonitor::new(|s| { 199 | #[cfg(unix)] 200 | writeln!(&mut stdout_cpy, "{}", s).unwrap(); 201 | #[cfg(windows)] 202 | println!("{}", s); 203 | }); 204 | 205 | // We need a shared map to store our state before a crash. 206 | // This way, we are able to continue fuzzing afterwards. 207 | let mut shmem_provider = StdShMemProvider::new()?; 208 | 209 | let (state, mut mgr) = match SimpleRestartingEventManager::launch(monitor, &mut shmem_provider) 210 | { 211 | // The restarting state will spawn the same process again as child, then restarted it each time it crashes. 212 | Ok(res) => res, 213 | Err(err) => match err { 214 | Error::ShuttingDown => { 215 | return Ok(()); 216 | } 217 | _ => { 218 | panic!("Failed to setup the restarter: {}", err); 219 | } 220 | }, 221 | }; 222 | 223 | // Create an observation channel using the coverage map 224 | // We don't use the hitcounts (see the Cargo.toml, we use pcguard_edges) 225 | let edges_observer = 226 | HitcountsMapObserver::new(unsafe { std_edges_map_observer("edges") }).track_indices(); 227 | 228 | // Create an observation channel to keep track of the execution time 229 | let time_observer = TimeObserver::new("time"); 230 | 231 | // Feedback to rate the interestingness of an input 232 | // This one is composed by two Feedbacks in OR 233 | let mut feedback = feedback_or!( 234 | // New maximization map feedback linked to the edges observer and the feedback state 235 | MaxMapFeedback::new(&edges_observer), 236 | // Time feedback, this one does not need a feedback state 237 | TimeFeedback::new(&time_observer) 238 | ); 239 | 240 | // A feedback to choose if an input is a solution or not 241 | let mut objective = CrashFeedback::new(); 242 | 243 | // If not restarting, create a State from scratch 244 | let mut state = state.unwrap_or_else(|| { 245 | StdState::new( 246 | // RNG 247 | StdRand::with_seed(current_nanos()), 248 | // Corpus that will be evolved, we keep it in memory for performance 249 | InMemoryOnDiskCorpus::new(corpus_dir).unwrap(), 250 | // Corpus in which we store solutions (crashes in this example), 251 | // on disk so the user can get them after stopping the fuzzer 252 | OnDiskCorpus::new(objective_dir).unwrap(), 253 | // States of the feedbacks. 254 | // They are the data related to the feedbacks that you want to persist in the State. 255 | &mut feedback, 256 | &mut objective, 257 | ) 258 | .unwrap() 259 | }); 260 | 261 | println!("Let's fuzz :)"); 262 | 263 | // The actual target run starts here. 264 | // Call LLVMFUzzerInitialize() if present. 265 | let args: Vec = env::args().collect(); 266 | if unsafe { libfuzzer_initialize(&args) } == -1 { 267 | println!("Warning: LLVMFuzzerInitialize failed with -1") 268 | } 269 | 270 | let mutator = StdMutationalStage::new(StdScheduledMutator::new( 271 | havoc_mutations().merge(tokens_mutations()), 272 | )); 273 | 274 | // A minimization+queue policy to get testcasess from the corpus 275 | let scheduler = IndexesLenTimeMinimizerScheduler::new(&edges_observer, QueueScheduler::new()); 276 | 277 | // A fuzzer with feedbacks and a corpus scheduler 278 | let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); 279 | 280 | // The wrapped harness function, calling out to the LLVM-style harness 281 | let mut harness = |input: &BytesInput| { 282 | let target = input.target_bytes(); 283 | let buf = target.as_slice(); 284 | unsafe { libfuzzer_test_one_input(buf) }; 285 | ExitKind::Ok 286 | }; 287 | 288 | let ngram_hook = NgramHook::new(); 289 | // Create the executor for an in-process function with one observer for edge coverage and one for the execution time 290 | let mut executor = HookableInProcessExecutor::with_timeout_generic( 291 | tuple_list!(ngram_hook), 292 | &mut harness, 293 | tuple_list!(edges_observer, time_observer), 294 | &mut fuzzer, 295 | &mut state, 296 | &mut mgr, 297 | timeout, 298 | )?; 299 | 300 | // The order of the stages matter! 301 | let mut stages = tuple_list!(mutator); 302 | 303 | // Read tokens 304 | if state.metadata_map().get::().is_none() { 305 | let mut toks = Tokens::default(); 306 | if let Some(tokenfile) = tokenfile { 307 | toks.add_from_file(tokenfile)?; 308 | } 309 | #[cfg(target_os = "linux")] 310 | { 311 | toks += autotokens()?; 312 | } 313 | 314 | if !toks.is_empty() { 315 | state.add_metadata(toks); 316 | } 317 | } 318 | 319 | // In case the corpus is empty (on first run), reset 320 | if state.corpus().count() < 1 { 321 | state 322 | .load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &[seed_dir.clone()]) 323 | .unwrap_or_else(|_| { 324 | println!("Failed to load initial corpus at {:?}", &seed_dir); 325 | process::exit(0); 326 | }); 327 | println!("We imported {} inputs from disk.", state.corpus().count()); 328 | } 329 | 330 | // Remove target ouput (logs still survive) 331 | #[cfg(unix)] 332 | { 333 | let null_fd = file_null.as_raw_fd(); 334 | dup2(null_fd, io::stdout().as_raw_fd())?; 335 | dup2(null_fd, io::stderr().as_raw_fd())?; 336 | } 337 | 338 | fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)?; 339 | 340 | // Never reached 341 | Ok(()) 342 | } 343 | -------------------------------------------------------------------------------- /naive_ngram8/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "naive_ngram8" 3 | version = "0.0.1" 4 | authors = ["Andrea Fioraldi ", "Dominik Maier "] 5 | edition = "2021" 6 | 7 | [features] 8 | no_link_main = ["libafl_targets/libfuzzer_no_link_main"] 9 | 10 | [dependencies] 11 | libafl = { path = "../LibAFL/libafl/" } 12 | libafl_targets = { path = "../LibAFL/libafl_targets/", features = ["sancov_pcguard_hitcounts", "libfuzzer", "sancov_ngram8"] } 13 | libafl_bolts = { path = "../LibAFL/libafl_bolts/" } 14 | # TODO Include it only when building cc 15 | libafl_cc = { path = "../LibAFL/libafl_cc/" } 16 | clap = { version = "~4.2", features = ["default"] } 17 | nix = { version = "0.29", features = ["fs"] } 18 | mimalloc = { version = "*", default-features = false } 19 | 20 | [lib] 21 | crate-type = ["staticlib"] 22 | -------------------------------------------------------------------------------- /naive_ngram8/src/bin/naive_ngram8_cc.rs: -------------------------------------------------------------------------------- 1 | use libafl_cc::ToolWrapper; 2 | use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses}; 3 | use std::env; 4 | pub fn main() { 5 | let args: Vec = env::args().collect(); 6 | if args.len() > 1 { 7 | let mut dir = env::current_exe().unwrap(); 8 | let wrapper_name = dir.file_name().unwrap().to_str().unwrap(); 9 | 10 | let is_cpp = match wrapper_name[wrapper_name.len()-2..].to_lowercase().as_str() { 11 | "cc" => false, 12 | "++" | "pp" | "xx" => true, 13 | _ => panic!("Could not figure out if c or c++ warpper was called. Expected {:?} to end with c or cxx", dir), 14 | }; 15 | 16 | dir.pop(); 17 | 18 | let mut cc = ClangWrapper::new(); 19 | 20 | #[cfg(target_os = "linux")] 21 | cc.add_pass(LLVMPasses::AutoTokens); 22 | 23 | if let Some(code) = cc 24 | .cpp(is_cpp) 25 | // silence the compiler wrapper output, needed for some configure scripts. 26 | .silence(true) 27 | // add arguments only if --libafl or --libafl-no-link are present 28 | .need_libafl_arg(true) 29 | .parse_args(&args) 30 | .expect("Failed to parse the command line") 31 | .link_staticlib(&dir, env!("CARGO_PKG_NAME")) 32 | .add_arg("-fsanitize-coverage=trace-pc-guard") 33 | .add_passes_linking_arg("-lm") 34 | .run() 35 | .expect("Failed to run the wrapped compiler") 36 | { 37 | std::process::exit(code); 38 | } 39 | } else { 40 | panic!("LibAFL CC: No Arguments given"); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /naive_ngram8/src/bin/naive_ngram8_cxx.rs: -------------------------------------------------------------------------------- 1 | pub mod naive_ngram8_cc; 2 | 3 | fn main() { 4 | naive_ngram8_cc::main() 5 | } 6 | -------------------------------------------------------------------------------- /naive_ngram8/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A singlethreaded libfuzzer-like fuzzer that can auto-restart. 2 | use mimalloc::MiMalloc; 3 | #[global_allocator] 4 | static GLOBAL: MiMalloc = MiMalloc; 5 | use libafl::observers::CanTrack; 6 | use libafl::HasMetadata; 7 | use libafl_bolts::{ 8 | current_nanos, 9 | os::dup2, 10 | rands::StdRand, 11 | shmem::{ShMemProvider, StdShMemProvider}, 12 | tuples::{tuple_list, Merge}, 13 | AsSlice, 14 | }; 15 | 16 | use clap::{Arg, Command}; 17 | use core::time::Duration; 18 | #[cfg(unix)] 19 | use nix::{self, unistd::dup}; 20 | #[cfg(unix)] 21 | use std::os::unix::io::{AsRawFd, FromRawFd}; 22 | use std::{ 23 | env, 24 | fs::{self, File}, 25 | io::{self, Read, Write}, 26 | path::PathBuf, 27 | process, 28 | }; 29 | 30 | use libafl::{ 31 | corpus::{Corpus, InMemoryOnDiskCorpus, OnDiskCorpus}, 32 | events::SimpleRestartingEventManager, 33 | executors::{inprocess::HookableInProcessExecutor, ExitKind}, 34 | feedback_or, 35 | feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback}, 36 | fuzzer::{Fuzzer, StdFuzzer}, 37 | inputs::{BytesInput, HasTargetBytes}, 38 | monitors::SimpleMonitor, 39 | mutators::{havoc_mutations::havoc_mutations, tokens_mutations, StdScheduledMutator, Tokens}, 40 | observers::{HitcountsMapObserver, TimeObserver}, 41 | schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler}, 42 | stages::StdMutationalStage, 43 | state::{HasCorpus, StdState}, 44 | Error, 45 | }; 46 | use libafl_targets::{ 47 | libfuzzer_initialize, libfuzzer_test_one_input, std_edges_map_observer, NgramHook, 48 | }; 49 | 50 | #[cfg(target_os = "linux")] 51 | use libafl_targets::autotokens; 52 | 53 | /// The fuzzer main (as `no_mangle` C function) 54 | #[no_mangle] 55 | pub fn libafl_main() { 56 | // Registry the metadata types used in this fuzzer 57 | // Needed only on no_std 58 | //RegistryBuilder::register::(); 59 | 60 | let res = match Command::new(env!("CARGO_PKG_NAME")) 61 | .version(env!("CARGO_PKG_VERSION")) 62 | .author("AFLplusplus team") 63 | .about("LibAFL-based fuzzer for Fuzzbench") 64 | .arg( 65 | Arg::new("out") 66 | .short('o') 67 | .long("output") 68 | .help("The directory to place finds in ('corpus')"), 69 | ) 70 | .arg( 71 | Arg::new("in") 72 | .short('i') 73 | .long("input") 74 | .help("The directory to read initial inputs from ('seeds')"), 75 | ) 76 | .arg( 77 | Arg::new("tokens") 78 | .short('x') 79 | .long("tokens") 80 | .help("A file to read tokens from, to be used during fuzzing"), 81 | ) 82 | .arg( 83 | Arg::new("timeout") 84 | .short('t') 85 | .long("timeout") 86 | .help("Timeout for each individual execution, in milliseconds") 87 | .default_value("1200"), 88 | ) 89 | .arg(Arg::new("remaining")) 90 | .try_get_matches() 91 | { 92 | Ok(res) => res, 93 | Err(err) => { 94 | println!( 95 | "Syntax: {}, [-x dictionary] -o corpus_dir -i seed_dir\n{:?}", 96 | env::current_exe() 97 | .unwrap_or_else(|_| "fuzzer".into()) 98 | .to_string_lossy(), 99 | err, 100 | ); 101 | return; 102 | } 103 | }; 104 | 105 | println!( 106 | "Workdir: {:?}", 107 | env::current_dir().unwrap().to_string_lossy().to_string() 108 | ); 109 | 110 | if let Some(filenames) = res.get_many::("remaining") { 111 | let filenames: Vec<&str> = filenames.map(String::as_str).collect(); 112 | if !filenames.is_empty() { 113 | run_testcases(&filenames); 114 | return; 115 | } 116 | } 117 | 118 | // For fuzzbench, crashes and finds are inside the same `corpus` directory, in the "queue" and "crashes" subdir. 119 | let mut out_dir = PathBuf::from( 120 | res.get_one::("out") 121 | .expect("The --output parameter is missing") 122 | .to_string(), 123 | ); 124 | if fs::create_dir(&out_dir).is_err() { 125 | println!("Out dir at {:?} already exists.", &out_dir); 126 | if !out_dir.is_dir() { 127 | println!("Out dir at {:?} is not a valid directory!", &out_dir); 128 | return; 129 | } 130 | } 131 | let mut crashes = out_dir.clone(); 132 | crashes.push("crashes"); 133 | out_dir.push("queue"); 134 | 135 | let in_dir = PathBuf::from( 136 | res.get_one::("in") 137 | .expect("The --input parameter is missing") 138 | .to_string(), 139 | ); 140 | if !in_dir.is_dir() { 141 | println!("In dir at {:?} is not a valid directory!", &in_dir); 142 | return; 143 | } 144 | 145 | let tokens = res.get_one::("tokens").map(PathBuf::from); 146 | 147 | let timeout = Duration::from_millis( 148 | res.get_one::("timeout") 149 | .unwrap() 150 | .to_string() 151 | .parse() 152 | .expect("Could not parse timeout in milliseconds"), 153 | ); 154 | 155 | fuzz(out_dir, crashes, in_dir, tokens, timeout).expect("An error occurred while fuzzing"); 156 | } 157 | 158 | fn run_testcases(filenames: &[&str]) { 159 | // The actual target run starts here. 160 | // Call LLVMFUzzerInitialize() if present. 161 | let args: Vec = env::args().collect(); 162 | if unsafe { libfuzzer_initialize(&args) } == -1 { 163 | println!("Warning: LLVMFuzzerInitialize failed with -1") 164 | } 165 | 166 | println!( 167 | "You are not fuzzing, just executing {} testcases", 168 | filenames.len() 169 | ); 170 | for fname in filenames { 171 | println!("Executing {}", fname); 172 | 173 | let mut file = File::open(fname).expect("No file found"); 174 | let mut buffer = vec![]; 175 | file.read_to_end(&mut buffer).expect("Buffer overflow"); 176 | 177 | unsafe { libfuzzer_test_one_input(&buffer) }; 178 | } 179 | } 180 | 181 | /// The actual fuzzer 182 | fn fuzz( 183 | corpus_dir: PathBuf, 184 | objective_dir: PathBuf, 185 | seed_dir: PathBuf, 186 | tokenfile: Option, 187 | timeout: Duration, 188 | ) -> Result<(), Error> { 189 | #[cfg(unix)] 190 | let mut stdout_cpy = unsafe { 191 | let new_fd = dup(io::stdout().as_raw_fd())?; 192 | File::from_raw_fd(new_fd) 193 | }; 194 | #[cfg(unix)] 195 | let file_null = File::open("/dev/null")?; 196 | 197 | // 'While the monitor are state, they are usually used in the broker - which is likely never restarted 198 | let monitor = SimpleMonitor::new(|s| { 199 | #[cfg(unix)] 200 | writeln!(&mut stdout_cpy, "{}", s).unwrap(); 201 | #[cfg(windows)] 202 | println!("{}", s); 203 | }); 204 | 205 | // We need a shared map to store our state before a crash. 206 | // This way, we are able to continue fuzzing afterwards. 207 | let mut shmem_provider = StdShMemProvider::new()?; 208 | 209 | let (state, mut mgr) = match SimpleRestartingEventManager::launch(monitor, &mut shmem_provider) 210 | { 211 | // The restarting state will spawn the same process again as child, then restarted it each time it crashes. 212 | Ok(res) => res, 213 | Err(err) => match err { 214 | Error::ShuttingDown => { 215 | return Ok(()); 216 | } 217 | _ => { 218 | panic!("Failed to setup the restarter: {}", err); 219 | } 220 | }, 221 | }; 222 | // Create an observation channel using the coverage map 223 | // We don't use the hitcounts (see the Cargo.toml, we use pcguard_edges) 224 | let edges_observer = 225 | HitcountsMapObserver::new(unsafe { std_edges_map_observer("edges") }).track_indices(); 226 | 227 | // Create an observation channel to keep track of the execution time 228 | let time_observer = TimeObserver::new("time"); 229 | 230 | // Feedback to rate the interestingness of an input 231 | // This one is composed by two Feedbacks in OR 232 | let mut feedback = feedback_or!( 233 | // New maximization map feedback linked to the edges observer and the feedback state 234 | MaxMapFeedback::new(&edges_observer), 235 | // Time feedback, this one does not need a feedback state 236 | TimeFeedback::new(&time_observer) 237 | ); 238 | 239 | // A feedback to choose if an input is a solution or not 240 | let mut objective = CrashFeedback::new(); 241 | 242 | // If not restarting, create a State from scratch 243 | let mut state = state.unwrap_or_else(|| { 244 | StdState::new( 245 | // RNG 246 | StdRand::with_seed(current_nanos()), 247 | // Corpus that will be evolved, we keep it in memory for performance 248 | InMemoryOnDiskCorpus::new(corpus_dir).unwrap(), 249 | // Corpus in which we store solutions (crashes in this example), 250 | // on disk so the user can get them after stopping the fuzzer 251 | OnDiskCorpus::new(objective_dir).unwrap(), 252 | // States of the feedbacks. 253 | // They are the data related to the feedbacks that you want to persist in the State. 254 | &mut feedback, 255 | &mut objective, 256 | ) 257 | .unwrap() 258 | }); 259 | 260 | println!("Let's fuzz :)"); 261 | 262 | // The actual target run starts here. 263 | // Call LLVMFUzzerInitialize() if present. 264 | let args: Vec = env::args().collect(); 265 | if unsafe { libfuzzer_initialize(&args) } == -1 { 266 | println!("Warning: LLVMFuzzerInitialize failed with -1") 267 | } 268 | 269 | let mutator = StdMutationalStage::new(StdScheduledMutator::new( 270 | havoc_mutations().merge(tokens_mutations()), 271 | )); 272 | 273 | // A minimization+queue policy to get testcasess from the corpus 274 | let scheduler = IndexesLenTimeMinimizerScheduler::new(&edges_observer, QueueScheduler::new()); 275 | 276 | // A fuzzer with feedbacks and a corpus scheduler 277 | let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); 278 | 279 | // The wrapped harness function, calling out to the LLVM-style harness 280 | let mut harness = |input: &BytesInput| { 281 | let target = input.target_bytes(); 282 | let buf = target.as_slice(); 283 | unsafe { libfuzzer_test_one_input(buf) }; 284 | ExitKind::Ok 285 | }; 286 | 287 | let ngram_hook = NgramHook::new(); 288 | // Create the executor for an in-process function with one observer for edge coverage and one for the execution time 289 | let mut executor = HookableInProcessExecutor::with_timeout_generic( 290 | tuple_list!(ngram_hook), 291 | &mut harness, 292 | tuple_list!(edges_observer, time_observer), 293 | &mut fuzzer, 294 | &mut state, 295 | &mut mgr, 296 | timeout, 297 | )?; 298 | 299 | // The order of the stages matter! 300 | let mut stages = tuple_list!(mutator); 301 | 302 | // Read tokens 303 | if state.metadata_map().get::().is_none() { 304 | let mut toks = Tokens::default(); 305 | if let Some(tokenfile) = tokenfile { 306 | toks.add_from_file(tokenfile)?; 307 | } 308 | #[cfg(target_os = "linux")] 309 | { 310 | toks += autotokens()?; 311 | } 312 | 313 | if !toks.is_empty() { 314 | state.add_metadata(toks); 315 | } 316 | } 317 | 318 | // In case the corpus is empty (on first run), reset 319 | if state.corpus().count() < 1 { 320 | state 321 | .load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &[seed_dir.clone()]) 322 | .unwrap_or_else(|_| { 323 | println!("Failed to load initial corpus at {:?}", &seed_dir); 324 | process::exit(0); 325 | }); 326 | println!("We imported {} inputs from disk.", state.corpus().count()); 327 | } 328 | 329 | // Remove target ouput (logs still survive) 330 | #[cfg(unix)] 331 | { 332 | let null_fd = file_null.as_raw_fd(); 333 | dup2(null_fd, io::stdout().as_raw_fd())?; 334 | dup2(null_fd, io::stderr().as_raw_fd())?; 335 | } 336 | 337 | fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)?; 338 | 339 | // Never reached 340 | Ok(()) 341 | } 342 | -------------------------------------------------------------------------------- /nautilus/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nautilus" 3 | version = "0.0.1" 4 | authors = ["Andrea Fioraldi ", "Dominik Maier "] 5 | edition = "2021" 6 | 7 | [features] 8 | no_link_main = ["libafl_targets/libfuzzer_no_link_main"] 9 | 10 | [dependencies] 11 | libafl = { path = "../LibAFL/libafl/", features = ["default", "nautilus"] } 12 | libafl_bolts = { path = "../LibAFL/libafl_bolts/" } 13 | libafl_targets = { path = "../LibAFL/libafl_targets/", features = ["sancov_pcguard_hitcounts", "libfuzzer"] } 14 | serde_json = "1.0.68" 15 | # TODO Include it only when building cc 16 | libafl_cc = { path = "../LibAFL/libafl_cc/" } 17 | nix = { version = "0.29", features = ["fs"] } 18 | clap = { version = "4.0", features = ["derive"] } 19 | mimalloc = { version = "*", default-features = false } 20 | postcard = { version = "0.7", features = ["alloc"] } # no_std compatible serde serialization fromat 21 | 22 | [lib] 23 | crate-type = ["staticlib"] 24 | -------------------------------------------------------------------------------- /nautilus/src/bin/nautilus_cc.rs: -------------------------------------------------------------------------------- 1 | use libafl_cc::ToolWrapper; 2 | use libafl_cc::{ClangWrapper, CompilerWrapper}; 3 | use std::env; 4 | pub fn main() { 5 | let args: Vec = env::args().collect(); 6 | if args.len() > 1 { 7 | let mut dir = env::current_exe().unwrap(); 8 | let wrapper_name = dir.file_name().unwrap().to_str().unwrap(); 9 | 10 | let is_cpp = match wrapper_name[wrapper_name.len()-2..].to_lowercase().as_str() { 11 | "cc" => false, 12 | "++" | "pp" | "xx" => true, 13 | _ => panic!("Could not figure out if c or c++ warpper was called. Expected {:?} to end with c or cxx", dir), 14 | }; 15 | 16 | dir.pop(); 17 | 18 | let mut cc = ClangWrapper::new(); 19 | if let Some(code) = cc 20 | .cpp(is_cpp) 21 | // silence the compiler wrapper output, needed for some configure scripts. 22 | .silence(true) 23 | // add arguments only if --libafl or --libafl-no-link are present 24 | .need_libafl_arg(true) 25 | .parse_args(&args) 26 | .expect("Failed to parse the command line") 27 | .link_staticlib(&dir, env!("CARGO_PKG_NAME")) 28 | .add_arg("-fsanitize-coverage=trace-pc-guard") 29 | // needed by Nautilus 30 | .add_link_arg("-Wl,--push-state,-Bstatic") 31 | .add_link_arg("-L/usr/local/lib/python3.8/config-3.8-x86_64-linux-gnu/") 32 | .add_link_arg("-L/usr/lib/python3.8/config-3.8-x86_64-linux-gnu/") 33 | .add_link_arg("-lpython3.8") 34 | .add_link_arg("-Wl,--pop-state") 35 | .add_link_arg("-lutil") 36 | .add_link_arg("-lexpat") 37 | .add_link_arg("-lz") 38 | .run() 39 | .expect("Failed to run the wrapped compiler") 40 | { 41 | std::process::exit(code); 42 | } 43 | } else { 44 | panic!("LibAFL CC: No Arguments given"); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /nautilus/src/bin/nautilus_cxx.rs: -------------------------------------------------------------------------------- 1 | pub mod nautilus_cc; 2 | 3 | fn main() { 4 | nautilus_cc::main() 5 | } 6 | -------------------------------------------------------------------------------- /nautilus_mopt/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nautilus_mopt" 3 | version = "0.0.1" 4 | authors = ["Andrea Fioraldi ", "Dominik Maier "] 5 | edition = "2021" 6 | 7 | [features] 8 | no_link_main = ["libafl_targets/libfuzzer_no_link_main"] 9 | 10 | [dependencies] 11 | libafl = { path = "../LibAFL/libafl/", features = ["default", "nautilus"] } 12 | libafl_bolts = { path = "../LibAFL/libafl_bolts/" } 13 | libafl_targets = { path = "../LibAFL/libafl_targets/", features = ["sancov_pcguard_hitcounts", "libfuzzer"] } 14 | serde_json = "1.0.68" 15 | # TODO Include it only when building cc 16 | libafl_cc = { path = "../LibAFL/libafl_cc/" } 17 | nix = { version = "0.29", features = ["fs"] } 18 | clap = { version = "4.0", features = ["derive"] } 19 | mimalloc = { version = "*", default-features = false } 20 | postcard = { version = "0.7", features = ["alloc"] } # no_std compatible serde serialization fromat 21 | 22 | [lib] 23 | crate-type = ["staticlib"] 24 | -------------------------------------------------------------------------------- /nautilus_mopt/src/bin/nautilus_mopt_cc.rs: -------------------------------------------------------------------------------- 1 | use libafl_cc::ToolWrapper; 2 | use libafl_cc::{ClangWrapper, CompilerWrapper}; 3 | use std::env; 4 | pub fn main() { 5 | let args: Vec = env::args().collect(); 6 | if args.len() > 1 { 7 | let mut dir = env::current_exe().unwrap(); 8 | let wrapper_name = dir.file_name().unwrap().to_str().unwrap(); 9 | 10 | let is_cpp = match wrapper_name[wrapper_name.len()-2..].to_lowercase().as_str() { 11 | "cc" => false, 12 | "++" | "pp" | "xx" => true, 13 | _ => panic!("Could not figure out if c or c++ warpper was called. Expected {:?} to end with c or cxx", dir), 14 | }; 15 | 16 | dir.pop(); 17 | 18 | let mut cc = ClangWrapper::new(); 19 | if let Some(code) = cc 20 | .cpp(is_cpp) 21 | // silence the compiler wrapper output, needed for some configure scripts. 22 | .silence(true) 23 | // add arguments only if --libafl or --libafl-no-link are present 24 | .need_libafl_arg(true) 25 | .parse_args(&args) 26 | .expect("Failed to parse the command line") 27 | .link_staticlib(&dir, env!("CARGO_PKG_NAME")) 28 | .add_arg("-fsanitize-coverage=trace-pc-guard") 29 | // needed by Nautilus 30 | .add_link_arg("-Wl,--push-state,-Bstatic") 31 | .add_link_arg("-L/usr/local/lib/python3.8/config-3.8-x86_64-linux-gnu/") 32 | .add_link_arg("-L/usr/lib/python3.8/config-3.8-x86_64-linux-gnu/") 33 | .add_link_arg("-lpython3.8") 34 | .add_link_arg("-Wl,--pop-state") 35 | .add_link_arg("-lutil") 36 | .add_link_arg("-lexpat") 37 | .add_link_arg("-lz") 38 | .run() 39 | .expect("Failed to run the wrapped compiler") 40 | { 41 | std::process::exit(code); 42 | } 43 | } else { 44 | panic!("LibAFL CC: No Arguments given"); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /nautilus_mopt/src/bin/nautilus_mopt_cxx.rs: -------------------------------------------------------------------------------- 1 | pub mod nautilus_mopt_cc; 2 | 3 | fn main() { 4 | nautilus_mopt_cc::main() 5 | } 6 | -------------------------------------------------------------------------------- /rand_scheduler/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rand_scheduler" 3 | version = "0.0.1" 4 | authors = ["Andrea Fioraldi ", "Dominik Maier "] 5 | edition = "2021" 6 | 7 | [features] 8 | no_link_main = ["libafl_targets/libfuzzer_no_link_main"] 9 | 10 | [dependencies] 11 | libafl = { path = "../LibAFL/libafl/" } 12 | libafl_bolts = { path = "../LibAFL/libafl_bolts/" } 13 | libafl_targets = { path = "../LibAFL/libafl_targets/", features = ["sancov_pcguard_hitcounts", "libfuzzer"] } 14 | # TODO Include it only when building cc 15 | libafl_cc = { path = "../LibAFL/libafl_cc/" } 16 | clap = { version = "~4.2", features = ["default"] } 17 | nix = { version = "0.29", features = ["fs"] } 18 | mimalloc = { version = "*", default-features = false } 19 | 20 | [lib] 21 | crate-type = ["staticlib"] 22 | -------------------------------------------------------------------------------- /rand_scheduler/src/bin/rand_scheduler_cc.rs: -------------------------------------------------------------------------------- 1 | use libafl_cc::ToolWrapper; 2 | use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses}; 3 | use std::env; 4 | pub fn main() { 5 | let args: Vec = env::args().collect(); 6 | if args.len() > 1 { 7 | let mut dir = env::current_exe().unwrap(); 8 | let wrapper_name = dir.file_name().unwrap().to_str().unwrap(); 9 | 10 | let is_cpp = match wrapper_name[wrapper_name.len()-2..].to_lowercase().as_str() { 11 | "cc" => false, 12 | "++" | "pp" | "xx" => true, 13 | _ => panic!("Could not figure out if c or c++ warpper was called. Expected {:?} to end with c or cxx", dir), 14 | }; 15 | 16 | dir.pop(); 17 | 18 | let mut cc = ClangWrapper::new(); 19 | 20 | #[cfg(target_os = "linux")] 21 | cc.add_pass(LLVMPasses::AutoTokens); 22 | 23 | if let Some(code) = cc 24 | .cpp(is_cpp) 25 | // silence the compiler wrapper output, needed for some configure scripts. 26 | .silence(true) 27 | // add arguments only if --libafl or --libafl-no-link are present 28 | .need_libafl_arg(true) 29 | .parse_args(&args) 30 | .expect("Failed to parse the command line") 31 | .link_staticlib(&dir, env!("CARGO_PKG_NAME")) 32 | .add_arg("-fsanitize-coverage=trace-pc-guard") 33 | .run() 34 | .expect("Failed to run the wrapped compiler") 35 | { 36 | std::process::exit(code); 37 | } 38 | } else { 39 | panic!("LibAFL CC: No Arguments given"); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /rand_scheduler/src/bin/rand_scheduler_cxx.rs: -------------------------------------------------------------------------------- 1 | pub mod rand_scheduler_cc; 2 | 3 | fn main() { 4 | rand_scheduler_cc::main() 5 | } 6 | -------------------------------------------------------------------------------- /rand_scheduler/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A singlethreaded libfuzzer-like fuzzer that can auto-restart. 2 | use mimalloc::MiMalloc; 3 | #[global_allocator] 4 | static GLOBAL: MiMalloc = MiMalloc; 5 | 6 | use libafl::observers::CanTrack; 7 | use libafl::HasMetadata; 8 | use libafl_bolts::{ 9 | current_nanos, 10 | os::dup2, 11 | rands::StdRand, 12 | shmem::{ShMemProvider, StdShMemProvider}, 13 | tuples::{tuple_list, Merge}, 14 | AsSlice, 15 | }; 16 | 17 | use clap::{Arg, Command}; 18 | use core::time::Duration; 19 | #[cfg(unix)] 20 | use nix::{self, unistd::dup}; 21 | #[cfg(unix)] 22 | use std::os::unix::io::{AsRawFd, FromRawFd}; 23 | use std::{ 24 | env, 25 | fs::{self, File}, 26 | io::{self, Read, Write}, 27 | path::PathBuf, 28 | process, 29 | }; 30 | 31 | use libafl::{ 32 | corpus::{Corpus, InMemoryOnDiskCorpus, OnDiskCorpus}, 33 | events::SimpleRestartingEventManager, 34 | executors::{inprocess::InProcessExecutor, ExitKind}, 35 | feedback_or, 36 | feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback}, 37 | fuzzer::{Fuzzer, StdFuzzer}, 38 | inputs::{BytesInput, HasTargetBytes}, 39 | monitors::SimpleMonitor, 40 | mutators::{havoc_mutations::havoc_mutations, tokens_mutations, StdScheduledMutator, Tokens}, 41 | observers::{HitcountsMapObserver, TimeObserver}, 42 | schedulers::RandScheduler, 43 | stages::StdMutationalStage, 44 | state::{HasCorpus, StdState}, 45 | Error, 46 | }; 47 | use libafl_targets::{libfuzzer_initialize, libfuzzer_test_one_input, std_edges_map_observer}; 48 | 49 | #[cfg(target_os = "linux")] 50 | use libafl_targets::autotokens; 51 | 52 | /// The fuzzer main (as `no_mangle` C function) 53 | #[no_mangle] 54 | pub fn libafl_main() { 55 | // Registry the metadata types used in this fuzzer 56 | // Needed only on no_std 57 | //RegistryBuilder::register::(); 58 | 59 | let res = match Command::new(env!("CARGO_PKG_NAME")) 60 | .version(env!("CARGO_PKG_VERSION")) 61 | .author("AFLplusplus team") 62 | .about("LibAFL-based fuzzer for Fuzzbench") 63 | .arg( 64 | Arg::new("out") 65 | .short('o') 66 | .long("output") 67 | .help("The directory to place finds in ('corpus')"), 68 | ) 69 | .arg( 70 | Arg::new("in") 71 | .short('i') 72 | .long("input") 73 | .help("The directory to read initial inputs from ('seeds')"), 74 | ) 75 | .arg( 76 | Arg::new("tokens") 77 | .short('x') 78 | .long("tokens") 79 | .help("A file to read tokens from, to be used during fuzzing"), 80 | ) 81 | .arg( 82 | Arg::new("timeout") 83 | .short('t') 84 | .long("timeout") 85 | .help("Timeout for each individual execution, in milliseconds") 86 | .default_value("1200"), 87 | ) 88 | .arg(Arg::new("remaining")) 89 | .try_get_matches() 90 | { 91 | Ok(res) => res, 92 | Err(err) => { 93 | println!( 94 | "Syntax: {}, [-x dictionary] -o corpus_dir -i seed_dir\n{:?}", 95 | env::current_exe() 96 | .unwrap_or_else(|_| "fuzzer".into()) 97 | .to_string_lossy(), 98 | err, 99 | ); 100 | return; 101 | } 102 | }; 103 | 104 | println!( 105 | "Workdir: {:?}", 106 | env::current_dir().unwrap().to_string_lossy().to_string() 107 | ); 108 | 109 | if let Some(filenames) = res.get_many::("remaining") { 110 | let filenames: Vec<&str> = filenames.map(String::as_str).collect(); 111 | if !filenames.is_empty() { 112 | run_testcases(&filenames); 113 | return; 114 | } 115 | } 116 | 117 | // For fuzzbench, crashes and finds are inside the same `corpus` directory, in the "queue" and "crashes" subdir. 118 | let mut out_dir = PathBuf::from( 119 | res.get_one::("out") 120 | .expect("The --output parameter is missing") 121 | .to_string(), 122 | ); 123 | if fs::create_dir(&out_dir).is_err() { 124 | println!("Out dir at {:?} already exists.", &out_dir); 125 | if !out_dir.is_dir() { 126 | println!("Out dir at {:?} is not a valid directory!", &out_dir); 127 | return; 128 | } 129 | } 130 | let mut crashes = out_dir.clone(); 131 | crashes.push("crashes"); 132 | out_dir.push("queue"); 133 | 134 | let in_dir = PathBuf::from( 135 | res.get_one::("in") 136 | .expect("The --input parameter is missing") 137 | .to_string(), 138 | ); 139 | if !in_dir.is_dir() { 140 | println!("In dir at {:?} is not a valid directory!", &in_dir); 141 | return; 142 | } 143 | 144 | let tokens = res.get_one::("tokens").map(PathBuf::from); 145 | 146 | let timeout = Duration::from_millis( 147 | res.get_one::("timeout") 148 | .unwrap() 149 | .to_string() 150 | .parse() 151 | .expect("Could not parse timeout in milliseconds"), 152 | ); 153 | 154 | fuzz(out_dir, crashes, in_dir, tokens, timeout).expect("An error occurred while fuzzing"); 155 | } 156 | 157 | fn run_testcases(filenames: &[&str]) { 158 | // The actual target run starts here. 159 | // Call LLVMFUzzerInitialize() if present. 160 | let args: Vec = env::args().collect(); 161 | if unsafe { libfuzzer_initialize(&args) } == -1 { 162 | println!("Warning: LLVMFuzzerInitialize failed with -1") 163 | } 164 | 165 | println!( 166 | "You are not fuzzing, just executing {} testcases", 167 | filenames.len() 168 | ); 169 | for fname in filenames { 170 | println!("Executing {}", fname); 171 | 172 | let mut file = File::open(fname).expect("No file found"); 173 | let mut buffer = vec![]; 174 | file.read_to_end(&mut buffer).expect("Buffer overflow"); 175 | 176 | unsafe { libfuzzer_test_one_input(&buffer) }; 177 | } 178 | } 179 | 180 | /// The actual fuzzer 181 | fn fuzz( 182 | corpus_dir: PathBuf, 183 | objective_dir: PathBuf, 184 | seed_dir: PathBuf, 185 | tokenfile: Option, 186 | timeout: Duration, 187 | ) -> Result<(), Error> { 188 | #[cfg(unix)] 189 | let mut stdout_cpy = unsafe { 190 | let new_fd = dup(io::stdout().as_raw_fd())?; 191 | File::from_raw_fd(new_fd) 192 | }; 193 | #[cfg(unix)] 194 | let file_null = File::open("/dev/null")?; 195 | 196 | // 'While the monitor are state, they are usually used in the broker - which is likely never restarted 197 | let monitor = SimpleMonitor::new(|s| { 198 | #[cfg(unix)] 199 | writeln!(&mut stdout_cpy, "{}", s).unwrap(); 200 | #[cfg(windows)] 201 | println!("{}", s); 202 | }); 203 | 204 | // We need a shared map to store our state before a crash. 205 | // This way, we are able to continue fuzzing afterwards. 206 | let mut shmem_provider = StdShMemProvider::new()?; 207 | 208 | let (state, mut mgr) = match SimpleRestartingEventManager::launch(monitor, &mut shmem_provider) 209 | { 210 | // The restarting state will spawn the same process again as child, then restarted it each time it crashes. 211 | Ok(res) => res, 212 | Err(err) => match err { 213 | Error::ShuttingDown => { 214 | return Ok(()); 215 | } 216 | _ => { 217 | panic!("Failed to setup the restarter: {}", err); 218 | } 219 | }, 220 | }; 221 | 222 | // Create an observation channel using the coverage map 223 | // We don't use the hitcounts (see the Cargo.toml, we use pcguard_edges) 224 | let edges_observer = 225 | HitcountsMapObserver::new(unsafe { std_edges_map_observer("edges") }).track_indices(); 226 | 227 | // Create an observation channel to keep track of the execution time 228 | let time_observer = TimeObserver::new("time"); 229 | 230 | // Feedback to rate the interestingness of an input 231 | // This one is composed by two Feedbacks in OR 232 | let mut feedback = feedback_or!( 233 | // New maximization map feedback linked to the edges observer and the feedback state 234 | MaxMapFeedback::new(&edges_observer), 235 | // Time feedback, this one does not need a feedback state 236 | TimeFeedback::new(&time_observer) 237 | ); 238 | 239 | // A feedback to choose if an input is a solution or not 240 | let mut objective = CrashFeedback::new(); 241 | 242 | // If not restarting, create a State from scratch 243 | let mut state = state.unwrap_or_else(|| { 244 | StdState::new( 245 | // RNG 246 | StdRand::with_seed(current_nanos()), 247 | // Corpus that will be evolved, we keep it in memory for performance 248 | InMemoryOnDiskCorpus::new(corpus_dir).unwrap(), 249 | // Corpus in which we store solutions (crashes in this example), 250 | // on disk so the user can get them after stopping the fuzzer 251 | OnDiskCorpus::new(objective_dir).unwrap(), 252 | // States of the feedbacks. 253 | // They are the data related to the feedbacks that you want to persist in the State. 254 | &mut feedback, 255 | &mut objective, 256 | ) 257 | .unwrap() 258 | }); 259 | 260 | println!("Let's fuzz :)"); 261 | 262 | // The actual target run starts here. 263 | // Call LLVMFUzzerInitialize() if present. 264 | let args: Vec = env::args().collect(); 265 | if unsafe { libfuzzer_initialize(&args) } == -1 { 266 | println!("Warning: LLVMFuzzerInitialize failed with -1") 267 | } 268 | 269 | let mutator = StdMutationalStage::new(StdScheduledMutator::new( 270 | havoc_mutations().merge(tokens_mutations()), 271 | )); 272 | 273 | // A minimization+queue policy to get testcasess from the corpus 274 | let scheduler = RandScheduler::new(); 275 | 276 | // A fuzzer with feedbacks and a corpus scheduler 277 | let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); 278 | 279 | // The wrapped harness function, calling out to the LLVM-style harness 280 | let mut harness = |input: &BytesInput| { 281 | let target = input.target_bytes(); 282 | let buf = target.as_slice(); 283 | unsafe { libfuzzer_test_one_input(buf) }; 284 | ExitKind::Ok 285 | }; 286 | 287 | // Create the executor for an in-process function with one observer for edge coverage and one for the execution time 288 | let mut executor = InProcessExecutor::with_timeout( 289 | &mut harness, 290 | tuple_list!(edges_observer, time_observer), 291 | &mut fuzzer, 292 | &mut state, 293 | &mut mgr, 294 | timeout, 295 | )?; 296 | 297 | // The order of the stages matter! 298 | let mut stages = tuple_list!(mutator); 299 | 300 | // Read tokens 301 | if state.metadata_map().get::().is_none() { 302 | let mut toks = Tokens::default(); 303 | if let Some(tokenfile) = tokenfile { 304 | toks.add_from_file(tokenfile)?; 305 | } 306 | #[cfg(target_os = "linux")] 307 | { 308 | toks += autotokens()?; 309 | } 310 | 311 | if !toks.is_empty() { 312 | state.add_metadata(toks); 313 | } 314 | } 315 | 316 | // In case the corpus is empty (on first run), reset 317 | if state.corpus().count() < 1 { 318 | state 319 | .load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &[seed_dir.clone()]) 320 | .unwrap_or_else(|_| { 321 | println!("Failed to load initial corpus at {:?}", &seed_dir); 322 | process::exit(0); 323 | }); 324 | println!("We imported {} inputs from disk.", state.corpus().count()); 325 | } 326 | 327 | // Remove target ouput (logs still survive) 328 | #[cfg(unix)] 329 | { 330 | let null_fd = file_null.as_raw_fd(); 331 | dup2(null_fd, io::stdout().as_raw_fd())?; 332 | dup2(null_fd, io::stderr().as_raw_fd())?; 333 | } 334 | 335 | fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)?; 336 | 337 | // Never reached 338 | Ok(()) 339 | } 340 | -------------------------------------------------------------------------------- /rust-toolchain: -------------------------------------------------------------------------------- 1 | nightly-2024-08-12 2 | -------------------------------------------------------------------------------- /stub_rt.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | __attribute__ ((weak)) void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { 4 | } 5 | 6 | __attribute__ ((weak)) void __sanitizer_cov_trace_pc_guard(uint32_t *guard) { 7 | } 8 | 9 | __attribute__ ((weak)) void __cmplog_rtn_hook(uint8_t *ptr1, uint8_t *ptr2) { 10 | } 11 | 12 | __attribute__ ((weak)) void __cmplog_rtn_gcc_stdstring_cstring(uint8_t *stdstring, uint8_t *cstring) { 13 | } 14 | 15 | __attribute__ ((weak)) void __cmplog_rtn_gcc_stdstring_stdstring(uint8_t *stdstring1, uint8_t *stdstring2) { 16 | } 17 | 18 | __attribute__ ((weak)) void __cmplog_rtn_llvm_stdstring_cstring(uint8_t *stdstring, uint8_t *cstring) { 19 | } 20 | 21 | __attribute__ ((weak)) void __cmplog_rtn_llvm_stdstring_stdstring(uint8_t *stdstring1, uint8_t *stdstring2) { 22 | } 23 | 24 | extern void libafl_main(void); 25 | 26 | int main(int argc, char **argv) { libafl_main(); return 0; } 27 | -------------------------------------------------------------------------------- /text/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "text" 3 | version = "0.0.1" 4 | authors = ["Andrea Fioraldi ", "Dominik Maier "] 5 | edition = "2021" 6 | 7 | [features] 8 | no_link_main = ["libafl_targets/libfuzzer_no_link_main"] 9 | 10 | [dependencies] 11 | libafl = { path = "../LibAFL/libafl/" } 12 | libafl_bolts = { path = "../LibAFL/libafl_bolts/" } 13 | libafl_targets = { path = "../LibAFL/libafl_targets/", features = ["sancov_pcguard_hitcounts", "sancov_cmplog", "libfuzzer"] } 14 | # TODO Include it only when building cc 15 | libafl_cc = { path = "../LibAFL/libafl_cc/" } 16 | clap = { version = "~4.2", features = ["default"] } 17 | nix = { version = "0.29", features = ["fs"] } 18 | mimalloc = { version = "*", default-features = false } 19 | content_inspector = "0.2.4" 20 | 21 | [lib] 22 | crate-type = ["staticlib"] 23 | -------------------------------------------------------------------------------- /text/src/bin/text_cc.rs: -------------------------------------------------------------------------------- 1 | use libafl_cc::ToolWrapper; 2 | use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses}; 3 | use std::env; 4 | pub fn main() { 5 | let args: Vec = env::args().collect(); 6 | if args.len() > 1 { 7 | let mut dir = env::current_exe().unwrap(); 8 | let wrapper_name = dir.file_name().unwrap().to_str().unwrap(); 9 | 10 | let is_cpp = match wrapper_name[wrapper_name.len()-2..].to_lowercase().as_str() { 11 | "cc" => false, 12 | "++" | "pp" | "xx" => true, 13 | _ => panic!("Could not figure out if c or c++ warpper was called. Expected {:?} to end with c or cxx", dir), 14 | }; 15 | 16 | dir.pop(); 17 | 18 | let mut cc = ClangWrapper::new(); 19 | 20 | #[cfg(target_os = "linux")] 21 | cc.add_pass(LLVMPasses::AutoTokens); 22 | 23 | if let Some(code) = cc 24 | .cpp(is_cpp) 25 | // silence the compiler wrapper output, needed for some configure scripts. 26 | .silence(true) 27 | // add arguments only if --libafl or --libafl-no-link are present 28 | .need_libafl_arg(true) 29 | .parse_args(&args) 30 | .expect("Failed to parse the command line") 31 | .link_staticlib(&dir, env!("CARGO_PKG_NAME")) 32 | .add_arg("-fsanitize-coverage=trace-pc-guard,trace-cmp") 33 | .add_pass(LLVMPasses::CmpLogRtn) 34 | .run() 35 | .expect("Failed to run the wrapped compiler") 36 | { 37 | std::process::exit(code); 38 | } 39 | } else { 40 | panic!("LibAFL CC: No Arguments given"); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /text/src/bin/text_cxx.rs: -------------------------------------------------------------------------------- 1 | pub mod text_cc; 2 | 3 | fn main() { 4 | text_cc::main() 5 | } 6 | -------------------------------------------------------------------------------- /token_level/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "token_level" 3 | version = "0.0.1" 4 | authors = ["Andrea Fioraldi ", "Dominik Maier "] 5 | edition = "2021" 6 | 7 | [features] 8 | no_link_main = ["libafl_targets/libfuzzer_no_link_main"] 9 | 10 | [dependencies] 11 | libafl = { path = "../LibAFL/libafl/", features = ["default", "nautilus"] } 12 | libafl_bolts = { path = "../LibAFL/libafl_bolts/" } 13 | libafl_targets = { path = "../LibAFL/libafl_targets/", features = ["sancov_pcguard_hitcounts", "libfuzzer"] } 14 | serde_json = "1.0.68" 15 | # TODO Include it only when building cc 16 | libafl_cc = { path = "../LibAFL/libafl_cc/" } 17 | nix = { version = "0.29", features = ["fs"] } 18 | clap = { version = "4.0", features = ["derive"] } 19 | mimalloc = { version = "*", default-features = false } 20 | postcard = { version = "0.7", features = ["alloc"] } # no_std compatible serde serialization fromat 21 | 22 | [lib] 23 | crate-type = ["staticlib"] 24 | -------------------------------------------------------------------------------- /token_level/src/bin/token_level_cc.rs: -------------------------------------------------------------------------------- 1 | use libafl_cc::ToolWrapper; 2 | use libafl_cc::{ClangWrapper, CompilerWrapper}; 3 | use std::env; 4 | pub fn main() { 5 | let args: Vec = env::args().collect(); 6 | if args.len() > 1 { 7 | let mut dir = env::current_exe().unwrap(); 8 | let wrapper_name = dir.file_name().unwrap().to_str().unwrap(); 9 | 10 | let is_cpp = match wrapper_name[wrapper_name.len()-2..].to_lowercase().as_str() { 11 | "cc" => false, 12 | "++" | "pp" | "xx" => true, 13 | _ => panic!("Could not figure out if c or c++ warpper was called. Expected {:?} to end with c or cxx", dir), 14 | }; 15 | 16 | dir.pop(); 17 | 18 | let mut cc = ClangWrapper::new(); 19 | if let Some(code) = cc 20 | .cpp(is_cpp) 21 | // silence the compiler wrapper output, needed for some configure scripts. 22 | .silence(true) 23 | // add arguments only if --libafl or --libafl-no-link are present 24 | .need_libafl_arg(true) 25 | .parse_args(&args) 26 | .expect("Failed to parse the command line") 27 | .link_staticlib(&dir, env!("CARGO_PKG_NAME")) 28 | .add_arg("-fsanitize-coverage=trace-pc-guard") 29 | // needed by Nautilus 30 | .add_link_arg("-Wl,--push-state,-Bstatic") 31 | .add_link_arg("-L/usr/local/lib/python3.8/config-3.8-x86_64-linux-gnu/") 32 | .add_link_arg("-L/usr/lib/python3.8/config-3.8-x86_64-linux-gnu/") 33 | .add_link_arg("-lpython3.8") 34 | .add_link_arg("-Wl,--pop-state") 35 | .add_link_arg("-lutil") 36 | .add_link_arg("-lexpat") 37 | .add_link_arg("-lz") 38 | .run() 39 | .expect("Failed to run the wrapped compiler") 40 | { 41 | std::process::exit(code); 42 | } 43 | } else { 44 | panic!("LibAFL CC: No Arguments given"); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /token_level/src/bin/token_level_cxx.rs: -------------------------------------------------------------------------------- 1 | pub mod token_level_cc; 2 | 3 | fn main() { 4 | token_level_cc::main() 5 | } 6 | -------------------------------------------------------------------------------- /value_profile/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "value_profile" 3 | version = "0.0.1" 4 | authors = ["Andrea Fioraldi ", "Dominik Maier "] 5 | edition = "2021" 6 | 7 | [features] 8 | no_link_main = ["libafl_targets/libfuzzer_no_link_main"] 9 | 10 | [dependencies] 11 | libafl = { path = "../LibAFL/libafl/" } 12 | libafl_bolts = { path = "../LibAFL/libafl_bolts/" } 13 | libafl_targets = { path = "../LibAFL/libafl_targets/", features = ["sancov_pcguard_hitcounts", "sancov_value_profile", "libfuzzer"] } 14 | # TODO Include it only when building cc 15 | libafl_cc = { path = "../LibAFL/libafl_cc/" } 16 | clap = { version = "~4.2", features = ["default"] } 17 | nix = { version = "0.29", features = ["fs"] } 18 | mimalloc = { version = "*", default-features = false } 19 | 20 | [lib] 21 | crate-type = ["staticlib"] 22 | -------------------------------------------------------------------------------- /value_profile/src/bin/value_profile_cc.rs: -------------------------------------------------------------------------------- 1 | use libafl_cc::ToolWrapper; 2 | use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses}; 3 | use std::env; 4 | pub fn main() { 5 | let args: Vec = env::args().collect(); 6 | if args.len() > 1 { 7 | let mut dir = env::current_exe().unwrap(); 8 | let wrapper_name = dir.file_name().unwrap().to_str().unwrap(); 9 | 10 | let is_cpp = match wrapper_name[wrapper_name.len()-2..].to_lowercase().as_str() { 11 | "cc" => false, 12 | "++" | "pp" | "xx" => true, 13 | _ => panic!("Could not figure out if c or c++ warpper was called. Expected {:?} to end with c or cxx", dir), 14 | }; 15 | 16 | dir.pop(); 17 | 18 | let mut cc = ClangWrapper::new(); 19 | 20 | #[cfg(target_os = "linux")] 21 | cc.add_pass(LLVMPasses::AutoTokens); 22 | 23 | if let Some(code) = cc 24 | .cpp(is_cpp) 25 | // silence the compiler wrapper output, needed for some configure scripts. 26 | .silence(true) 27 | // add arguments only if --libafl or --libafl-no-link are present 28 | .need_libafl_arg(true) 29 | .parse_args(&args) 30 | .expect("Failed to parse the command line") 31 | .link_staticlib(&dir, env!("CARGO_PKG_NAME")) 32 | .add_arg("-fsanitize-coverage=trace-pc-guard,trace-cmp") 33 | .run() 34 | .expect("Failed to run the wrapped compiler") 35 | { 36 | std::process::exit(code); 37 | } 38 | } else { 39 | panic!("LibAFL CC: No Arguments given"); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /value_profile/src/bin/value_profile_cxx.rs: -------------------------------------------------------------------------------- 1 | pub mod value_profile_cc; 2 | 3 | fn main() { 4 | value_profile_cc::main() 5 | } 6 | -------------------------------------------------------------------------------- /value_profile/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A singlethreaded libfuzzer-like fuzzer that can auto-restart. 2 | use mimalloc::MiMalloc; 3 | #[global_allocator] 4 | static GLOBAL: MiMalloc = MiMalloc; 5 | 6 | use libafl::observers::CanTrack; 7 | use libafl::HasMetadata; 8 | use libafl_bolts::{ 9 | current_nanos, 10 | os::dup2, 11 | rands::StdRand, 12 | shmem::{ShMemProvider, StdShMemProvider}, 13 | tuples::{tuple_list, Merge}, 14 | AsSlice, 15 | }; 16 | 17 | use clap::{Arg, Command}; 18 | use core::time::Duration; 19 | #[cfg(unix)] 20 | use nix::{self, unistd::dup}; 21 | #[cfg(unix)] 22 | use std::os::unix::io::{AsRawFd, FromRawFd}; 23 | use std::{ 24 | env, 25 | fs::{self, File}, 26 | io::{self, Read, Write}, 27 | path::PathBuf, 28 | process, 29 | }; 30 | 31 | use libafl::{ 32 | corpus::{Corpus, InMemoryOnDiskCorpus, OnDiskCorpus}, 33 | events::SimpleRestartingEventManager, 34 | executors::{inprocess::InProcessExecutor, ExitKind}, 35 | feedback_or, 36 | feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback}, 37 | fuzzer::{Fuzzer, StdFuzzer}, 38 | inputs::{BytesInput, HasTargetBytes}, 39 | monitors::SimpleMonitor, 40 | mutators::{havoc_mutations::havoc_mutations, tokens_mutations, StdScheduledMutator, Tokens}, 41 | observers::{HitcountsMapObserver, StdMapObserver, TimeObserver}, 42 | schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler}, 43 | stages::StdMutationalStage, 44 | state::{HasCorpus, StdState}, 45 | Error, 46 | }; 47 | use libafl_targets::{ 48 | libfuzzer_initialize, libfuzzer_test_one_input, std_edges_map_observer, CMP_MAP, 49 | }; 50 | 51 | #[cfg(target_os = "linux")] 52 | use libafl_targets::autotokens; 53 | 54 | /// The fuzzer main (as `no_mangle` C function) 55 | #[no_mangle] 56 | pub fn libafl_main() { 57 | // Registry the metadata types used in this fuzzer 58 | // Needed only on no_std 59 | //RegistryBuilder::register::(); 60 | 61 | let res = match Command::new(env!("CARGO_PKG_NAME")) 62 | .version(env!("CARGO_PKG_VERSION")) 63 | .author("AFLplusplus team") 64 | .about("LibAFL-based fuzzer for Fuzzbench") 65 | .arg( 66 | Arg::new("out") 67 | .short('o') 68 | .long("output") 69 | .help("The directory to place finds in ('corpus')"), 70 | ) 71 | .arg( 72 | Arg::new("in") 73 | .short('i') 74 | .long("input") 75 | .help("The directory to read initial inputs from ('seeds')"), 76 | ) 77 | .arg( 78 | Arg::new("tokens") 79 | .short('x') 80 | .long("tokens") 81 | .help("A file to read tokens from, to be used during fuzzing"), 82 | ) 83 | .arg( 84 | Arg::new("timeout") 85 | .short('t') 86 | .long("timeout") 87 | .help("Timeout for each individual execution, in milliseconds") 88 | .default_value("1200"), 89 | ) 90 | .arg(Arg::new("remaining")) 91 | .try_get_matches() 92 | { 93 | Ok(res) => res, 94 | Err(err) => { 95 | println!( 96 | "Syntax: {}, [-x dictionary] -o corpus_dir -i seed_dir\n{:?}", 97 | env::current_exe() 98 | .unwrap_or_else(|_| "fuzzer".into()) 99 | .to_string_lossy(), 100 | err, 101 | ); 102 | return; 103 | } 104 | }; 105 | 106 | println!( 107 | "Workdir: {:?}", 108 | env::current_dir().unwrap().to_string_lossy().to_string() 109 | ); 110 | 111 | if let Some(filenames) = res.get_many::("remaining") { 112 | let filenames: Vec<&str> = filenames.map(String::as_str).collect(); 113 | if !filenames.is_empty() { 114 | run_testcases(&filenames); 115 | return; 116 | } 117 | } 118 | 119 | // For fuzzbench, crashes and finds are inside the same `corpus` directory, in the "queue" and "crashes" subdir. 120 | let mut out_dir = PathBuf::from( 121 | res.get_one::("out") 122 | .expect("The --output parameter is missing") 123 | .to_string(), 124 | ); 125 | if fs::create_dir(&out_dir).is_err() { 126 | println!("Out dir at {:?} already exists.", &out_dir); 127 | if !out_dir.is_dir() { 128 | println!("Out dir at {:?} is not a valid directory!", &out_dir); 129 | return; 130 | } 131 | } 132 | let mut crashes = out_dir.clone(); 133 | crashes.push("crashes"); 134 | out_dir.push("queue"); 135 | 136 | let in_dir = PathBuf::from( 137 | res.get_one::("in") 138 | .expect("The --input parameter is missing") 139 | .to_string(), 140 | ); 141 | if !in_dir.is_dir() { 142 | println!("In dir at {:?} is not a valid directory!", &in_dir); 143 | return; 144 | } 145 | 146 | let tokens = res.get_one::("tokens").map(PathBuf::from); 147 | 148 | let timeout = Duration::from_millis( 149 | res.get_one::("timeout") 150 | .unwrap() 151 | .to_string() 152 | .parse() 153 | .expect("Could not parse timeout in milliseconds"), 154 | ); 155 | 156 | fuzz(out_dir, crashes, in_dir, tokens, timeout).expect("An error occurred while fuzzing"); 157 | } 158 | 159 | fn run_testcases(filenames: &[&str]) { 160 | // The actual target run starts here. 161 | // Call LLVMFUzzerInitialize() if present. 162 | let args: Vec = env::args().collect(); 163 | if unsafe { libfuzzer_initialize(&args) } == -1 { 164 | println!("Warning: LLVMFuzzerInitialize failed with -1") 165 | } 166 | 167 | println!( 168 | "You are not fuzzing, just executing {} testcases", 169 | filenames.len() 170 | ); 171 | for fname in filenames { 172 | println!("Executing {}", fname); 173 | 174 | let mut file = File::open(fname).expect("No file found"); 175 | let mut buffer = vec![]; 176 | file.read_to_end(&mut buffer).expect("Buffer overflow"); 177 | 178 | unsafe { libfuzzer_test_one_input(&buffer) }; 179 | } 180 | } 181 | 182 | /// The actual fuzzer 183 | fn fuzz( 184 | corpus_dir: PathBuf, 185 | objective_dir: PathBuf, 186 | seed_dir: PathBuf, 187 | tokenfile: Option, 188 | timeout: Duration, 189 | ) -> Result<(), Error> { 190 | #[cfg(unix)] 191 | let mut stdout_cpy = unsafe { 192 | let new_fd = dup(io::stdout().as_raw_fd())?; 193 | File::from_raw_fd(new_fd) 194 | }; 195 | #[cfg(unix)] 196 | let file_null = File::open("/dev/null")?; 197 | 198 | // 'While the monitor are state, they are usually used in the broker - which is likely never restarted 199 | let monitor = SimpleMonitor::new(|s| { 200 | #[cfg(unix)] 201 | writeln!(&mut stdout_cpy, "{}", s).unwrap(); 202 | #[cfg(windows)] 203 | println!("{}", s); 204 | }); 205 | 206 | // We need a shared map to store our state before a crash. 207 | // This way, we are able to continue fuzzing afterwards. 208 | let mut shmem_provider = StdShMemProvider::new()?; 209 | 210 | let (state, mut mgr) = match SimpleRestartingEventManager::launch(monitor, &mut shmem_provider) 211 | { 212 | // The restarting state will spawn the same process again as child, then restarted it each time it crashes. 213 | Ok(res) => res, 214 | Err(err) => match err { 215 | Error::ShuttingDown => { 216 | return Ok(()); 217 | } 218 | _ => { 219 | panic!("Failed to setup the restarter: {}", err); 220 | } 221 | }, 222 | }; 223 | 224 | // Create an observation channel using the coverage map 225 | // We don't use the hitcounts (see the Cargo.toml, we use pcguard_edges) 226 | let edges_observer = 227 | HitcountsMapObserver::new(unsafe { std_edges_map_observer("edges") }).track_indices(); 228 | 229 | // Create an observation channel to keep track of the execution time 230 | let time_observer = TimeObserver::new("time"); 231 | 232 | let cmps = core::ptr::addr_of_mut!(CMP_MAP); 233 | let cmps_observer = unsafe { StdMapObserver::new("cmps", &mut *cmps) }; 234 | 235 | // Feedback to rate the interestingness of an input 236 | // This one is composed by two Feedbacks in OR 237 | let mut feedback = feedback_or!( 238 | // New maximization map feedback linked to the edges observer and the feedback state 239 | MaxMapFeedback::new(&edges_observer), 240 | // Cmp max feedback 241 | MaxMapFeedback::new(&cmps_observer), 242 | // Time feedback, this one does not need a feedback state 243 | TimeFeedback::new(&time_observer) 244 | ); 245 | 246 | // A feedback to choose if an input is a solution or not 247 | let mut objective = CrashFeedback::new(); 248 | 249 | // If not restarting, create a State from scratch 250 | let mut state = state.unwrap_or_else(|| { 251 | StdState::new( 252 | // RNG 253 | StdRand::with_seed(current_nanos()), 254 | // Corpus that will be evolved, we keep it in memory for performance 255 | InMemoryOnDiskCorpus::new(corpus_dir).unwrap(), 256 | // Corpus in which we store solutions (crashes in this example), 257 | // on disk so the user can get them after stopping the fuzzer 258 | OnDiskCorpus::new(objective_dir).unwrap(), 259 | // States of the feedbacks. 260 | // They are the data related to the feedbacks that you want to persist in the State. 261 | &mut feedback, 262 | &mut objective, 263 | ) 264 | .unwrap() 265 | }); 266 | 267 | println!("Let's fuzz :)"); 268 | 269 | // The actual target run starts here. 270 | // Call LLVMFUzzerInitialize() if present. 271 | let args: Vec = env::args().collect(); 272 | if unsafe { libfuzzer_initialize(&args) } == -1 { 273 | println!("Warning: LLVMFuzzerInitialize failed with -1") 274 | } 275 | 276 | let mutator = StdMutationalStage::new(StdScheduledMutator::new( 277 | havoc_mutations().merge(tokens_mutations()), 278 | )); 279 | 280 | // A minimization+queue policy to get testcasess from the corpus 281 | let scheduler = IndexesLenTimeMinimizerScheduler::new(&edges_observer, QueueScheduler::new()); 282 | 283 | // A fuzzer with feedbacks and a corpus scheduler 284 | let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); 285 | 286 | // The wrapped harness function, calling out to the LLVM-style harness 287 | let mut harness = |input: &BytesInput| { 288 | let target = input.target_bytes(); 289 | let buf = target.as_slice(); 290 | unsafe { libfuzzer_test_one_input(buf) }; 291 | ExitKind::Ok 292 | }; 293 | 294 | // Create the executor for an in-process function with one observer for edge coverage and one for the execution time 295 | let mut executor = InProcessExecutor::with_timeout( 296 | &mut harness, 297 | tuple_list!(edges_observer, cmps_observer, time_observer), 298 | &mut fuzzer, 299 | &mut state, 300 | &mut mgr, 301 | timeout, 302 | )?; 303 | 304 | // The order of the stages matter! 305 | let mut stages = tuple_list!(mutator); 306 | 307 | // Read tokens 308 | if state.metadata_map().get::().is_none() { 309 | let mut toks = Tokens::default(); 310 | if let Some(tokenfile) = tokenfile { 311 | toks.add_from_file(tokenfile)?; 312 | } 313 | #[cfg(target_os = "linux")] 314 | { 315 | toks += autotokens()?; 316 | } 317 | 318 | if !toks.is_empty() { 319 | state.add_metadata(toks); 320 | } 321 | } 322 | 323 | // In case the corpus is empty (on first run), reset 324 | if state.corpus().count() < 1 { 325 | state 326 | .load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &[seed_dir.clone()]) 327 | .unwrap_or_else(|_| { 328 | println!("Failed to load initial corpus at {:?}", &seed_dir); 329 | process::exit(0); 330 | }); 331 | println!("We imported {} inputs from disk.", state.corpus().count()); 332 | } 333 | 334 | // Remove target ouput (logs still survive) 335 | #[cfg(unix)] 336 | { 337 | let null_fd = file_null.as_raw_fd(); 338 | dup2(null_fd, io::stdout().as_raw_fd())?; 339 | dup2(null_fd, io::stderr().as_raw_fd())?; 340 | } 341 | 342 | fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)?; 343 | 344 | // Never reached 345 | Ok(()) 346 | } 347 | -------------------------------------------------------------------------------- /value_profile_cmplog/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "value_profile_cmplog" 3 | version = "0.0.1" 4 | authors = ["Andrea Fioraldi ", "Dominik Maier "] 5 | edition = "2021" 6 | 7 | [features] 8 | no_link_main = ["libafl_targets/libfuzzer_no_link_main"] 9 | 10 | [dependencies] 11 | libafl = { path = "../LibAFL/libafl/" } 12 | libafl_bolts = { path = "../LibAFL/libafl_bolts/" } 13 | libafl_targets = { path = "../LibAFL/libafl_targets/", features = ["sancov_pcguard_hitcounts", "sancov_value_profile", "sancov_cmplog", "libfuzzer"] } 14 | # TODO Include it only when building cc 15 | libafl_cc = { path = "../LibAFL/libafl_cc/" } 16 | clap = { version = "~4.2", features = ["default"] } 17 | nix = { version = "0.29", features = ["fs"] } 18 | mimalloc = { version = "*", default-features = false } 19 | 20 | [lib] 21 | crate-type = ["staticlib"] 22 | -------------------------------------------------------------------------------- /value_profile_cmplog/src/bin/value_profile_cmplog_cc.rs: -------------------------------------------------------------------------------- 1 | use libafl_cc::ToolWrapper; 2 | use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses}; 3 | use std::env; 4 | pub fn main() { 5 | let args: Vec = env::args().collect(); 6 | if args.len() > 1 { 7 | let mut dir = env::current_exe().unwrap(); 8 | let wrapper_name = dir.file_name().unwrap().to_str().unwrap(); 9 | 10 | let is_cpp = match wrapper_name[wrapper_name.len()-2..].to_lowercase().as_str() { 11 | "cc" => false, 12 | "++" | "pp" | "xx" => true, 13 | _ => panic!("Could not figure out if c or c++ warpper was called. Expected {:?} to end with c or cxx", dir), 14 | }; 15 | 16 | dir.pop(); 17 | 18 | let mut cc = ClangWrapper::new(); 19 | 20 | #[cfg(target_os = "linux")] 21 | cc.add_pass(LLVMPasses::AutoTokens); 22 | 23 | if let Some(code) = cc 24 | .cpp(is_cpp) 25 | // silence the compiler wrapper output, needed for some configure scripts. 26 | .silence(true) 27 | // add arguments only if --libafl or --libafl-no-link are present 28 | .need_libafl_arg(true) 29 | .parse_args(&args) 30 | .expect("Failed to parse the command line") 31 | .link_staticlib(&dir, env!("CARGO_PKG_NAME")) 32 | .add_arg("-fsanitize-coverage=trace-pc-guard,trace-cmp") 33 | .add_pass(LLVMPasses::CmpLogRtn) 34 | .run() 35 | .expect("Failed to run the wrapped compiler") 36 | { 37 | std::process::exit(code); 38 | } 39 | } else { 40 | panic!("LibAFL CC: No Arguments given"); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /value_profile_cmplog/src/bin/value_profile_cmplog_cxx.rs: -------------------------------------------------------------------------------- 1 | pub mod value_profile_cmplog_cc; 2 | 3 | fn main() { 4 | value_profile_cmplog_cc::main() 5 | } 6 | -------------------------------------------------------------------------------- /weighted/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "weighted" 3 | version = "0.0.1" 4 | authors = ["Andrea Fioraldi ", "Dominik Maier "] 5 | edition = "2021" 6 | 7 | [features] 8 | no_link_main = ["libafl_targets/libfuzzer_no_link_main"] 9 | 10 | [dependencies] 11 | libafl = { path = "../LibAFL/libafl/" } 12 | libafl_bolts = { path = "../LibAFL/libafl_bolts/" } 13 | libafl_targets = { path = "../LibAFL/libafl_targets/", features = ["sancov_pcguard_hitcounts", "libfuzzer"] } 14 | # TODO Include it only when building cc 15 | libafl_cc = { path = "../LibAFL/libafl_cc/" } 16 | clap = { version = "~4.2", features = ["default"] } 17 | nix = { version = "0.29", features = ["fs"] } 18 | mimalloc = { version = "*", default-features = false } 19 | 20 | [lib] 21 | crate-type = ["staticlib"] 22 | -------------------------------------------------------------------------------- /weighted/src/bin/weighted_cc.rs: -------------------------------------------------------------------------------- 1 | use libafl_cc::ToolWrapper; 2 | use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses}; 3 | use std::env; 4 | pub fn main() { 5 | let args: Vec = env::args().collect(); 6 | if args.len() > 1 { 7 | let mut dir = env::current_exe().unwrap(); 8 | let wrapper_name = dir.file_name().unwrap().to_str().unwrap(); 9 | 10 | let is_cpp = match wrapper_name[wrapper_name.len()-2..].to_lowercase().as_str() { 11 | "cc" => false, 12 | "++" | "pp" | "xx" => true, 13 | _ => panic!("Could not figure out if c or c++ warpper was called. Expected {:?} to end with c or cxx", dir), 14 | }; 15 | 16 | dir.pop(); 17 | 18 | let mut cc = ClangWrapper::new(); 19 | 20 | #[cfg(target_os = "linux")] 21 | cc.add_pass(LLVMPasses::AutoTokens); 22 | 23 | if let Some(code) = cc 24 | .cpp(is_cpp) 25 | // silence the compiler wrapper output, needed for some configure scripts. 26 | .silence(true) 27 | // add arguments only if --libafl or --libafl-no-link are present 28 | .need_libafl_arg(true) 29 | .parse_args(&args) 30 | .expect("Failed to parse the command line") 31 | .link_staticlib(&dir, env!("CARGO_PKG_NAME")) 32 | .add_arg("-fsanitize-coverage=trace-pc-guard") 33 | .run() 34 | .expect("Failed to run the wrapped compiler") 35 | { 36 | std::process::exit(code); 37 | } 38 | } else { 39 | panic!("LibAFL CC: No Arguments given"); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /weighted/src/bin/weighted_cxx.rs: -------------------------------------------------------------------------------- 1 | pub mod weighted_cc; 2 | 3 | fn main() { 4 | weighted_cc::main() 5 | } 6 | -------------------------------------------------------------------------------- /weighted/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A singlethreaded libfuzzer-like fuzzer that can auto-restart. 2 | use mimalloc::MiMalloc; 3 | #[global_allocator] 4 | static GLOBAL: MiMalloc = MiMalloc; 5 | 6 | use libafl::observers::CanTrack; 7 | use libafl::HasMetadata; 8 | use libafl_bolts::{ 9 | current_nanos, 10 | os::dup2, 11 | rands::StdRand, 12 | shmem::{ShMemProvider, StdShMemProvider}, 13 | tuples::{tuple_list, Merge}, 14 | AsSlice, 15 | }; 16 | 17 | use clap::{Arg, Command}; 18 | use core::time::Duration; 19 | #[cfg(unix)] 20 | use nix::{self, unistd::dup}; 21 | #[cfg(unix)] 22 | use std::os::unix::io::{AsRawFd, FromRawFd}; 23 | use std::{ 24 | env, 25 | fs::{self, File}, 26 | io::{self, Read, Write}, 27 | path::PathBuf, 28 | process, 29 | }; 30 | 31 | use libafl::{ 32 | corpus::{Corpus, InMemoryOnDiskCorpus, OnDiskCorpus}, 33 | events::SimpleRestartingEventManager, 34 | executors::{inprocess::InProcessExecutor, ExitKind}, 35 | feedback_or, 36 | feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback}, 37 | fuzzer::{Fuzzer, StdFuzzer}, 38 | inputs::{BytesInput, HasTargetBytes}, 39 | monitors::SimpleMonitor, 40 | mutators::{havoc_mutations::havoc_mutations, tokens_mutations, StdScheduledMutator, Tokens}, 41 | observers::{HitcountsMapObserver, TimeObserver}, 42 | schedulers::{IndexesLenTimeMinimizerScheduler, StdWeightedScheduler}, 43 | stages::{power::StdPowerMutationalStage, CalibrationStage}, 44 | state::{HasCorpus, StdState}, 45 | Error, 46 | }; 47 | use libafl_targets::{libfuzzer_initialize, libfuzzer_test_one_input, std_edges_map_observer}; 48 | 49 | #[cfg(target_os = "linux")] 50 | use libafl_targets::autotokens; 51 | 52 | /// The fuzzer main (as `no_mangle` C function) 53 | #[no_mangle] 54 | pub fn libafl_main() { 55 | // Registry the metadata types used in this fuzzer 56 | // Needed only on no_std 57 | //RegistryBuilder::register::(); 58 | 59 | let res = match Command::new(env!("CARGO_PKG_NAME")) 60 | .version(env!("CARGO_PKG_VERSION")) 61 | .author("AFLplusplus team") 62 | .about("LibAFL-based fuzzer for Fuzzbench") 63 | .arg( 64 | Arg::new("out") 65 | .short('o') 66 | .long("output") 67 | .help("The directory to place finds in ('corpus')"), 68 | ) 69 | .arg( 70 | Arg::new("in") 71 | .short('i') 72 | .long("input") 73 | .help("The directory to read initial inputs from ('seeds')"), 74 | ) 75 | .arg( 76 | Arg::new("tokens") 77 | .short('x') 78 | .long("tokens") 79 | .help("A file to read tokens from, to be used during fuzzing"), 80 | ) 81 | .arg( 82 | Arg::new("timeout") 83 | .short('t') 84 | .long("timeout") 85 | .help("Timeout for each individual execution, in milliseconds") 86 | .default_value("1200"), 87 | ) 88 | .arg(Arg::new("remaining")) 89 | .try_get_matches() 90 | { 91 | Ok(res) => res, 92 | Err(err) => { 93 | println!( 94 | "Syntax: {}, [-x dictionary] -o corpus_dir -i seed_dir\n{:?}", 95 | env::current_exe() 96 | .unwrap_or_else(|_| "fuzzer".into()) 97 | .to_string_lossy(), 98 | err, 99 | ); 100 | return; 101 | } 102 | }; 103 | 104 | println!( 105 | "Workdir: {:?}", 106 | env::current_dir().unwrap().to_string_lossy().to_string() 107 | ); 108 | 109 | if let Some(filenames) = res.get_many::("remaining") { 110 | let filenames: Vec<&str> = filenames.map(String::as_str).collect(); 111 | if !filenames.is_empty() { 112 | run_testcases(&filenames); 113 | return; 114 | } 115 | } 116 | 117 | // For fuzzbench, crashes and finds are inside the same `corpus` directory, in the "queue" and "crashes" subdir. 118 | let mut out_dir = PathBuf::from( 119 | res.get_one::("out") 120 | .expect("The --output parameter is missing") 121 | .to_string(), 122 | ); 123 | if fs::create_dir(&out_dir).is_err() { 124 | println!("Out dir at {:?} already exists.", &out_dir); 125 | if !out_dir.is_dir() { 126 | println!("Out dir at {:?} is not a valid directory!", &out_dir); 127 | return; 128 | } 129 | } 130 | let mut crashes = out_dir.clone(); 131 | crashes.push("crashes"); 132 | out_dir.push("queue"); 133 | 134 | let in_dir = PathBuf::from( 135 | res.get_one::("in") 136 | .expect("The --input parameter is missing") 137 | .to_string(), 138 | ); 139 | if !in_dir.is_dir() { 140 | println!("In dir at {:?} is not a valid directory!", &in_dir); 141 | return; 142 | } 143 | 144 | let tokens = res.get_one::("tokens").map(PathBuf::from); 145 | 146 | let timeout = Duration::from_millis( 147 | res.get_one::("timeout") 148 | .unwrap() 149 | .to_string() 150 | .parse() 151 | .expect("Could not parse timeout in milliseconds"), 152 | ); 153 | 154 | fuzz(out_dir, crashes, in_dir, tokens, timeout).expect("An error occurred while fuzzing"); 155 | } 156 | 157 | fn run_testcases(filenames: &[&str]) { 158 | // The actual target run starts here. 159 | // Call LLVMFUzzerInitialize() if present. 160 | let args: Vec = env::args().collect(); 161 | if unsafe { libfuzzer_initialize(&args) } == -1 { 162 | println!("Warning: LLVMFuzzerInitialize failed with -1") 163 | } 164 | 165 | println!( 166 | "You are not fuzzing, just executing {} testcases", 167 | filenames.len() 168 | ); 169 | for fname in filenames { 170 | println!("Executing {}", fname); 171 | 172 | let mut file = File::open(fname).expect("No file found"); 173 | let mut buffer = vec![]; 174 | file.read_to_end(&mut buffer).expect("Buffer overflow"); 175 | 176 | unsafe { libfuzzer_test_one_input(&buffer) }; 177 | } 178 | } 179 | 180 | /// The actual fuzzer 181 | fn fuzz( 182 | corpus_dir: PathBuf, 183 | objective_dir: PathBuf, 184 | seed_dir: PathBuf, 185 | tokenfile: Option, 186 | timeout: Duration, 187 | ) -> Result<(), Error> { 188 | #[cfg(unix)] 189 | let mut stdout_cpy = unsafe { 190 | let new_fd = dup(io::stdout().as_raw_fd())?; 191 | File::from_raw_fd(new_fd) 192 | }; 193 | #[cfg(unix)] 194 | let file_null = File::open("/dev/null")?; 195 | 196 | // 'While the monitor are state, they are usually used in the broker - which is likely never restarted 197 | let monitor = SimpleMonitor::new(|s| { 198 | #[cfg(unix)] 199 | writeln!(&mut stdout_cpy, "{}", s).unwrap(); 200 | #[cfg(windows)] 201 | println!("{}", s); 202 | }); 203 | 204 | // We need a shared map to store our state before a crash. 205 | // This way, we are able to continue fuzzing afterwards. 206 | let mut shmem_provider = StdShMemProvider::new()?; 207 | 208 | let (state, mut mgr) = match SimpleRestartingEventManager::launch(monitor, &mut shmem_provider) 209 | { 210 | // The restarting state will spawn the same process again as child, then restarted it each time it crashes. 211 | Ok(res) => res, 212 | Err(err) => match err { 213 | Error::ShuttingDown => { 214 | return Ok(()); 215 | } 216 | _ => { 217 | panic!("Failed to setup the restarter: {}", err); 218 | } 219 | }, 220 | }; 221 | 222 | // Create an observation channel using the coverage map 223 | // We don't use the hitcounts (see the Cargo.toml, we use pcguard_edges) 224 | let edges_observer = 225 | HitcountsMapObserver::new(unsafe { std_edges_map_observer("edges") }).track_indices(); 226 | 227 | let map_feedback = MaxMapFeedback::new(&edges_observer); 228 | let calibration = CalibrationStage::new(&map_feedback); 229 | // Create an observation channel to keep track of the execution time 230 | let time_observer = TimeObserver::new("time"); 231 | 232 | // Feedback to rate the interestingness of an input 233 | // This one is composed by two Feedbacks in OR 234 | let mut feedback = feedback_or!( 235 | // New maximization map feedback linked to the edges observer and the feedback state 236 | map_feedback, 237 | // Time feedback, this one does not need a feedback state 238 | TimeFeedback::new(&time_observer) 239 | ); 240 | 241 | // A feedback to choose if an input is a solution or not 242 | let mut objective = CrashFeedback::new(); 243 | 244 | // If not restarting, create a State from scratch 245 | let mut state = state.unwrap_or_else(|| { 246 | StdState::new( 247 | // RNG 248 | StdRand::with_seed(current_nanos()), 249 | // Corpus that will be evolved, we keep it in memory for performance 250 | InMemoryOnDiskCorpus::new(corpus_dir).unwrap(), 251 | // Corpus in which we store solutions (crashes in this example), 252 | // on disk so the user can get them after stopping the fuzzer 253 | OnDiskCorpus::new(objective_dir).unwrap(), 254 | // States of the feedbacks. 255 | // They are the data related to the feedbacks that you want to persist in the State. 256 | &mut feedback, 257 | &mut objective, 258 | ) 259 | .unwrap() 260 | }); 261 | 262 | println!("Let's fuzz :)"); 263 | 264 | // The actual target run starts here. 265 | // Call LLVMFUzzerInitialize() if present. 266 | let args: Vec = env::args().collect(); 267 | if unsafe { libfuzzer_initialize(&args) } == -1 { 268 | println!("Warning: LLVMFuzzerInitialize failed with -1") 269 | } 270 | 271 | let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations())); 272 | 273 | let power: StdPowerMutationalStage<_, _, BytesInput, _, _> = 274 | StdPowerMutationalStage::new(mutator); 275 | 276 | // A minimization+queue policy to get testcasess from the corpus 277 | let scheduler = IndexesLenTimeMinimizerScheduler::new( 278 | &edges_observer, 279 | StdWeightedScheduler::new(&mut state, &edges_observer), 280 | ); 281 | 282 | // A fuzzer with feedbacks and a corpus scheduler 283 | let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); 284 | 285 | // The wrapped harness function, calling out to the LLVM-style harness 286 | let mut harness = |input: &BytesInput| { 287 | let target = input.target_bytes(); 288 | let buf = target.as_slice(); 289 | unsafe { libfuzzer_test_one_input(buf) }; 290 | ExitKind::Ok 291 | }; 292 | 293 | // Create the executor for an in-process function with one observer for edge coverage and one for the execution time 294 | let mut executor = InProcessExecutor::with_timeout( 295 | &mut harness, 296 | tuple_list!(edges_observer, time_observer), 297 | &mut fuzzer, 298 | &mut state, 299 | &mut mgr, 300 | timeout, 301 | )?; 302 | 303 | // The order of the stages matter! 304 | let mut stages = tuple_list!(calibration, power); 305 | 306 | // Read tokens 307 | if state.metadata_map().get::().is_none() { 308 | let mut toks = Tokens::default(); 309 | if let Some(tokenfile) = tokenfile { 310 | toks.add_from_file(tokenfile)?; 311 | } 312 | #[cfg(target_os = "linux")] 313 | { 314 | toks += autotokens()?; 315 | } 316 | 317 | if !toks.is_empty() { 318 | state.add_metadata(toks); 319 | } 320 | } 321 | 322 | // In case the corpus is empty (on first run), reset 323 | if state.corpus().count() < 1 { 324 | state 325 | .load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &[seed_dir.clone()]) 326 | .unwrap_or_else(|_| { 327 | println!("Failed to load initial corpus at {:?}", &seed_dir); 328 | process::exit(0); 329 | }); 330 | println!("We imported {} inputs from disk.", state.corpus().count()); 331 | } 332 | 333 | // Remove target ouput (logs still survive) 334 | #[cfg(unix)] 335 | { 336 | let null_fd = file_null.as_raw_fd(); 337 | dup2(null_fd, io::stdout().as_raw_fd())?; 338 | dup2(null_fd, io::stderr().as_raw_fd())?; 339 | } 340 | 341 | fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)?; 342 | 343 | // Never reached 344 | Ok(()) 345 | } 346 | --------------------------------------------------------------------------------