Mutational Push Stage (#356)
* initial commit for push stage * cleanup, no_std, clippy * clippy * fuzzes * readme * fmt
This commit is contained in:
parent
e914cc9c14
commit
3e85cf22de
1
fuzzers/push_stage_harness/.gitignore
vendored
Normal file
1
fuzzers/push_stage_harness/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
libpng-*
|
22
fuzzers/push_stage_harness/Cargo.toml
Normal file
22
fuzzers/push_stage_harness/Cargo.toml
Normal file
@ -0,0 +1,22 @@
|
||||
[package]
|
||||
name = "push_stage_harness"
|
||||
version = "0.5.0"
|
||||
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
|
||||
edition = "2021"
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = []
|
||||
|
||||
[profile.dev]
|
||||
panic = "abort"
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
opt-level = 3
|
||||
debug = true
|
||||
|
||||
[dependencies]
|
||||
libafl = { path = "../../libafl/" }
|
4
fuzzers/push_stage_harness/README.md
Normal file
4
fuzzers/push_stage_harness/README.md
Normal file
@ -0,0 +1,4 @@
|
||||
# Push Stage Harness
|
||||
|
||||
This is a minimalistic example create a fuzzer that pulls data out of LibAFL, instead of being called by it repeatedly.
|
||||
In contrast to Kloroutines, this should be reasonably sound and fast.
|
128
fuzzers/push_stage_harness/src/main.rs
Normal file
128
fuzzers/push_stage_harness/src/main.rs
Normal file
@ -0,0 +1,128 @@
|
||||
//! A fuzzer that uses a `PushStage`, generating input to be subsequently executed,
|
||||
//! instead of executing input iteslf in a loop.
|
||||
//! Using this method, we can add LibAFL, for example, into an emulation loop
|
||||
//! or use its mutations for another fuzzer.
|
||||
//! This is a less hacky alternative to the `KloRoutines` based fuzzer, that will also work on non-`Unix`.
|
||||
|
||||
use core::cell::{Cell, RefCell};
|
||||
use std::{path::PathBuf, rc::Rc};
|
||||
|
||||
use libafl::{
|
||||
bolts::{current_nanos, rands::StdRand, tuples::tuple_list},
|
||||
corpus::{
|
||||
Corpus, CorpusScheduler, InMemoryCorpus, OnDiskCorpus, QueueCorpusScheduler, Testcase,
|
||||
},
|
||||
events::SimpleEventManager,
|
||||
executors::ExitKind,
|
||||
feedbacks::{CrashFeedback, MapFeedbackState, MaxMapFeedback},
|
||||
fuzzer::StdFuzzer,
|
||||
inputs::{BytesInput, HasTargetBytes},
|
||||
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
|
||||
observers::StdMapObserver,
|
||||
stages::push::StdMutationalPushStage,
|
||||
state::{HasCorpus, StdState},
|
||||
stats::SimpleStats,
|
||||
};
|
||||
|
||||
/// Coverage map with explicit assignments due to the lack of instrumentation
|
||||
static mut SIGNALS: [u8; 16] = [0; 16];
|
||||
|
||||
/// Assign a signal to the signals map
|
||||
fn signals_set(idx: usize) {
|
||||
unsafe { SIGNALS[idx] = 1 };
|
||||
}
|
||||
|
||||
#[allow(clippy::similar_names)]
|
||||
pub fn main() {
|
||||
// Create an observation channel using the signals map
|
||||
let observer = StdMapObserver::new("signals", unsafe { &mut SIGNALS });
|
||||
|
||||
// The state of the edges feedback.
|
||||
let feedback_state = MapFeedbackState::with_observer(&observer);
|
||||
|
||||
// Feedback to rate the interestingness of an input
|
||||
let feedback = MaxMapFeedback::<_, BytesInput, _, _, _>::new(&feedback_state, &observer);
|
||||
|
||||
// A feedback to choose if an input is a solution or not
|
||||
let objective = CrashFeedback::new();
|
||||
|
||||
// create a State from scratch
|
||||
let mut state = StdState::new(
|
||||
// RNG
|
||||
StdRand::with_seed(current_nanos()),
|
||||
// Corpus that will be evolved, we keep it in memory for performance
|
||||
InMemoryCorpus::new(),
|
||||
// Corpus in which we store solutions (crashes in this example),
|
||||
// on disk so the user can get them after stopping the fuzzer
|
||||
OnDiskCorpus::new(PathBuf::from("./crashes")).unwrap(),
|
||||
// States of the feedbacks.
|
||||
// They are the data related to the feedbacks that you want to persist in the State.
|
||||
tuple_list!(feedback_state),
|
||||
);
|
||||
|
||||
// The Stats trait define how the fuzzer stats are reported to the user
|
||||
let stats = SimpleStats::new(|s| println!("{}", s));
|
||||
|
||||
// The event manager handle the various events generated during the fuzzing loop
|
||||
// such as the notification of the addition of a new item to the corpus
|
||||
let mgr = SimpleEventManager::new(stats);
|
||||
|
||||
// A queue policy to get testcasess from the corpus
|
||||
let scheduler = QueueCorpusScheduler::new();
|
||||
|
||||
// Create the executor for an in-process function with just one observer
|
||||
//let mut executor = InProcessExecutor::new(&mut harness, &mut fuzzer, &mut state, &mut mgr)
|
||||
// .expect("Failed to create the Executor");
|
||||
|
||||
let testcase = Testcase::new(BytesInput::new(b"aaaa".to_vec()));
|
||||
//self.feedback_mut().append_metadata(state, &mut testcase)?;
|
||||
let idx = state.corpus_mut().add(testcase).unwrap();
|
||||
scheduler.on_add(&mut state, idx).unwrap();
|
||||
|
||||
// A fuzzer with feedbacks and a corpus scheduler
|
||||
let fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||
|
||||
// Generate 8 initial inputs
|
||||
//state.generate_initial_inputs(&mut fuzzer, &mut executor, &mut generator, &mut mgr, 8);
|
||||
// .expect("Failed to generate the initial corpus");
|
||||
|
||||
// Setup a mutational stage with a basic bytes mutator
|
||||
let mutator = StdScheduledMutator::new(havoc_mutations());
|
||||
|
||||
let exit_kind = Rc::new(Cell::new(None));
|
||||
|
||||
let stage_idx = 0;
|
||||
|
||||
let observers = tuple_list!(observer);
|
||||
|
||||
// All fuzzer elements are hidden behind Rc<RefCell>>, so we can reuse them for multiple stages.
|
||||
let push_stage = StdMutationalPushStage::new(
|
||||
mutator,
|
||||
Rc::new(RefCell::new(fuzzer)),
|
||||
Rc::new(RefCell::new(state)),
|
||||
Rc::new(RefCell::new(mgr)),
|
||||
Rc::new(RefCell::new(observers)),
|
||||
exit_kind.clone(),
|
||||
stage_idx,
|
||||
);
|
||||
|
||||
// Loop, the input, getting a new entry from the push stage each iteration.
|
||||
for input in push_stage {
|
||||
let input = input.unwrap();
|
||||
let target = input.target_bytes();
|
||||
let buf = target.as_slice();
|
||||
signals_set(0);
|
||||
if !buf.is_empty() && buf[0] == b'a' {
|
||||
signals_set(1);
|
||||
if buf.len() > 1 && buf[1] == b'b' {
|
||||
signals_set(2);
|
||||
if buf.len() > 2 && buf[2] == b'c' {
|
||||
panic!("=)");
|
||||
}
|
||||
}
|
||||
}
|
||||
(*exit_kind).replace(Some(ExitKind::Ok));
|
||||
}
|
||||
|
||||
println!("One iteration done.");
|
||||
}
|
@ -40,7 +40,7 @@ use crate::{
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// How an execution finished.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||
pub enum ExitKind {
|
||||
/// The run exited normally.
|
||||
Ok,
|
||||
|
@ -385,7 +385,7 @@ where
|
||||
Event::NewTestcase {
|
||||
input,
|
||||
observers_buf,
|
||||
exit_kind: exit_kind.clone(),
|
||||
exit_kind: *exit_kind,
|
||||
corpus_size: state.corpus().count(),
|
||||
client_config: manager.configuration(),
|
||||
time: current_time(),
|
||||
|
@ -1,3 +1,7 @@
|
||||
//! This module contains the `concolic` stages, which can trace a target using symbolic execution
|
||||
//! and use the results for fuzzer input and mutations.
|
||||
//!
|
||||
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use crate::{
|
||||
|
@ -8,6 +8,8 @@ Other stages may enrich [`crate::corpus::Testcase`]s with metadata.
|
||||
pub mod mutational;
|
||||
pub use mutational::{MutationalStage, StdMutationalStage};
|
||||
|
||||
pub mod push;
|
||||
|
||||
pub mod tracing;
|
||||
pub use tracing::{ShadowTracingStage, TracingStage};
|
||||
|
||||
@ -15,7 +17,6 @@ pub mod calibrate;
|
||||
pub use calibrate::{CalibrationStage, PowerScheduleMetadata};
|
||||
|
||||
pub mod power;
|
||||
use crate::Error;
|
||||
pub use power::PowerMutationalStage;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
@ -25,6 +26,7 @@ pub use concolic::ConcolicTracingStage;
|
||||
#[cfg(feature = "std")]
|
||||
pub use concolic::SimpleConcolicMutationalStage;
|
||||
|
||||
use crate::Error;
|
||||
/// A stage is one step in the fuzzing process.
|
||||
/// Multiple stages will be scheduled one by one for each input.
|
||||
pub trait Stage<E, EM, S, Z> {
|
||||
|
@ -1,3 +1,6 @@
|
||||
//| The [`MutationalStage`] is the default stage used during fuzzing.
|
||||
//! For the current input, it will perform a range of random mutations, and then run them in the executor.
|
||||
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use crate::{
|
||||
|
12
libafl/src/stages/push/mod.rs
Normal file
12
libafl/src/stages/push/mod.rs
Normal file
@ -0,0 +1,12 @@
|
||||
//! While normal stages call the executor over and over again, push stages turn this concept upside down:
|
||||
//! A push stage instead returns an iterator that generates a new result for each time it gets called.
|
||||
//! With the new testcase, you will have to take care about testcase execution, manually.
|
||||
//! The push stage relies on internal muttability of the supplied `Observers`.
|
||||
//!
|
||||
|
||||
/// Mutational stage is the normal fuzzing stage,
|
||||
pub mod mutational;
|
||||
pub use mutational::StdMutationalPushStage;
|
||||
|
||||
/// A push stage is a generator that returns a single testcase for each call.
|
||||
pub trait PushStage<E, EM, S, Z>: Iterator {}
|
280
libafl/src/stages/push/mutational.rs
Normal file
280
libafl/src/stages/push/mutational.rs
Normal file
@ -0,0 +1,280 @@
|
||||
//| The [`MutationalStage`] is the default stage used during fuzzing.
|
||||
//! For the current input, it will perform a range of random mutations, and then run them in the executor.
|
||||
|
||||
use alloc::rc::Rc;
|
||||
use core::{
|
||||
borrow::BorrowMut,
|
||||
cell::{Cell, RefCell},
|
||||
marker::PhantomData,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
bolts::{current_time, rands::Rand},
|
||||
corpus::{Corpus, CorpusScheduler},
|
||||
events::EventManager,
|
||||
executors::ExitKind,
|
||||
inputs::Input,
|
||||
mark_feature_time,
|
||||
mutators::Mutator,
|
||||
observers::ObserversTuple,
|
||||
start_timer,
|
||||
state::{HasClientPerfStats, HasCorpus, HasRand},
|
||||
Error, EvaluatorObservers, ExecutionProcessor, Fuzzer, HasCorpusScheduler,
|
||||
};
|
||||
|
||||
#[cfg(feature = "introspection")]
|
||||
use crate::stats::PerfFeature;
|
||||
|
||||
/// Send a stats update all 15 (or more) seconds
|
||||
const STATS_TIMEOUT_DEFAULT: Duration = Duration::from_secs(15);
|
||||
|
||||
pub static DEFAULT_MUTATIONAL_MAX_ITERATIONS: u64 = 128;
|
||||
|
||||
/// A Mutational push stage is the stage in a fuzzing run that mutates inputs.
|
||||
/// Mutational push stages will usually have a range of mutations that are
|
||||
/// being applied to the input one by one, between executions.
|
||||
/// The push version, in contrast to the normal stage, will return each testcase, instead of executing it.
|
||||
///
|
||||
/// Default value, how many iterations each stage gets, as an upper bound
|
||||
/// It may randomly continue earlier.
|
||||
///
|
||||
/// The default mutational push stage
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct StdMutationalPushStage<C, CS, EM, I, M, OT, R, S, Z>
|
||||
where
|
||||
C: Corpus<I>,
|
||||
CS: CorpusScheduler<I, S>,
|
||||
EM: EventManager<(), I, S, Z>,
|
||||
I: Input,
|
||||
M: Mutator<I, S>,
|
||||
OT: ObserversTuple<I, S>,
|
||||
R: Rand,
|
||||
S: HasClientPerfStats + HasCorpus<C, I> + HasRand<R>,
|
||||
Z: ExecutionProcessor<I, OT, S>
|
||||
+ EvaluatorObservers<I, OT, S>
|
||||
+ Fuzzer<(), EM, I, S, ()>
|
||||
+ HasCorpusScheduler<CS, I, S>,
|
||||
{
|
||||
initialized: bool,
|
||||
state: Rc<RefCell<S>>,
|
||||
current_iter: Option<usize>,
|
||||
current_corpus_idx: Option<usize>,
|
||||
testcases_to_do: usize,
|
||||
testcases_done: usize,
|
||||
|
||||
fuzzer: Rc<RefCell<Z>>,
|
||||
event_mgr: Rc<RefCell<EM>>,
|
||||
|
||||
current_input: Option<I>, // Todo: Get rid of copy
|
||||
|
||||
stage_idx: i32,
|
||||
|
||||
mutator: M,
|
||||
#[allow(clippy::type_complexity)]
|
||||
phantom: PhantomData<(C, CS, (), EM, I, R, OT, S, Z)>,
|
||||
last_stats_time: Duration,
|
||||
observers: Rc<RefCell<OT>>,
|
||||
exit_kind: Rc<Cell<Option<ExitKind>>>,
|
||||
}
|
||||
|
||||
impl<C, CS, EM, I, M, OT, R, S, Z> StdMutationalPushStage<C, CS, EM, I, M, OT, R, S, Z>
|
||||
where
|
||||
C: Corpus<I>,
|
||||
CS: CorpusScheduler<I, S>,
|
||||
EM: EventManager<(), I, S, Z>,
|
||||
I: Input,
|
||||
M: Mutator<I, S>,
|
||||
OT: ObserversTuple<I, S>,
|
||||
R: Rand,
|
||||
S: HasClientPerfStats + HasCorpus<C, I> + HasRand<R>,
|
||||
Z: ExecutionProcessor<I, OT, S>
|
||||
+ EvaluatorObservers<I, OT, S>
|
||||
+ Fuzzer<(), EM, I, S, ()>
|
||||
+ HasCorpusScheduler<CS, I, S>,
|
||||
{
|
||||
/// Gets the number of iterations as a random number
|
||||
#[allow(clippy::unused_self, clippy::unnecessary_wraps)] // TODO: we should put this function into a trait later
|
||||
fn iterations(&self, state: &mut S, _corpus_idx: usize) -> Result<usize, Error> {
|
||||
Ok(1 + state.rand_mut().below(DEFAULT_MUTATIONAL_MAX_ITERATIONS) as usize)
|
||||
}
|
||||
|
||||
pub fn set_current_corpus_idx(&mut self, current_corpus_idx: usize) {
|
||||
self.current_corpus_idx = Some(current_corpus_idx);
|
||||
}
|
||||
|
||||
/// Creates a new default mutational stage
|
||||
fn init(&mut self) -> Result<(), Error> {
|
||||
let state: &mut S = &mut (*self.state).borrow_mut();
|
||||
|
||||
// Find a testcase to work on, unless someone already set it
|
||||
self.current_corpus_idx = Some(if let Some(corpus_idx) = self.current_corpus_idx {
|
||||
corpus_idx
|
||||
} else {
|
||||
let fuzzer: &mut Z = &mut (*self.fuzzer).borrow_mut();
|
||||
fuzzer.scheduler().next(state)?
|
||||
});
|
||||
|
||||
self.testcases_to_do = self.iterations(state, self.current_corpus_idx.unwrap())?;
|
||||
self.testcases_done = 0;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn pre_exec(&mut self) -> Option<Result<I, Error>> {
|
||||
let state: &mut S = &mut (*self.state).borrow_mut();
|
||||
|
||||
if self.testcases_done >= self.testcases_to_do {
|
||||
// finished with this cicle.
|
||||
return None;
|
||||
}
|
||||
|
||||
start_timer!(state);
|
||||
let mut input = state
|
||||
.corpus()
|
||||
.get(self.current_corpus_idx.unwrap())
|
||||
.unwrap()
|
||||
.borrow_mut()
|
||||
.load_input()
|
||||
.unwrap()
|
||||
.clone();
|
||||
mark_feature_time!(state, PerfFeature::GetInputFromCorpus);
|
||||
|
||||
start_timer!(state);
|
||||
self.mutator
|
||||
.mutate(state, &mut input, self.stage_idx as i32)
|
||||
.unwrap();
|
||||
mark_feature_time!(state, PerfFeature::Mutate);
|
||||
|
||||
self.current_input = Some(input.clone()); // TODO: Get rid of this
|
||||
|
||||
Some(Ok(input))
|
||||
}
|
||||
|
||||
fn post_exec(&mut self) -> Result<(), Error> {
|
||||
// todo: isintersting, etc.
|
||||
|
||||
let state: &mut S = &mut (*self.state).borrow_mut();
|
||||
|
||||
let fuzzer: &mut Z = &mut (*self.fuzzer).borrow_mut();
|
||||
let event_mgr: &mut EM = &mut (*self.event_mgr).borrow_mut();
|
||||
let observers_refcell: &RefCell<OT> = self.observers.borrow_mut();
|
||||
let observers: &mut OT = &mut observers_refcell.borrow_mut();
|
||||
|
||||
fuzzer.process_execution(
|
||||
state,
|
||||
event_mgr,
|
||||
self.current_input.take().unwrap(),
|
||||
observers,
|
||||
&self.exit_kind.get().unwrap(),
|
||||
true,
|
||||
)?;
|
||||
|
||||
start_timer!(state);
|
||||
self.mutator
|
||||
.post_exec(state, self.stage_idx as i32, Some(self.testcases_done))?;
|
||||
mark_feature_time!(state, PerfFeature::MutatePostExec);
|
||||
self.testcases_done += 1;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, CS, EM, I, M, OT, R, S, Z> Iterator for StdMutationalPushStage<C, CS, EM, I, M, OT, R, S, Z>
|
||||
where
|
||||
C: Corpus<I>,
|
||||
CS: CorpusScheduler<I, S>,
|
||||
EM: EventManager<(), I, S, Z>,
|
||||
I: Input,
|
||||
M: Mutator<I, S>,
|
||||
OT: ObserversTuple<I, S>,
|
||||
R: Rand,
|
||||
S: HasClientPerfStats + HasCorpus<C, I> + HasRand<R>,
|
||||
Z: ExecutionProcessor<I, OT, S>
|
||||
+ EvaluatorObservers<I, OT, S>
|
||||
+ Fuzzer<(), EM, I, S, ()>
|
||||
+ HasCorpusScheduler<CS, I, S>,
|
||||
{
|
||||
type Item = Result<I, Error>;
|
||||
|
||||
fn next(&mut self) -> Option<Result<I, Error>> {
|
||||
let step_success = if self.initialized {
|
||||
// We already ran once
|
||||
self.post_exec()
|
||||
} else {
|
||||
self.init() // TODO: Corpus idx
|
||||
};
|
||||
if let Err(err) = step_success {
|
||||
//let errored = true;
|
||||
return Some(Err(err));
|
||||
}
|
||||
|
||||
//for i in 0..num {
|
||||
let ret = self.pre_exec();
|
||||
if ret.is_none() {
|
||||
// We're done.
|
||||
self.initialized = false;
|
||||
self.current_corpus_idx = None;
|
||||
|
||||
let state: &mut S = &mut (*self.state).borrow_mut();
|
||||
//let fuzzer: &mut Z = &mut (*self.fuzzer).borrow_mut();
|
||||
let event_mgr: &mut EM = &mut (*self.event_mgr).borrow_mut();
|
||||
|
||||
self.last_stats_time = Z::maybe_report_stats(
|
||||
state,
|
||||
event_mgr,
|
||||
self.last_stats_time,
|
||||
STATS_TIMEOUT_DEFAULT,
|
||||
)
|
||||
.unwrap();
|
||||
//self.fuzzer.maybe_report_stats();
|
||||
} else {
|
||||
self.exit_kind.replace(None);
|
||||
}
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, CS, EM, I, M, OT, R, S, Z> StdMutationalPushStage<C, CS, EM, I, M, OT, R, S, Z>
|
||||
where
|
||||
C: Corpus<I>,
|
||||
CS: CorpusScheduler<I, S>,
|
||||
EM: EventManager<(), I, S, Z>,
|
||||
I: Input,
|
||||
M: Mutator<I, S>,
|
||||
OT: ObserversTuple<I, S>,
|
||||
R: Rand,
|
||||
S: HasClientPerfStats + HasCorpus<C, I> + HasRand<R>,
|
||||
Z: ExecutionProcessor<I, OT, S>
|
||||
+ EvaluatorObservers<I, OT, S>
|
||||
+ Fuzzer<(), EM, I, S, ()>
|
||||
+ HasCorpusScheduler<CS, I, S>,
|
||||
{
|
||||
/// Creates a new default mutational stage
|
||||
pub fn new(
|
||||
mutator: M,
|
||||
fuzzer: Rc<RefCell<Z>>,
|
||||
state: Rc<RefCell<S>>,
|
||||
event_mgr: Rc<RefCell<EM>>,
|
||||
observers: Rc<RefCell<OT>>,
|
||||
exit_kind: Rc<Cell<Option<ExitKind>>>,
|
||||
stage_idx: i32,
|
||||
) -> Self {
|
||||
Self {
|
||||
mutator,
|
||||
phantom: PhantomData,
|
||||
initialized: false,
|
||||
state,
|
||||
current_iter: None,
|
||||
current_corpus_idx: None, // todo
|
||||
testcases_to_do: 0,
|
||||
testcases_done: 0,
|
||||
current_input: None,
|
||||
stage_idx,
|
||||
fuzzer,
|
||||
event_mgr,
|
||||
observers,
|
||||
exit_kind,
|
||||
last_stats_time: current_time(),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
use core::{marker::PhantomData, mem::drop};
|
||||
//! The tracing stage can trace the target and enrich a testcase with metadata, for example for `CmpLog`.
|
||||
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use crate::{
|
||||
corpus::Corpus,
|
||||
@ -63,10 +65,9 @@ where
|
||||
mark_feature_time!(state, PerfFeature::PreExecObservers);
|
||||
|
||||
start_timer!(state);
|
||||
drop(
|
||||
self.tracer_executor
|
||||
.run_target(fuzzer, state, manager, &input)?,
|
||||
);
|
||||
let _ = self
|
||||
.tracer_executor
|
||||
.run_target(fuzzer, state, manager, &input)?;
|
||||
mark_feature_time!(state, PerfFeature::TargetExecution);
|
||||
|
||||
*state.executions_mut() += 1;
|
||||
@ -145,7 +146,7 @@ where
|
||||
mark_feature_time!(state, PerfFeature::PreExecObservers);
|
||||
|
||||
start_timer!(state);
|
||||
drop(executor.run_target(fuzzer, state, manager, &input)?);
|
||||
let _ = executor.run_target(fuzzer, state, manager, &input)?;
|
||||
mark_feature_time!(state, PerfFeature::TargetExecution);
|
||||
|
||||
*state.executions_mut() += 1;
|
||||
|
Loading…
x
Reference in New Issue
Block a user