From 95f7c155bd5dfec71abe8b03062c5f81ddc4936f Mon Sep 17 00:00:00 2001 From: "Dongjia \"toka\" Zhang" Date: Thu, 24 Apr 2025 19:34:24 +0200 Subject: [PATCH] Make fuzzer hold testcase bytes converter (#3127) * add * fixing * done * fix * a * no std * mm * aaa * fix * mm * please stop * please stop * please stop * Just * aaaaa * aaa * plz stop * aaa * mmm * aaa * AAAAAAAAAAAAAA * a bit faster CI? * win * wtf are these garbages..? * no cache? * ??? * always * ?/ * aa * aa * glib * glib * pixman * AAA * AA * unicorn * unicron * nyx --- .github/workflows/build_and_test.yml | 6 +- .../fuzzer-tester-prepare/action.yml | 28 +-- .github/workflows/librasan-prepare/action.yml | 2 +- .../qemu-fuzzer-tester-prepare/action.yml | 2 +- .github/workflows/ubuntu-prepare/action.yml | 5 +- .../windows-tester-prepare/action.yml | 2 +- .gitignore | 3 - .../baby_fuzzer_custom_executor/src/main.rs | 13 +- fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs | 4 +- .../forkserver_simple_nautilus/src/main.rs | 14 +- .../structure_aware/nautilus_sync/src/lib.rs | 4 +- libafl/src/events/launcher.rs | 2 +- libafl/src/events/llmp/mod.rs | 13 +- libafl/src/executors/forkserver.rs | 160 +++++++----------- libafl/src/executors/hooks/timer.rs | 8 +- libafl/src/executors/inprocess/inner.rs | 2 +- libafl/src/executors/inprocess_fork/inner.rs | 2 +- libafl/src/fuzzer/mod.rs | 158 ++++++++++++----- libafl/src/inputs/mod.rs | 71 ++------ libafl/src/inputs/nautilus.rs | 38 ++--- libafl/src/mutators/mod.rs | 2 +- libafl/src/state/mod.rs | 11 +- libafl_bolts/src/core_affinity.rs | 7 +- libafl_bolts/src/lib.rs | 2 +- libafl_bolts/src/llmp.rs | 2 +- libafl_bolts/src/minibsod.rs | 8 +- libafl_bolts/src/os/mod.rs | 2 +- libafl_bolts/src/os/unix_signals.rs | 2 +- libafl_bolts/src/ownedref.rs | 4 +- libafl_bolts/src/shmem.rs | 2 +- libafl_bolts/src/staterestore.rs | 2 +- libafl_frida/src/asan/asan_rt.rs | 2 +- libafl_frida/src/executor.rs | 56 +++--- libafl_intelpt/src/linux.rs | 2 +- libafl_libfuzzer/runtime/src/tmin.rs | 1 - scripts/clippy.sh | 6 +- 36 files changed, 310 insertions(+), 338 deletions(-) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 5fe3b7cbd0..a11c70c5a9 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -270,7 +270,7 @@ jobs: - ./fuzzers/binary_only/fuzzbench_qemu - ./fuzzers/binary_only/intel_pt_baby_fuzzer - ./fuzzers/binary_only/intel_pt_command_executor - - ./fuzzers/binary_only/tinyinst_simple + # - ./fuzzers/binary_only/tinyinst_simple # Forkserver - ./fuzzers/forkserver/forkserver_simple @@ -326,6 +326,8 @@ jobs: steps: - uses: actions/checkout@v4 - uses: ./.github/workflows/fuzzer-tester-prepare + with: + fuzzer-name: ${{ matrix.fuzzer }} - name: Build and run example fuzzers (Linux) if: runner.os == 'Linux' shell: bash @@ -366,7 +368,7 @@ jobs: - name: "Install dependencies" if: runner.os == 'Linux' shell: bash - run: sudo apt-get update && sudo apt-get install gcc gcc-arm-linux-gnueabihf gcc-aarch64-linux-gnu + run: sudo apt-get update && sudo apt-get install gcc gcc-arm-linux-gnueabihf gcc-aarch64-linux-gnu gcc-riscv64-linux-gnu - name: Build and run example fuzzers (Linux) if: runner.os == 'Linux' shell: bash diff --git a/.github/workflows/fuzzer-tester-prepare/action.yml b/.github/workflows/fuzzer-tester-prepare/action.yml index 2e81026495..e7b8956821 100644 --- a/.github/workflows/fuzzer-tester-prepare/action.yml +++ b/.github/workflows/fuzzer-tester-prepare/action.yml @@ -1,5 +1,9 @@ name: Setup Rust Environment description: Sets up the Rust environment for the CI workflow +inputs: + fuzzer-name: + description: 'The fuzzer name to run' + required: true runs: using: composite steps: @@ -8,33 +12,33 @@ runs: submodules: true fetch-depth: 0 - uses: ./.github/workflows/ubuntu-prepare - - uses: Swatinem/rust-cache@v2 - with: { shared-key: "${{ runner.os }}-shared-fuzzer-cache" } - - name: Install fuzzers deps - shell: bash - run: sudo apt-get update && sudo apt-get install -y nasm nlohmann-json3-dev gcc-aarch64-linux-gnu g++-aarch64-linux-gnu gcc-mipsel-linux-gnu g++-mipsel-linux-gnu gcc-riscv64-linux-gnu gcc-powerpc-linux-gnu g++-powerpc-linux-gnu libc6-dev-i386-cross libc6-dev libc6-dev-i386 lib32gcc-11-dev lib32stdc++-11-dev libgtk-3-dev pax-utils python3-msgpack python3-jinja2 - name: enable mult-thread for `make` shell: bash run: export MAKEFLAGS="-j$(expr $(nproc) \+ 1)" - name: Add no_std toolchain + if: ${{ inputs.fuzzer-name == './fuzzers/fuzz_anything/baby_no_std' }} shell: bash run: rustup toolchain install nightly-x86_64-unknown-linux-gnu ; rustup component add rust-src --toolchain nightly-x86_64-unknown-linux-gnu - - name: Add wasm target + - name: Add nyx deps + if: ${{ inputs.fuzzer-name == './fuzzers/full_system/nyx_launcher' || inputs.fuzzer-name == './fuzzers/full_system/nyx_libxml2_standalone' || inputs.fuzzer-name == './fuzzers/full_system/nyx_libxml2_parallel' }} shell: bash - run: rustup target add wasm32-unknown-unknown + run: sudo apt update && sudo apt install -y libgtk-3-dev pax-utils python3-msgpack python3-jinja2 libcapstone-dev - name: install just uses: extractions/setup-just@v2 with: - just-version: 1.39.0 + just-version: '1.40.0' + + - name: Add wasm target + if: ${{ inputs.fuzzer-name == './fuzzers/fuzz_anything/baby_fuzzer_wasm' }} + shell: bash + run: rustup target add wasm32-unknown-unknown - name: install wasm-pack + if: ${{ inputs.fuzzer-name == './fuzzers/fuzz_anything/baby_fuzzer_wasm' }} uses: baptiste0928/cargo-install@v3 with: crate: wasm-pack - - name: install cxxbridge-cmd - uses: baptiste0928/cargo-install@v3 - with: - crate: cxxbridge-cmd - name: install chrome + if: ${{ inputs.fuzzer-name == './fuzzers/fuzz_anything/baby_fuzzer_wasm' }} uses: browser-actions/setup-chrome@v1 with: chrome-version: stable diff --git a/.github/workflows/librasan-prepare/action.yml b/.github/workflows/librasan-prepare/action.yml index e70e66913a..b409963f7f 100644 --- a/.github/workflows/librasan-prepare/action.yml +++ b/.github/workflows/librasan-prepare/action.yml @@ -47,7 +47,7 @@ runs: - name: install just uses: extractions/setup-just@v2 with: - just-version: 1.39.0 + just-version: '1.40.0' - name: Install cargo-binstall shell: bash run: | diff --git a/.github/workflows/qemu-fuzzer-tester-prepare/action.yml b/.github/workflows/qemu-fuzzer-tester-prepare/action.yml index 2ffcc9ec67..d1c2a45bc1 100644 --- a/.github/workflows/qemu-fuzzer-tester-prepare/action.yml +++ b/.github/workflows/qemu-fuzzer-tester-prepare/action.yml @@ -16,7 +16,7 @@ runs: - name: install just uses: extractions/setup-just@v2 with: - just-version: 1.39.0 + just-version: '1.40.0' - uses: actions/checkout@v4 with: submodules: true diff --git a/.github/workflows/ubuntu-prepare/action.yml b/.github/workflows/ubuntu-prepare/action.yml index 79e76531a6..f264e50cb5 100644 --- a/.github/workflows/ubuntu-prepare/action.yml +++ b/.github/workflows/ubuntu-prepare/action.yml @@ -7,13 +7,12 @@ runs: shell: bash run: | sudo apt-get update - sudo apt-get install -y curl lsb-release wget software-properties-common gnupg shellcheck pax-utils \ - nasm libsqlite3-dev libc6-dev libgtk-3-dev gcc g++ libslirp-dev libz3-dev build-essential \ + sudo apt-get install -y curl lsb-release wget software-properties-common gnupg shellcheck pax-utils libsqlite3-dev libpixman-1-dev libc6-dev gcc g++ build-essential libglib2.0-dev - uses: dtolnay/rust-toolchain@stable - name: install just uses: extractions/setup-just@v2 with: - just-version: 1.39.0 + just-version: '1.40.0' - name: Add stable clippy shell: bash run: rustup toolchain install stable --component clippy --allow-downgrade diff --git a/.github/workflows/windows-tester-prepare/action.yml b/.github/workflows/windows-tester-prepare/action.yml index 51f4e91ba1..9187db9538 100644 --- a/.github/workflows/windows-tester-prepare/action.yml +++ b/.github/workflows/windows-tester-prepare/action.yml @@ -18,4 +18,4 @@ runs: - name: install just uses: extractions/setup-just@v2 with: - just-version: 1.39.0 + just-version: '1.40.0' diff --git a/.gitignore b/.gitignore index dfc7992f3d..a5ec63de7e 100644 --- a/.gitignore +++ b/.gitignore @@ -5,9 +5,6 @@ vendor # cargo lockfiles except from binaries **/Cargo.lock -# !fuzzers/**/Cargo.lock -# !utils/**/Cargo.lock - .DS_Store .env diff --git a/fuzzers/baby/baby_fuzzer_custom_executor/src/main.rs b/fuzzers/baby/baby_fuzzer_custom_executor/src/main.rs index 45e9603d2d..0cffd6dbcf 100644 --- a/fuzzers/baby/baby_fuzzer_custom_executor/src/main.rs +++ b/fuzzers/baby/baby_fuzzer_custom_executor/src/main.rs @@ -14,15 +14,15 @@ use libafl::{ feedbacks::{CrashFeedback, MaxMapFeedback}, fuzzer::{Fuzzer, StdFuzzer}, generators::RandPrintablesGenerator, - inputs::HasTargetBytes, + inputs::{HasTargetBytes, NopBytesConverter}, mutators::{havoc_mutations::havoc_mutations, scheduled::HavocScheduledMutator}, observers::StdMapObserver, schedulers::QueueScheduler, stages::{mutational::StdMutationalStage, AflStatsStage, CalibrationStage}, state::{HasCorpus, HasExecutions, StdState}, + BloomInputFilter, StdFuzzerBuilder, }; use libafl_bolts::{current_nanos, nonzero, rands::StdRand, tuples::tuple_list, AsSlice}; - /// Coverage map with explicit assignments due to the lack of instrumentation static mut SIGNALS: [u8; 16] = [0; 16]; static mut SIGNALS_PTR: *mut u8 = &raw mut SIGNALS as _; @@ -138,8 +138,13 @@ pub fn main() { #[cfg(not(feature = "bloom_input_filter"))] let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); #[cfg(feature = "bloom_input_filter")] - let mut fuzzer = - StdFuzzer::with_bloom_input_filter(scheduler, feedback, objective, 10_000_000, 0.001); + let filter = BloomInputFilter::new(10_000_000, 0.001); + #[cfg(feature = "bloom_input_filter")] + let mut fuzzer = StdFuzzerBuilder::new() + .input_filter(filter) + .bytes_converter(NopBytesConverter::default()) + .build(scheduler, feedback, objective) + .unwrap(); // Create the executor for an in-process function with just one observer let executor = CustomExecutor::new(&state); diff --git a/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs b/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs index 9ff3c5b07c..ef265a3c3e 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs @@ -22,7 +22,7 @@ use libafl::{ CaptureTimeoutFeedback, ConstFeedback, CrashFeedback, MaxMapFeedback, TimeFeedback, }, fuzzer::StdFuzzer, - inputs::{BytesInput, NopTargetBytesConverter}, + inputs::BytesInput, mutators::{havoc_mutations, tokens_mutations, AFLppRedQueen, HavocScheduledMutator, Tokens}, observers::{CanTrack, HitcountsMapObserver, StdMapObserver, TimeObserver}, schedulers::{ @@ -567,7 +567,7 @@ fn base_forkserver_builder<'a>( opt: &'a Opt, shmem_provider: &'a mut UnixShMemProvider, fuzzer_dir: &Path, -) -> ForkserverExecutorBuilder<'a, NopTargetBytesConverter, UnixShMemProvider> { +) -> ForkserverExecutorBuilder<'a, UnixShMemProvider> { let mut executor = ForkserverExecutor::builder() .program(opt.executable.clone()) .coverage_map_size(opt.map_size.unwrap_or(AFL_DEFAULT_MAP_SIZE)) diff --git a/fuzzers/structure_aware/forkserver_simple_nautilus/src/main.rs b/fuzzers/structure_aware/forkserver_simple_nautilus/src/main.rs index 37776ad6dc..e8ac49e7ca 100644 --- a/fuzzers/structure_aware/forkserver_simple_nautilus/src/main.rs +++ b/fuzzers/structure_aware/forkserver_simple_nautilus/src/main.rs @@ -10,9 +10,9 @@ use libafl::{ feedbacks::{ CrashFeedback, MaxMapFeedback, NautilusChunksMetadata, NautilusFeedback, TimeFeedback, }, - fuzzer::{Fuzzer, StdFuzzer}, + fuzzer::Fuzzer, generators::{NautilusContext, NautilusGenerator}, - inputs::{NautilusInput, NautilusTargetBytesConverter}, + inputs::{NautilusBytesConverter, NautilusInput}, monitors::SimpleMonitor, mutators::{ HavocScheduledMutator, NautilusRandomMutator, NautilusRecursionMutator, @@ -22,7 +22,7 @@ use libafl::{ schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler}, stages::mutational::StdMutationalStage, state::StdState, - HasMetadata, + BloomInputFilter, HasMetadata, StdFuzzerBuilder, }; use libafl_bolts::{ current_nanos, @@ -166,7 +166,12 @@ pub fn main() { let scheduler = IndexesLenTimeMinimizerScheduler::new(&edges_observer, QueueScheduler::new()); // A fuzzer with feedbacks and a corpus scheduler - let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); + let converter = NautilusBytesConverter::new(&context); + let mut fuzzer = StdFuzzerBuilder::new() + .input_filter(BloomInputFilter::default()) + .bytes_converter(converter) + .build(scheduler, feedback, objective) + .unwrap(); // If we should debug the child let debug_child = opt.debug_child; @@ -186,7 +191,6 @@ pub fn main() { .coverage_map_size(MAP_SIZE) .timeout(Duration::from_millis(opt.timeout)) .kill_signal(opt.signal) - .target_bytes_converter(NautilusTargetBytesConverter::new(&context)) .build(tuple_list!(time_observer, edges_observer)) .unwrap(); diff --git a/fuzzers/structure_aware/nautilus_sync/src/lib.rs b/fuzzers/structure_aware/nautilus_sync/src/lib.rs index 25d984622c..045136c3e5 100644 --- a/fuzzers/structure_aware/nautilus_sync/src/lib.rs +++ b/fuzzers/structure_aware/nautilus_sync/src/lib.rs @@ -15,7 +15,7 @@ use libafl::{ feedbacks::{CrashFeedback, MaxMapFeedback, NautilusChunksMetadata, NautilusFeedback}, fuzzer::{Fuzzer, StdFuzzer}, generators::{NautilusContext, NautilusGenerator}, - inputs::{NautilusInput, NautilusToBytesInputConverter}, + inputs::{NautilusBytesConverter, NautilusInput}, monitors::SimpleMonitor, mutators::{ HavocScheduledMutator, NautilusRandomMutator, NautilusRecursionMutator, @@ -126,7 +126,7 @@ pub extern "C" fn libafl_main() { .build_on_port( shmem_provider.clone(), port, - Some(NautilusToBytesInputConverter::new(&context)), + Some(NautilusBytesConverter::new(&context)), none_input_converter!(), ) .unwrap() diff --git a/libafl/src/events/launcher.rs b/libafl/src/events/launcher.rs index 834c51f8c3..0a07757fb6 100644 --- a/libafl/src/events/launcher.rs +++ b/libafl/src/events/launcher.rs @@ -366,7 +366,7 @@ where "Not spawning broker (spawn_broker is false). Waiting for fuzzer children to exit..." ); unsafe { - libc::waitpid(*handle, &mut status, 0); + libc::waitpid(*handle, &raw mut status, 0); if status != 0 { log::info!("Client with pid {handle} exited with status {status}"); } diff --git a/libafl/src/events/llmp/mod.rs b/libafl/src/events/llmp/mod.rs index a599b2e827..1aa721448a 100644 --- a/libafl/src/events/llmp/mod.rs +++ b/libafl/src/events/llmp/mod.rs @@ -18,7 +18,7 @@ use crate::{ Error, events::{Event, EventFirer, EventWithStats}, fuzzer::EvaluatorObservers, - inputs::{Input, InputConverter, NopInput, NopInputConverter}, + inputs::{Input, InputConverter, NopInput}, state::{HasCurrentTestcase, HasSolutions, NopState}, }; @@ -88,16 +88,7 @@ pub struct LlmpEventConverter { phantom: PhantomData<(I, S)>, } -impl - LlmpEventConverter< - NopInput, - NopInputConverter, - NopInputConverter, - NopState, - NopShMem, - NopShMemProvider, - > -{ +impl LlmpEventConverter, NopShMem, NopShMemProvider> { /// Create a builder for [`LlmpEventConverter`] #[must_use] pub fn builder() -> LlmpEventConverterBuilder { diff --git a/libafl/src/executors/forkserver.rs b/libafl/src/executors/forkserver.rs index 2b555c802b..856817c40b 100644 --- a/libafl/src/executors/forkserver.rs +++ b/libafl/src/executors/forkserver.rs @@ -21,7 +21,6 @@ use libafl_bolts::{ AsSlice, AsSliceMut, InputLocation, TargetArgs, Truncate, fs::{InputFile, get_unique_std_input_file}, os::{dup2, pipes::Pipe}, - ownedref::OwnedSlice, shmem::{ShMem, ShMemProvider, UnixShMem, UnixShMemProvider}, tuples::{Handle, Handled, MatchNameRef, Prepend, RefIndexable}, }; @@ -42,9 +41,9 @@ use crate::observers::{ AsanBacktraceObserver, get_asan_runtime_flags, get_asan_runtime_flags_with_log_path, }; use crate::{ - Error, + Error, HasBytesConverter, executors::{Executor, ExitKind, HasObservers}, - inputs::{BytesInput, Input, NopTargetBytesConverter, TargetBytesConverter}, + inputs::{Input, InputToBytes}, mutators::Tokens, observers::{MapObserver, Observer, ObserversTuple}, state::HasExecutions, @@ -223,7 +222,7 @@ impl ConfigTarget for Command { #[cfg(target_os = "openbsd")] let ret = unsafe { libc::setrlimit(libc::RLIMIT_RSS, &r) }; #[cfg(not(target_os = "openbsd"))] - let ret = unsafe { libc::setrlimit(libc::RLIMIT_AS, &r) }; + let ret = unsafe { libc::setrlimit(libc::RLIMIT_AS, &raw const r) }; if ret < 0 { return Err(io::Error::last_os_error()); } @@ -240,7 +239,7 @@ impl ConfigTarget for Command { rlim_cur: if enable { RLIM_INFINITY } else { 0 }, rlim_max: if enable { RLIM_INFINITY } else { 0 }, }; - let ret = unsafe { libc::setrlimit(libc::RLIMIT_CORE, &r0) }; + let ret = unsafe { libc::setrlimit(libc::RLIMIT_CORE, &raw const r0) }; if ret < 0 { return Err(io::Error::last_os_error()); } @@ -576,11 +575,10 @@ impl Forkserver { /// /// Shared memory feature is also available, but you have to set things up in your code. /// Please refer to AFL++'s docs. -pub struct ForkserverExecutor { +pub struct ForkserverExecutor { target: OsString, args: Vec, input_file: InputFile, - target_bytes_converter: TC, uses_shmem_testcase: bool, forkserver: Forkserver, observers: OT, @@ -595,9 +593,8 @@ pub struct ForkserverExecutor { crash_exitcode: Option, } -impl Debug for ForkserverExecutor +impl Debug for ForkserverExecutor where - TC: Debug, OT: Debug, SHM: Debug, { @@ -606,7 +603,6 @@ where .field("target", &self.target) .field("args", &self.args) .field("input_file", &self.input_file) - .field("target_bytes_converter", &self.target_bytes_converter) .field("uses_shmem_testcase", &self.uses_shmem_testcase) .field("forkserver", &self.forkserver) .field("observers", &self.observers) @@ -615,20 +611,17 @@ where } } -impl ForkserverExecutor<(), (), (), UnixShMem, ()> { +impl ForkserverExecutor<(), (), UnixShMem, ()> { /// Builder for `ForkserverExecutor` #[must_use] - pub fn builder() - -> ForkserverExecutorBuilder<'static, NopTargetBytesConverter, UnixShMemProvider> - { + pub fn builder() -> ForkserverExecutorBuilder<'static, UnixShMemProvider> { ForkserverExecutorBuilder::new() } } -impl ForkserverExecutor +impl ForkserverExecutor where OT: ObserversTuple, - TC: TargetBytesConverter, SHM: ShMem, { /// The `target` binary that's going to run. @@ -663,7 +656,7 @@ where /// Execute input and increase the execution counter. #[inline] - fn execute_input(&mut self, state: &mut S, input: &I) -> Result + fn execute_input(&mut self, state: &mut S, input: &[u8]) -> Result where S: HasExecutions, { @@ -672,27 +665,7 @@ where self.execute_input_uncounted(input) } - /// Execute input, but side-step the execution counter. - #[inline] - fn execute_input_uncounted(&mut self, input: &I) -> Result { - let mut exit_kind = ExitKind::Ok; - - let last_run_timed_out = self.forkserver.last_run_timed_out_raw(); - - let mut input_bytes = self.target_bytes_converter.to_target_bytes(input); - let mut input_size = input_bytes.as_slice().len(); - if input_size > self.max_input_size { - // Truncate like AFL++ does - input_size = self.max_input_size; - } else if input_size < self.min_input_size { - // Extend like AFL++ does - input_size = self.min_input_size; - let mut input_bytes_copy = Vec::with_capacity(input_size); - input_bytes_copy - .as_slice_mut() - .copy_from_slice(input_bytes.as_slice()); - input_bytes = OwnedSlice::from(input_bytes_copy); - } + fn map_input_to_shmem(&mut self, input: &[u8], input_size: usize) -> Result<(), Error> { let input_size_in_bytes = input_size.to_ne_bytes(); if self.uses_shmem_testcase { debug_assert!( @@ -706,10 +679,35 @@ where map.as_slice_mut()[..SHMEM_FUZZ_HDR_SIZE] .copy_from_slice(&input_size_in_bytes[..SHMEM_FUZZ_HDR_SIZE]); map.as_slice_mut()[SHMEM_FUZZ_HDR_SIZE..(SHMEM_FUZZ_HDR_SIZE + input_size)] - .copy_from_slice(&input_bytes.as_slice()[..input_size]); + .copy_from_slice(&input[..input_size]); } else { - self.input_file - .write_buf(&input_bytes.as_slice()[..input_size])?; + self.input_file.write_buf(&input[..input_size])?; + } + Ok(()) + } + + /// Execute input, but side-step the execution counter. + #[inline] + fn execute_input_uncounted(&mut self, input: &[u8]) -> Result { + let mut exit_kind = ExitKind::Ok; + + let last_run_timed_out = self.forkserver.last_run_timed_out_raw(); + + let mut input_size = input.len(); + if input_size > self.max_input_size { + // Truncate like AFL++ does + input_size = self.max_input_size; + self.map_input_to_shmem(input, input_size)?; + } else if input_size < self.min_input_size { + // Extend like AFL++ does + input_size = self.min_input_size; + let mut input_bytes_copy = Vec::with_capacity(input_size); + input_bytes_copy + .as_slice_mut() + .copy_from_slice(input.as_slice()); + self.map_input_to_shmem(&input_bytes_copy, input_size)?; + } else { + self.map_input_to_shmem(input, input_size)?; } self.forkserver.set_last_run_timed_out(false); @@ -771,7 +769,7 @@ where /// The builder for `ForkserverExecutor` #[derive(Debug)] #[expect(clippy::struct_excessive_bools)] -pub struct ForkserverExecutorBuilder<'a, TC, SP> { +pub struct ForkserverExecutorBuilder<'a, SP> { program: Option, arguments: Vec, envs: Vec<(OsString, OsString)>, @@ -790,10 +788,9 @@ pub struct ForkserverExecutorBuilder<'a, TC, SP> { #[cfg(feature = "regex")] asan_obs: Option>, crash_exitcode: Option, - target_bytes_converter: TC, } -impl TargetArgs for ForkserverExecutorBuilder<'_, TC, SP> { +impl TargetArgs for ForkserverExecutorBuilder<'_, SP> { fn arguments_ref(&self) -> &Vec { &self.arguments } @@ -830,7 +827,7 @@ impl TargetArgs for ForkserverExecutorBuilder<'_, TC, SP> { } } -impl<'a, TC, SHM, SP> ForkserverExecutorBuilder<'a, TC, SP> +impl<'a, SHM, SP> ForkserverExecutorBuilder<'a, SP> where SHM: ShMem, SP: ShMemProvider, @@ -844,10 +841,9 @@ where pub fn build( mut self, observers: OT, - ) -> Result, Error> + ) -> Result, Error> where OT: ObserversTuple, - TC: TargetBytesConverter, { let (forkserver, input_file, map) = self.build_helper()?; @@ -898,17 +894,16 @@ where .clone() .unwrap_or(AsanBacktraceObserver::default().handle()), crash_exitcode: self.crash_exitcode, - target_bytes_converter: self.target_bytes_converter, }) } /// Builds `ForkserverExecutor` downsizing the coverage map to fit exaclty the AFL++ map size. - #[expect(clippy::pedantic, clippy::type_complexity)] + #[expect(clippy::pedantic)] pub fn build_dynamic_map( mut self, mut map_observer: A, other_observers: OT, - ) -> Result, Error> + ) -> Result, Error> where A: Observer + AsMut, I: Input, @@ -962,7 +957,6 @@ where .clone() .unwrap_or(AsanBacktraceObserver::default().handle()), crash_exitcode: self.crash_exitcode, - target_bytes_converter: self.target_bytes_converter, }) } @@ -1319,18 +1313,20 @@ where /// Determine if the asan observer is present (always false if feature "regex" is disabled) #[cfg(feature = "regex")] + #[must_use] pub fn has_asan_obs(&self) -> bool { self.asan_obs.is_some() } /// Determine if the asan observer is present (always false if feature "regex" is disabled) #[cfg(not(feature = "regex"))] + #[must_use] pub fn has_asan_obs(&self) -> bool { false } } -impl<'a> ForkserverExecutorBuilder<'a, NopTargetBytesConverter, UnixShMemProvider> { +impl<'a> ForkserverExecutorBuilder<'a, UnixShMemProvider> { /// Creates a new `AFL`-style [`ForkserverExecutor`] with the given target, arguments and observers. /// This is the builder for `ForkserverExecutor` /// This Forkserver will attempt to provide inputs over shared mem when `shmem_provider` is given. @@ -1338,8 +1334,7 @@ impl<'a> ForkserverExecutorBuilder<'a, NopTargetBytesConverter, Unix /// in case no input file is specified. /// If `debug_child` is set, the child will print to `stdout`/`stderr`. #[must_use] - pub fn new() - -> ForkserverExecutorBuilder<'a, NopTargetBytesConverter, UnixShMemProvider> { + pub fn new() -> ForkserverExecutorBuilder<'a, UnixShMemProvider> { ForkserverExecutorBuilder { program: None, arguments: vec![], @@ -1359,17 +1354,16 @@ impl<'a> ForkserverExecutorBuilder<'a, NopTargetBytesConverter, Unix #[cfg(feature = "regex")] asan_obs: None, crash_exitcode: None, - target_bytes_converter: NopTargetBytesConverter::new(), } } } -impl<'a, TC> ForkserverExecutorBuilder<'a, TC, UnixShMemProvider> { +impl<'a> ForkserverExecutorBuilder<'a, UnixShMemProvider> { /// Shmem provider for forkserver's shared memory testcase feature. pub fn shmem_provider( self, shmem_provider: &'a mut SP, - ) -> ForkserverExecutorBuilder<'a, TC, SP> { + ) -> ForkserverExecutorBuilder<'a, SP> { ForkserverExecutorBuilder { // Set the new provider shmem_provider: Some(shmem_provider), @@ -1391,71 +1385,39 @@ impl<'a, TC> ForkserverExecutorBuilder<'a, TC, UnixShMemProvider> { #[cfg(feature = "regex")] asan_obs: self.asan_obs, crash_exitcode: self.crash_exitcode, - target_bytes_converter: self.target_bytes_converter, } } } -impl<'a, TC, SP> ForkserverExecutorBuilder<'a, TC, SP> { - /// Shmem provider for forkserver's shared memory testcase feature. - pub fn target_bytes_converter>( - self, - target_bytes_converter: TC2, - ) -> ForkserverExecutorBuilder<'a, TC2, SP> { - ForkserverExecutorBuilder { - // Set the new provider - shmem_provider: self.shmem_provider, - // Copy all other values from the old Builder - program: self.program, - arguments: self.arguments, - envs: self.envs, - debug_child: self.debug_child, - uses_shmem_testcase: self.uses_shmem_testcase, - is_persistent: self.is_persistent, - is_deferred_frksrv: self.is_deferred_frksrv, - autotokens: self.autotokens, - input_location: InputLocation::StdIn, - map_size: self.map_size, - max_input_size: self.max_input_size, - min_input_size: self.min_input_size, - kill_signal: self.kill_signal, - timeout: self.timeout, - #[cfg(feature = "regex")] - asan_obs: self.asan_obs, - crash_exitcode: self.crash_exitcode, - target_bytes_converter, - } - } -} - -impl Default - for ForkserverExecutorBuilder<'_, NopTargetBytesConverter, UnixShMemProvider> -{ +impl Default for ForkserverExecutorBuilder<'_, UnixShMemProvider> { fn default() -> Self { Self::new() } } -impl Executor for ForkserverExecutor +impl Executor for ForkserverExecutor where OT: ObserversTuple, S: HasExecutions, - TC: TargetBytesConverter, SHM: ShMem, + Z: HasBytesConverter, + Z::Converter: InputToBytes, { #[inline] fn run_target( &mut self, - _fuzzer: &mut Z, + fuzzer: &mut Z, state: &mut S, _mgr: &mut EM, input: &I, ) -> Result { - self.execute_input(state, input) + let converter = fuzzer.converter_mut(); + let bytes = converter.to_bytes(input); + self.execute_input(state, bytes.as_slice()) } } -impl HasTimeout for ForkserverExecutor { +impl HasTimeout for ForkserverExecutor { #[inline] fn timeout(&self) -> Duration { self.timeout.into() @@ -1467,7 +1429,7 @@ impl HasTimeout for ForkserverExecutor { } } -impl HasObservers for ForkserverExecutor +impl HasObservers for ForkserverExecutor where OT: ObserversTuple, { diff --git a/libafl/src/executors/hooks/timer.rs b/libafl/src/executors/hooks/timer.rs index c31b98450a..b94aaef76c 100644 --- a/libafl/src/executors/hooks/timer.rs +++ b/libafl/src/executors/hooks/timer.rs @@ -190,7 +190,7 @@ impl TimerStruct { let mut critical = CRITICAL_SECTION::default(); unsafe { - InitializeCriticalSection(&mut critical); + InitializeCriticalSection(&raw mut critical); } Self { milli_sec, @@ -258,7 +258,7 @@ impl TimerStruct { // # Safety // Safe because the variables are all alive at this time and don't contain pointers. unsafe { - setitimer(ITIMER_REAL, &mut self.itimerval, core::ptr::null_mut()); + setitimer(ITIMER_REAL, &raw mut self.itimerval, core::ptr::null_mut()); } } @@ -289,7 +289,7 @@ impl TimerStruct { LeaveCriticalSection(self.critical_mut()); compiler_fence(Ordering::SeqCst); - SetThreadpoolTimer(*self.ptp_timer(), Some(&ft), 0, None); + SetThreadpoolTimer(*self.ptp_timer(), Some(&raw const ft), 0, None); } } @@ -317,7 +317,7 @@ impl TimerStruct { // No user-provided values. unsafe { let mut itimerval_zero: Itimerval = core::mem::zeroed(); - setitimer(ITIMER_REAL, &mut itimerval_zero, core::ptr::null_mut()); + setitimer(ITIMER_REAL, &raw mut itimerval_zero, core::ptr::null_mut()); } } diff --git a/libafl/src/executors/inprocess/inner.rs b/libafl/src/executors/inprocess/inner.rs index 12f961fe6f..9fb54251f2 100644 --- a/libafl/src/executors/inprocess/inner.rs +++ b/libafl/src/executors/inprocess/inner.rs @@ -199,7 +199,7 @@ where This number 0x20000 could vary depending on the compilers optimization for future compression library changes. */ let mut stack_reserved = 0x20000; - SetThreadStackGuarantee(&mut stack_reserved)?; + SetThreadStackGuarantee(&raw mut stack_reserved)?; } #[cfg(all(feature = "std", windows))] diff --git a/libafl/src/executors/inprocess_fork/inner.rs b/libafl/src/executors/inprocess_fork/inner.rs index da095d70f2..08d5b5aacb 100644 --- a/libafl/src/executors/inprocess_fork/inner.rs +++ b/libafl/src/executors/inprocess_fork/inner.rs @@ -139,7 +139,7 @@ where } #[cfg(not(target_os = "linux"))] { - setitimer(ITIMER_REAL, &mut self.itimerval, null_mut()); + setitimer(ITIMER_REAL, &raw mut self.itimerval, null_mut()); } // log::trace!("{v:#?} {}", nix::errno::errno()); diff --git a/libafl/src/fuzzer/mod.rs b/libafl/src/fuzzer/mod.rs index ee39c2d6d6..55830c2961 100644 --- a/libafl/src/fuzzer/mod.rs +++ b/libafl/src/fuzzer/mod.rs @@ -21,7 +21,7 @@ use crate::{ }, executors::{Executor, ExitKind, HasObservers}, feedbacks::Feedback, - inputs::Input, + inputs::{Input, NopBytesConverter}, mark_feature_time, observers::ObserversTuple, schedulers::Scheduler, @@ -78,6 +78,16 @@ pub trait HasObjective { fn set_share_objectives(&mut self, share_objectives: bool); } +/// Can convert input to another type +pub trait HasBytesConverter { + /// The converter itself + type Converter; + /// the input converter + fn converter(&self) -> &Self::Converter; + /// the input converter(mut) + fn converter_mut(&mut self) -> &mut Self::Converter; +} + /// Evaluates if an input is interesting using the feedback pub trait ExecutionProcessor { /// Check the outcome of the execution, find if it is worth for corpus or objectives @@ -294,16 +304,17 @@ impl ExecuteInputResult { /// Your default fuzzer instance, for everyday use. #[derive(Debug)] -pub struct StdFuzzer { +pub struct StdFuzzer { scheduler: CS, feedback: F, objective: OF, + bytes_converter: IC, input_filter: IF, // Handles whether to share objective testcases among nodes share_objectives: bool, } -impl HasScheduler for StdFuzzer +impl HasScheduler for StdFuzzer where CS: Scheduler, { @@ -318,7 +329,7 @@ where } } -impl HasFeedback for StdFuzzer { +impl HasFeedback for StdFuzzer { type Feedback = F; fn feedback(&self) -> &Self::Feedback { @@ -330,7 +341,7 @@ impl HasFeedback for StdFuzzer { } } -impl HasObjective for StdFuzzer { +impl HasObjective for StdFuzzer { type Objective = OF; fn objective(&self) -> &OF { @@ -350,7 +361,8 @@ impl HasObjective for StdFuzzer { } } -impl ExecutionProcessor for StdFuzzer +impl ExecutionProcessor + for StdFuzzer where CS: Scheduler, EM: EventFirer, @@ -546,7 +558,8 @@ where } } -impl EvaluatorObservers for StdFuzzer +impl EvaluatorObservers + for StdFuzzer where CS: Scheduler, E: HasObservers + Executor, @@ -604,10 +617,19 @@ pub struct BloomInputFilter { bloom: BloomFilter, } +#[cfg(feature = "std")] +impl Default for BloomInputFilter { + fn default() -> Self { + let bloom = BloomFilter::with_false_pos(1e-4).expected_items(10_000_000); + Self { bloom } + } +} + #[cfg(feature = "std")] impl BloomInputFilter { #[must_use] - fn new(items_count: usize, fp_p: f64) -> Self { + /// Constructor + pub fn new(items_count: usize, fp_p: f64) -> Self { let bloom = BloomFilter::with_false_pos(fp_p).expected_items(items_count); Self { bloom } } @@ -621,7 +643,7 @@ impl InputFilter for BloomInputFilter { } } -impl Evaluator for StdFuzzer +impl Evaluator for StdFuzzer where CS: Scheduler, E: HasObservers + Executor, @@ -777,7 +799,7 @@ where } } -impl EventProcessor for StdFuzzer +impl EventProcessor for StdFuzzer where CS: Scheduler, E: HasObservers + Executor, @@ -859,7 +881,7 @@ where } } -impl Fuzzer for StdFuzzer +impl Fuzzer for StdFuzzer where CS: Scheduler, E: HasObservers + Executor, @@ -991,42 +1013,93 @@ where } } -impl StdFuzzer { - /// Create a new [`StdFuzzer`] with standard behavior and the provided duplicate input execution filter. - pub fn with_input_filter(scheduler: CS, feedback: F, objective: OF, input_filter: IF) -> Self { +/// The builder for std fuzzer +#[derive(Debug, Default)] +pub struct StdFuzzerBuilder { + bytes_converter: Option, + input_filter: Option, +} + +impl StdFuzzerBuilder<(), ()> { + /// Contstuctor + #[must_use] + pub fn new() -> Self { Self { - scheduler, - feedback, - objective, - input_filter, - share_objectives: false, + input_filter: None, + bytes_converter: None, } } } -impl StdFuzzer { - /// Create a new [`StdFuzzer`] with standard behavior and no duplicate input execution filtering. - pub fn new(scheduler: CS, feedback: F, objective: OF) -> Self { - Self::with_input_filter(scheduler, feedback, objective, NopInputFilter) +impl StdFuzzerBuilder<(), IF> { + /// set input converter + pub fn bytes_converter(self, bytes_converter: IC) -> StdFuzzerBuilder { + StdFuzzerBuilder { + bytes_converter: Some(bytes_converter), + input_filter: self.input_filter, + } } } -#[cfg(feature = "std")] // hashing requires std -impl StdFuzzer { - /// Create a new [`StdFuzzer`], which, with a certain certainty, executes each input only once. - /// - /// This is achieved by hashing each input and using a bloom filter to differentiate inputs. - /// - /// Use this implementation if hashing each input is very fast compared to executing potential duplicate inputs. - pub fn with_bloom_input_filter( +impl StdFuzzerBuilder { + /// set input filter + pub fn input_filter(self, input_filter: IF) -> StdFuzzerBuilder { + StdFuzzerBuilder { + bytes_converter: self.bytes_converter, + input_filter: Some(input_filter), + } + } +} + +impl StdFuzzerBuilder { + /// build it + pub fn build( + self, scheduler: CS, feedback: F, objective: OF, - items_count: usize, - fp_p: f64, - ) -> Self { - let input_filter = BloomInputFilter::new(items_count, fp_p); - Self::with_input_filter(scheduler, feedback, objective, input_filter) + ) -> Result, Error> { + let Some(bytes_converter) = self.bytes_converter else { + return Err(Error::illegal_argument("input converter not set")); + }; + let Some(input_filter) = self.input_filter else { + return Err(Error::illegal_argument("input filter not set")); + }; + + Ok(StdFuzzer { + bytes_converter, + input_filter, + scheduler, + feedback, + objective, + share_objectives: false, + }) + } +} + +impl HasBytesConverter for StdFuzzer { + type Converter = IC; + + fn converter(&self) -> &Self::Converter { + &self.bytes_converter + } + + fn converter_mut(&mut self) -> &mut Self::Converter { + &mut self.bytes_converter + } +} + +impl StdFuzzer { + /// Create a new [`StdFuzzer`] with standard behavior and no duplicate input execution filtering. + pub fn new(scheduler: CS, feedback: F, objective: OF) -> Self { + Self { + scheduler, + feedback, + objective, + bytes_converter: NopBytesConverter::default(), + input_filter: NopInputFilter, + share_objectives: false, + } } } @@ -1042,7 +1115,7 @@ pub trait ExecutesInput { ) -> Result; } -impl ExecutesInput for StdFuzzer +impl ExecutesInput for StdFuzzer where CS: Scheduler, E: Executor + HasObservers, @@ -1137,12 +1210,12 @@ mod tests { use libafl_bolts::rands::StdRand; - use super::{Evaluator, StdFuzzer}; use crate::{ corpus::InMemoryCorpus, events::NopEventManager, executors::{ExitKind, InProcessExecutor}, - inputs::BytesInput, + fuzzer::{BloomInputFilter, Evaluator, StdFuzzerBuilder}, + inputs::{BytesInput, NopBytesConverter}, schedulers::StdScheduler, state::StdState, }; @@ -1151,7 +1224,12 @@ mod tests { fn filtered_execution() { let execution_count = RefCell::new(0); let scheduler = StdScheduler::new(); - let mut fuzzer = StdFuzzer::with_bloom_input_filter(scheduler, (), (), 100, 1e-4); + let bloom_filter = BloomInputFilter::default(); + let mut fuzzer = StdFuzzerBuilder::new() + .input_filter(bloom_filter) + .bytes_converter(NopBytesConverter::default()) + .build(scheduler, (), ()) + .unwrap(); let mut state = StdState::new( StdRand::new(), InMemoryCorpus::new(), diff --git a/libafl/src/inputs/mod.rs b/libafl/src/inputs/mod.rs index 67626ca7fc..082d23d095 100644 --- a/libafl/src/inputs/mod.rs +++ b/libafl/src/inputs/mod.rs @@ -39,7 +39,6 @@ use core::{ clone::Clone, fmt::Debug, hash::Hash, - marker::PhantomData, ops::{DerefMut, RangeBounds}, }; #[cfg(feature = "std")] @@ -116,6 +115,12 @@ pub trait InputConverter: Debug { fn convert(&mut self, input: Self::From) -> Result; } +/// This trait can transfor any input to bytes +pub trait InputToBytes: Debug { + /// Transform to bytes + fn to_bytes<'a>(&mut self, input: &'a I) -> OwnedSlice<'a, u8>; +} + /// `None` type to satisfy the type infearence in an `Option` #[macro_export] macro_rules! none_input_converter { @@ -293,29 +298,16 @@ impl ResizableMutator for &mut Vec { } } -#[derive(Debug)] -/// Basic `InputConverter` with just one type that is not converting -pub struct NopInputConverter { - phantom: PhantomData, -} +#[derive(Debug, Default)] +/// Basic `NopBytesConverter` with just one type that is not converting +pub struct NopBytesConverter {} -impl Default for NopInputConverter { - fn default() -> Self { - Self { - phantom: PhantomData, - } - } -} - -impl InputConverter for NopInputConverter +impl InputToBytes for NopBytesConverter where - I: Input, + I: HasTargetBytes + Debug, { - type From = I; - type To = I; - - fn convert(&mut self, input: Self::From) -> Result { - Ok(input) + fn to_bytes<'a>(&mut self, input: &'a I) -> OwnedSlice<'a, u8> { + input.target_bytes() } } @@ -363,40 +355,3 @@ where (self.convert_cb)(input) } } - -/// A converter that converts from `input` to target bytes -pub trait TargetBytesConverter { - /// Create target bytes - fn to_target_bytes<'a>(&mut self, input: &'a I) -> OwnedSlice<'a, u8>; -} - -/// Simply gets the target bytes out from a [`HasTargetBytes`] type. -#[derive(Debug)] -pub struct NopTargetBytesConverter { - phantom: PhantomData, -} - -impl NopTargetBytesConverter { - /// Create a new [`NopTargetBytesConverter`] - #[must_use] - pub fn new() -> NopTargetBytesConverter { - Self { - phantom: PhantomData, - } - } -} - -impl Default for NopTargetBytesConverter { - fn default() -> Self { - Self::new() - } -} - -impl TargetBytesConverter for NopTargetBytesConverter -where - I: HasTargetBytes, -{ - fn to_target_bytes<'a>(&mut self, input: &'a I) -> OwnedSlice<'a, u8> { - input.target_bytes() - } -} diff --git a/libafl/src/inputs/nautilus.rs b/libafl/src/inputs/nautilus.rs index ccabb17cb5..a4c41adb48 100644 --- a/libafl/src/inputs/nautilus.rs +++ b/libafl/src/inputs/nautilus.rs @@ -8,16 +8,15 @@ use core::{ use libafl_bolts::{HasLen, ownedref::OwnedSlice}; use serde::{Deserialize, Serialize}; -use super::TargetBytesConverter; +use super::BytesInput; use crate::{ - Error, common::nautilus::grammartec::{ newtypes::NodeId, rule::RuleIdOrCustom, tree::{Tree, TreeLike}, }, generators::nautilus::NautilusContext, - inputs::{BytesInput, Input, InputConverter}, + inputs::{Input, InputConverter, InputToBytes}, }; /// An [`Input`] implementation for `Nautilus` grammar. @@ -97,49 +96,34 @@ impl Hash for NautilusInput { } } -/// `InputConverter` to convert from `NautilusInput` to `BytesInput` +/// Convert from `NautilusInput` to `BytesInput` #[derive(Debug)] -pub struct NautilusToBytesInputConverter<'a> { +pub struct NautilusBytesConverter<'a> { ctx: &'a NautilusContext, } -impl<'a> NautilusToBytesInputConverter<'a> { +impl<'a> NautilusBytesConverter<'a> { #[must_use] - /// Create a new `NautilusToBytesInputConverter` from a context + /// Create a new `NautilusBytesConverter` from a context pub fn new(ctx: &'a NautilusContext) -> Self { Self { ctx } } } -impl InputConverter for NautilusToBytesInputConverter<'_> { +impl InputConverter for NautilusBytesConverter<'_> { type From = NautilusInput; type To = BytesInput; - fn convert(&mut self, input: Self::From) -> Result { + fn convert(&mut self, input: Self::From) -> Result { let mut bytes = vec![]; input.unparse(self.ctx, &mut bytes); Ok(BytesInput::new(bytes)) } } -/// A converter to convert a nautilus context to target bytes -#[derive(Debug)] -pub struct NautilusTargetBytesConverter<'a> { - /// The Nautilus Context - ctx: &'a NautilusContext, -} - -impl<'a> NautilusTargetBytesConverter<'a> { - /// Create a new [`NautilusTargetBytesConverter`] - #[must_use] - pub fn new(ctx: &'a NautilusContext) -> NautilusTargetBytesConverter<'a> { - NautilusTargetBytesConverter { ctx } - } -} - -impl TargetBytesConverter for NautilusTargetBytesConverter<'_> { - fn to_target_bytes<'a>(&mut self, input: &'a NautilusInput) -> OwnedSlice<'a, u8> { - let mut bytes = Vec::new(); +impl InputToBytes for NautilusBytesConverter<'_> { + fn to_bytes<'a>(&mut self, input: &'a NautilusInput) -> OwnedSlice<'a, u8> { + let mut bytes = vec![]; input.unparse(self.ctx, &mut bytes); OwnedSlice::from(bytes) } diff --git a/libafl/src/mutators/mod.rs b/libafl/src/mutators/mod.rs index 4a0776bba1..4d17df1ac6 100644 --- a/libafl/src/mutators/mod.rs +++ b/libafl/src/mutators/mod.rs @@ -93,7 +93,7 @@ impl From for MutationId { /// /// [`MutationResult::Skipped`] does not necessarily mean that the input changed, /// just that the mutator did something. For slow targets, consider using -/// a filtered fuzzer (see [`crate::fuzzer::StdFuzzer::with_input_filter`]) +/// a fuzzer with a input filter /// or wrapping your mutator in a [`hash::MutationChecker`]. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum MutationResult { diff --git a/libafl/src/state/mod.rs b/libafl/src/state/mod.rs index 6bde85520b..4d86e84760 100644 --- a/libafl/src/state/mod.rs +++ b/libafl/src/state/mod.rs @@ -40,7 +40,6 @@ use crate::{ inputs::{Input, NopInput}, stages::StageId, }; - /// The maximum size of a testcase pub const DEFAULT_MAX_SIZE: usize = 1_048_576; @@ -203,8 +202,8 @@ impl Debug for LoadConfig<'_, I, S, Z> { #[derive(Serialize, Deserialize, Clone, Debug)] #[serde(bound = " C: serde::Serialize + for<'a> serde::Deserialize<'a>, + R: serde::Serialize + for<'a> serde::Deserialize<'a>, SC: serde::Serialize + for<'a> serde::Deserialize<'a>, - R: serde::Serialize + for<'a> serde::Deserialize<'a> ")] pub struct StdState { /// RNG instance @@ -1110,7 +1109,15 @@ where { self.generate_initial_internal(fuzzer, executor, generator, manager, num, false) } +} +impl StdState +where + C: Corpus, + I: Input, + R: Rand, + SC: Corpus, +{ /// Creates a new `State`, taking ownership of all of the individual components during fuzzing. pub fn new( rand: R, diff --git a/libafl_bolts/src/core_affinity.rs b/libafl_bolts/src/core_affinity.rs index 48fd148c3a..5e6d15b9da 100644 --- a/libafl_bolts/src/core_affinity.rs +++ b/libafl_bolts/src/core_affinity.rs @@ -278,7 +278,7 @@ mod linux { sched_setaffinity( 0, // Defaults to current thread size_of::(), - &set, + &raw const set, ) }; @@ -297,7 +297,7 @@ mod linux { sched_getaffinity( 0, // Defaults to current thread size_of::(), - &mut set, + &raw mut set, ) }; @@ -452,7 +452,8 @@ mod windows { let mut outga = GROUP_AFFINITY::default(); - let result = SetThreadGroupAffinity(GetCurrentThread(), &ga, Some(&mut outga)); + let result = + SetThreadGroupAffinity(GetCurrentThread(), &raw const ga, Some(&raw mut outga)); if result.0 == 0 { Err(Error::unknown("Failed to set_for_current")) } else { diff --git a/libafl_bolts/src/lib.rs b/libafl_bolts/src/lib.rs index e41ad4715c..9b9ca7e4fd 100644 --- a/libafl_bolts/src/lib.rs +++ b/libafl_bolts/src/lib.rs @@ -1127,7 +1127,7 @@ mod windows_logging { h_stdout, bytes.as_ptr() as *const _, bytes.len() as u32, - &mut bytes_written, + &raw mut bytes_written, ptr::null_mut(), ) }; diff --git a/libafl_bolts/src/llmp.rs b/libafl_bolts/src/llmp.rs index a08baeecd5..6122d34f59 100644 --- a/libafl_bolts/src/llmp.rs +++ b/libafl_bolts/src/llmp.rs @@ -2281,7 +2281,7 @@ impl CtrlHandler for LlmpShutdownSignalHandler { fn handle(&mut self, ctrl_type: u32) -> bool { log::info!("LLMP: Received shutdown signal, ctrl_type {ctrl_type:?}"); unsafe { - ptr::write_volatile(&mut self.shutting_down, true); + ptr::write_volatile(&raw mut self.shutting_down, true); } true } diff --git a/libafl_bolts/src/minibsod.rs b/libafl_bolts/src/minibsod.rs index 5195d3229f..239229d538 100644 --- a/libafl_bolts/src/minibsod.rs +++ b/libafl_bolts/src/minibsod.rs @@ -1022,11 +1022,11 @@ fn write_minibsod(writer: &mut BufWriter) -> Result<(), std::io::Er r = unsafe { mach_vm_region_recurse( task, - &mut addr, - &mut sz, - &mut reg, + &raw mut addr, + &raw mut sz, + &raw mut reg, pvminfo.as_mut_ptr() as vm_region_recurse_info_t, - &mut _cnt, + &raw mut _cnt, ) }; if r != libc::KERN_SUCCESS { diff --git a/libafl_bolts/src/os/mod.rs b/libafl_bolts/src/os/mod.rs index 1f118ff174..19d216d470 100644 --- a/libafl_bolts/src/os/mod.rs +++ b/libafl_bolts/src/os/mod.rs @@ -58,7 +58,7 @@ impl ChildHandle { pub fn status(&self) -> i32 { let mut status = -1; unsafe { - libc::waitpid(self.pid, &mut status, 0); + libc::waitpid(self.pid, &raw mut status, 0); } libc::WEXITSTATUS(status) } diff --git a/libafl_bolts/src/os/unix_signals.rs b/libafl_bolts/src/os/unix_signals.rs index 3b9ddd1041..f807bfd21d 100644 --- a/libafl_bolts/src/os/unix_signals.rs +++ b/libafl_bolts/src/os/unix_signals.rs @@ -524,7 +524,7 @@ pub unsafe fn setup_signal_handler( pub fn ucontext() -> Result { let mut ucontext = unsafe { mem::zeroed() }; if cfg!(not(any(target_os = "openbsd", target_os = "haiku"))) { - if unsafe { getcontext(&mut ucontext) } == 0 { + if unsafe { getcontext(&raw mut ucontext) } == 0 { Ok(ucontext) } else { #[cfg(not(feature = "std"))] diff --git a/libafl_bolts/src/ownedref.rs b/libafl_bolts/src/ownedref.rs index d3e74022e0..445e5225f5 100644 --- a/libafl_bolts/src/ownedref.rs +++ b/libafl_bolts/src/ownedref.rs @@ -1159,7 +1159,7 @@ impl OwnedMutPtr { pub fn as_ptr(&self) -> *const T { match self { OwnedMutPtr::Ptr(ptr) => *ptr, - OwnedMutPtr::Owned(owned) => &**owned, + OwnedMutPtr::Owned(owned) => &raw const **owned, } } @@ -1168,7 +1168,7 @@ impl OwnedMutPtr { pub fn as_mut_ptr(&mut self) -> *mut T { match self { OwnedMutPtr::Ptr(ptr) => *ptr, - OwnedMutPtr::Owned(owned) => &mut **owned, + OwnedMutPtr::Owned(owned) => &raw mut **owned, } } } diff --git a/libafl_bolts/src/shmem.rs b/libafl_bolts/src/shmem.rs index b7222b3cb5..6f3b351ee5 100644 --- a/libafl_bolts/src/shmem.rs +++ b/libafl_bolts/src/shmem.rs @@ -1452,7 +1452,7 @@ pub mod unix_shmem { let fd = i32::from(id); unsafe { let mut stat = core::mem::zeroed(); - if fstat(fd, &mut stat) == -1 { + if fstat(fd, &raw mut stat) == -1 { return Err(Error::unknown( "Failed to map the memfd mapping".to_string(), )); diff --git a/libafl_bolts/src/staterestore.rs b/libafl_bolts/src/staterestore.rs index ebd7d12c3c..c75b2772eb 100644 --- a/libafl_bolts/src/staterestore.rs +++ b/libafl_bolts/src/staterestore.rs @@ -51,7 +51,7 @@ impl StateShMemContent { /// Get a length that's safe to deref from this map, or error. pub fn buf_len_checked(&self, shmem_size: usize) -> Result { - let buf_len = unsafe { read_volatile(&self.buf_len) }; + let buf_len = unsafe { read_volatile(&raw const self.buf_len) }; if size_of::() + buf_len > shmem_size { Err(Error::illegal_state(format!( "Stored buf_len is larger than the shared map! Shared data corrupted? Expected {shmem_size} bytes max, but got {} (buf_len {buf_len})", diff --git a/libafl_frida/src/asan/asan_rt.rs b/libafl_frida/src/asan/asan_rt.rs index 2312b8cd78..52c9a05bc1 100644 --- a/libafl_frida/src/asan/asan_rt.rs +++ b/libafl_frida/src/asan/asan_rt.rs @@ -544,7 +544,7 @@ impl AsanRuntime { // Write something to (hopefully) make sure the val isn't optimized out unsafe { - write_volatile(&mut stack_var, 0xfadbeef); + write_volatile(&raw mut stack_var, 0xfadbeef); } let range = Self::range_for_address(stack_address); diff --git a/libafl_frida/src/executor.rs b/libafl_frida/src/executor.rs index 7dc15130bb..ee626f8028 100644 --- a/libafl_frida/src/executor.rs +++ b/libafl_frida/src/executor.rs @@ -15,9 +15,9 @@ use frida_gum::{ #[cfg(windows)] use libafl::executors::{hooks::inprocess::InProcessHooks, inprocess::HasInProcessHooks}; use libafl::{ - Error, + Error, HasBytesConverter, executors::{Executor, ExitKind, HasObservers, InProcessExecutor}, - inputs::{Input, NopTargetBytesConverter, TargetBytesConverter}, + inputs::{Input, InputToBytes}, observers::ObserversTuple, state::{HasCurrentTestcase, HasExecutions, HasSolutions}, }; @@ -30,7 +30,7 @@ use crate::helper::{FridaInstrumentationHelper, FridaRuntimeTuple}; use crate::windows_hooks::initialize; /// The [`FridaInProcessExecutor`] is an [`Executor`] that executes the target in the same process, usinig [`frida`](https://frida.re/) for binary-only instrumentation. -pub struct FridaInProcessExecutor<'a, 'b, EM, H, I, OT, RT, S, TC, Z> { +pub struct FridaInProcessExecutor<'a, 'b, EM, H, I, OT, RT, S, Z> { base: InProcessExecutor<'a, EM, H, I, OT, S, Z>, /// `thread_id` for the Stalker thread_id: Option, @@ -38,13 +38,11 @@ pub struct FridaInProcessExecutor<'a, 'b, EM, H, I, OT, RT, S, TC, Z> { stalker: Stalker, /// User provided callback for instrumentation helper: Rc>>, - target_bytes_converter: TC, followed: bool, _phantom: PhantomData<&'b u8>, } -impl Debug - for FridaInProcessExecutor<'_, '_, EM, H, I, OT, RT, S, TC, Z> +impl Debug for FridaInProcessExecutor<'_, '_, EM, H, I, OT, RT, S, Z> where OT: Debug, { @@ -57,17 +55,16 @@ where } } -impl Executor - for FridaInProcessExecutor<'_, '_, EM, H, I, OT, RT, S, TC, Z> +impl Executor + for FridaInProcessExecutor<'_, '_, EM, H, I, OT, RT, S, Z> where H: FnMut(&I) -> ExitKind, I: Input, - S: HasExecutions, - S: HasCurrentTestcase, - S: HasSolutions, - TC: TargetBytesConverter, + S: HasExecutions + HasCurrentTestcase + HasSolutions, OT: ObserversTuple, RT: FridaRuntimeTuple, + Z: HasBytesConverter, + Z::Converter: InputToBytes, { /// Instruct the target about the input and run #[inline] @@ -78,7 +75,8 @@ where mgr: &mut EM, input: &I, ) -> Result { - let target_bytes = self.target_bytes_converter.to_target_bytes(input); + let converter = fuzzer.converter_mut(); + let target_bytes = converter.to_bytes(input); self.helper.borrow_mut().pre_exec(target_bytes.as_slice())?; if self.helper.borrow_mut().stalker_enabled() { if !(self.followed) { @@ -125,8 +123,8 @@ where } } -impl HasObservers - for FridaInProcessExecutor<'_, '_, EM, H, I, OT, RT, S, TC, Z> +impl HasObservers + for FridaInProcessExecutor<'_, '_, EM, H, I, OT, RT, S, Z> { type Observers = OT; #[inline] @@ -140,8 +138,7 @@ impl HasObservers } } -impl<'a, 'b, EM, H, I, OT, RT, S, Z> - FridaInProcessExecutor<'a, 'b, EM, H, I, OT, RT, S, NopTargetBytesConverter, Z> +impl<'a, 'b, EM, H, I, OT, RT, S, Z> FridaInProcessExecutor<'a, 'b, EM, H, I, OT, RT, S, Z> where RT: FridaRuntimeTuple, { @@ -151,13 +148,7 @@ where base: InProcessExecutor<'a, EM, H, I, OT, S, Z>, helper: Rc>>, ) -> Self { - FridaInProcessExecutor::with_target_bytes_converter( - gum, - base, - helper, - None, - NopTargetBytesConverter::new(), - ) + FridaInProcessExecutor::with_target_bytes_converter(gum, base, helper, None) } /// Creates a new [`FridaInProcessExecutor`] tracking the given `thread_id`. @@ -167,17 +158,11 @@ where helper: Rc>>, thread_id: u32, ) -> Self { - FridaInProcessExecutor::with_target_bytes_converter( - gum, - base, - helper, - Some(thread_id), - NopTargetBytesConverter::new(), - ) + FridaInProcessExecutor::with_target_bytes_converter(gum, base, helper, Some(thread_id)) } } -impl<'a, 'b, EM, H, I, OT, RT, S, TC, Z> FridaInProcessExecutor<'a, 'b, EM, H, I, OT, RT, S, TC, Z> +impl<'a, 'b, EM, H, I, OT, RT, S, Z> FridaInProcessExecutor<'a, 'b, EM, H, I, OT, RT, S, Z> where RT: FridaRuntimeTuple, { @@ -187,7 +172,6 @@ where base: InProcessExecutor<'a, EM, H, I, OT, S, Z>, helper: Rc>>, thread_id: Option, - target_bytes_converter: TC, ) -> Self { let mut stalker = Stalker::new(gum); let ranges = helper.borrow_mut().ranges().clone(); @@ -237,7 +221,6 @@ where thread_id, stalker, helper, - target_bytes_converter, followed: false, _phantom: PhantomData, } @@ -245,13 +228,12 @@ where } #[cfg(windows)] -impl<'a, 'b, EM, H, I, OT, RT, S, TC, Z> HasInProcessHooks - for FridaInProcessExecutor<'a, 'b, EM, H, I, OT, RT, S, TC, Z> +impl<'a, 'b, EM, H, I, OT, RT, S, Z> HasInProcessHooks + for FridaInProcessExecutor<'a, 'b, EM, H, I, OT, RT, S, Z> where H: FnMut(&I) -> ExitKind, S: HasSolutions + HasCurrentTestcase + HasExecutions, I: Input, - TC: TargetBytesConverter, OT: ObserversTuple, RT: FridaRuntimeTuple, { diff --git a/libafl_intelpt/src/linux.rs b/libafl_intelpt/src/linux.rs index a5ce4243d7..760ce98e49 100644 --- a/libafl_intelpt/src/linux.rs +++ b/libafl_intelpt/src/linux.rs @@ -903,7 +903,7 @@ fn linux_version() -> Result<(usize, usize, usize), ()> { domainname: [0; 65], }; - if unsafe { libc::uname(&mut uname_data) } != 0 { + if unsafe { libc::uname(&raw mut uname_data) } != 0 { return Err(()); } diff --git a/libafl_libfuzzer/runtime/src/tmin.rs b/libafl_libfuzzer/runtime/src/tmin.rs index 2cc245bac8..328a4280c8 100644 --- a/libafl_libfuzzer/runtime/src/tmin.rs +++ b/libafl_libfuzzer/runtime/src/tmin.rs @@ -24,7 +24,6 @@ use libafl_bolts::{ use libafl_targets::LLVMCustomMutator; use crate::{CustomMutationStatus, options::LibfuzzerOptions}; - type TMinState = StdState, BytesInput, RomuDuoJrRand, InMemoryCorpus>; diff --git a/scripts/clippy.sh b/scripts/clippy.sh index 0b5f610be4..473d254971 100755 --- a/scripts/clippy.sh +++ b/scripts/clippy.sh @@ -31,12 +31,12 @@ if [[ "$OSTYPE" == "linux-gnu"* ]]; then "libafl_frida" "libafl_libfuzzer" "libafl_libfuzzer_runtime" - "libafl_intelpt" - "libafl_nyx" "libafl_qemu" "libafl_tinyinst" "libafl_qemu/libafl_qemu_build" "libafl_qemu/libafl_qemu_sys" + "libafl_nyx" + "libafl_intelpt" ) fi @@ -70,6 +70,8 @@ for project in "${PROJECTS[@]}"; do fi done +eval "$CLIPPY_CMD --workspace -- $RUSTC_FLAGS" + echo "Clippy run completed for all specified projects." # Last run it on all