Skip to content

Commit 76a95bc

Browse files
authored
Introduce AFL_EXIT_ON_SEED_ISSUES (#2085)
* introduce load_initial_inputs_disallow_solution to exit if a solution is found during seed loading * fmt * rename CorpusError to Corpus * add LoadConfig to simplify configuration for loading initial inputs * Rename Error::Corpus to Error::InvalidCorpus Add documentation to LoadConfig struct fix nostd for LoadConfig --------- Co-authored-by: aarnav <[email protected]>
1 parent d349651 commit 76a95bc

File tree

2 files changed

+103
-24
lines changed

2 files changed

+103
-24
lines changed

libafl/src/state/mod.rs

+89-24
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,24 @@ pub trait HasLastReportTime {
193193
fn last_report_time_mut(&mut self) -> &mut Option<Duration>;
194194
}
195195

196+
/// Struct that holds the options for input loading
197+
#[cfg(feature = "std")]
198+
pub struct LoadConfig<'a, I, S, Z> {
199+
/// Load Input even if it was deemed "uninteresting" by the fuzzer
200+
forced: bool,
201+
/// Function to load input from a Path
202+
loader: &'a mut dyn FnMut(&mut Z, &mut S, &Path) -> Result<I, Error>,
203+
/// Error if Input leads to a Solution.
204+
exit_on_solution: bool,
205+
}
206+
207+
#[cfg(feature = "std")]
208+
impl<'a, I, S, Z> Debug for LoadConfig<'a, I, S, Z> {
209+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
210+
write!(f, "LoadConfig {{}}")
211+
}
212+
}
213+
196214
/// The state a fuzz run.
197215
#[derive(Serialize, Deserialize, Clone, Debug)]
198216
#[serde(bound = "
@@ -647,8 +665,7 @@ where
647665
executor: &mut E,
648666
manager: &mut EM,
649667
file_list: &[PathBuf],
650-
forced: bool,
651-
loader: &mut dyn FnMut(&mut Z, &mut Self, &Path) -> Result<I, Error>,
668+
load_config: LoadConfig<I, Self, Z>,
652669
) -> Result<(), Error>
653670
where
654671
E: UsesState<State = Self>,
@@ -664,45 +681,45 @@ where
664681
self.remaining_initial_files = Some(file_list.to_vec());
665682
}
666683

667-
self.continue_loading_initial_inputs_custom(fuzzer, executor, manager, forced, loader)
684+
self.continue_loading_initial_inputs_custom(fuzzer, executor, manager, load_config)
668685
}
686+
669687
fn load_file<E, EM, Z>(
670688
&mut self,
671689
path: &PathBuf,
672690
manager: &mut EM,
673691
fuzzer: &mut Z,
674692
executor: &mut E,
675-
forced: bool,
676-
loader: &mut dyn FnMut(&mut Z, &mut Self, &Path) -> Result<I, Error>,
677-
) -> Result<(), Error>
693+
config: &mut LoadConfig<I, Self, Z>,
694+
) -> Result<ExecuteInputResult, Error>
678695
where
679696
E: UsesState<State = Self>,
680697
EM: EventFirer<State = Self>,
681698
Z: Evaluator<E, EM, State = Self>,
682699
{
683700
log::info!("Loading file {:?} ...", &path);
684-
let input = loader(fuzzer, self, path)?;
685-
if forced {
701+
let input = (config.loader)(fuzzer, self, path)?;
702+
if config.forced {
686703
let _: CorpusId = fuzzer.add_input(self, executor, manager, input)?;
704+
Ok(ExecuteInputResult::Corpus)
687705
} else {
688706
let (res, _) = fuzzer.evaluate_input(self, executor, manager, input.clone())?;
689707
if res == ExecuteInputResult::None {
690708
fuzzer.add_disabled_input(self, input)?;
691709
log::warn!("input {:?} was not interesting, adding as disabled.", &path);
692710
}
711+
Ok(res)
693712
}
694-
Ok(())
695713
}
696714
/// Loads initial inputs from the passed-in `in_dirs`.
697-
/// If `forced` is true, will add all testcases, no matter what.
698-
/// This method takes a list of files.
715+
/// This method takes a list of files and a `LoadConfig`
716+
/// which specifies the special handling of initial inputs
699717
fn continue_loading_initial_inputs_custom<E, EM, Z>(
700718
&mut self,
701719
fuzzer: &mut Z,
702720
executor: &mut E,
703721
manager: &mut EM,
704-
forced: bool,
705-
loader: &mut dyn FnMut(&mut Z, &mut Self, &Path) -> Result<I, Error>,
722+
mut config: LoadConfig<I, Self, Z>,
706723
) -> Result<(), Error>
707724
where
708725
E: UsesState<State = Self>,
@@ -712,7 +729,13 @@ where
712729
loop {
713730
match self.next_file() {
714731
Ok(path) => {
715-
self.load_file(&path, manager, fuzzer, executor, forced, loader)?;
732+
let res = self.load_file(&path, manager, fuzzer, executor, &mut config)?;
733+
if config.exit_on_solution && matches!(res, ExecuteInputResult::Solution) {
734+
return Err(Error::invalid_corpus(format!(
735+
"Input {} resulted in a solution.",
736+
path.display()
737+
)));
738+
}
716739
}
717740
Err(Error::IteratorEnd(_, _)) => break,
718741
Err(e) => return Err(e),
@@ -751,8 +774,11 @@ where
751774
executor,
752775
manager,
753776
file_list,
754-
false,
755-
&mut |_, _, path| I::from_file(path),
777+
LoadConfig {
778+
loader: &mut |_, _, path| I::from_file(path),
779+
forced: false,
780+
exit_on_solution: false,
781+
},
756782
)
757783
}
758784

@@ -776,8 +802,11 @@ where
776802
fuzzer,
777803
executor,
778804
manager,
779-
true,
780-
&mut |_, _, path| I::from_file(path),
805+
LoadConfig {
806+
loader: &mut |_, _, path| I::from_file(path),
807+
forced: true,
808+
exit_on_solution: false,
809+
},
781810
)
782811
}
783812
/// Loads initial inputs from the passed-in `in_dirs`.
@@ -800,8 +829,11 @@ where
800829
executor,
801830
manager,
802831
file_list,
803-
true,
804-
&mut |_, _, path| I::from_file(path),
832+
LoadConfig {
833+
loader: &mut |_, _, path| I::from_file(path),
834+
forced: true,
835+
exit_on_solution: false,
836+
},
805837
)
806838
}
807839

