From 9acb9da4f085ff95622779efc5f5160711e7ec64 Mon Sep 17 00:00:00 2001 From: Erieke Weitenberg Date: Thu, 28 Nov 2024 15:11:19 +0100 Subject: [PATCH 01/22] Adds our own basic executor --- src/executor.rs | 98 +++++++++++++++++++++++++++++++++++++++++++++++++ src/fuzzer.rs | 16 +++----- src/main.rs | 1 + 3 files changed, 104 insertions(+), 11 deletions(-) create mode 100644 src/executor.rs diff --git a/src/executor.rs b/src/executor.rs new file mode 100644 index 0000000..3459a66 --- /dev/null +++ b/src/executor.rs @@ -0,0 +1,98 @@ +use std::borrow::BorrowMut; + +use libafl::{ + events::{EventFirer, EventRestarter}, + prelude::{Executor, ExitKind, HasObservers, ObserversTuple, UsesObservers}, + state::{HasExecutions, UsesState}, +}; +use libafl_bolts::prelude::RefIndexable; + +use crate::input::OpenApiInput; + +type FuzzerState = crate::state::OpenApiFuzzerState< + OpenApiInput, + libafl::corpus::InMemoryOnDiskCorpus, + libafl_bolts::rands::RomuDuoJrRand, + libafl::corpus::OnDiskCorpus, +>; + +pub struct SequenceExecutor<'h, H, OT> +where + H: FnMut(&OpenApiInput) -> Result + ?Sized, + OT: ObserversTuple, +{ + harness_fn: &'h mut H, + observers: OT, + phantom: std::marker::PhantomData, +} + +impl<'h, EM, Z, H, OT> Executor for SequenceExecutor<'h, H, OT> +where + H: FnMut(&OpenApiInput) -> Result + ?Sized, + Z: UsesState, + EM: UsesState + EventFirer + EventRestarter, + OT: ObserversTuple, +{ + fn run_target( + &mut self, + _fuzzer: &mut Z, + state: &mut Self::State, + _event_manager: &mut EM, + input: &Self::Input, + ) -> Result { + *state.executions_mut() += 1; + + self.pre_exec(state, input); + + let ret = self.harness_fn.borrow_mut()(input); + + self.post_exec(state, input); + ret + } +} + +impl<'h, H, OT> SequenceExecutor<'h, H, OT> +where + H: FnMut(&OpenApiInput) -> Result + ?Sized, + OT: ObserversTuple, +{ + pub fn new(harness_fn: &'h mut H, observers: OT) -> Self { + Self { + harness_fn, + observers, + phantom: std::marker::PhantomData, + } + } + fn pre_exec(&mut self, state: &mut FuzzerState, input: &OpenApiInput) {} + fn post_exec(&mut self, state: &mut FuzzerState, input: &OpenApiInput) {} +} + +impl<'h, H, OT> UsesState for SequenceExecutor<'h, H, OT> +where + H: FnMut(&OpenApiInput) -> Result + ?Sized, + OT: ObserversTuple, +{ + type State = FuzzerState; +} + +impl<'h, H, OT> UsesObservers for SequenceExecutor<'h, H, OT> +where + H: FnMut(&OpenApiInput) -> Result + ?Sized, + OT: ObserversTuple, +{ + type Observers = OT; +} + +impl<'h, H, OT> HasObservers for SequenceExecutor<'h, H, OT> +where + H: FnMut(&OpenApiInput) -> Result + ?Sized, + OT: ObserversTuple, +{ + fn observers(&self) -> RefIndexable<&Self::Observers, Self::Observers> { + RefIndexable::from(&self.observers) + } + + fn observers_mut(&mut self) -> RefIndexable<&mut Self::Observers, Self::Observers> { + RefIndexable::from(&mut self.observers) + } +} diff --git a/src/fuzzer.rs b/src/fuzzer.rs index b42ba17..b771d28 100644 --- a/src/fuzzer.rs +++ b/src/fuzzer.rs @@ -27,7 +27,7 @@ use libafl::Fuzzer; // This may be marked unused, but will make the compiler giv use libafl::{ corpus::OnDiskCorpus, events::{Event, SimpleEventManager}, - executors::{inprocess::InProcessExecutor, ExitKind}, + executors::ExitKind, feedbacks::{CrashFeedback, MaxMapFeedback}, fuzzer::StdFuzzer, monitors::UserStats, @@ -48,6 +48,7 @@ use std::time::{Duration, Instant}; use log::{debug, error, info}; use crate::coverage_clients::endpoint::EndpointCoverageClient; +use crate::executor::SequenceExecutor; use crate::{ configuration::{Configuration, CrashCriterion}, coverage_clients::CoverageClient, @@ -277,19 +278,11 @@ pub fn fuzz() -> Result<()> { |s: String| info!("{}", s), ); - exit_kind + Ok(exit_kind) }; // Create the executor for an in-process function with just one observer - let mut executor = InProcessExecutor::with_timeout( - &mut harness, - collective_observer, - &mut fuzzer, - &mut state, - &mut mgr, - Duration::from_millis(0), // disable the timeout - ) - .context("Failed to create the Executor")?; + let mut executor = SequenceExecutor::new(&mut harness, collective_observer); let manual_interrupt = setup_interrupt()?; @@ -528,6 +521,7 @@ fn update_coverage( SimpleEventManager, NopState>, >() .expect("Can not load the event manager"); + // TODO if covered != stats.last_covered { stats.last_covered = covered; diff --git a/src/main.rs b/src/main.rs index fd37737..0659631 100644 --- a/src/main.rs +++ b/src/main.rs @@ -45,6 +45,7 @@ mod authentication; mod configuration; pub mod coverage_clients; mod debug_writer; +pub mod executor; mod fuzzer; pub mod header; mod initial_corpus; From 02f5aa1e96ab3006adf83156de3897908062c231 Mon Sep 17 00:00:00 2001 From: Erieke Weitenberg Date: Thu, 28 Nov 2024 15:55:30 +0100 Subject: [PATCH 02/22] Move the update of the stats to the post_exec of executor --- src/executor.rs | 151 ++++++++++++++++++++++++++++++++++++++++++------ src/fuzzer.rs | 141 +++++--------------------------------------- 2 files changed, 147 insertions(+), 145 deletions(-) diff --git a/src/executor.rs b/src/executor.rs index 3459a66..8a11d61 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -1,13 +1,29 @@ -use std::borrow::BorrowMut; +use std::{ + borrow::{BorrowMut, Cow}, + marker::PhantomData, + sync::{Arc, Mutex}, + time::Instant, +}; use libafl::{ - events::{EventFirer, EventRestarter}, - prelude::{Executor, ExitKind, HasObservers, ObserversTuple, UsesObservers}, + events::{Event, EventFirer, EventRestarter}, + prelude::{ + AggregatorOps, Executor, ExitKind, HasObservers, ObserversTuple, UserStats, UserStatsValue, + UsesObservers, + }, state::{HasExecutions, UsesState}, }; use libafl_bolts::prelude::RefIndexable; +use log::error; + +use crate::{ + coverage_clients::{endpoint::EndpointCoverageClient, CoverageClient}, + input::OpenApiInput, + reporting::{sqlite::MySqLite, Reporting}, +}; -use crate::input::OpenApiInput; +/// How often to print a new log line +const CLIENT_STATS_TIME_WINDOW_SECS: u64 = 5; type FuzzerState = crate::state::OpenApiFuzzerState< OpenApiInput, @@ -18,17 +34,27 @@ type FuzzerState = crate::state::OpenApiFuzzerState< pub struct SequenceExecutor<'h, H, OT> where - H: FnMut(&OpenApiInput) -> Result + ?Sized, + H: FnMut(&OpenApiInput) -> (Result, u64) + ?Sized, OT: ObserversTuple, { harness_fn: &'h mut H, observers: OT, - phantom: std::marker::PhantomData, + phantom: PhantomData, + + coverage_client: Box, + endpoint_client: Arc>, + reporter: &'h Option, + + // Logging stats + performed_requests: u64, + last_window_time: Instant, + last_covered: u64, + last_endpoint_covered: u64, } impl<'h, EM, Z, H, OT> Executor for SequenceExecutor<'h, H, OT> where - H: FnMut(&OpenApiInput) -> Result + ?Sized, + H: FnMut(&OpenApiInput) -> (Result, u64) + ?Sized, Z: UsesState, EM: UsesState + EventFirer + EventRestarter, OT: ObserversTuple, @@ -37,39 +63,130 @@ where &mut self, _fuzzer: &mut Z, state: &mut Self::State, - _event_manager: &mut EM, + event_manager: &mut EM, input: &Self::Input, ) -> Result { *state.executions_mut() += 1; self.pre_exec(state, input); - let ret = self.harness_fn.borrow_mut()(input); + let (ret, performed_requests) = self.harness_fn.borrow_mut()(input); + self.performed_requests += performed_requests; - self.post_exec(state, input); + self.post_exec(state, input, event_manager); ret } } impl<'h, H, OT> SequenceExecutor<'h, H, OT> where - H: FnMut(&OpenApiInput) -> Result + ?Sized, + H: FnMut(&OpenApiInput) -> (Result, u64) + ?Sized, OT: ObserversTuple, { - pub fn new(harness_fn: &'h mut H, observers: OT) -> Self { + pub fn new( + harness_fn: &'h mut H, + observers: OT, + coverage_client: Box, + endpoint_client: Arc>, + reporter: &'h Option, + ) -> Self { Self { harness_fn, observers, - phantom: std::marker::PhantomData, + phantom: PhantomData, + + coverage_client, + endpoint_client, + reporter, + + performed_requests: 0, + last_window_time: Instant::now(), + last_covered: 0, + last_endpoint_covered: 0, } } fn pre_exec(&mut self, state: &mut FuzzerState, input: &OpenApiInput) {} - fn post_exec(&mut self, state: &mut FuzzerState, input: &OpenApiInput) {} + + fn post_exec( + &mut self, + state: &mut FuzzerState, + _input: &OpenApiInput, + event_manager: &mut EM, + ) where + EM: UsesState + EventFirer + EventRestarter, + { + self.coverage_client.fetch_coverage(true); + let (covered, total) = self.coverage_client.max_coverage_ratio(); + self.endpoint_client.fetch_coverage(true); + let (e_covered, e_total) = self.endpoint_client.max_coverage_ratio(); + + // Add own user stats + let cov_stats = UserStatsValue::Ratio(covered, total); + let end_cov_stats = UserStatsValue::Ratio(e_covered, e_total); + + let req_stats = UserStatsValue::Number(self.performed_requests); + + if covered != self.last_covered { + self.last_covered = covered; + // send the coverage stats to the event manager for use in the monitor + if let Err(e) = event_manager.fire( + state, + Event::UpdateUserStats { + name: Cow::Borrowed("wuppiefuzz_code_coverage"), + value: UserStats::new(cov_stats, AggregatorOps::None), + phantom: PhantomData, + }, + ) { + error!("Err: failed to fire event{:?}", e) + } + } + + if e_covered != self.last_endpoint_covered { + self.last_endpoint_covered = e_covered; + // send the coverage stats to the event manager for use in the monitor + if let Err(e) = event_manager.fire( + state, + Event::UpdateUserStats { + name: Cow::Borrowed("wuppiefuzz_endpoint_coverage"), + value: UserStats::new(end_cov_stats, AggregatorOps::None), + phantom: PhantomData, + }, + ) { + error!("Err: failed to fire event{:?}", e) + } + } + + let current_time = Instant::now(); + let diff = current_time.duration_since(self.last_window_time).as_secs(); + if diff > CLIENT_STATS_TIME_WINDOW_SECS { + self.last_window_time = current_time; + // send the request stats to the event manager for use in the monitor + if let Err(e) = event_manager.fire( + state, + Event::UpdateUserStats { + name: Cow::Borrowed("requests"), + value: UserStats::new(req_stats, AggregatorOps::None), + phantom: PhantomData, + }, + ) { + error!("Err: failed to fire event{:?}", e) + } + } + + self.reporter + .report_coverage(covered, total, e_covered, e_total) + } + + /// Uses the embedded coverage clients to generate a coverage report + pub fn generate_coverage_report(&self, report_path: &std::path::Path) { + self.endpoint_client.generate_coverage_report(report_path); + self.coverage_client.generate_coverage_report(report_path); + } } impl<'h, H, OT> UsesState for SequenceExecutor<'h, H, OT> where - H: FnMut(&OpenApiInput) -> Result + ?Sized, + H: FnMut(&OpenApiInput) -> (Result, u64) + ?Sized, OT: ObserversTuple, { type State = FuzzerState; @@ -77,7 +194,7 @@ where impl<'h, H, OT> UsesObservers for SequenceExecutor<'h, H, OT> where - H: FnMut(&OpenApiInput) -> Result + ?Sized, + H: FnMut(&OpenApiInput) -> (Result, u64) + ?Sized, OT: ObserversTuple, { type Observers = OT; @@ -85,7 +202,7 @@ where impl<'h, H, OT> HasObservers for SequenceExecutor<'h, H, OT> where - H: FnMut(&OpenApiInput) -> Result + ?Sized, + H: FnMut(&OpenApiInput) -> (Result, u64) + ?Sized, OT: ObserversTuple, { fn observers(&self) -> RefIndexable<&Self::Observers, Self::Observers> { diff --git a/src/fuzzer.rs b/src/fuzzer.rs index b771d28..b881c60 100644 --- a/src/fuzzer.rs +++ b/src/fuzzer.rs @@ -2,18 +2,15 @@ use anyhow::{Context, Result}; use libafl::corpus::Corpus; use libafl::events::EventFirer; -use libafl::executors::hooks::inprocess::inprocess_get_event_manager; use libafl::executors::{Executor, HasObservers}; use libafl::feedbacks::{DifferentIsNovel, Feedback, MapFeedback, MaxReducer, TimeFeedback}; -use libafl::inputs::BytesInput; -use libafl::monitors::{AggregatorOps, UserStatsValue}; use libafl::mutators::StdScheduledMutator; use libafl::observers::{CanTrack, ExplicitTracking, MultiMapObserver, TimeObserver}; use libafl::schedulers::{ powersched::PowerSchedule, IndexesLenTimeMinimizerScheduler, PowerQueueScheduler, }; use libafl::stages::{CalibrationStage, StdPowerMutationalStage}; -use libafl::state::{HasCorpus, HasExecutions, NopState, State}; +use libafl::state::{HasCorpus, HasExecutions, State}; use libafl::{feedback_or, ExecutionProcessor}; use libafl::{ExecuteInputResult, HasNamedMetadata}; @@ -30,11 +27,9 @@ use libafl::{ executors::ExitKind, feedbacks::{CrashFeedback, MaxMapFeedback}, fuzzer::StdFuzzer, - monitors::UserStats, observers::StdMapObserver, }; use libafl_bolts::{current_nanos, rands::StdRand, tuples::tuple_list}; -use std::borrow::Cow; use std::ops::DerefMut; use std::fs::create_dir_all; @@ -175,13 +170,12 @@ pub fn fuzz() -> Result<()> { // Keep track of the number of inputs let mut inputs_tested = 0; - // Logging the number of executed requests - let mut stats = LoggingStats::new(); // The closure that we want to fuzz let mut harness = |inputs: &OpenApiInput| { let mut exit_kind = ExitKind::Ok; inputs_tested += 1; + let mut performed_requests = 0; let mut parameter_feedback = ParameterFeedback::new(inputs.0.len()); log::debug!("Sending {} requests", inputs.0.len()); @@ -218,7 +212,7 @@ pub fn fuzz() -> Result<()> { match client.execute(request_built) { Ok(response) => { - stats.performed_requests += 1; + performed_requests += 1; let response: Response = response.into(); endpoint_coverage_client.lock().unwrap().cover( @@ -270,19 +264,18 @@ pub fn fuzz() -> Result<()> { } parameter_feedback.process_post_request(request_index, request); } - update_coverage( - &mut code_coverage_client, - &mut endpoint_coverage_client, - &reporter, - &mut stats, - |s: String| info!("{}", s), - ); - - Ok(exit_kind) + + (Ok(exit_kind), performed_requests) }; // Create the executor for an in-process function with just one observer - let mut executor = SequenceExecutor::new(&mut harness, collective_observer); + let mut executor = SequenceExecutor::new( + &mut harness, + collective_observer, + code_coverage_client, + endpoint_coverage_client.clone(), + &reporter, + ); let manual_interrupt = setup_interrupt()?; @@ -359,8 +352,7 @@ pub fn fuzz() -> Result<()> { } if let Some(report_path) = report_path { - endpoint_coverage_client.generate_coverage_report(&report_path); - code_coverage_client.generate_coverage_report(&report_path); + executor.generate_coverage_report(&report_path); } Ok(()) @@ -467,110 +459,3 @@ fn generate_report_path() -> PathBuf { create_dir_all(&report_path).expect("unable to make reports directory"); report_path } - -/// How often to print a new log line -const CLIENT_STATS_TIME_WINDOW_SECS: u64 = 5; - -#[derive(Clone, Copy)] -struct LoggingStats { - performed_requests: u64, - last_window_time: Instant, - last_covered: u64, - last_endpoint_covered: u64, -} - -impl LoggingStats { - fn new() -> Self { - Self { - performed_requests: 0, - last_window_time: Instant::now(), - last_covered: 0, - last_endpoint_covered: 0, - } - } -} - -/// Updates the LibAFL event manager with coverage information fetched from the -/// endpoint coverage monitor and any line coverage client. Also updates the given -/// (MySqLite) reporter -fn update_coverage( - code_coverage_client: &mut Box, - endpoint_coverage_client: &mut Arc>, - reporter: &dyn Reporting, - stats: &mut LoggingStats, - _print_fn: F, -) { - code_coverage_client.fetch_coverage(true); - let (covered, total) = code_coverage_client.max_coverage_ratio(); - endpoint_coverage_client.fetch_coverage(true); - let (e_covered, e_total) = endpoint_coverage_client.max_coverage_ratio(); - - // This input is needed for event_manager.fire, but it doesn't seem to make - // a difference whether it is meaningful or not, and cloning the entire thing - // in the harness (because it's immutable there and we need it to be mutable) - // seems wasteful. - let mut state = NopState::new(); - - // Add own user stats - let cov_stats = UserStatsValue::Ratio(covered, total); - let end_cov_stats = UserStatsValue::Ratio(e_covered, e_total); - - let req_stats = UserStatsValue::Number(stats.performed_requests); - - let event_manager = inprocess_get_event_manager::< - SimpleEventManager, NopState>, - >() - .expect("Can not load the event manager"); - // TODO - - if covered != stats.last_covered { - stats.last_covered = covered; - // send the coverage stats to the event manager for use in the monitor - if let Err(e) = event_manager.fire( - &mut state, - Event::UpdateUserStats { - name: Cow::Borrowed("wuppiefuzz_code_coverage"), - value: UserStats::new(cov_stats, AggregatorOps::None), - phantom: PhantomData, - }, - ) { - error!("Err: failed to fire event{:?}", e) - } - } - - if e_covered != stats.last_endpoint_covered { - stats.last_endpoint_covered = e_covered; - // send the coverage stats to the event manager for use in the monitor - if let Err(e) = event_manager.fire( - &mut state, - Event::UpdateUserStats { - name: Cow::Borrowed("wuppiefuzz_endpoint_coverage"), - value: UserStats::new(end_cov_stats, AggregatorOps::None), - phantom: PhantomData, - }, - ) { - error!("Err: failed to fire event{:?}", e) - } - } - - let current_time = Instant::now(); - let diff = current_time - .duration_since(stats.last_window_time) - .as_secs(); - if diff > CLIENT_STATS_TIME_WINDOW_SECS { - stats.last_window_time = current_time; - // send the request stats to the event manager for use in the monitor - if let Err(e) = event_manager.fire( - &mut state, - Event::UpdateUserStats { - name: Cow::Borrowed("requests"), - value: UserStats::new(req_stats, AggregatorOps::None), - phantom: PhantomData, - }, - ) { - error!("Err: failed to fire event{:?}", e) - } - } - - reporter.report_coverage(covered, total, e_covered, e_total) -} From ab59bc0bb7f9e1ca5527ef763295cbb980fab59a Mon Sep 17 00:00:00 2001 From: Erieke Weitenberg Date: Tue, 3 Dec 2024 14:19:24 +0100 Subject: [PATCH 03/22] simplify fn chain --- src/openapi/examples.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/openapi/examples.rs b/src/openapi/examples.rs index 10bce01..9fbda02 100644 --- a/src/openapi/examples.rs +++ b/src/openapi/examples.rs @@ -803,9 +803,8 @@ fn interesting_params_from_string_type(string: &openapiv3::StringType) -> Vec(&compiled_regex) - .take(1000) // Limit number of samples - .filter(|s| filter_regex.is_match(s)) - .next() + .take(1000) + .find(|s| filter_regex.is_match(s)) { return vec![serde_json::Value::String(sample)]; } From 07e4323fd1973cbb0a3f0716e93bbe3a0619c49c Mon Sep 17 00:00:00 2001 From: Erieke Weitenberg Date: Tue, 3 Dec 2024 14:19:30 +0100 Subject: [PATCH 04/22] move harness to executor --- src/executor.rs | 167 ++++++++++++++++++++++++++++++++++++++++++------ src/fuzzer.rs | 123 +++-------------------------------- 2 files changed, 154 insertions(+), 136 deletions(-) diff --git a/src/executor.rs b/src/executor.rs index 8a11d61..2843c8f 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -1,8 +1,8 @@ use std::{ - borrow::{BorrowMut, Cow}, + borrow::Cow, marker::PhantomData, sync::{Arc, Mutex}, - time::Instant, + time::{Duration, Instant}, }; use libafl::{ @@ -14,11 +14,22 @@ use libafl::{ state::{HasExecutions, UsesState}, }; use libafl_bolts::prelude::RefIndexable; -use log::error; +use log::{debug, error}; +use openapiv3::OpenAPI; +use reqwest::blocking::Client; +use reqwest_cookie_store::CookieStoreMutex; use crate::{ + authentication::Authentication, + configuration::{Configuration, CrashCriterion}, coverage_clients::{endpoint::EndpointCoverageClient, CoverageClient}, input::OpenApiInput, + openapi::{ + build_request::build_request_from_input, + curl_request::CurlRequest, + validate_response::{validate_response, Response}, + }, + parameter_feedback::ParameterFeedback, reporting::{sqlite::MySqLite, Reporting}, }; @@ -32,29 +43,33 @@ type FuzzerState = crate::state::OpenApiFuzzerState< libafl::corpus::OnDiskCorpus, >; -pub struct SequenceExecutor<'h, H, OT> +pub struct SequenceExecutor<'h, OT> where - H: FnMut(&OpenApiInput) -> (Result, u64) + ?Sized, OT: ObserversTuple, { - harness_fn: &'h mut H, observers: OT, - phantom: PhantomData, + + api: &'h OpenAPI, + config: &'h Configuration, + authentication: Authentication, + cookie_store: Arc, + + http_client: Client, coverage_client: Box, endpoint_client: Arc>, reporter: &'h Option, // Logging stats + inputs_tested: usize, performed_requests: u64, last_window_time: Instant, last_covered: u64, last_endpoint_covered: u64, } -impl<'h, EM, Z, H, OT> Executor for SequenceExecutor<'h, H, OT> +impl<'h, EM, Z, OT> Executor for SequenceExecutor<'h, OT> where - H: FnMut(&OpenApiInput) -> (Result, u64) + ?Sized, Z: UsesState, EM: UsesState + EventFirer + EventRestarter, OT: ObserversTuple, @@ -70,7 +85,7 @@ where self.pre_exec(state, input); - let (ret, performed_requests) = self.harness_fn.borrow_mut()(input); + let (ret, performed_requests) = self.harness(input); self.performed_requests += performed_requests; self.post_exec(state, input, event_manager); @@ -78,27 +93,37 @@ where } } -impl<'h, H, OT> SequenceExecutor<'h, H, OT> +impl<'h, OT> SequenceExecutor<'h, OT> where - H: FnMut(&OpenApiInput) -> (Result, u64) + ?Sized, OT: ObserversTuple, { + /// Create a new SequenceExecutor. pub fn new( - harness_fn: &'h mut H, observers: OT, + api: &'h OpenAPI, + config: &'h Configuration, + http_client: Client, + authentication: Authentication, + cookie_store: Arc, coverage_client: Box, endpoint_client: Arc>, reporter: &'h Option, ) -> Self { Self { - harness_fn, observers, - phantom: PhantomData, + + api, + config, + + http_client, + authentication, + cookie_store, coverage_client, endpoint_client, reporter, + inputs_tested: 0, performed_requests: 0, last_window_time: Instant::now(), last_covered: 0, @@ -107,6 +132,109 @@ where } fn pre_exec(&mut self, state: &mut FuzzerState, input: &OpenApiInput) {} + fn harness(&mut self, inputs: &OpenApiInput) -> (Result, u64) { + let mut exit_kind = ExitKind::Ok; + self.inputs_tested += 1; + let mut performed_requests = 0; + + let mut parameter_feedback = ParameterFeedback::new(inputs.0.len()); + log::debug!("Sending {} requests", inputs.0.len()); + 'chain: for (request_index, request) in inputs.0.iter().enumerate() { + let mut request = request.clone(); + log::trace!("OpenAPI request:\n{:#?}", request); + if let Err(error) = request.resolve_parameter_references(¶meter_feedback) { + debug!( + "Cannot instantiate request: missing value for backreferenced parameter: {}. Maybe the earlier request crashed?", + error + ); + break 'chain; + }; + let request_builder = match build_request_from_input( + &self.http_client, + &self.cookie_store, + self.api, + &request, + ) { + None => continue, + Some(r) => r.timeout(Duration::from_millis(self.config.request_timeout)), + }; + + let request_built = match request_builder.build() { + Ok(request) => request, + Err(err) => { + // We don't expect errors to occur in the reqwest builder. If one occurs, + // it's not the target's fault, so we don't set ExitKind::Crash or Timeout. + error!("Error building request: {err}"); + break; + } + }; + + let curl_request = CurlRequest(&request_built, &self.authentication); + let reporter_request_id = + self.reporter + .report_request(&request, &curl_request, self.inputs_tested); + let curl_request = curl_request.to_string(); + + match self.http_client.execute(request_built) { + Ok(response) => { + performed_requests += 1; + let response: Response = response.into(); + + self.endpoint_client.lock().unwrap().cover( + request.method, + request.path.clone(), + response.status(), + curl_request, + response.text().unwrap_or_else(|_| { + String::from("Unable to decode the response to UTF-8") + }), + ); + self.reporter + .report_response(&response, reporter_request_id); + log::trace!("Got response {}", response.status()); + + if response.status() == 429 { + log::warn!("HTTP status 429 'Too Many Requests' encountered!"); + log::warn!("Rate limiting is likely active on the program under test."); + log::warn!("This hinders fuzz testing. Consider disabling it."); + } + + if response.status().is_server_error() { + exit_kind = ExitKind::Crash; + log::debug!("OpenAPI-input resulted in server error response, ignoring rest of request chain."); + break 'chain; + } else { + if self.config.crash_criterion == CrashCriterion::AllErrors { + if let Err(validation_err) = + validate_response(self.api, &request, &response) + { + log::debug!("OpenAPI-input resulted in validation error: {validation_err}, ignoring rest of request chain."); + exit_kind = ExitKind::Crash; + break 'chain; + } + } + if response.status().is_success() { + parameter_feedback.process_response(request_index, response); + } + } + } + Err(e) => { + self.reporter + .report_response_error(&e.to_string(), reporter_request_id); + error!("{}", e); + exit_kind = ExitKind::Timeout; + log::debug!( + "OpenAPI-request resulted in timeout, ignoring rest of request chain." + ); + break; + } + } + parameter_feedback.process_post_request(request_index, request); + } + + (Ok(exit_kind), performed_requests) + } + fn post_exec( &mut self, state: &mut FuzzerState, @@ -184,25 +312,22 @@ where } } -impl<'h, H, OT> UsesState for SequenceExecutor<'h, H, OT> +impl<'h, OT> UsesState for SequenceExecutor<'h, OT> where - H: FnMut(&OpenApiInput) -> (Result, u64) + ?Sized, OT: ObserversTuple, { type State = FuzzerState; } -impl<'h, H, OT> UsesObservers for SequenceExecutor<'h, H, OT> +impl<'h, OT> UsesObservers for SequenceExecutor<'h, OT> where - H: FnMut(&OpenApiInput) -> (Result, u64) + ?Sized, OT: ObserversTuple, { type Observers = OT; } -impl<'h, H, OT> HasObservers for SequenceExecutor<'h, H, OT> +impl<'h, OT> HasObservers for SequenceExecutor<'h, OT> where - H: FnMut(&OpenApiInput) -> (Result, u64) + ?Sized, OT: ObserversTuple, { fn observers(&self) -> RefIndexable<&Self::Observers, Self::Observers> { diff --git a/src/fuzzer.rs b/src/fuzzer.rs index b881c60..a6f9e9a 100644 --- a/src/fuzzer.rs +++ b/src/fuzzer.rs @@ -40,24 +40,13 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; use std::time::{Duration, Instant}; -use log::{debug, error, info}; +use log::{error, info}; use crate::coverage_clients::endpoint::EndpointCoverageClient; use crate::executor::SequenceExecutor; use crate::{ - configuration::{Configuration, CrashCriterion}, - coverage_clients::CoverageClient, - input::OpenApiInput, - monitors::CoverageMonitor, - openapi::{ - build_request::build_request_from_input, - curl_request::CurlRequest, - validate_response::{validate_response, Response}, - }, - openapi_mutator::havoc_mutations_openapi, - parameter_feedback::ParameterFeedback, - reporting::Reporting, - state::OpenApiFuzzerState, + configuration::Configuration, coverage_clients::CoverageClient, input::OpenApiInput, + monitors::CoverageMonitor, openapi_mutator::havoc_mutations_openapi, state::OpenApiFuzzerState, }; /// Main fuzzer function. @@ -168,110 +157,14 @@ pub fn fuzz() -> Result<()> { let reporter = crate::reporting::sqlite::get_reporter(config)?; - // Keep track of the number of inputs - let mut inputs_tested = 0; - - // The closure that we want to fuzz - let mut harness = |inputs: &OpenApiInput| { - let mut exit_kind = ExitKind::Ok; - inputs_tested += 1; - let mut performed_requests = 0; - - let mut parameter_feedback = ParameterFeedback::new(inputs.0.len()); - log::debug!("Sending {} requests", inputs.0.len()); - 'chain: for (request_index, request) in inputs.0.iter().enumerate() { - let mut request = request.clone(); - log::trace!("OpenAPI request:\n{:#?}", request); - if let Err(error) = request.resolve_parameter_references(¶meter_feedback) { - debug!( - "Cannot instantiate request: missing value for backreferenced parameter: {}. Maybe the earlier request crashed?", - error - ); - break 'chain; - }; - let request_builder = - match build_request_from_input(&client, &cookie_store, &api, &request) { - None => continue, - Some(r) => r.timeout(Duration::from_millis(config.request_timeout)), - }; - - let request_built = match request_builder.build() { - Ok(request) => request, - Err(err) => { - // We don't expect errors to occur in the reqwest builder. If one occurs, - // it's not the target's fault, so we don't set ExitKind::Crash or Timeout. - error!("Error building request: {err}"); - break; - } - }; - - let curl_request = CurlRequest(&request_built, &authentication); - let reporter_request_id = - reporter.report_request(&request, &curl_request, inputs_tested); - let curl_request = curl_request.to_string(); - - match client.execute(request_built) { - Ok(response) => { - performed_requests += 1; - let response: Response = response.into(); - - endpoint_coverage_client.lock().unwrap().cover( - request.method, - request.path.clone(), - response.status(), - curl_request, - response.text().unwrap_or_else(|_| { - String::from("Unable to decode the response to UTF-8") - }), - ); - reporter.report_response(&response, reporter_request_id); - log::trace!("Got response {}", response.status()); - - if response.status() == 429 { - log::warn!("HTTP status 429 'Too Many Requests' encountered!"); - log::warn!("Rate limiting is likely active on the program under test."); - log::warn!("This hinders fuzz testing. Consider disabling it."); - } - - if response.status().is_server_error() { - exit_kind = ExitKind::Crash; - log::debug!("OpenAPI-input resulted in server error response, ignoring rest of request chain."); - break 'chain; - } else { - if config.crash_criterion == CrashCriterion::AllErrors { - if let Err(validation_err) = - validate_response(&api, &request, &response) - { - log::debug!("OpenAPI-input resulted in validation error: {validation_err}, ignoring rest of request chain."); - exit_kind = ExitKind::Crash; - break 'chain; - } - } - if response.status().is_success() { - parameter_feedback.process_response(request_index, response); - } - } - } - Err(e) => { - reporter.report_response_error(&e.to_string(), reporter_request_id); - error!("{}", e); - exit_kind = ExitKind::Timeout; - log::debug!( - "OpenAPI-request resulted in timeout, ignoring rest of request chain." - ); - break; - } - } - parameter_feedback.process_post_request(request_index, request); - } - - (Ok(exit_kind), performed_requests) - }; - // Create the executor for an in-process function with just one observer let mut executor = SequenceExecutor::new( - &mut harness, collective_observer, + &api, + config, + client, + authentication, + cookie_store.clone(), code_coverage_client, endpoint_coverage_client.clone(), &reporter, From cda7079ffadfd2b9a2e5a1cb72c3a59db0d918e2 Mon Sep 17 00:00:00 2001 From: Erieke Weitenberg Date: Tue, 3 Dec 2024 14:28:44 +0100 Subject: [PATCH 05/22] Move client and its accessories to the executor --- src/executor.rs | 17 ++++++++++------- src/fuzzer.rs | 7 +------ 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/executor.rs b/src/executor.rs index 2843c8f..b6a572f 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -1,3 +1,6 @@ +//! The executor module contains our custom executor, which implements the harness (for sending +//! OpenAPI-based requests to the target) and statistics tracking (mainly coverage). + use std::{ borrow::Cow, marker::PhantomData, @@ -43,6 +46,7 @@ type FuzzerState = crate::state::OpenApiFuzzerState< libafl::corpus::OnDiskCorpus, >; +/// The Executor for sending Sequences of OpenAPI requests to the target. pub struct SequenceExecutor<'h, OT> where OT: ObserversTuple, @@ -102,14 +106,13 @@ where observers: OT, api: &'h OpenAPI, config: &'h Configuration, - http_client: Client, - authentication: Authentication, - cookie_store: Arc, coverage_client: Box, endpoint_client: Arc>, reporter: &'h Option, - ) -> Self { - Self { + ) -> anyhow::Result { + let (authentication, cookie_store, http_client) = crate::build_http_client()?; + + Ok(Self { observers, api, @@ -128,9 +131,9 @@ where last_window_time: Instant::now(), last_covered: 0, last_endpoint_covered: 0, - } + }) } - fn pre_exec(&mut self, state: &mut FuzzerState, input: &OpenApiInput) {} + fn pre_exec(&mut self, _state: &mut FuzzerState, _input: &OpenApiInput) {} fn harness(&mut self, inputs: &OpenApiInput) -> (Result, u64) { let mut exit_kind = ExitKind::Ok; diff --git a/src/fuzzer.rs b/src/fuzzer.rs index a6f9e9a..43d83f2 100644 --- a/src/fuzzer.rs +++ b/src/fuzzer.rs @@ -153,8 +153,6 @@ pub fn fuzz() -> Result<()> { let power = StdPowerMutationalStage::new(mutator_openapi); let mut stages = tuple_list!(calibration, power); - let (authentication, cookie_store, client) = crate::build_http_client()?; - let reporter = crate::reporting::sqlite::get_reporter(config)?; // Create the executor for an in-process function with just one observer @@ -162,13 +160,10 @@ pub fn fuzz() -> Result<()> { collective_observer, &api, config, - client, - authentication, - cookie_store.clone(), code_coverage_client, endpoint_coverage_client.clone(), &reporter, - ); + )?; let manual_interrupt = setup_interrupt()?; From 1fe36b71ac5392543a224d721fa3b1e69991cef8 Mon Sep 17 00:00:00 2001 From: Erieke Weitenberg Date: Wed, 4 Dec 2024 14:46:38 +0100 Subject: [PATCH 06/22] refactor post_exec somewhat --- src/executor.rs | 68 ++++++++++++++++++++++++++----------------------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/src/executor.rs b/src/executor.rs index b6a572f..5a91174 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -252,39 +252,26 @@ where let (e_covered, e_total) = self.endpoint_client.max_coverage_ratio(); // Add own user stats - let cov_stats = UserStatsValue::Ratio(covered, total); - let end_cov_stats = UserStatsValue::Ratio(e_covered, e_total); - - let req_stats = UserStatsValue::Number(self.performed_requests); - if covered != self.last_covered { self.last_covered = covered; // send the coverage stats to the event manager for use in the monitor - if let Err(e) = event_manager.fire( + update_stats( state, - Event::UpdateUserStats { - name: Cow::Borrowed("wuppiefuzz_code_coverage"), - value: UserStats::new(cov_stats, AggregatorOps::None), - phantom: PhantomData, - }, - ) { - error!("Err: failed to fire event{:?}", e) - } + event_manager, + "wuppiefuzz_code_coverage", + UserStatsValue::Ratio(covered, total), + ); } if e_covered != self.last_endpoint_covered { self.last_endpoint_covered = e_covered; // send the coverage stats to the event manager for use in the monitor - if let Err(e) = event_manager.fire( + update_stats( state, - Event::UpdateUserStats { - name: Cow::Borrowed("wuppiefuzz_endpoint_coverage"), - value: UserStats::new(end_cov_stats, AggregatorOps::None), - phantom: PhantomData, - }, - ) { - error!("Err: failed to fire event{:?}", e) - } + event_manager, + "wuppiefuzz_endpoint_coverage", + UserStatsValue::Ratio(e_covered, e_total), + ); } let current_time = Instant::now(); @@ -292,16 +279,12 @@ where if diff > CLIENT_STATS_TIME_WINDOW_SECS { self.last_window_time = current_time; // send the request stats to the event manager for use in the monitor - if let Err(e) = event_manager.fire( + update_stats( state, - Event::UpdateUserStats { - name: Cow::Borrowed("requests"), - value: UserStats::new(req_stats, AggregatorOps::None), - phantom: PhantomData, - }, - ) { - error!("Err: failed to fire event{:?}", e) - } + event_manager, + "requests", + UserStatsValue::Number(self.performed_requests), + ); } self.reporter @@ -315,6 +298,27 @@ where } } +/// Uses the given event manager to log an event with the given name and value +fn update_stats( + state: &mut FuzzerState, + event_manager: &mut EM, + name: &'static str, + value: UserStatsValue, +) where + EM: UsesState + EventFirer + EventRestarter, +{ + if let Err(e) = event_manager.fire( + state, + Event::UpdateUserStats { + name: Cow::Borrowed(name), + value: UserStats::new(value, AggregatorOps::None), + phantom: PhantomData, + }, + ) { + error!("Err: failed to fire event {name}: {:?}", e) + } +} + impl<'h, OT> UsesState for SequenceExecutor<'h, OT> where OT: ObserversTuple, From f02f6121da2146d316e1f0a6574d79fd458483b8 Mon Sep 17 00:00:00 2001 From: Erieke Weitenberg Date: Wed, 4 Dec 2024 15:09:31 +0100 Subject: [PATCH 07/22] more logical order for impls --- src/executor.rs | 50 ++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/src/executor.rs b/src/executor.rs index 5a91174..f9dcd7b 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -72,31 +72,6 @@ where last_endpoint_covered: u64, } -impl<'h, EM, Z, OT> Executor for SequenceExecutor<'h, OT> -where - Z: UsesState, - EM: UsesState + EventFirer + EventRestarter, - OT: ObserversTuple, -{ - fn run_target( - &mut self, - _fuzzer: &mut Z, - state: &mut Self::State, - event_manager: &mut EM, - input: &Self::Input, - ) -> Result { - *state.executions_mut() += 1; - - self.pre_exec(state, input); - - let (ret, performed_requests) = self.harness(input); - self.performed_requests += performed_requests; - - self.post_exec(state, input, event_manager); - ret - } -} - impl<'h, OT> SequenceExecutor<'h, OT> where OT: ObserversTuple, @@ -298,6 +273,31 @@ where } } +impl<'h, EM, Z, OT> Executor for SequenceExecutor<'h, OT> +where + Z: UsesState, + EM: UsesState + EventFirer + EventRestarter, + OT: ObserversTuple, +{ + fn run_target( + &mut self, + _fuzzer: &mut Z, + state: &mut Self::State, + event_manager: &mut EM, + input: &Self::Input, + ) -> Result { + *state.executions_mut() += 1; + + self.pre_exec(state, input); + + let (ret, performed_requests) = self.harness(input); + self.performed_requests += performed_requests; + + self.post_exec(state, input, event_manager); + ret + } +} + /// Uses the given event manager to log an event with the given name and value fn update_stats( state: &mut FuzzerState, From 852723a9a4236bfb6a9f2eafaa1990ffe045410f Mon Sep 17 00:00:00 2001 From: Erieke Weitenberg Date: Wed, 4 Dec 2024 15:14:46 +0100 Subject: [PATCH 08/22] small refactors --- src/executor.rs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/executor.rs b/src/executor.rs index f9dcd7b..40f1bd9 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -122,9 +122,9 @@ where log::trace!("OpenAPI request:\n{:#?}", request); if let Err(error) = request.resolve_parameter_references(¶meter_feedback) { debug!( - "Cannot instantiate request: missing value for backreferenced parameter: {}. Maybe the earlier request crashed?", - error - ); + "Cannot instantiate request: missing value for backreferenced parameter: {}. Maybe the earlier request crashed?", + error + ); break 'chain; }; let request_builder = match build_request_from_input( @@ -196,14 +196,11 @@ where } } } - Err(e) => { + Err(transport_error) => { self.reporter - .report_response_error(&e.to_string(), reporter_request_id); - error!("{}", e); + .report_response_error(&transport_error.to_string(), reporter_request_id); + error!("{}", transport_error); exit_kind = ExitKind::Timeout; - log::debug!( - "OpenAPI-request resulted in timeout, ignoring rest of request chain." - ); break; } } From 11321a3c3f1383314a307656ea740441aac0573d Mon Sep 17 00:00:00 2001 From: Erieke Weitenberg Date: Wed, 4 Dec 2024 15:19:57 +0100 Subject: [PATCH 09/22] more small refactors --- src/executor.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/executor.rs b/src/executor.rs index 40f1bd9..637a520 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -108,6 +108,7 @@ where last_endpoint_covered: 0, }) } + fn pre_exec(&mut self, _state: &mut FuzzerState, _input: &OpenApiInput) {} fn harness(&mut self, inputs: &OpenApiInput) -> (Result, u64) { @@ -270,15 +271,15 @@ where } } -impl<'h, EM, Z, OT> Executor for SequenceExecutor<'h, OT> +impl<'h, EM, FZ, OT> Executor for SequenceExecutor<'h, OT> where - Z: UsesState, + FZ: UsesState, EM: UsesState + EventFirer + EventRestarter, OT: ObserversTuple, { fn run_target( &mut self, - _fuzzer: &mut Z, + _fuzzer: &mut FZ, state: &mut Self::State, event_manager: &mut EM, input: &Self::Input, From 19b58617e5785cb3b94e25c78f7ac32039b309b2 Mon Sep 17 00:00:00 2001 From: Erieke Weitenberg Date: Wed, 4 Dec 2024 15:31:36 +0100 Subject: [PATCH 10/22] document and simplify harness interface --- src/executor.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/executor.rs b/src/executor.rs index 637a520..9a32622 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -47,6 +47,8 @@ type FuzzerState = crate::state::OpenApiFuzzerState< >; /// The Executor for sending Sequences of OpenAPI requests to the target. +/// It is responsible for executing inputs chosen by the fuzzer, and tracking +/// statistics about coverage and errors. pub struct SequenceExecutor<'h, OT> where OT: ObserversTuple, @@ -109,9 +111,10 @@ where }) } - fn pre_exec(&mut self, _state: &mut FuzzerState, _input: &OpenApiInput) {} - - fn harness(&mut self, inputs: &OpenApiInput) -> (Result, u64) { + /// Executes the given input, tracking and using response parameters and verifying responses. + /// Returns the target's performance as ExitKind, and the number of requests successfully + /// executed (i.e. before an error occurred). + fn harness(&mut self, inputs: &OpenApiInput) -> (ExitKind, u64) { let mut exit_kind = ExitKind::Ok; self.inputs_tested += 1; let mut performed_requests = 0; @@ -208,7 +211,7 @@ where parameter_feedback.process_post_request(request_index, request); } - (Ok(exit_kind), performed_requests) + (exit_kind, performed_requests) } fn post_exec( @@ -286,13 +289,11 @@ where ) -> Result { *state.executions_mut() += 1; - self.pre_exec(state, input); - let (ret, performed_requests) = self.harness(input); self.performed_requests += performed_requests; self.post_exec(state, input, event_manager); - ret + Ok(ret) } } From 2e1ff5a80cd915d3bbedfc17fe66a51a9a7c6817 Mon Sep 17 00:00:00 2001 From: Thomas Rooijakkers Date: Thu, 5 Dec 2024 12:55:33 +0100 Subject: [PATCH 11/22] Move manual interrupt to executor --- src/executor.rs | 15 +++++++++++++-- src/fuzzer.rs | 11 +++-------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/executor.rs b/src/executor.rs index 9a32622..b8a62c0 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -4,7 +4,7 @@ use std::{ borrow::Cow, marker::PhantomData, - sync::{Arc, Mutex}, + sync::{atomic::{AtomicBool, Ordering}, Arc, Mutex}, time::{Duration, Instant}, }; @@ -66,6 +66,8 @@ where endpoint_client: Arc>, reporter: &'h Option, + manual_interrupt: Arc, + // Logging stats inputs_tested: usize, performed_requests: u64, @@ -86,6 +88,7 @@ where coverage_client: Box, endpoint_client: Arc>, reporter: &'h Option, + manual_interrupt: Arc, ) -> anyhow::Result { let (authentication, cookie_store, http_client) = crate::build_http_client()?; @@ -103,6 +106,8 @@ where endpoint_client, reporter, + manual_interrupt, + inputs_tested: 0, performed_requests: 0, last_window_time: Instant::now(), @@ -264,7 +269,13 @@ where } self.reporter - .report_coverage(covered, total, e_covered, e_total) + .report_coverage(covered, total, e_covered, e_total); + + if self.manual_interrupt.load(Ordering::Relaxed) { + if let Err(e) = event_manager.fire(state, Event::Stop) { + error!("Err: failed to fire event{:?}", e); + } + } } /// Uses the embedded coverage clients to generate a coverage report diff --git a/src/fuzzer.rs b/src/fuzzer.rs index 43d83f2..8249b75 100644 --- a/src/fuzzer.rs +++ b/src/fuzzer.rs @@ -155,6 +155,8 @@ pub fn fuzz() -> Result<()> { let reporter = crate::reporting::sqlite::get_reporter(config)?; + let manual_interrupt = setup_interrupt()?; + // Create the executor for an in-process function with just one observer let mut executor = SequenceExecutor::new( collective_observer, @@ -163,10 +165,9 @@ pub fn fuzz() -> Result<()> { code_coverage_client, endpoint_coverage_client.clone(), &reporter, + manual_interrupt, )?; - let manual_interrupt = setup_interrupt()?; - // Fire an event to print the initial corpus size let corpus_size = state.corpus().count(); let executions = *state.executions(); @@ -231,12 +232,6 @@ pub fn fuzz() -> Result<()> { ) { error!("Err: failed to fire event{:?}", e) } - if manual_interrupt.load(Ordering::Relaxed) { - if let Err(e) = mgr.fire(&mut state, Event::Stop) { - error!("Err: failed to fire event{:?}", e); - break; - } - } } if let Some(report_path) = report_path { From 06b08e5988e679bbfbd0cc10fd3ab2fbbf1590af Mon Sep 17 00:00:00 2001 From: Thomas Rooijakkers Date: Fri, 6 Dec 2024 13:46:37 +0100 Subject: [PATCH 12/22] Implement near instant stop after ctrl+c --- src/executor.rs | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/src/executor.rs b/src/executor.rs index b8a62c0..8699350 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -9,12 +9,10 @@ use std::{ }; use libafl::{ - events::{Event, EventFirer, EventRestarter}, - prelude::{ + events::{Event, EventFirer, EventProcessor, EventRestarter}, prelude::{ AggregatorOps, Executor, ExitKind, HasObservers, ObserversTuple, UserStats, UserStatsValue, UsesObservers, - }, - state::{HasExecutions, UsesState}, + }, state::{HasExecutions, Stoppable, UsesState}, Error }; use libafl_bolts::prelude::RefIndexable; use log::{debug, error}; @@ -219,6 +217,22 @@ where (exit_kind, performed_requests) } + fn pre_exec( + &mut self, + state: &mut FuzzerState, + _input: &OpenApiInput, + event_manager: &mut EM, + ) -> Result<(), Error> + where + EM: UsesState + EventFirer + EventProcessor, + { + if state.stop_requested() { + event_manager.on_shutdown()?; + return Err(Error::shutting_down()); + } + Ok(()) + } + fn post_exec( &mut self, state: &mut FuzzerState, @@ -275,6 +289,7 @@ where if let Err(e) = event_manager.fire(state, Event::Stop) { error!("Err: failed to fire event{:?}", e); } + state.request_stop(); } } @@ -288,7 +303,7 @@ where impl<'h, EM, FZ, OT> Executor for SequenceExecutor<'h, OT> where FZ: UsesState, - EM: UsesState + EventFirer + EventRestarter, + EM: UsesState + EventFirer + EventRestarter + EventProcessor, OT: ObserversTuple, { fn run_target( @@ -300,6 +315,11 @@ where ) -> Result { *state.executions_mut() += 1; + match self.pre_exec(state, input, event_manager) { + Err(Error::ShuttingDown) => return Err(Error::ShuttingDown), + Ok(_) | Err(_) => (), + } + let (ret, performed_requests) = self.harness(input); self.performed_requests += performed_requests; @@ -315,7 +335,7 @@ fn update_stats( name: &'static str, value: UserStatsValue, ) where - EM: UsesState + EventFirer + EventRestarter, + EM: UsesState + EventFirer + EventRestarter { if let Err(e) = event_manager.fire( state, From be327587195b958652c42122b0cf0e61090ffa3d Mon Sep 17 00:00:00 2001 From: Thomas Rooijakkers Date: Fri, 6 Dec 2024 13:52:37 +0100 Subject: [PATCH 13/22] Align more with libAFL by discarding stop request after processing it --- src/executor.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/executor.rs b/src/executor.rs index 8699350..a8ee33c 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -227,6 +227,7 @@ where EM: UsesState + EventFirer + EventProcessor, { if state.stop_requested() { + state.discard_stop_request(); event_manager.on_shutdown()?; return Err(Error::shutting_down()); } From 3d7927f7e0ac3274cf5de9c580684b76e90223d0 Mon Sep 17 00:00:00 2001 From: Thomas Rooijakkers Date: Fri, 6 Dec 2024 14:11:45 +0100 Subject: [PATCH 14/22] Properly implement timeout and move to executor +formatting --- src/executor.rs | 39 ++++++++++++++++++++++++++++++--------- src/fuzzer.rs | 9 ++------- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/src/executor.rs b/src/executor.rs index a8ee33c..5e7929a 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -4,15 +4,21 @@ use std::{ borrow::Cow, marker::PhantomData, - sync::{atomic::{AtomicBool, Ordering}, Arc, Mutex}, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, Mutex, + }, time::{Duration, Instant}, }; use libafl::{ - events::{Event, EventFirer, EventProcessor, EventRestarter}, prelude::{ + events::{Event, EventFirer, EventProcessor, EventRestarter}, + prelude::{ AggregatorOps, Executor, ExitKind, HasObservers, ObserversTuple, UserStats, UserStatsValue, UsesObservers, - }, state::{HasExecutions, Stoppable, UsesState}, Error + }, + state::{HasExecutions, Stoppable, UsesState}, + Error, }; use libafl_bolts::prelude::RefIndexable; use log::{debug, error}; @@ -65,6 +71,8 @@ where reporter: &'h Option, manual_interrupt: Arc, + maybe_timeout_secs: Option, + starting_time: Instant, // Logging stats inputs_tested: usize, @@ -105,6 +113,8 @@ where reporter, manual_interrupt, + maybe_timeout_secs: config.timeout.map(|t| Duration::from_secs(t.get())), + starting_time: Instant::now(), inputs_tested: 0, performed_requests: 0, @@ -222,9 +232,11 @@ where state: &mut FuzzerState, _input: &OpenApiInput, event_manager: &mut EM, - ) -> Result<(), Error> - where - EM: UsesState + EventFirer + EventProcessor, + ) -> Result<(), Error> + where + EM: UsesState + + EventFirer + + EventProcessor, { if state.stop_requested() { state.discard_stop_request(); @@ -286,7 +298,13 @@ where self.reporter .report_coverage(covered, total, e_covered, e_total); - if self.manual_interrupt.load(Ordering::Relaxed) { + // If we interrupt using ctrl+c or the timeout is over, request stop! + if self.manual_interrupt.load(Ordering::Relaxed) + | self + .maybe_timeout_secs + .map(|timeout| Instant::now() - self.starting_time > timeout) + .unwrap_or(false) + { if let Err(e) = event_manager.fire(state, Event::Stop) { error!("Err: failed to fire event{:?}", e); } @@ -304,7 +322,10 @@ where impl<'h, EM, FZ, OT> Executor for SequenceExecutor<'h, OT> where FZ: UsesState, - EM: UsesState + EventFirer + EventRestarter + EventProcessor, + EM: UsesState + + EventFirer + + EventRestarter + + EventProcessor, OT: ObserversTuple, { fn run_target( @@ -336,7 +357,7 @@ fn update_stats( name: &'static str, value: UserStatsValue, ) where - EM: UsesState + EventFirer + EventRestarter + EM: UsesState + EventFirer + EventRestarter, { if let Err(e) = event_manager.fire( state, diff --git a/src/fuzzer.rs b/src/fuzzer.rs index 8249b75..451109a 100644 --- a/src/fuzzer.rs +++ b/src/fuzzer.rs @@ -204,17 +204,12 @@ pub fn fuzz() -> Result<()> { } log::debug!("Start fuzzing loop"); - let maybe_timeout_secs = config.timeout.map(|t| Duration::from_secs(t.get())); - let starting_time = Instant::now(); // check for timeout if applicable - while maybe_timeout_secs - .map(|timeout| Instant::now() - starting_time < timeout) - .unwrap_or(true) - { + loop { match fuzzer.fuzz_one(&mut stages, &mut executor, &mut state, &mut mgr) { Ok(_) => (), Err(libafl_bolts::Error::ShuttingDown) => { - return Ok(()); + break; } Err(err) => { return Err(err).context("Error in the fuzz loop"); From 9f558a919b2a43e9d57b2efafc9fc7fdcfe89ec8 Mon Sep 17 00:00:00 2001 From: Thomas Rooijakkers Date: Fri, 6 Dec 2024 14:13:06 +0100 Subject: [PATCH 15/22] clean-up --- src/fuzzer.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/fuzzer.rs b/src/fuzzer.rs index 451109a..aec716d 100644 --- a/src/fuzzer.rs +++ b/src/fuzzer.rs @@ -38,7 +38,6 @@ use std::path::PathBuf; use std::ptr::write_volatile; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; -use std::time::{Duration, Instant}; use log::{error, info}; @@ -204,11 +203,11 @@ pub fn fuzz() -> Result<()> { } log::debug!("Start fuzzing loop"); - // check for timeout if applicable loop { match fuzzer.fuzz_one(&mut stages, &mut executor, &mut state, &mut mgr) { Ok(_) => (), Err(libafl_bolts::Error::ShuttingDown) => { + log::info!("[Fuzzing campaign ended] Thanks for using WuppieFuzz!"); break; } Err(err) => { From 4e6b39156a6d73775fcdb68e39eb2d0d07452f84 Mon Sep 17 00:00:00 2001 From: Thomas Rooijakkers Date: Fri, 6 Dec 2024 14:23:49 +0100 Subject: [PATCH 16/22] Move interrupt to executor --- src/executor.rs | 21 +++++++++++++++++++-- src/fuzzer.rs | 22 ---------------------- 2 files changed, 19 insertions(+), 24 deletions(-) diff --git a/src/executor.rs b/src/executor.rs index 5e7929a..cec4e41 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -94,7 +94,6 @@ where coverage_client: Box, endpoint_client: Arc>, reporter: &'h Option, - manual_interrupt: Arc, ) -> anyhow::Result { let (authentication, cookie_store, http_client) = crate::build_http_client()?; @@ -112,7 +111,7 @@ where endpoint_client, reporter, - manual_interrupt, + manual_interrupt: setup_interrupt()?, maybe_timeout_secs: config.timeout.map(|t| Duration::from_secs(t.get())), starting_time: Instant::now(), @@ -350,6 +349,24 @@ where } } +/// Installs the Ctrl-C interrupt handler +fn setup_interrupt() -> Result, anyhow::Error> { + let manual_interrupt = Arc::new(AtomicBool::new(false)); + { + let manual_interrupt = Arc::clone(&manual_interrupt); + ctrlc::set_handler(move || { + let second_time_pressed = manual_interrupt.swap(true, Ordering::Relaxed); + if second_time_pressed { + log::info!("[User input] Ctrl + c pressed, again - exiting forcefully!"); + std::process::exit(0); + } else { + log::info!("[User input] Ctrl + c pressed, starting graceful shutdown."); + } + })?; + } + Ok(manual_interrupt) +} + /// Uses the given event manager to log an event with the given name and value fn update_stats( state: &mut FuzzerState, diff --git a/src/fuzzer.rs b/src/fuzzer.rs index aec716d..9e2e0f9 100644 --- a/src/fuzzer.rs +++ b/src/fuzzer.rs @@ -36,7 +36,6 @@ use std::fs::create_dir_all; use std::path::PathBuf; #[cfg(windows)] use std::ptr::write_volatile; -use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; use log::{error, info}; @@ -154,8 +153,6 @@ pub fn fuzz() -> Result<()> { let reporter = crate::reporting::sqlite::get_reporter(config)?; - let manual_interrupt = setup_interrupt()?; - // Create the executor for an in-process function with just one observer let mut executor = SequenceExecutor::new( collective_observer, @@ -164,7 +161,6 @@ pub fn fuzz() -> Result<()> { code_coverage_client, endpoint_coverage_client.clone(), &reporter, - manual_interrupt, )?; // Fire an event to print the initial corpus size @@ -307,24 +303,6 @@ fn setup_line_coverage<'a>( )) } -/// Installs the Ctrl-C interrupt handler -fn setup_interrupt() -> Result, anyhow::Error> { - let manual_interrupt = Arc::new(AtomicBool::new(false)); - { - let manual_interrupt = Arc::clone(&manual_interrupt); - ctrlc::set_handler(move || { - let second_time_pressed = manual_interrupt.swap(true, Ordering::Relaxed); - if second_time_pressed { - info!("Ctrl + c pressed, again - exiting forcefully!"); - std::process::exit(0); - } else { - info!("Ctrl + c pressed, starting graceful shutdown."); - } - })?; - } - Ok(manual_interrupt) -} - /// Creates and returns the report path for this run. It is typically of the form /// `reports/2023-06-13T105302.602Z`, the filename being an ISO 8601 timestamp. fn generate_report_path() -> PathBuf { From 0674a8d4fdf2b29cf708ea0a17d97f2c5dd57784 Mon Sep 17 00:00:00 2001 From: Thomas Rooijakkers Date: Fri, 6 Dec 2024 14:50:33 +0100 Subject: [PATCH 17/22] move reporter --- src/executor.rs | 5 ++--- src/fuzzer.rs | 3 --- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/executor.rs b/src/executor.rs index cec4e41..ded4a0c 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -68,7 +68,7 @@ where coverage_client: Box, endpoint_client: Arc>, - reporter: &'h Option, + reporter: Option, manual_interrupt: Arc, maybe_timeout_secs: Option, @@ -93,7 +93,6 @@ where config: &'h Configuration, coverage_client: Box, endpoint_client: Arc>, - reporter: &'h Option, ) -> anyhow::Result { let (authentication, cookie_store, http_client) = crate::build_http_client()?; @@ -109,7 +108,7 @@ where coverage_client, endpoint_client, - reporter, + reporter: crate::reporting::sqlite::get_reporter(config)?, manual_interrupt: setup_interrupt()?, maybe_timeout_secs: config.timeout.map(|t| Duration::from_secs(t.get())), diff --git a/src/fuzzer.rs b/src/fuzzer.rs index 9e2e0f9..bd0b063 100644 --- a/src/fuzzer.rs +++ b/src/fuzzer.rs @@ -151,8 +151,6 @@ pub fn fuzz() -> Result<()> { let power = StdPowerMutationalStage::new(mutator_openapi); let mut stages = tuple_list!(calibration, power); - let reporter = crate::reporting::sqlite::get_reporter(config)?; - // Create the executor for an in-process function with just one observer let mut executor = SequenceExecutor::new( collective_observer, @@ -160,7 +158,6 @@ pub fn fuzz() -> Result<()> { config, code_coverage_client, endpoint_coverage_client.clone(), - &reporter, )?; // Fire an event to print the initial corpus size From b96704a16a13c3294be860f75e5736e01569f1c2 Mon Sep 17 00:00:00 2001 From: Erieke Weitenberg Date: Thu, 16 Jan 2025 14:40:35 +0100 Subject: [PATCH 18/22] some import reordering --- src/fuzzer.rs | 79 ++++++++++++++++++++++++++------------------------- 1 file changed, 40 insertions(+), 39 deletions(-) diff --git a/src/fuzzer.rs b/src/fuzzer.rs index 255e801..f94bb49 100644 --- a/src/fuzzer.rs +++ b/src/fuzzer.rs @@ -1,52 +1,53 @@ -use anyhow::{Context, Result}; - -use libafl::corpus::Corpus; -use libafl::events::EventFirer; -use libafl::executors::{Executor, HasObservers}; -use libafl::feedbacks::{DifferentIsNovel, Feedback, MapFeedback, MaxReducer, TimeFeedback}; -use libafl::inputs::UsesInput; -use libafl::mutators::StdScheduledMutator; -use libafl::observers::{CanTrack, ExplicitTracking, MultiMapObserver, TimeObserver}; -use libafl::schedulers::{ - powersched::PowerSchedule, IndexesLenTimeMinimizerScheduler, PowerQueueScheduler, +#[cfg(windows)] +use std::ptr::write_volatile; +use std::{ + fs::create_dir_all, + marker::PhantomData, + ops::DerefMut, + path::PathBuf, + sync::{Arc, Mutex}, }; -use libafl::stages::{CalibrationStage, StdPowerMutationalStage}; -use libafl::state::{HasCorpus, HasExecutions, UsesState}; -use libafl::{feedback_or, ExecutionProcessor}; -use libafl::{ExecuteInputResult, HasNamedMetadata}; - -use libafl_bolts::current_time; -use libafl_bolts::prelude::OwnedMutSlice; -use libafl_bolts::tuples::MatchName; -use openapiv3::OpenAPI; +use anyhow::{Context, Result}; #[allow(unused_imports)] use libafl::Fuzzer; // This may be marked unused, but will make the compiler give you crucial error messages use libafl::{ - corpus::OnDiskCorpus, - events::{Event, SimpleEventManager}, - executors::ExitKind, - feedbacks::{CrashFeedback, MaxMapFeedback}, + corpus::{Corpus, OnDiskCorpus}, + events::{Event, EventFirer, SimpleEventManager}, + executors::{Executor, ExitKind, HasObservers}, + feedback_or, + feedbacks::{ + CrashFeedback, DifferentIsNovel, Feedback, MapFeedback, MaxMapFeedback, MaxReducer, + TimeFeedback, + }, fuzzer::StdFuzzer, - observers::StdMapObserver, + inputs::UsesInput, + mutators::StdScheduledMutator, + observers::{CanTrack, ExplicitTracking, MultiMapObserver, StdMapObserver, TimeObserver}, + schedulers::{ + powersched::PowerSchedule, IndexesLenTimeMinimizerScheduler, PowerQueueScheduler, + }, + stages::{CalibrationStage, StdPowerMutationalStage}, + state::{HasCorpus, HasExecutions, UsesState}, + ExecuteInputResult, ExecutionProcessor, HasNamedMetadata, +}; +use libafl_bolts::{ + current_nanos, current_time, + prelude::OwnedMutSlice, + rands::StdRand, + tuples::{tuple_list, MatchName}, }; -use libafl_bolts::{current_nanos, rands::StdRand, tuples::tuple_list}; -use std::marker::PhantomData; -use std::ops::DerefMut; - -use std::fs::create_dir_all; -use std::path::PathBuf; -#[cfg(windows)] -use std::ptr::write_volatile; -use std::sync::{Arc, Mutex}; - use log::{error, info}; +use openapiv3::OpenAPI; -use crate::coverage_clients::endpoint::EndpointCoverageClient; -use crate::executor::SequenceExecutor; use crate::{ - configuration::Configuration, coverage_clients::CoverageClient, input::OpenApiInput, - monitors::CoverageMonitor, openapi_mutator::havoc_mutations_openapi, state::OpenApiFuzzerState, + configuration::Configuration, + coverage_clients::{endpoint::EndpointCoverageClient, CoverageClient}, + executor::SequenceExecutor, + input::OpenApiInput, + monitors::CoverageMonitor, + openapi_mutator::havoc_mutations_openapi, + state::OpenApiFuzzerState, }; /// Main fuzzer function. From 012f477fca61207a05cf0ebacfd1004a687e3110 Mon Sep 17 00:00:00 2001 From: Erieke Weitenberg Date: Thu, 16 Jan 2025 15:14:01 +0100 Subject: [PATCH 19/22] Fix --report by looping state through harness into reporting --- src/executor.rs | 10 ++++++--- src/reporting/mod.rs | 50 ++++++++++++++++++++++++----------------- src/reporting/sqlite.rs | 23 +++++++++++++++---- 3 files changed, 55 insertions(+), 28 deletions(-) diff --git a/src/executor.rs b/src/executor.rs index d51d0c1..c475e66 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -124,7 +124,11 @@ where /// Executes the given input, tracking and using response parameters and verifying responses. /// Returns the target's performance as ExitKind, and the number of requests successfully /// executed (i.e. before an error occurred). - fn harness(&mut self, inputs: &OpenApiInput) -> (ExitKind, u64) { + fn harness( + &mut self, + inputs: &OpenApiInput, + state: &mut ::State, + ) -> (ExitKind, u64) { let mut exit_kind = ExitKind::Ok; self.inputs_tested += 1; let mut performed_requests = 0; @@ -164,7 +168,7 @@ where let curl_request = CurlRequest(&request_built, &self.authentication); let reporter_request_id = self.reporter - .report_request(&request, &curl_request, self.inputs_tested); + .report_request(&request, &curl_request, state, self.inputs_tested); let curl_request = curl_request.to_string(); match self.http_client.execute(request_built) { @@ -338,7 +342,7 @@ where return Err(Error::ShuttingDown); } - let (ret, performed_requests) = self.harness(input); + let (ret, performed_requests) = self.harness(input, state); self.performed_requests += performed_requests; self.post_exec(state, input, event_manager); diff --git a/src/reporting/mod.rs b/src/reporting/mod.rs index 4a0417c..3848f53 100644 --- a/src/reporting/mod.rs +++ b/src/reporting/mod.rs @@ -1,7 +1,6 @@ use libafl::{ corpus::{Corpus, InMemoryOnDiskCorpus, OnDiskCorpus}, - executors::hooks::inprocess::inprocess_get_state, - state::HasCorpus, + state::{HasCorpus, State}, }; use crate::{ @@ -15,9 +14,15 @@ pub mod sqlite; // The reporting trait allows reporting requests and responses for later analysis. // The type `T` is the type used by the underlying data store to refer to records, // so that information can be added to a record made earlier. -pub trait Reporting { +pub trait Reporting { /// Report the request with the corresponding input id for further analysis - fn report_request(&self, request: &OpenApiRequest, curl: &CurlRequest, input_id: usize) -> T; + fn report_request( + &self, + request: &OpenApiRequest, + curl: &CurlRequest, + state: &S, + input_id: usize, + ) -> T; /// Report a valid response link to the corresponding request fn report_response(&self, response: &Response, request_id: T); @@ -35,14 +40,21 @@ pub trait Reporting { ); } -impl Reporting for Option +impl Reporting for Option where - R: Reporting, + R: Reporting, T: Default, + S: State, { - fn report_request(&self, request: &OpenApiRequest, curl: &CurlRequest, input_id: usize) -> T { + fn report_request( + &self, + request: &OpenApiRequest, + curl: &CurlRequest, + state: &S, + input_id: usize, + ) -> T { match self.as_ref() { - Some(reporter) => reporter.report_request(request, curl, input_id), + Some(reporter) => reporter.report_request(request, curl, state, input_id), _ => Default::default(), } } @@ -77,19 +89,15 @@ where } } -fn get_current_test_case_file_name() -> Option { - let corpus = unsafe { - inprocess_get_state::< - OpenApiFuzzerState< - OpenApiInput, - InMemoryOnDiskCorpus, - libafl_bolts::rands::RomuDuoJrRand, - OnDiskCorpus, - >, - >() - .expect("State is gone??") - .corpus() - }; +fn get_current_test_case_file_name( + state: &OpenApiFuzzerState< + OpenApiInput, + InMemoryOnDiskCorpus, + libafl_bolts::rands::RomuDuoJrRand, + OnDiskCorpus, + >, +) -> Option { + let corpus = state.corpus(); corpus .current() .and_then(|id| corpus.get(id).ok()) diff --git a/src/reporting/sqlite.rs b/src/reporting/sqlite.rs index da3a854..692cf96 100644 --- a/src/reporting/sqlite.rs +++ b/src/reporting/sqlite.rs @@ -2,14 +2,16 @@ use std::{fs::create_dir_all, path::Path}; use anyhow::Context; use chrono::SecondsFormat; +use libafl::corpus::{InMemoryOnDiskCorpus, OnDiskCorpus}; use log::info; use rusqlite::{named_params, Connection}; use crate::{ configuration::Configuration, - input::OpenApiRequest, + input::{OpenApiInput, OpenApiRequest}, openapi::{curl_request::CurlRequest, validate_response::Response}, reporting::Reporting, + state::OpenApiFuzzerState, }; /// Instantiates a MySqLite reporter if desired by the configuration @@ -101,8 +103,21 @@ impl MySqLite { } } -impl Reporting for MySqLite { - fn report_request(&self, request: &OpenApiRequest, curl: &CurlRequest, input_id: usize) -> i64 { +type Oafs = OpenApiFuzzerState< + OpenApiInput, + InMemoryOnDiskCorpus, + libafl_bolts::rands::RomuDuoJrRand, + OnDiskCorpus, +>; + +impl Reporting for MySqLite { + fn report_request( + &self, + request: &OpenApiRequest, + curl: &CurlRequest, + state: &Oafs, + input_id: usize, + ) -> i64 { let path = &request.path; let method = request.method.to_string(); @@ -111,7 +126,7 @@ impl Reporting for MySqLite { .expect("Could not prepare insert statement for request"); let params = named_params! { ":timestamp": time.to_rfc3339_opts(SecondsFormat::Millis, true), - ":testcase": super::get_current_test_case_file_name(), + ":testcase": super::get_current_test_case_file_name(state), ":path": path, ":type": method, ":data": curl.to_string(), From 852ee9f2a407f44d66134739ee584bbf33287297 Mon Sep 17 00:00:00 2001 From: Thomas Rooijakkers Date: Fri, 17 Jan 2025 15:59:12 +0100 Subject: [PATCH 20/22] Changelog entry --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 23d00fd..00bc392 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ ## Features +- Adds custom executor enabling proper timeout kill in + [#49](https://github.com/TNO-S3/WuppieFuzz/pull/49) + ## Fixes # v1.1.2 (2025-01-16) From 0b106d970d45aab6204554fb72aaf5ea29f9f372 Mon Sep 17 00:00:00 2001 From: Erieke Weitenberg Date: Wed, 22 Jan 2025 11:13:32 +0100 Subject: [PATCH 21/22] Removing unsafe is considered Nice(tm) in the rust world --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c092efe..02d60a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ ## Features -- Adds custom executor enabling proper timeout kill in +- Adds custom executor enabling proper timeout kill and removing unsafe code in [#49](https://github.com/TNO-S3/WuppieFuzz/pull/49) ## Fixes From a6a5d3752350cb7e4248326d9a7a936e8d513125 Mon Sep 17 00:00:00 2001 From: Erieke Weitenberg Date: Wed, 22 Jan 2025 11:13:43 +0100 Subject: [PATCH 22/22] count execution when it's done --- src/executor.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/executor.rs b/src/executor.rs index 2b01847..45dc969 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -328,13 +328,12 @@ where event_manager: &mut EM, input: &OpenApiInput, ) -> Result { - *state.executions_mut() += 1; - if let Err(Error::ShuttingDown) = self.pre_exec(state, input, event_manager) { return Err(Error::ShuttingDown); } let (ret, performed_requests) = self.harness(input, state); + *state.executions_mut() += 1; self.performed_requests += performed_requests; self.post_exec(state, input, event_manager);