diff --git a/fuzzers/others/libafl-fuzz/.gitignore b/fuzzers/others/libafl-fuzz/.gitignore new file mode 100644 index 0000000000..0a452b5af1 --- /dev/null +++ b/fuzzers/others/libafl-fuzz/.gitignore @@ -0,0 +1,4 @@ +test/out-cmplog +test/out-instr +test/output-cmplog/ +test/output/ diff --git a/fuzzers/others/libafl-fuzz/Makefile.toml b/fuzzers/others/libafl-fuzz/Makefile.toml index f4e9e9e136..4e9105b031 100644 --- a/fuzzers/others/libafl-fuzz/Makefile.toml +++ b/fuzzers/others/libafl-fuzz/Makefile.toml @@ -12,7 +12,7 @@ FUZZER = '${CARGO_TARGET_DIR}/${PROFILE_DIR}/${FUZZER_NAME}' LLVM_CONFIG = { value = "llvm-config-18", condition = { env_not_set = [ "LLVM_CONFIG", ] } } -AFL_VERSION = "598a3c6b5e24bd33e84b914e145810d39f88adf6" +AFL_VERSION = "8b35dd49be5f846e945f6d6a9414623d195a99cb" AFL_DIR = { value = "${PROJECT_DIR}/AFLplusplus" } AFL_CC_PATH = { value = "${AFL_DIR}/afl-clang-fast" } CC = { value = "clang" } diff --git a/fuzzers/others/libafl-fuzz/rustfmt.toml b/fuzzers/others/libafl-fuzz/rustfmt.toml new file mode 100644 index 0000000000..44b6aab556 --- /dev/null +++ b/fuzzers/others/libafl-fuzz/rustfmt.toml @@ -0,0 +1,2 @@ +group_imports = "StdExternalCrate" +imports_granularity = "Crate" diff --git a/fuzzers/others/libafl-fuzz/src/afl_stats.rs b/fuzzers/others/libafl-fuzz/src/afl_stats.rs index 1113d25cb5..48203fa47d 100644 --- a/fuzzers/others/libafl-fuzz/src/afl_stats.rs +++ b/fuzzers/others/libafl-fuzz/src/afl_stats.rs @@ -4,7 +4,7 @@ use std::{ fmt::Display, fs::{File, OpenOptions}, io::{BufRead, BufReader, Write}, - path::PathBuf, + path::{Path, PathBuf}, process, }; @@ -444,7 +444,7 @@ where } } - fn create_plot_data_file(fuzzer_dir: &PathBuf) -> Result<(), Error> { + fn create_plot_data_file(fuzzer_dir: &Path) -> Result<(), Error> { let path = fuzzer_dir.join("plot_data"); if path.exists() { // check if it contains any data @@ -458,10 +458,10 @@ where Ok(()) } - fn create_fuzzer_stats_file(fuzzer_dir: &PathBuf) -> Result<(), Error> { + fn create_fuzzer_stats_file(fuzzer_dir: &Path) -> Result<(), Error> { let path = fuzzer_dir.join("fuzzer_stats"); if !path.exists() { - OpenOptions::new().append(true).create(true).open(path)?; + _ = OpenOptions::new().append(true).create(true).open(path)?; } Ok(()) } @@ -470,7 +470,7 @@ where let tmp_file = self.fuzzer_dir.join(".fuzzer_stats_tmp"); let stats_file = self.fuzzer_dir.join("fuzzer_stats"); std::fs::write(&tmp_file, stats.to_string())?; - std::fs::copy(&tmp_file, &stats_file)?; + _ = std::fs::copy(&tmp_file, &stats_file)?; std::fs::remove_file(tmp_file)?; Ok(()) } diff --git a/fuzzers/others/libafl-fuzz/src/corpus.rs b/fuzzers/others/libafl-fuzz/src/corpus.rs index 0499f6d764..8d3afae1bd 100644 --- a/fuzzers/others/libafl-fuzz/src/corpus.rs +++ b/fuzzers/others/libafl-fuzz/src/corpus.rs @@ -2,7 +2,7 @@ use std::{ borrow::Cow, fs::File, io::{self, BufRead, BufReader}, - path::{Path, PathBuf}, + path::Path, }; use libafl::{ @@ -39,10 +39,12 @@ pub fn generate_base_filename(state: &mut LibaflFuzzState) -> String { name } +// The function needs to be compatible with CustomFilepathToTestcaseFeedback +#[allow(clippy::unnecessary_wraps)] pub fn set_corpus_filepath( state: &mut LibaflFuzzState, testcase: &mut Testcase, - _fuzzer_dir: &PathBuf, + _fuzzer_dir: &Path, ) -> Result<(), Error> { let mut name = generate_base_filename(state); if testcase.hit_feedbacks().contains(&Cow::Borrowed("edges")) { @@ -53,10 +55,12 @@ pub fn set_corpus_filepath( Ok(()) } +// The function needs to be compatible with CustomFilepathToTestcaseFeedback +#[allow(clippy::unnecessary_wraps)] pub fn set_solution_filepath( state: &mut LibaflFuzzState, testcase: &mut Testcase, - output_dir: &PathBuf, + output_dir: &Path, ) -> Result<(), Error> { // sig:0SIGNAL // TODO: verify if 0 time if objective found during seed loading @@ -137,7 +141,7 @@ pub fn check_autoresume(fuzzer_dir: &Path, auto_resume: bool) -> Result std::io::Result<()> { +pub fn create_dir_if_not_exists(path: &Path) -> io::Result<()> { if path.is_file() { return Err(io::Error::new( // TODO: change this to ErrorKind::NotADirectory @@ -158,8 +162,8 @@ pub fn create_dir_if_not_exists(path: &PathBuf) -> std::io::Result<()> { } } -pub fn remove_main_node_file(output_dir: &PathBuf) -> Result<(), Error> { - for entry in std::fs::read_dir(output_dir)?.filter_map(std::result::Result::ok) { +pub fn remove_main_node_file(output_dir: &Path) -> Result<(), Error> { + for entry in std::fs::read_dir(output_dir)?.filter_map(Result::ok) { let path = entry.path(); if path.is_dir() && path.join("is_main_node").exists() { std::fs::remove_file(path.join("is_main_node"))?; diff --git a/fuzzers/others/libafl-fuzz/src/env_parser.rs b/fuzzers/others/libafl-fuzz/src/env_parser.rs index fc58715ffe..d1b2def508 100644 --- a/fuzzers/others/libafl-fuzz/src/env_parser.rs +++ b/fuzzers/others/libafl-fuzz/src/env_parser.rs @@ -38,8 +38,7 @@ pub fn parse_envs(opt: &mut Opt) -> Result<(), Error> { opt.no_autodict = parse_bool(&res)?; } if let Ok(res) = std::env::var("AFL_MAP_SIZE") { - let map_size = res.parse()?; - validate_map_size(map_size)?; + let map_size = validate_map_size(res.parse()?)?; opt.map_size = Some(map_size); }; if let Ok(res) = std::env::var("AFL_IGNORE_TIMEOUT") { @@ -131,7 +130,7 @@ fn parse_target_env(s: &str) -> Result>, Error> { let env_regex = regex::Regex::new(r"([^\s=]+)\s*=\s*([^\s]+)").unwrap(); let mut target_env = HashMap::new(); for vars in env_regex.captures_iter(s) { - target_env.insert( + _ = target_env.insert( vars.get(1) .ok_or(Error::illegal_argument("invalid AFL_TARGET_ENV format"))? .as_str() diff --git a/fuzzers/others/libafl-fuzz/src/executor.rs b/fuzzers/others/libafl-fuzz/src/executor.rs index 0db9a8f34c..8496ff0ebe 100644 --- a/fuzzers/others/libafl-fuzz/src/executor.rs +++ b/fuzzers/others/libafl-fuzz/src/executor.rs @@ -1,6 +1,6 @@ use std::{ fs::File, - os::{linux::fs::MetadataExt, unix::fs::PermissionsExt}, + os::unix::fs::PermissionsExt, path::{Path, PathBuf}, }; @@ -54,7 +54,7 @@ pub fn check_binary(opt: &mut Opt, shmem_env_var: &str) -> Result<(), Error> { let metadata = bin_path.metadata()?; // AFL++ does not follow symlinks, BUT we do. let is_reg = bin_path.is_file(); - let bin_size = metadata.st_size(); + let bin_size = metadata.len(); let is_executable = metadata.permissions().mode() & 0o111 != 0; if !is_reg || !is_executable || bin_size < 4 { return Err(Error::illegal_argument(format!( @@ -84,6 +84,7 @@ pub fn check_binary(opt: &mut Opt, shmem_env_var: &str) -> Result<(), Error> { } // check if the binary is an ELF file + #[cfg(target_os = "linux")] if mmap[0..4] != [0x7f, 0x45, 0x4c, 0x46] { return Err(Error::illegal_argument(format!( "Program '{}' is not an ELF binary", @@ -91,7 +92,7 @@ pub fn check_binary(opt: &mut Opt, shmem_env_var: &str) -> Result<(), Error> { ))); } - #[cfg(all(target_os = "macos", not(target_arch = "arm")))] + #[cfg(target_vendor = "apple")] { if (mmap[0] != 0xCF || mmap[1] != 0xFA || mmap[2] != 0xED) && (mmap[0] != 0xCA || mmap[1] != 0xFE || mmap[2] != 0xBA) @@ -186,13 +187,18 @@ fn find_executable_in_path>(executable: &P) -> Option { } pub fn find_afl_binary(filename: &str, same_dir_as: Option) -> Result { - let is_library = - filename.contains('.') && filename.ends_with(".so") || filename.ends_with(".dylib"); - - let permission = if is_library { - S_IRUSR // user can read + let extension = Path::new(filename).extension(); + let is_library = if let Some(extension) = extension { + extension.eq_ignore_ascii_case("so") || extension.eq_ignore_ascii_case("dylib") } else { - S_IXUSR // user can exec + false + }; + + #[allow(clippy::useless_conversion)] // u16 on MacOS, u32 on Linux + let permission = if is_library { + u32::from(S_IRUSR) // user can read + } else { + u32::from(S_IXUSR) // user can exec }; // First we check if it is present in AFL_PATH @@ -229,7 +235,7 @@ pub fn find_afl_binary(filename: &str, same_dir_as: Option) -> Result

bool { +fn check_file_found(file: &Path, perm: u32) -> bool { if !file.exists() { return false; } diff --git a/fuzzers/others/libafl-fuzz/src/feedback/filepath.rs b/fuzzers/others/libafl-fuzz/src/feedback/filepath.rs index 4e2326571c..42abcb1d26 100644 --- a/fuzzers/others/libafl-fuzz/src/feedback/filepath.rs +++ b/fuzzers/others/libafl-fuzz/src/feedback/filepath.rs @@ -2,7 +2,7 @@ use std::{ borrow::Cow, fmt::{Debug, Formatter}, marker::PhantomData, - path::PathBuf, + path::{Path, PathBuf}, }; use libafl::{ @@ -26,7 +26,7 @@ pub struct CustomFilepathToTestcaseFeedback where I: Input, S: State, - F: FnMut(&mut S, &mut Testcase, &PathBuf) -> Result<(), Error>, + F: FnMut(&mut S, &mut Testcase, &Path) -> Result<(), Error>, { /// Closure that returns the filename. func: F, @@ -39,7 +39,7 @@ impl CustomFilepathToTestcaseFeedback where I: Input, S: State, - F: FnMut(&mut S, &mut Testcase, &PathBuf) -> Result<(), Error>, + F: FnMut(&mut S, &mut Testcase, &Path) -> Result<(), Error>, { /// Create a new [`CustomFilepathToTestcaseFeedback`]. pub fn new(func: F, out_dir: PathBuf) -> Self { @@ -56,7 +56,7 @@ impl FeedbackFactory, T> where I: Input, S: State, - F: FnMut(&mut S, &mut Testcase, &PathBuf) -> Result<(), Error> + Clone, + F: FnMut(&mut S, &mut Testcase, &Path) -> Result<(), Error> + Clone, { fn create_feedback(&self, _ctx: &T) -> CustomFilepathToTestcaseFeedback { Self { @@ -71,7 +71,7 @@ impl Named for CustomFilepathToTestcaseFeedback where I: Input, S: State, - F: FnMut(&mut S, &mut Testcase, &PathBuf) -> Result<(), Error>, + F: FnMut(&mut S, &mut Testcase, &Path) -> Result<(), Error>, { fn name(&self) -> &Cow<'static, str> { static NAME: Cow<'static, str> = Cow::Borrowed("CustomFilepathToTestcaseFeedback"); @@ -83,7 +83,7 @@ impl Debug for CustomFilepathToTestcaseFeedback where I: Input, S: State, - F: FnMut(&mut S, &mut Testcase, &PathBuf) -> Result<(), Error>, + F: FnMut(&mut S, &mut Testcase, &Path) -> Result<(), Error>, { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.debug_struct("CustomFilepathToTestcaseFeedback") @@ -94,7 +94,7 @@ where impl Feedback for CustomFilepathToTestcaseFeedback where S: State, - F: FnMut(&mut S, &mut Testcase, &PathBuf) -> Result<(), Error>, + F: FnMut(&mut S, &mut Testcase, &Path) -> Result<(), Error>, I: Input, { #[allow(clippy::wrong_self_convention)] diff --git a/fuzzers/others/libafl-fuzz/src/feedback/persistent_record.rs b/fuzzers/others/libafl-fuzz/src/feedback/persistent_record.rs index 7cb0f85401..caa74599b5 100644 --- a/fuzzers/others/libafl-fuzz/src/feedback/persistent_record.rs +++ b/fuzzers/others/libafl-fuzz/src/feedback/persistent_record.rs @@ -102,7 +102,7 @@ where if self.should_run() { self.record.push_back(input.clone()); if self.record.len() == self.record_size { - self.record.pop_front(); + drop(self.record.pop_front()); } } Ok(false) diff --git a/fuzzers/others/libafl-fuzz/src/feedback/seed.rs b/fuzzers/others/libafl-fuzz/src/feedback/seed.rs index a3c2b82651..9ef5ab216c 100644 --- a/fuzzers/others/libafl-fuzz/src/feedback/seed.rs +++ b/fuzzers/others/libafl-fuzz/src/feedback/seed.rs @@ -11,7 +11,7 @@ use crate::Opt; /// A wrapper feedback used to determine actions for initial seeds. /// Handles `AFL_EXIT_ON_SEED_ISSUES`, `AFL_IGNORE_SEED_ISSUES` & default afl-fuzz behavior /// then, essentially becomes benign -#[allow(clippy::module_name_repetitions)] +#[allow(clippy::module_name_repetitions, clippy::struct_excessive_bools)] #[derive(Debug)] pub struct SeedFeedback where diff --git a/fuzzers/others/libafl-fuzz/src/fuzzer.rs b/fuzzers/others/libafl-fuzz/src/fuzzer.rs index 0bacd76af5..3bd20c29e8 100644 --- a/fuzzers/others/libafl-fuzz/src/fuzzer.rs +++ b/fuzzers/others/libafl-fuzz/src/fuzzer.rs @@ -1,4 +1,9 @@ -use std::{borrow::Cow, marker::PhantomData, path::PathBuf, time::Duration}; +use std::{ + borrow::Cow, + marker::PhantomData, + path::{Path, PathBuf}, + time::Duration, +}; use libafl::{ corpus::{CachedOnDiskCorpus, Corpus, OnDiskCorpus}, @@ -33,7 +38,7 @@ use libafl_bolts::{ fs::get_unique_std_input_file, ownedref::OwnedRefMut, rands::StdRand, - shmem::{ShMem, ShMemProvider, StdShMemProvider}, + shmem::{ShMem, ShMemProvider, UnixShMemProvider}, tuples::{tuple_list, Handled, Merge}, AsSliceMut, }; @@ -66,7 +71,7 @@ pub fn run_client( LibaflFuzzState, SP, >, - fuzzer_dir: &PathBuf, + fuzzer_dir: &Path, core_id: CoreId, opt: &Opt, is_main_node: bool, @@ -76,7 +81,7 @@ where SP: ShMemProvider, { // Create the shared memory map for comms with the forkserver - let mut shmem_provider = StdShMemProvider::new().unwrap(); + let mut shmem_provider = UnixShMemProvider::new().unwrap(); let mut shmem = shmem_provider .new_shmem(opt.map_size.unwrap_or(AFL_DEFAULT_MAP_SIZE)) .unwrap(); @@ -109,7 +114,7 @@ where // Create a AFLStatsStage; let afl_stats_stage = AflStatsStage::new( opt, - fuzzer_dir.clone(), + fuzzer_dir.to_path_buf(), &edges_observer, user_token_count, !opt.no_autodict, @@ -130,7 +135,7 @@ where feedback_or!( map_feedback, TimeFeedback::new(&time_observer), - CustomFilepathToTestcaseFeedback::new(set_corpus_filepath, fuzzer_dir.clone()) + CustomFilepathToTestcaseFeedback::new(set_corpus_filepath, fuzzer_dir.to_path_buf()) ), opt, ); @@ -153,7 +158,7 @@ where ), MaxMapFeedback::with_name("edges_objective", &edges_observer) ), - CustomFilepathToTestcaseFeedback::new(set_solution_filepath, fuzzer_dir.clone()), + CustomFilepathToTestcaseFeedback::new(set_solution_filepath, fuzzer_dir.to_path_buf()), PersitentRecordFeedback::new(opt.persistent_record), ); @@ -163,7 +168,7 @@ where StdRand::with_seed(opt.rng_seed.unwrap_or(current_nanos())), // TODO: configure testcache size CachedOnDiskCorpus::::new(fuzzer_dir.join("queue"), 1000).unwrap(), - OnDiskCorpus::::new(fuzzer_dir.clone()).unwrap(), + OnDiskCorpus::::new(fuzzer_dir).unwrap(), &mut feedback, &mut objective, ) @@ -234,19 +239,19 @@ where } // Create the base Executor - let mut executor = base_executor(opt, &mut shmem_provider, fuzzer_dir)?; + let mut executor_builder = base_executor_builder(opt, &mut shmem_provider, fuzzer_dir); // Set a custom exit code to be interpreted as a Crash if configured. if let Some(crash_exitcode) = opt.crash_exitcode { - executor = executor.crash_exitcode(crash_exitcode); + executor_builder = executor_builder.crash_exitcode(crash_exitcode); } // Enable autodict if configured if !opt.no_autodict { - executor = executor.autotokens(&mut tokens); + executor_builder = executor_builder.autotokens(&mut tokens); }; // Finalize and build our Executor - let mut executor = executor + let mut executor = executor_builder .build(tuple_list!(time_observer, edges_observer)) .unwrap(); @@ -270,7 +275,7 @@ where )))? .to_string(); filename = format!("id:{id:0>6},time:0,execs:0,orig:{filename}"); - let cpy_res = std::fs::copy(&path, queue_dir.join(filename)); + let cpy_res = std::fs::copy(path, queue_dir.join(filename)); match cpy_res { Err(e) if e.kind() == std::io::ErrorKind::InvalidInput => { println!("skipping {} since it is not a regular file", path.display()); @@ -354,7 +359,7 @@ where // Create the CmpLog executor. // Cmplog has 25% execution overhead so we give it double the timeout - let cmplog_executor = base_executor(opt, &mut shmem_provider, fuzzer_dir)? + let cmplog_executor = base_executor_builder(opt, &mut shmem_provider, fuzzer_dir) .timeout(Duration::from_millis(opt.hang_timeout * 2)) .program(cmplog_executable_path) .build(tuple_list!(cmplog_observer)) @@ -422,11 +427,11 @@ where // TODO: serialize state when exiting. } -fn base_executor<'a>( +fn base_executor_builder<'a>( opt: &'a Opt, - shmem_provider: &'a mut StdShMemProvider, - fuzzer_dir: &PathBuf, -) -> Result, Error> { + shmem_provider: &'a mut UnixShMemProvider, + fuzzer_dir: &Path, +) -> ForkserverExecutorBuilder<'a, UnixShMemProvider> { let mut executor = ForkserverExecutor::builder() .program(opt.executable.clone()) .coverage_map_size(opt.map_size.unwrap_or(AFL_DEFAULT_MAP_SIZE)) @@ -469,7 +474,7 @@ fn base_executor<'a>( .expect("to find afl-qemu-trace"), ); } - Ok(executor) + executor } pub fn fuzzer_target_mode(opt: &Opt) -> Cow<'static, str> { diff --git a/fuzzers/others/libafl-fuzz/src/main.rs b/fuzzers/others/libafl-fuzz/src/main.rs index ab1a2deb56..01b4b8c967 100644 --- a/fuzzers/others/libafl-fuzz/src/main.rs +++ b/fuzzers/others/libafl-fuzz/src/main.rs @@ -1,12 +1,69 @@ +#![forbid(unexpected_cfgs)] +#![allow(incomplete_features)] +#![warn(clippy::cargo)] +#![allow(ambiguous_glob_reexports)] +#![deny(clippy::cargo_common_metadata)] +#![deny(rustdoc::broken_intra_doc_links)] +#![deny(clippy::all)] #![deny(clippy::pedantic)] -#![allow(clippy::unsafe_derive_deserialize)] -#![allow(clippy::ptr_arg)] -#![allow(clippy::unnecessary_wraps)] -#![allow(clippy::module_name_repetitions)] -#![allow(clippy::similar_names)] -#![allow(clippy::too_many_lines)] -#![allow(clippy::struct_excessive_bools)] -#![allow(clippy::case_sensitive_file_extension_comparisons)] +#![allow( + clippy::unreadable_literal, + clippy::type_repetition_in_bounds, + clippy::missing_errors_doc, + clippy::cast_possible_truncation, + clippy::used_underscore_binding, + clippy::ptr_as_ptr, + clippy::missing_panics_doc, + clippy::missing_docs_in_private_items, + clippy::module_name_repetitions, + clippy::ptr_cast_constness, + clippy::unsafe_derive_deserialize, + clippy::similar_names, + clippy::too_many_lines, + clippy::into_iter_without_iter, // broken +)] +#![cfg_attr(not(test), warn( + missing_debug_implementations, + //missing_docs, + trivial_casts, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_qualifications, + //unused_results +))] +#![cfg_attr( + test, + deny( + missing_debug_implementations, + trivial_casts, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_qualifications, + unused_must_use, + //unused_results + ) +)] +#![cfg_attr( + test, + deny( + bad_style, + dead_code, + improper_ctypes, + non_shorthand_field_patterns, + no_mangle_generic_items, + overflowing_literals, + path_statements, + patterns_in_fns_without_body, + unconditional_recursion, + unused, + unused_allocation, + unused_comparisons, + unused_parens, + while_true + ) +)] use std::{collections::HashMap, path::PathBuf, time::Duration}; mod afl_stats; @@ -256,7 +313,7 @@ struct Opt { non_instrumented_mode: bool, } -#[allow(dead_code)] +#[allow(dead_code, clippy::struct_excessive_bools)] #[derive(Debug, Clone)] pub struct CmplogOpts { file_size: CmplogFileSize, @@ -285,6 +342,7 @@ impl From<&str> for CmplogFileSize { } } +#[allow(clippy::unnecessary_wraps)] // we need to be compatible with Clap's value_parser fn parse_cmplog_args(s: &str) -> Result { Ok(CmplogOpts { file_size: s.into(),