@@ -823,8 +855,38 @@ where
823855
fuzzer,
824856
executor,
825857
manager,
826-
false,
827-
&mut |_, _, path| I::from_file(path),
858+
LoadConfig {
859+
loader: &mut |_, _, path| I::from_file(path),
860+
forced: false,
861+
exit_on_solution: false,
862+
},
863+
)
864+
}
865+
866+
/// Loads initial inputs from the passed-in `in_dirs`.
867+
/// Will return a `CorpusError` if a solution is found
868+
pub fn load_initial_inputs_disallow_solution<E, EM, Z>(
869+
&mut self,
870+
fuzzer: &mut Z,
871+
executor: &mut E,
872+
manager: &mut EM,
873+
in_dirs: &[PathBuf],
874+
) -> Result<(), Error>
875+
where
876+
E: UsesState<State = Self>,
877+
EM: EventFirer<State = Self>,
878+
Z: Evaluator<E, EM, State = Self>,
879+
{
880+
self.canonicalize_input_dirs(in_dirs)?;
881+
self.continue_loading_initial_inputs_custom(
882+
fuzzer,
883+
executor,
884+
manager,
885+
LoadConfig {
886+
loader: &mut |_, _, path| I::from_file(path),
887+
forced: false,
888+
exit_on_solution: true,
889+
},
828890
)
829891
}
830892

@@ -862,8 +924,11 @@ where
862924
fuzzer,
863925
executor,
864926
manager,
865-
false,
866-
&mut |_, _, path| I::from_file(path),
927+
LoadConfig {
928+
loader: &mut |_, _, path| I::from_file(path),
929+
forced: false,
930+
exit_on_solution: false,
931+
},
867932
)?;
868933
} else {
869934
self.canonicalize_input_dirs(in_dirs)?;

libafl_bolts/src/lib.rs

+14
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,8 @@ pub enum Error {
322322
OsError(io::Error, String, ErrorBacktrace),
323323
/// Something else happened
324324
Unknown(String, ErrorBacktrace),
325+
/// Error with the corpora
326+
InvalidCorpus(String, ErrorBacktrace),
325327
}
326328

327329
impl Error {
@@ -438,6 +440,14 @@ impl Error {
438440
{
439441
Error::Unknown(arg.into(), ErrorBacktrace::new())
440442
}
443+
/// Error with corpora
444+
#[must_use]
445+
pub fn invalid_corpus<S>(arg: S) -> Self
446+
where
447+
S: Into<String>,
448+
{
449+
Error::InvalidCorpus(arg.into(), ErrorBacktrace::new())
450+
}
441451
}
442452

443453
impl Display for Error {
@@ -498,6 +508,10 @@ impl Display for Error {
498508
write!(f, "Unknown error: {0}", &s)?;
499509
display_error_backtrace(f, b)
500510
}
511+
Self::InvalidCorpus(s, b) => {
512+
write!(f, "Invalid corpus: {0}", &s)?;
513+
display_error_backtrace(f, b)
514+
}
501515
}
502516
}
503517
}

0 commit comments

Comments
 (0)