Move Input loading and dumping APIs from Testcase to Corpus (#1201)

* Less allocatiosn for filenames

* clippy for wasm fuzzer

* Reworked filename and rename APIs

* python, clippy

* fmt

* More cleanup, fixed metadata location

* clippy

* fix fuzzbench_text / cached len, invert parameters (state first)

* clippy

* oops

* Caching for paths

* simplified, fixed

* no_std

* cached_len

* Nider API for input getting
This commit is contained in:
Dominik Maier 2023-04-18 12:14:49 +02:00 committed by GitHub
parent fd68c8a81f
commit 96e24d1c8b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 389 additions and 287 deletions

View File

@ -23,15 +23,16 @@ use crate::utils::set_panic_hook;
// defined for internal use by libafl // defined for internal use by libafl
#[no_mangle] #[no_mangle]
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
pub extern "C" fn external_current_millis() -> u64 { pub extern "C" fn external_current_millis() -> u64 {
let window: Window = web_sys::window().expect("should be in browser to run this demo"); let window: Window = web_sys::window().expect("should be in browser to run this demo");
let performance: Performance = window let performance: Performance = window
.performance() .performance()
.expect("should be in browser to run this demo"); .expect("should be in browser to run this demo");
let now = performance.now() as u64; performance.now() as u64
now
} }
#[allow(clippy::missing_panics_doc)]
#[wasm_bindgen] #[wasm_bindgen]
pub fn fuzz() { pub fn fuzz() {
set_panic_hook(); set_panic_hook();
@ -39,7 +40,7 @@ pub fn fuzz() {
let mut signals = [0u8; 64]; let mut signals = [0u8; 64];
let signals_ptr = signals.as_mut_ptr(); let signals_ptr = signals.as_mut_ptr();
let signals_set = |i: usize| unsafe { let signals_set = |i: usize| unsafe {
*signals_ptr.offset(i as isize) += 1; *signals_ptr.add(i) += 1;
}; };
// The closure that we want to fuzz // The closure that we want to fuzz

View File

@ -28,6 +28,7 @@ clap = { version = "4.0", features = ["default"] }
nix = "0.26" nix = "0.26"
mimalloc = { version = "*", default-features = false } mimalloc = { version = "*", default-features = false }
content_inspector = "0.2.4" content_inspector = "0.2.4"
#log = "0.4"
[lib] [lib]
name = "fuzzbench" name = "fuzzbench"

View File

@ -288,6 +288,12 @@ fn fuzz_binary(
// This way, we are able to continue fuzzing afterwards. // This way, we are able to continue fuzzing afterwards.
let mut shmem_provider = StdShMemProvider::new()?; let mut shmem_provider = StdShMemProvider::new()?;
/*
// For debugging
let mut mgr = SimpleEventManager::new(monitor);
let mut state = None;
*/
let (state, mut mgr) = match SimpleRestartingEventManager::launch(monitor, &mut shmem_provider) let (state, mut mgr) = match SimpleRestartingEventManager::launch(monitor, &mut shmem_provider)
{ {
// The restarting state will spawn the same process again as child, then restarted it each time it crashes. // The restarting state will spawn the same process again as child, then restarted it each time it crashes.
@ -435,8 +441,8 @@ fn fuzz_binary(
if state.must_load_initial_inputs() { if state.must_load_initial_inputs() {
state state
.load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &[seed_dir.clone()]) .load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &[seed_dir.clone()])
.unwrap_or_else(|_| { .unwrap_or_else(|e| {
println!("Failed to load initial corpus at {:?}", &seed_dir); println!("Failed to load initial corpus at {seed_dir:?} - {e:?}");
process::exit(0); process::exit(0);
}); });
println!("We imported {} inputs from disk.", state.corpus().count()); println!("We imported {} inputs from disk.", state.corpus().count());
@ -491,6 +497,14 @@ fn fuzz_text(
// This way, we are able to continue fuzzing afterwards. // This way, we are able to continue fuzzing afterwards.
let mut shmem_provider = StdShMemProvider::new()?; let mut shmem_provider = StdShMemProvider::new()?;
/*
// For debugging
log::set_max_level(log::LevelFilter::Trace);
SimpleStderrLogger::set_logger()?;
let mut mgr = SimpleEventManager::new(monitor);
let mut state = None;
*/
let (state, mut mgr) = match SimpleRestartingEventManager::launch(monitor, &mut shmem_provider) let (state, mut mgr) = match SimpleRestartingEventManager::launch(monitor, &mut shmem_provider)
{ {
// The restarting state will spawn the same process again as child, then restarted it each time it crashes. // The restarting state will spawn the same process again as child, then restarted it each time it crashes.
@ -654,8 +668,8 @@ fn fuzz_text(
if state.must_load_initial_inputs() { if state.must_load_initial_inputs() {
state state
.load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &[seed_dir.clone()]) .load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &[seed_dir.clone()])
.unwrap_or_else(|_| { .unwrap_or_else(|e| {
println!("Failed to load initial corpus at {:?}", &seed_dir); println!("Failed to load initial corpus at {seed_dir:?} {e:?}");
process::exit(0); process::exit(0);
}); });
println!("We imported {} inputs from disk.", state.corpus().count()); println!("We imported {} inputs from disk.", state.corpus().count());

View File

@ -25,7 +25,7 @@ impl<S> TestcaseScore<S> for PacketLenTestcaseScore
where where
S: HasCorpus<Input = PacketData> + HasMetadata, S: HasCorpus<Input = PacketData> + HasMetadata,
{ {
fn compute(entry: &mut Testcase<PacketData>, _state: &S) -> Result<f64, Error> { fn compute(_state: &S, entry: &mut Testcase<PacketData>) -> Result<f64, Error> {
Ok(entry Ok(entry
.metadata_map() .metadata_map()
.get::<PacketLenMetadata>() .get::<PacketLenMetadata>()

View File

@ -0,0 +1,7 @@
{
"metadata": {
"map": {}
},
"exec_time": null,
"executions": 0
}

View File

@ -73,7 +73,7 @@ where
fn get(&self, idx: CorpusId) -> Result<&RefCell<Testcase<I>>, Error> { fn get(&self, idx: CorpusId) -> Result<&RefCell<Testcase<I>>, Error> {
let testcase = { self.inner.get(idx)? }; let testcase = { self.inner.get(idx)? };
if testcase.borrow().input().is_none() { if testcase.borrow().input().is_none() {
let _: &I = testcase.borrow_mut().load_input()?; self.load_input_into(&mut testcase.borrow_mut())?;
let mut borrowed_num = 0; let mut borrowed_num = 0;
while self.cached_indexes.borrow().len() >= self.cache_max_len { while self.cached_indexes.borrow().len() >= self.cache_max_len {
let removed = self.cached_indexes.borrow_mut().pop_front().unwrap(); let removed = self.cached_indexes.borrow_mut().pop_front().unwrap();
@ -128,6 +128,16 @@ where
fn nth(&self, nth: usize) -> CorpusId { fn nth(&self, nth: usize) -> CorpusId {
self.inner.nth(nth) self.inner.nth(nth)
} }
#[inline]
fn load_input_into(&self, testcase: &mut Testcase<Self::Input>) -> Result<(), Error> {
self.inner.load_input_into(testcase)
}
#[inline]
fn store_input_from(&self, testcase: &Testcase<Self::Input>) -> Result<(), Error> {
self.inner.store_input_from(testcase)
}
} }
impl<I> HasTestcase for CachedOnDiskCorpus<I> impl<I> HasTestcase for CachedOnDiskCorpus<I>

View File

@ -380,6 +380,17 @@ where
fn nth(&self, nth: usize) -> CorpusId { fn nth(&self, nth: usize) -> CorpusId {
self.storage.keys[nth] self.storage.keys[nth]
} }
#[inline]
fn load_input_into(&self, _: &mut Testcase<Self::Input>) -> Result<(), Error> {
// Inputs never get evicted, nothing to load here.
Ok(())
}
#[inline]
fn store_input_from(&self, _: &Testcase<Self::Input>) -> Result<(), Error> {
Ok(())
}
} }
impl<I> HasTestcase for InMemoryCorpus<I> impl<I> HasTestcase for InMemoryCorpus<I>

View File

@ -3,6 +3,7 @@
//! For a lower memory footprint, consider using [`crate::corpus::CachedOnDiskCorpus`] //! For a lower memory footprint, consider using [`crate::corpus::CachedOnDiskCorpus`]
//! which only stores a certain number of [`Testcase`]s and removes additional ones in a FIFO manner. //! which only stores a certain number of [`Testcase`]s and removes additional ones in a FIFO manner.
use alloc::string::String;
use core::{cell::RefCell, time::Duration}; use core::{cell::RefCell, time::Duration};
#[cfg(feature = "std")] #[cfg(feature = "std")]
use std::{fs, fs::File, io::Write}; use std::{fs, fs::File, io::Write};
@ -72,7 +73,9 @@ where
#[inline] #[inline]
fn add(&mut self, testcase: Testcase<I>) -> Result<CorpusId, Error> { fn add(&mut self, testcase: Testcase<I>) -> Result<CorpusId, Error> {
let idx = self.inner.add(testcase)?; let idx = self.inner.add(testcase)?;
self.save_testcase(&mut self.get(idx).unwrap().borrow_mut(), idx)?; let testcase = &mut self.get(idx).unwrap().borrow_mut();
self.save_testcase(testcase, idx)?;
*testcase.input_mut() = None;
Ok(idx) Ok(idx)
} }
@ -81,7 +84,9 @@ where
fn replace(&mut self, idx: CorpusId, testcase: Testcase<I>) -> Result<Testcase<I>, Error> { fn replace(&mut self, idx: CorpusId, testcase: Testcase<I>) -> Result<Testcase<I>, Error> {
let entry = self.inner.replace(idx, testcase)?; let entry = self.inner.replace(idx, testcase)?;
self.remove_testcase(&entry)?; self.remove_testcase(&entry)?;
self.save_testcase(&mut self.get(idx).unwrap().borrow_mut(), idx)?; let testcase = &mut self.get(idx).unwrap().borrow_mut();
self.save_testcase(testcase, idx)?;
*testcase.input_mut() = None;
Ok(entry) Ok(entry)
} }
@ -135,6 +140,28 @@ where
fn nth(&self, nth: usize) -> CorpusId { fn nth(&self, nth: usize) -> CorpusId {
self.inner.nth(nth) self.inner.nth(nth)
} }
fn load_input_into(&self, testcase: &mut Testcase<Self::Input>) -> Result<(), Error> {
if testcase.input_mut().is_none() {
let Some(file_path) = testcase.file_path().as_ref() else {
return Err(Error::illegal_argument("No file path set for testcase. Could not load inputs."));
};
let input = I::from_file(file_path)?;
testcase.set_input(input);
}
Ok(())
}
fn store_input_from(&self, testcase: &Testcase<Self::Input>) -> Result<(), Error> {
// Store the input to disk
let Some(file_path) = testcase.file_path() else {
return Err(Error::illegal_argument("No file path set for testcase. Could not store input to disk."));
};
let Some(input) = testcase.input() else {
return Err(Error::illegal_argument("No input available for testcase. Could not store anything."));
};
input.to_file(file_path)
}
} }
impl<I> HasTestcase for InMemoryOnDiskCorpus<I> impl<I> HasTestcase for InMemoryOnDiskCorpus<I>
@ -212,47 +239,109 @@ where
}) })
} }
/// Sets the filename for a [`Testcase`].
/// If an error gets returned from the corpus (i.e., file exists), we'll have to retry with a different filename.
#[inline]
pub fn rename_testcase(
&self,
testcase: &mut Testcase<I>,
filename: String,
) -> Result<(), Error> {
if testcase.filename().is_some() {
// We are renaming!
let old_filename = testcase.filename_mut().take().unwrap();
let new_filename = filename;
// Do operations below when new filename is specified
if old_filename == new_filename {
*testcase.filename_mut() = Some(old_filename);
return Ok(());
}
let new_lock_filename = format!(".{new_filename}.lafl_lock");
// Try to create lock file for new testcases
if OpenOptions::new()
.create(true)
.write(true)
.open(self.dir_path.join(new_lock_filename))
.is_err()
{
*testcase.filename_mut() = Some(old_filename);
return Err(Error::illegal_state(
"unable to create lock file for new testcase",
));
}
let new_file_path = self.dir_path.join(&new_filename);
fs::rename(testcase.file_path().as_ref().unwrap(), &new_file_path)?;
let new_metadata_path = {
if let Some(old_metadata_path) = testcase.metadata_path() {
// We have metadata. Let's rename it.
let new_metadata_path = self.dir_path.join(format!(".{new_filename}.metadata"));
fs::rename(old_metadata_path, &new_metadata_path)?;
Some(new_metadata_path)
} else {
None
}
};
*testcase.metadata_path_mut() = new_metadata_path;
*testcase.filename_mut() = Some(new_filename);
*testcase.file_path_mut() = Some(new_file_path);
Ok(())
} else {
Err(Error::illegal_argument(
"Cannot rename testcase without name!",
))
}
}
fn save_testcase(&self, testcase: &mut Testcase<I>, idx: CorpusId) -> Result<(), Error> { fn save_testcase(&self, testcase: &mut Testcase<I>, idx: CorpusId) -> Result<(), Error> {
if testcase.filename().is_none() { let file_name_orig = testcase.filename_mut().take().unwrap_or_else(|| {
// TODO walk entry metadata to ask for pieces of filename (e.g. :havoc in AFL) // TODO walk entry metadata to ask for pieces of filename (e.g. :havoc in AFL)
let file_orig = testcase.input().as_ref().unwrap().generate_name(idx.0); testcase.input().as_ref().unwrap().generate_name(idx.0)
let mut file = file_orig.clone(); });
if testcase.file_path().is_some() {
// We already have a valid path, no need to do calculate anything
*testcase.filename_mut() = Some(file_name_orig);
} else {
// New testcase, we need to save it.
let mut file_name = file_name_orig.clone();
let mut ctr = 2; let mut ctr = 2;
let filename = loop { let (file_name, lockfile_path) = loop {
let lockfile = format!(".{file}.lafl_lock"); let lockfile_name = format!(".{file_name}.lafl_lock");
let lockfile_path = self.dir_path.join(lockfile_name);
if OpenOptions::new() if OpenOptions::new()
.write(true) .write(true)
.create_new(true) .create_new(true)
.open(self.dir_path.join(lockfile)) .open(&lockfile_path)
.is_ok() .is_ok()
{ {
break file; break (file_name, lockfile_path);
} }
file = format!("{file_orig}-{ctr}"); file_name = format!("{file_name_orig}-{ctr}");
ctr += 1; ctr += 1;
}; };
let file_path = self.dir_path.join(filename.clone()); *testcase.file_path_mut() = Some(self.dir_path.join(&file_name));
let filename_str = file_path.to_str().expect("Invalid Path"); *testcase.filename_mut() = Some(file_name);
testcase.set_filename(filename_str.into())?;
fs::remove_file(lockfile_path)?;
}
let lock_file_path = self.dir_path.join(format!(".{filename}.lafl_lock"));
fs::remove_file(lock_file_path)?;
};
if self.meta_format.is_some() { if self.meta_format.is_some() {
let mut filename = PathBuf::from(testcase.filename().as_ref().unwrap()); let metafile_name = format!(".{}.metadata", testcase.filename().as_ref().unwrap());
filename.set_file_name(format!( let metafile_path = self.dir_path.join(&metafile_name);
".{}.metadata", let mut tmpfile_path = metafile_path.clone();
filename.file_name().unwrap().to_string_lossy() tmpfile_path.set_file_name(format!(".{metafile_name}.tmp",));
));
let mut tmpfile_name = PathBuf::from(&filename);
tmpfile_name.set_file_name(format!(
".{}.tmp",
tmpfile_name.file_name().unwrap().to_string_lossy()
));
let ondisk_meta = OnDiskMetadata { let ondisk_meta = OnDiskMetadata {
metadata: testcase.metadata_map(), metadata: testcase.metadata_map(),
@ -260,7 +349,7 @@ where
executions: testcase.executions(), executions: testcase.executions(),
}; };
let mut tmpfile = File::create(&tmpfile_name)?; let mut tmpfile = File::create(&tmpfile_path)?;
let serialized = match self.meta_format.as_ref().unwrap() { let serialized = match self.meta_format.as_ref().unwrap() {
OnDiskMetadataFormat::Postcard => postcard::to_allocvec(&ondisk_meta)?, OnDiskMetadataFormat::Postcard => postcard::to_allocvec(&ondisk_meta)?,
@ -272,25 +361,23 @@ where
.unwrap(), .unwrap(),
}; };
tmpfile.write_all(&serialized)?; tmpfile.write_all(&serialized)?;
fs::rename(&tmpfile_name, &filename)?; fs::rename(&tmpfile_path, &metafile_path)?;
*testcase.metadata_path_mut() = Some(metafile_path);
} }
testcase
.store_input() self.store_input_from(testcase)?;
.expect("Could not save testcase to disk");
Ok(()) Ok(())
} }
fn remove_testcase(&self, testcase: &Testcase<I>) -> Result<(), Error> { fn remove_testcase(&self, testcase: &Testcase<I>) -> Result<(), Error> {
if let Some(filename) = testcase.filename() { if let Some(filename) = testcase.filename() {
fs::remove_file(filename)?; fs::remove_file(self.dir_path.join(filename))?;
}
if self.meta_format.is_some() { if self.meta_format.is_some() {
let mut filename = PathBuf::from(testcase.filename().as_ref().unwrap()); fs::remove_file(self.dir_path.join(format!(".{filename}.metadata")))?;
filename.set_file_name(format!( }
".{}.metadata", // also try to remove the corresponding `.lafl_lock` file if it still exists
filename.file_name().unwrap().to_string_lossy() // (even though it shouldn't exist anymore, at this point in time)
)); let _ = fs::remove_file(self.dir_path.join(format!(".{filename}.lafl_lock")));
fs::remove_file(filename)?;
} }
Ok(()) Ok(())
} }

View File

@ -115,7 +115,7 @@ where
while let Some(idx) = cur_id { while let Some(idx) = cur_id {
let (weight, input) = { let (weight, input) = {
let mut testcase = state.corpus().get(idx)?.borrow_mut(); let mut testcase = state.corpus().get(idx)?.borrow_mut();
let weight = TS::compute(&mut *testcase, state)? let weight = TS::compute(state, &mut *testcase)?
.to_u64() .to_u64()
.expect("Weight must be computable."); .expect("Weight must be computable.");
let input = testcase let input = testcase

View File

@ -71,7 +71,7 @@ macro_rules! random_corpus_id {
}}; }};
} }
/// Corpus with all current testcases /// Corpus with all current [`Testcase`]s, or solutions
pub trait Corpus: UsesInput + Serialize + for<'de> Deserialize<'de> { pub trait Corpus: UsesInput + Serialize + for<'de> Deserialize<'de> {
/// Returns the number of elements /// Returns the number of elements
fn count(&self) -> usize; fn count(&self) -> usize;
@ -84,7 +84,7 @@ pub trait Corpus: UsesInput + Serialize + for<'de> Deserialize<'de> {
/// Add an entry to the corpus and return its index /// Add an entry to the corpus and return its index
fn add(&mut self, testcase: Testcase<Self::Input>) -> Result<CorpusId, Error>; fn add(&mut self, testcase: Testcase<Self::Input>) -> Result<CorpusId, Error>;
/// Replaces the testcase at the given idx, returning the existing. /// Replaces the [`Testcase`] at the given idx, returning the existing.
fn replace( fn replace(
&mut self, &mut self,
idx: CorpusId, idx: CorpusId,
@ -130,9 +130,23 @@ pub trait Corpus: UsesInput + Serialize + for<'de> Deserialize<'de> {
.nth(nth) .nth(nth)
.expect("Failed to get the {nth} CorpusId") .expect("Failed to get the {nth} CorpusId")
} }
/// Method to load the input for this [`Testcase`] from persistent storage,
/// if necessary, and if was not already loaded (`== Some(input)`).
/// After this call, `testcase.input()` must always return `Some(input)`.
fn load_input_into(&self, testcase: &mut Testcase<Self::Input>) -> Result<(), Error>;
/// Method to store the input of this `Testcase` to persistent storage, if necessary.
fn store_input_from(&self, testcase: &Testcase<Self::Input>) -> Result<(), Error>;
/// Loads the `Input` for a given [`CorpusId`] from the [`Corpus`], and returns the clone.
fn cloned_input_for_id(&self, idx: CorpusId) -> Result<Self::Input, Error> {
let mut testcase = self.get(idx)?.borrow_mut();
Ok(testcase.load_input(self)?.clone())
}
} }
/// `Iterator` over the ids of a `Corpus` /// [`Iterator`] over the ids of a [`Corpus`]
#[derive(Debug)] #[derive(Debug)]
pub struct CorpusIdIterator<'a, C> pub struct CorpusIdIterator<'a, C>
where where
@ -368,6 +382,14 @@ pub mod pybind {
unwrap_me!(self.wrapper, c, { c.last() }) unwrap_me!(self.wrapper, c, { c.last() })
} }
fn load_input_into(&self, testcase: &mut Testcase<BytesInput>) -> Result<(), Error> {
unwrap_me!(self.wrapper, c, { c.load_input_into(testcase) })
}
fn store_input_from(&self, testcase: &Testcase<BytesInput>) -> Result<(), Error> {
unwrap_me!(self.wrapper, c, { c.store_input_from(testcase) })
}
/*fn ids<'a>(&'a self) -> CorpusIdIterator<'a, Self> { /*fn ids<'a>(&'a self) -> CorpusIdIterator<'a, Self> {
CorpusIdIterator { CorpusIdIterator {
corpus: self, corpus: self,

View File

@ -138,6 +138,16 @@ where
fn nth(&self, nth: usize) -> CorpusId { fn nth(&self, nth: usize) -> CorpusId {
self.inner.nth(nth) self.inner.nth(nth)
} }
#[inline]
fn load_input_into(&self, testcase: &mut Testcase<Self::Input>) -> Result<(), Error> {
self.inner.load_input_into(testcase)
}
#[inline]
fn store_input_from(&self, testcase: &Testcase<Self::Input>) -> Result<(), Error> {
self.inner.store_input_from(testcase)
}
} }
impl<I> HasTestcase for OnDiskCorpus<I> impl<I> HasTestcase for OnDiskCorpus<I>

View File

@ -9,10 +9,11 @@ use core::{
time::Duration, time::Duration,
}; };
#[cfg(feature = "std")] #[cfg(feature = "std")]
use std::fs; use std::path::PathBuf;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use super::Corpus;
use crate::{ use crate::{
bolts::{serdeany::SerdeAnyMap, HasLen}, bolts::{serdeany::SerdeAnyMap, HasLen},
corpus::CorpusId, corpus::CorpusId,
@ -43,12 +44,18 @@ pub struct Testcase<I>
where where
I: Input, I: Input,
{ {
/// The input of this testcase /// The [`Input`] of this [`Testcase`], or `None`, if it is not currently in memory
input: Option<I>, input: Option<I>,
/// Filename, if this testcase is backed by a file in the filesystem /// The filename for this [`Testcase`]
filename: Option<String>, filename: Option<String>,
/// Map of metadata associated with this testcase /// Complete path to the [`Input`] on disk, if this [`Testcase`] is backed by a file in the filesystem
#[cfg(feature = "std")]
file_path: Option<PathBuf>,
/// Map of metadata associated with this [`Testcase`]
metadata: SerdeAnyMap, metadata: SerdeAnyMap,
/// Complete path to the metadata [`SerdeAnyMap`] on disk, if this [`Testcase`] is backed by a file in the filesystem
#[cfg(feature = "std")]
metadata_path: Option<PathBuf>,
/// Time needed to execute the input /// Time needed to execute the input
exec_time: Option<Duration>, exec_time: Option<Duration>,
/// Cached len of the input, if any /// Cached len of the input, if any
@ -83,36 +90,13 @@ impl<I> Testcase<I>
where where
I: Input, I: Input,
{ {
/// Returns this testcase with a loaded input /// Returns this [`Testcase`] with a loaded `Input`]
pub fn load_input(&mut self) -> Result<&I, Error> { pub fn load_input<C: Corpus<Input = I>>(&mut self, corpus: &C) -> Result<&I, Error> {
if self.input.is_none() { corpus.load_input_into(self)?;
self.input = Some(I::from_file(self.filename.as_ref().unwrap())?);
}
Ok(self.input.as_ref().unwrap()) Ok(self.input.as_ref().unwrap())
} }
/// Store the input to disk if possible /// Get the input, if available any
pub fn store_input(&mut self) -> Result<bool, Error> {
match self.filename() {
Some(fname) => {
let saved = match self.input() {
None => false,
Some(i) => {
i.to_file(fname)?;
true
}
};
if saved {
// remove the input from memory
*self.input_mut() = None;
}
Ok(saved)
}
None => Ok(false),
}
}
/// Get the input, if any
#[inline] #[inline]
pub fn input(&self) -> &Option<I> { pub fn input(&self) -> &Option<I> {
&self.input &self.input
@ -144,55 +128,32 @@ where
&mut self.filename &mut self.filename
} }
/// Set the filename /// Get the filename path, if any
#[inline] #[inline]
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub fn set_filename(&mut self, filename: String) -> Result<(), Error> { pub fn file_path(&self) -> &Option<PathBuf> {
use std::fs::OpenOptions; &self.file_path
if self.filename.is_some() {
let f = self.filename.clone().unwrap();
let old_filename = f.as_str();
let new_filename = filename.as_str();
// Do operations below when new filename is specified
if old_filename.eq(new_filename) {
return Ok(());
}
let new_lock_filename = format!(".{new_filename}.lafl_lock");
// Try to create lock file for new testcases
if OpenOptions::new()
.create(true)
.write(true)
.open(&new_lock_filename)
.is_err()
{
return Err(Error::illegal_state(
"unable to create lock file for new testcase",
));
}
fs::rename(old_filename, new_filename)?;
let old_metadata_filename = format!(".{old_filename}.metadata");
let new_metadata_filename = format!(".{new_filename}.metadata");
fs::rename(old_metadata_filename, new_metadata_filename)?;
fs::remove_file(&new_lock_filename)?;
}
self.filename = Some(filename);
Ok(())
} }
/// Get the filename path, if any (mutable)
#[inline] #[inline]
#[cfg(feature = "no_std")] #[cfg(feature = "std")]
pub fn set_filename(&mut self, filename: String) -> Result<(), Error> { pub fn file_path_mut(&mut self) -> &mut Option<PathBuf> {
self.filename = Some(filename); &mut self.file_path
Ok(()) }
/// Get the metadata path, if any
#[inline]
#[cfg(feature = "std")]
pub fn metadata_path(&self) -> &Option<PathBuf> {
&self.metadata_path
}
/// Get the metadata path, if any (mutable)
#[inline]
#[cfg(feature = "std")]
pub fn metadata_path_mut(&mut self) -> &mut Option<PathBuf> {
&mut self.metadata_path
} }
/// Get the execution time of the testcase /// Get the execution time of the testcase
@ -313,6 +274,10 @@ where
scheduled_count: 0, scheduled_count: 0,
executions: 0, executions: 0,
parent_id: None, parent_id: None,
#[cfg(feature = "std")]
file_path: None,
#[cfg(feature = "std")]
metadata_path: None,
} }
} }
} }
@ -322,25 +287,30 @@ impl<I> Testcase<I>
where where
I: Input + HasLen, I: Input + HasLen,
{ {
/// Get the cached len /// Get the cached `len`. Will `Error::EmptyOptional` if `len` is not yet cached.
#[inline] #[inline]
pub fn cached_len(&mut self) -> Result<usize, Error> { pub fn cached_len(&mut self) -> Option<usize> {
Ok(match &self.input { self.cached_len
}
/// Get the `len` or calculate it, if not yet calculated.
#[allow(clippy::len_without_is_empty)]
pub fn load_len<C: Corpus<Input = I>>(&mut self, corpus: &C) -> Result<usize, Error> {
match &self.input {
Some(i) => { Some(i) => {
let l = i.len(); let l = i.len();
self.cached_len = Some(l); self.cached_len = Some(l);
l Ok(l)
} }
None => { None => {
if let Some(l) = self.cached_len { if let Some(l) = self.cached_len {
l Ok(l)
} else { } else {
let l = self.load_input()?.len(); corpus.load_input_into(self)?;
self.cached_len = Some(l); self.load_len(corpus)
l }
} }
} }
})
} }
} }
@ -471,11 +441,7 @@ pub mod pybind {
use pyo3::{prelude::*, types::PyDict}; use pyo3::{prelude::*, types::PyDict};
use super::{HasMetadata, Testcase}; use super::{HasMetadata, Testcase};
use crate::{ use crate::{bolts::ownedref::OwnedMutPtr, inputs::BytesInput, pybind::PythonMetadata};
bolts::ownedref::OwnedMutPtr,
inputs::{BytesInput, HasBytesVec},
pybind::PythonMetadata,
};
/// `PythonTestcase` with fixed generics /// `PythonTestcase` with fixed generics
pub type PythonTestcase = Testcase<BytesInput>; pub type PythonTestcase = Testcase<BytesInput>;
@ -514,14 +480,6 @@ pub mod pybind {
} }
} }
fn load_input(&mut self) -> &[u8] {
self.inner
.as_mut()
.load_input()
.expect("Failed to load input")
.bytes()
}
#[getter] #[getter]
fn exec_time_ms(&self) -> Option<u128> { fn exec_time_ms(&self) -> Option<u128> {
self.inner.as_ref().exec_time().map(|t| t.as_millis()) self.inner.as_ref().exec_time().map(|t| t.as_millis())

View File

@ -9,14 +9,14 @@ use serde_json;
use crate::{ use crate::{
bolts::tuples::Named, bolts::tuples::Named,
corpus::Testcase, corpus::{Corpus, Testcase},
events::EventFirer, events::EventFirer,
executors::ExitKind, executors::ExitKind,
feedbacks::Feedback, feedbacks::Feedback,
generators::NautilusContext, generators::NautilusContext,
inputs::{NautilusInput, UsesInput}, inputs::{NautilusInput, UsesInput},
observers::ObserversTuple, observers::ObserversTuple,
state::{HasClientPerfMonitor, HasMetadata}, state::{HasClientPerfMonitor, HasCorpus, HasMetadata},
Error, Error,
}; };
@ -82,7 +82,10 @@ impl<'a, S> Named for NautilusFeedback<'a, S> {
impl<'a, S> Feedback<S> for NautilusFeedback<'a, S> impl<'a, S> Feedback<S> for NautilusFeedback<'a, S>
where where
S: HasMetadata + HasClientPerfMonitor + UsesInput<Input = NautilusInput>, S: HasMetadata
+ HasClientPerfMonitor
+ UsesInput<Input = NautilusInput>
+ HasCorpus<Input = NautilusInput>,
{ {
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
fn is_interesting<EM, OT>( fn is_interesting<EM, OT>(
@ -109,7 +112,8 @@ where
where where
OT: ObserversTuple<S>, OT: ObserversTuple<S>,
{ {
let input = testcase.load_input()?.clone(); state.corpus().load_input_into(testcase)?;
let input = testcase.input().as_ref().unwrap().clone();
let meta = state let meta = state
.metadata_map_mut() .metadata_map_mut()
.get_mut::<NautilusChunksMetadata>() .get_mut::<NautilusChunksMetadata>()

View File

@ -332,13 +332,11 @@ where
} }
} }
let other_size = state let other_size = {
.corpus() let mut other_testcase = state.corpus().get(idx)?.borrow_mut();
.get(idx)? other_testcase.load_input(state.corpus())?.codes().len()
.borrow_mut() };
.load_input()?
.codes()
.len();
if other_size < 2 { if other_size < 2 {
return Ok(MutationResult::Skipped); return Ok(MutationResult::Skipped);
} }
@ -348,9 +346,6 @@ where
let to = state.rand_mut().below(size as u64) as usize; let to = state.rand_mut().below(size as u64) as usize;
let mut len = 1 + state.rand_mut().below((other_size - from) as u64) as usize; let mut len = 1 + state.rand_mut().below((other_size - from) as u64) as usize;
let mut other_testcase = state.corpus().get(idx)?.borrow_mut();
let other = other_testcase.load_input()?;
if size + len > max_size { if size + len > max_size {
if max_size > size { if max_size > size {
len = max_size - size; len = max_size - size;
@ -359,6 +354,10 @@ where
} }
} }
let other_testcase = state.corpus().get(idx)?.borrow_mut();
// no need to `load_input` again - we did that above already.
let other = other_testcase.input().as_ref().unwrap();
input.codes_mut().resize(size + len, 0); input.codes_mut().resize(size + len, 0);
unsafe { unsafe {
buffer_self_copy(input.codes_mut(), to, to + len, size - to); buffer_self_copy(input.codes_mut(), to, to + len, size - to);
@ -410,13 +409,12 @@ where
} }
} }
let other_size = state let other_size = {
.corpus() // new scope to make the borrow checker happy
.get(idx)? let mut other_testcase = state.corpus().get(idx)?.borrow_mut();
.borrow_mut() other_testcase.load_input(state.corpus())?.codes().len()
.load_input()? };
.codes()
.len();
if other_size < 2 { if other_size < 2 {
return Ok(MutationResult::Skipped); return Ok(MutationResult::Skipped);
} }
@ -425,8 +423,9 @@ where
let len = state.rand_mut().below(min(other_size - from, size) as u64) as usize; let len = state.rand_mut().below(min(other_size - from, size) as u64) as usize;
let to = state.rand_mut().below((size - len) as u64) as usize; let to = state.rand_mut().below((size - len) as u64) as usize;
let mut other_testcase = state.corpus().get(idx)?.borrow_mut(); let other_testcase = state.corpus().get(idx)?.borrow_mut();
let other = other_testcase.load_input()?; // no need to load the input again, it'll already be present at this point.
let other = other_testcase.input().as_ref().unwrap();
unsafe { unsafe {
buffer_copy(input.codes_mut(), other.codes(), from, to, len); buffer_copy(input.codes_mut(), other.codes(), from, to, len);

View File

@ -117,11 +117,10 @@ where
let rand_num = state.rand_mut().next() as usize; let rand_num = state.rand_mut().next() as usize;
let mut other_testcase = state.testcase_mut(idx)?; let mut other_testcase = state.corpus().get(idx)?.borrow_mut();
other_testcase.load_input()?; // Preload the input
if !other_testcase.has_metadata::<GramatronIdxMapMetadata>() { if !other_testcase.has_metadata::<GramatronIdxMapMetadata>() {
let meta = GramatronIdxMapMetadata::new(other_testcase.input().as_ref().unwrap()); let meta = GramatronIdxMapMetadata::new(other_testcase.load_input(state.corpus())?);
other_testcase.add_metadata(meta); other_testcase.add_metadata(meta);
} }
let meta = other_testcase let meta = other_testcase

View File

@ -1114,13 +1114,11 @@ where
} }
} }
let other_size = state let other_size = {
.corpus() let mut other_testcase = state.corpus().get(idx)?.borrow_mut();
.get(idx)? other_testcase.load_input(state.corpus())?.bytes().len()
.borrow_mut() };
.load_input()?
.bytes()
.len();
if other_size < 2 { if other_size < 2 {
return Ok(MutationResult::Skipped); return Ok(MutationResult::Skipped);
} }
@ -1128,9 +1126,6 @@ where
let range = rand_range(state, other_size, min(other_size, max_size - size)); let range = rand_range(state, other_size, min(other_size, max_size - size));
let target = state.rand_mut().below(size as u64) as usize; let target = state.rand_mut().below(size as u64) as usize;
let mut other_testcase = state.corpus().get(idx)?.borrow_mut();
let other = other_testcase.load_input()?;
input.bytes_mut().resize(size + range.len(), 0); input.bytes_mut().resize(size + range.len(), 0);
unsafe { unsafe {
buffer_self_copy( buffer_self_copy(
@ -1139,6 +1134,13 @@ where
target + range.len(), target + range.len(),
size - target, size - target,
); );
}
let other_testcase = state.corpus().get(idx)?.borrow_mut();
// No need to load the input again, it'll still be cached.
let other = other_testcase.input().as_ref().unwrap();
unsafe {
buffer_copy( buffer_copy(
input.bytes_mut(), input.bytes_mut(),
other.bytes(), other.bytes(),
@ -1193,13 +1195,11 @@ where
} }
} }
let other_size = state let other_size = {
.corpus() let mut testcase = state.corpus().get(idx)?.borrow_mut();
.get(idx)? testcase.load_input(state.corpus())?.bytes().len()
.borrow_mut() };
.load_input()?
.bytes()
.len();
if other_size < 2 { if other_size < 2 {
return Ok(MutationResult::Skipped); return Ok(MutationResult::Skipped);
} }
@ -1207,8 +1207,9 @@ where
let target = state.rand_mut().below(size as u64) as usize; let target = state.rand_mut().below(size as u64) as usize;
let range = rand_range(state, other_size, min(other_size, size - target)); let range = rand_range(state, other_size, min(other_size, size - target));
let mut other_testcase = state.corpus().get(idx)?.borrow_mut(); let other_testcase = state.corpus().get(idx)?.borrow_mut();
let other = other_testcase.load_input()?; // No need to load the input again, it'll still be cached.
let other = other_testcase.input().as_ref().unwrap();
unsafe { unsafe {
buffer_copy( buffer_copy(
@ -1279,7 +1280,7 @@ where
let (first_diff, last_diff) = { let (first_diff, last_diff) = {
let mut other_testcase = state.corpus().get(idx)?.borrow_mut(); let mut other_testcase = state.corpus().get(idx)?.borrow_mut();
let other = other_testcase.load_input()?; let other = other_testcase.load_input(state.corpus())?;
let mut counter: u32 = 0; let mut counter: u32 = 0;
loop { loop {
@ -1297,8 +1298,10 @@ where
let split_at = state.rand_mut().between(first_diff, last_diff) as usize; let split_at = state.rand_mut().between(first_diff, last_diff) as usize;
let mut other_testcase = state.corpus().get(idx)?.borrow_mut(); let other_testcase = state.corpus().get(idx)?.borrow_mut();
let other = other_testcase.load_input()?; // Input will already be loaded.
let other = other_testcase.input().as_ref().unwrap();
input input
.bytes_mut() .bytes_mut()
.splice(split_at.., other.bytes()[split_at..].iter().copied()); .splice(split_at.., other.bytes()[split_at..].iter().copied());

View File

@ -440,10 +440,7 @@ mod tests {
.add(Testcase::new(vec![b'd', b'e', b'f'].into())) .add(Testcase::new(vec![b'd', b'e', b'f'].into()))
.unwrap(); .unwrap();
let testcase = corpus let mut input = corpus.cloned_input_for_id(corpus.first().unwrap()).unwrap();
.get(corpus.first().unwrap())
.expect("Corpus did not contain entries");
let mut input = testcase.borrow_mut().load_input().unwrap().clone();
let mut feedback = ConstFeedback::new(false); let mut feedback = ConstFeedback::new(false);
let mut objective = ConstFeedback::new(false); let mut objective = ConstFeedback::new(false);
@ -481,10 +478,7 @@ mod tests {
.add(Testcase::new(vec![b'd', b'e', b'f'].into())) .add(Testcase::new(vec![b'd', b'e', b'f'].into()))
.unwrap(); .unwrap();
let testcase = corpus let mut input = corpus.cloned_input_for_id(corpus.first().unwrap()).unwrap();
.get(corpus.first().unwrap())
.expect("Corpus did not contain entries");
let mut input = testcase.borrow_mut().load_input().unwrap().clone();
let input_prior = input.clone(); let input_prior = input.clone();
let mut feedback = ConstFeedback::new(false); let mut feedback = ConstFeedback::new(false);

View File

@ -369,7 +369,7 @@ where
{ {
/// Compute the `weight` used in weighted corpus entry selection algo /// Compute the `weight` used in weighted corpus entry selection algo
#[allow(clippy::cast_precision_loss, clippy::cast_lossless)] #[allow(clippy::cast_precision_loss, clippy::cast_lossless)]
fn compute(entry: &mut Testcase<S::Input>, state: &S) -> Result<f64, Error> { fn compute(state: &S, entry: &mut Testcase<S::Input>) -> Result<f64, Error> {
// subtract # initial inputs to the corpus count // subtract # initial inputs to the corpus count
let mut energy = 0; let mut energy = 0;
let mut average_cost = (state.corpus().count() / state.executions()) as u64; let mut average_cost = (state.corpus().count() / state.executions()) as u64;

View File

@ -116,7 +116,7 @@ where
let mut map = HashMap::new(); let mut map = HashMap::new();
for i in state.corpus().ids() { for i in state.corpus().ids() {
let mut old = state.corpus().get(i)?.borrow_mut(); let mut old = state.corpus().get(i)?.borrow_mut();
let factor = F::compute(&mut *old, state)?; let factor = F::compute(state, &mut *old)?;
if let Some(old_map) = old.metadata_map_mut().get_mut::<M>() { if let Some(old_map) = old.metadata_map_mut().get_mut::<M>() {
let mut e_iter = entries.iter(); let mut e_iter = entries.iter();
let mut map_iter = old_map.as_slice().iter(); // ASSERTION: guaranteed to be in order? let mut map_iter = old_map.as_slice().iter(); // ASSERTION: guaranteed to be in order?
@ -256,7 +256,7 @@ where
let mut new_favoreds = vec![]; let mut new_favoreds = vec![];
{ {
let mut entry = state.corpus().get(idx)?.borrow_mut(); let mut entry = state.corpus().get(idx)?.borrow_mut();
let factor = F::compute(&mut *entry, state)?; let factor = F::compute(state, &mut *entry)?;
let meta = entry.metadata_map_mut().get_mut::<M>().ok_or_else(|| { let meta = entry.metadata_map_mut().get_mut::<M>().ok_or_else(|| {
Error::key_not_found(format!( Error::key_not_found(format!(
"Metadata needed for MinimizerScheduler not found in testcase #{idx}" "Metadata needed for MinimizerScheduler not found in testcase #{idx}"
@ -270,7 +270,7 @@ where
continue; continue;
} }
let mut old = state.corpus().get(*old_idx)?.borrow_mut(); let mut old = state.corpus().get(*old_idx)?.borrow_mut();
if factor > F::compute(&mut *old, state)? { if factor > F::compute(state, &mut *old)? {
continue; continue;
} }

View File

@ -70,7 +70,7 @@ where
#[allow(clippy::cast_precision_loss)] #[allow(clippy::cast_precision_loss)]
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn store_probability(&self, state: &mut S, idx: CorpusId) -> Result<(), Error> { pub fn store_probability(&self, state: &mut S, idx: CorpusId) -> Result<(), Error> {
let factor = F::compute(&mut *state.corpus().get(idx)?.borrow_mut(), state)?; let factor = F::compute(state, &mut *state.corpus().get(idx)?.borrow_mut())?;
if factor == 0.0 { if factor == 0.0 {
return Err(Error::illegal_state( return Err(Error::illegal_state(
"Infinity probability calculated for probabilistic sampling scheduler", "Infinity probability calculated for probabilistic sampling scheduler",
@ -186,7 +186,7 @@ mod tests {
where where
S: HasMetadata + HasCorpus, S: HasMetadata + HasCorpus,
{ {
fn compute(_: &mut Testcase<S::Input>, _state: &S) -> Result<f64, Error> { fn compute(_state: &S, _: &mut Testcase<S::Input>) -> Result<f64, Error> {
Ok(FACTOR) Ok(FACTOR)
} }
} }

View File

@ -107,10 +107,7 @@ mod tests {
let mut q = let mut q =
OnDiskCorpus::<BytesInput>::new(PathBuf::from("target/.test/fancy/path")).unwrap(); OnDiskCorpus::<BytesInput>::new(PathBuf::from("target/.test/fancy/path")).unwrap();
let t = Testcase::with_filename( let t = Testcase::with_filename(BytesInput::new(vec![0_u8; 4]), "fancyfile".into());
BytesInput::new(vec![0_u8; 4]),
"target/.test/fancy/path/fancyfile".into(),
);
q.add(t).unwrap(); q.add(t).unwrap();
let objective_q = let objective_q =
@ -133,7 +130,7 @@ mod tests {
.unwrap() .unwrap()
.clone(); .clone();
assert_eq!(filename, "target/.test/fancy/path/fancyfile"); assert_eq!(filename, "fancyfile");
fs::remove_dir_all("target/.test/fancy").unwrap(); fs::remove_dir_all("target/.test/fancy").unwrap();
} }

View File

@ -20,7 +20,7 @@ where
S: HasMetadata + HasCorpus, S: HasMetadata + HasCorpus,
{ {
/// Computes the favor factor of a [`Testcase`]. Lower is better. /// Computes the favor factor of a [`Testcase`]. Lower is better.
fn compute(entry: &mut Testcase<S::Input>, state: &S) -> Result<f64, Error>; fn compute(state: &S, entry: &mut Testcase<S::Input>) -> Result<f64, Error>;
} }
/// Multiply the testcase size with the execution time. /// Multiply the testcase size with the execution time.
@ -36,9 +36,10 @@ where
S::Input: HasLen, S::Input: HasLen,
{ {
#[allow(clippy::cast_precision_loss, clippy::cast_lossless)] #[allow(clippy::cast_precision_loss, clippy::cast_lossless)]
fn compute(entry: &mut Testcase<S::Input>, _state: &S) -> Result<f64, Error> { fn compute(state: &S, entry: &mut Testcase<S::Input>) -> Result<f64, Error> {
// TODO maybe enforce entry.exec_time().is_some() // TODO maybe enforce entry.exec_time().is_some()
Ok(entry.exec_time().map_or(1, |d| d.as_millis()) as f64 * entry.cached_len()? as f64) Ok(entry.exec_time().map_or(1, |d| d.as_millis()) as f64
* entry.load_len(state.corpus())? as f64)
} }
} }
@ -65,7 +66,7 @@ where
clippy::cast_sign_loss, clippy::cast_sign_loss,
clippy::cast_lossless clippy::cast_lossless
)] )]
fn compute(entry: &mut Testcase<S::Input>, state: &S) -> Result<f64, Error> { fn compute(state: &S, entry: &mut Testcase<S::Input>) -> Result<f64, Error> {
let psmeta = state.metadata::<SchedulerMetadata>()?; let psmeta = state.metadata::<SchedulerMetadata>()?;
let fuzz_mu = if let Some(strat) = psmeta.strat() { let fuzz_mu = if let Some(strat) = psmeta.strat() {
@ -273,7 +274,7 @@ where
{ {
/// Compute the `weight` used in weighted corpus entry selection algo /// Compute the `weight` used in weighted corpus entry selection algo
#[allow(clippy::cast_precision_loss, clippy::cast_lossless)] #[allow(clippy::cast_precision_loss, clippy::cast_lossless)]
fn compute(entry: &mut Testcase<S::Input>, state: &S) -> Result<f64, Error> { fn compute(state: &S, entry: &mut Testcase<S::Input>) -> Result<f64, Error> {
let mut weight = 1.0; let mut weight = 1.0;
let psmeta = state.metadata::<SchedulerMetadata>()?; let psmeta = state.metadata::<SchedulerMetadata>()?;

View File

@ -154,7 +154,7 @@ where
for i in state.corpus().ids() { for i in state.corpus().ids() {
let mut testcase = state.corpus().get(i)?.borrow_mut(); let mut testcase = state.corpus().get(i)?.borrow_mut();
let weight = F::compute(&mut *testcase, state)?; let weight = F::compute(state, &mut *testcase)?;
weights.insert(i, weight); weights.insert(i, weight);
sum += weight; sum += weight;
} }

View File

@ -115,12 +115,7 @@ where
let mut iter = self.stage_max; let mut iter = self.stage_max;
let input = state let input = state.corpus().cloned_input_for_id(corpus_idx)?;
.corpus()
.get(corpus_idx)?
.borrow_mut()
.load_input()?
.clone();
// Run once to get the initial calibration map // Run once to get the initial calibration map
executor.observers_mut().pre_exec_all(state, &input)?; executor.observers_mut().pre_exec_all(state, &input)?;
@ -158,12 +153,7 @@ where
let mut has_errors = false; let mut has_errors = false;
while i < iter { while i < iter {
let input = state let input = state.corpus().cloned_input_for_id(corpus_idx)?;
.corpus()
.get(corpus_idx)?
.borrow_mut()
.load_input()?
.clone();
executor.observers_mut().pre_exec_all(state, &input)?; executor.observers_mut().pre_exec_all(state, &input)?;
start = current_time(); start = current_time();

View File

@ -155,13 +155,7 @@ where
corpus_idx: CorpusId, corpus_idx: CorpusId,
name: &str, name: &str,
) -> Result<E::Input, Error> { ) -> Result<E::Input, Error> {
let mut input = state let mut input = state.corpus().cloned_input_for_id(corpus_idx)?;
.corpus()
.get(corpus_idx)?
.borrow_mut()
.load_input()
.unwrap()
.clone();
// The backup of the input // The backup of the input
let backup = input.clone(); let backup = input.clone();
// This is the buffer we'll randomly mutate during type_replace // This is the buffer we'll randomly mutate during type_replace

View File

@ -1,6 +1,6 @@
//! The [`DumpToDiskStage`] is a stage that dumps the corpus and the solutions to disk to e.g. allow AFL to sync //! The [`DumpToDiskStage`] is a stage that dumps the corpus and the solutions to disk to e.g. allow AFL to sync
use alloc::vec::Vec; use alloc::{string::String, vec::Vec};
use core::{clone::Clone, marker::PhantomData}; use core::{clone::Clone, marker::PhantomData};
use std::{fs, fs::File, io::Write, path::PathBuf}; use std::{fs, fs::File, io::Write, path::PathBuf};
@ -68,10 +68,16 @@ where
while let Some(i) = corpus_idx { while let Some(i) = corpus_idx {
let mut testcase = state.corpus().get(i)?.borrow_mut(); let mut testcase = state.corpus().get(i)?.borrow_mut();
let input = testcase.load_input()?; state.corpus().load_input_into(&mut testcase)?;
let bytes = (self.to_bytes)(input); let bytes = (self.to_bytes)(testcase.input().as_ref().unwrap());
let fname = self.corpus_dir.join(format!("id_{i}")); let fname = self.corpus_dir.join(format!(
"id_{i}_{}",
testcase
.filename()
.as_ref()
.map_or_else(|| "unnamed", String::as_str)
));
let mut f = File::create(fname)?; let mut f = File::create(fname)?;
drop(f.write_all(&bytes)); drop(f.write_all(&bytes));
@ -80,10 +86,16 @@ where
while let Some(i) = solutions_idx { while let Some(i) = solutions_idx {
let mut testcase = state.solutions().get(i)?.borrow_mut(); let mut testcase = state.solutions().get(i)?.borrow_mut();
let input = testcase.load_input()?; state.solutions().load_input_into(&mut testcase)?;
let bytes = (self.to_bytes)(input); let bytes = (self.to_bytes)(testcase.input().as_ref().unwrap());
let fname = self.solutions_dir.join(format!("id_{i}")); let fname = self.solutions_dir.join(format!(
"id_{i}_{}",
testcase
.filename()
.as_ref()
.map_or_else(|| "unnamed", String::as_str)
));
let mut f = File::create(fname)?; let mut f = File::create(fname)?;
drop(f.write_all(&bytes)); drop(f.write_all(&bytes));

View File

@ -79,10 +79,13 @@ where
) -> Result<(), Error> { ) -> Result<(), Error> {
let (mut payload, original, novelties) = { let (mut payload, original, novelties) = {
start_timer!(state); start_timer!(state);
state.corpus().get(corpus_idx)?.borrow_mut().load_input()?; {
let corpus = state.corpus();
let mut testcase = corpus.get(corpus_idx)?.borrow_mut();
corpus.load_input_into(&mut testcase)?;
}
mark_feature_time!(state, PerfFeature::GetInputFromCorpus); mark_feature_time!(state, PerfFeature::GetInputFromCorpus);
let mut entry = state.corpus().get(corpus_idx)?.borrow_mut(); let mut entry = state.corpus().get(corpus_idx)?.borrow_mut();
let input = entry.input_mut().as_mut().unwrap(); let input = entry.input_mut().as_mut().unwrap();
let payload: Vec<_> = input.bytes().iter().map(|&x| Some(x)).collect(); let payload: Vec<_> = input.bytes().iter().map(|&x| Some(x)).collect();

View File

@ -64,16 +64,18 @@ where
impl<I, S> MutatedTransform<I, S> for I impl<I, S> MutatedTransform<I, S> for I
where where
I: Input + Clone, I: Input + Clone,
S: HasCorpus<Input = I>,
{ {
type Post = (); type Post = ();
#[inline] #[inline]
fn try_transform_from( fn try_transform_from(
base: &mut Testcase<I>, base: &mut Testcase<I>,
_state: &S, state: &S,
_corpus_idx: CorpusId, _corpus_idx: CorpusId,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
Ok(base.load_input()?.clone()) state.corpus().load_input_into(base)?;
Ok(base.input().as_ref().unwrap().clone())
} }
#[inline] #[inline]

View File

@ -55,7 +55,7 @@ where
fn iterations(&self, state: &mut E::State, corpus_idx: CorpusId) -> Result<u64, Error> { fn iterations(&self, state: &mut E::State, corpus_idx: CorpusId) -> Result<u64, Error> {
// Update handicap // Update handicap
let mut testcase = state.corpus().get(corpus_idx)?.borrow_mut(); let mut testcase = state.corpus().get(corpus_idx)?.borrow_mut();
let score = F::compute(&mut *testcase, state)? as u64; let score = F::compute(state, &mut *testcase)? as u64;
Ok(score) Ok(score)
} }

View File

@ -137,14 +137,15 @@ where
} }
start_timer!(state); start_timer!(state);
let mut input = state
.corpus() let input = state
.get(self.current_corpus_idx.unwrap()) .corpus_mut()
.unwrap() .cloned_input_for_id(self.current_corpus_idx.unwrap());
.borrow_mut() let mut input = match input {
.load_input() Err(e) => return Some(Err(e)),
.unwrap() Ok(input) => input,
.clone(); };
mark_feature_time!(state, PerfFeature::GetInputFromCorpus); mark_feature_time!(state, PerfFeature::GetInputFromCorpus);
start_timer!(state); start_timer!(state);

View File

@ -268,7 +268,7 @@ where
last_id.map_or_else(|| state.corpus().first(), |id| state.corpus().next(id)); last_id.map_or_else(|| state.corpus().first(), |id| state.corpus().next(id));
while let Some(id) = cur_id { while let Some(id) = cur_id {
let input = state.testcase_mut(id)?.load_input()?.clone(); let input = state.corpus().cloned_input_for_id(id)?;
self.client.fire( self.client.fire(
state, state,

View File

@ -72,12 +72,7 @@ where
let num = self.iterations(state, base_corpus_idx)?; let num = self.iterations(state, base_corpus_idx)?;
start_timer!(state); start_timer!(state);
let mut base = state let mut base = state.corpus().cloned_input_for_id(base_corpus_idx)?;
.corpus()
.get(base_corpus_idx)?
.borrow_mut()
.load_input()?
.clone();
let mut hasher = RandomState::with_seeds(0, 0, 0, 0).build_hasher(); let mut hasher = RandomState::with_seeds(0, 0, 0, 0).build_hasher();
base.hash(&mut hasher); base.hash(&mut hasher);
let base_hash = hasher.finish(); let base_hash = hasher.finish();

View File

@ -51,12 +51,8 @@ where
corpus_idx: CorpusId, corpus_idx: CorpusId,
) -> Result<(), Error> { ) -> Result<(), Error> {
start_timer!(state); start_timer!(state);
let input = state let input = state.corpus().cloned_input_for_id(corpus_idx)?;
.corpus()
.get(corpus_idx)?
.borrow_mut()
.load_input()?
.clone();
mark_feature_time!(state, PerfFeature::GetInputFromCorpus); mark_feature_time!(state, PerfFeature::GetInputFromCorpus);
start_timer!(state); start_timer!(state);
@ -142,12 +138,7 @@ where
) -> Result<(), Error> { ) -> Result<(), Error> {
// First run with the un-mutated input // First run with the un-mutated input
let unmutated_input = state let unmutated_input = state.corpus().cloned_input_for_id(corpus_idx)?;
.corpus()
.get(corpus_idx)?
.borrow_mut()
.load_input()?
.clone();
if let Some(name) = &self.cmplog_observer_name { if let Some(name) = &self.cmplog_observer_name {
if let Some(ob) = self if let Some(ob) = self
@ -277,12 +268,8 @@ where
corpus_idx: CorpusId, corpus_idx: CorpusId,
) -> Result<(), Error> { ) -> Result<(), Error> {
start_timer!(state); start_timer!(state);
let input = state let input = state.corpus().cloned_input_for_id(corpus_idx)?;
.corpus()
.get(corpus_idx)?
.borrow_mut()
.load_input()?
.clone();
mark_feature_time!(state, PerfFeature::GetInputFromCorpus); mark_feature_time!(state, PerfFeature::GetInputFromCorpus);
start_timer!(state); start_timer!(state);