started moving to no_std
This commit is contained in:
parent
629287251f
commit
ec36301414
@ -9,9 +9,12 @@ edition = "2018"
|
|||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = "0.3" # Benchmarking
|
criterion = "0.3" # Benchmarking
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["std"]
|
||||||
|
std = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
xxhash-rust = { version = "0.8.0-beta.4", features = ["xxh3"] } # xxh3 hashing for rust
|
xxhash-rust = { version = "0.8.0-beta.4", features = ["xxh3"] } # xxh3 hashing for rust
|
||||||
thiserror = "1.0" # A nicer way to write Errors
|
|
||||||
hashbrown = "0.9" # A faster hashmap, nostd compatible
|
hashbrown = "0.9" # A faster hashmap, nostd compatible
|
||||||
libc = "0.2" # For (*nix) libc
|
libc = "0.2" # For (*nix) libc
|
||||||
num = "*"
|
num = "*"
|
@ -3,9 +3,12 @@ extern crate alloc;
|
|||||||
pub mod testcase;
|
pub mod testcase;
|
||||||
pub use testcase::{Testcase, TestcaseMetadata};
|
pub use testcase::{Testcase, TestcaseMetadata};
|
||||||
|
|
||||||
|
use alloc::borrow::ToOwned;
|
||||||
use alloc::rc::Rc;
|
use alloc::rc::Rc;
|
||||||
|
use alloc::vec::Vec;
|
||||||
use core::cell::RefCell;
|
use core::cell::RefCell;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
#[cfg(feature = "std")]
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use crate::inputs::Input;
|
use crate::inputs::Input;
|
||||||
@ -34,16 +37,10 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Add an entry to the corpus
|
/// Add an entry to the corpus
|
||||||
#[allow(unused_mut)]
|
fn add(&mut self, testcase: Rc<RefCell<Testcase<I>>>) {
|
||||||
fn add(&mut self, mut testcase: Rc<RefCell<Testcase<I>>>) {
|
|
||||||
self.entries_mut().push(testcase);
|
self.entries_mut().push(testcase);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add an input to the corpus
|
|
||||||
fn add_input(&mut self, input: I) {
|
|
||||||
self.add(Testcase::new(input.into()).into());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Removes an entry from the corpus, returning it if it was present.
|
/// Removes an entry from the corpus, returning it if it was present.
|
||||||
fn remove(&mut self, entry: &Testcase<I>) -> Option<Rc<RefCell<Testcase<I>>>> {
|
fn remove(&mut self, entry: &Testcase<I>) -> Option<Rc<RefCell<Testcase<I>>>> {
|
||||||
let mut i: usize = 0;
|
let mut i: usize = 0;
|
||||||
@ -135,6 +132,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
pub struct OnDiskCorpus<I, R>
|
pub struct OnDiskCorpus<I, R>
|
||||||
where
|
where
|
||||||
I: Input,
|
I: Input,
|
||||||
@ -145,6 +143,7 @@ where
|
|||||||
dir_path: PathBuf,
|
dir_path: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
impl<I, R> HasEntriesVec<I> for OnDiskCorpus<I, R>
|
impl<I, R> HasEntriesVec<I> for OnDiskCorpus<I, R>
|
||||||
where
|
where
|
||||||
I: Input,
|
I: Input,
|
||||||
@ -158,6 +157,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
impl<I, R> HasRand for OnDiskCorpus<I, R>
|
impl<I, R> HasRand for OnDiskCorpus<I, R>
|
||||||
where
|
where
|
||||||
I: Input,
|
I: Input,
|
||||||
@ -170,6 +170,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
impl<I, R> Corpus<I> for OnDiskCorpus<I, R>
|
impl<I, R> Corpus<I> for OnDiskCorpus<I, R>
|
||||||
where
|
where
|
||||||
I: Input,
|
I: Input,
|
||||||
@ -179,9 +180,9 @@ where
|
|||||||
fn add(&mut self, entry: Rc<RefCell<Testcase<I>>>) {
|
fn add(&mut self, entry: Rc<RefCell<Testcase<I>>>) {
|
||||||
if *entry.borrow().filename() == None {
|
if *entry.borrow().filename() == None {
|
||||||
// TODO walk entry metadatas to ask for pices of filename (e.g. :havoc in AFL)
|
// TODO walk entry metadatas to ask for pices of filename (e.g. :havoc in AFL)
|
||||||
let filename = &(String::from("id:") + &self.entries.len().to_string());
|
let filename = self.dir_path.join(format!("id_{}", &self.entries.len()));
|
||||||
let filename = self.dir_path.join(filename);
|
let filename_str = filename.to_str().expect("Invalid Path");
|
||||||
*entry.borrow_mut().filename_mut() = Some(filename);
|
*entry.borrow_mut().filename_mut() = Some(filename_str.into());
|
||||||
}
|
}
|
||||||
self.entries.push(entry);
|
self.entries.push(entry);
|
||||||
}
|
}
|
||||||
@ -189,6 +190,7 @@ where
|
|||||||
// TODO save and remove files, cache, etc..., ATM use just InMemoryCorpus
|
// TODO save and remove files, cache, etc..., ATM use just InMemoryCorpus
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
impl<I, R> OnDiskCorpus<I, R>
|
impl<I, R> OnDiskCorpus<I, R>
|
||||||
where
|
where
|
||||||
I: Input,
|
I: Input,
|
||||||
@ -352,6 +354,7 @@ And then:
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
#[cfg(feature = "std")]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::corpus::Corpus;
|
use crate::corpus::Corpus;
|
||||||
use crate::corpus::Testcase;
|
use crate::corpus::Testcase;
|
||||||
@ -363,11 +366,11 @@ mod tests {
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
||||||
fn test_queuecorpus() {
|
fn test_queuecorpus() {
|
||||||
let rand: Rc<_> = DefaultRand::preseeded().into();
|
let rand: Rc<_> = DefaultRand::new(0).into();
|
||||||
let mut q = QueueCorpus::new(OnDiskCorpus::new(&rand, PathBuf::from("fancy/path")));
|
let mut q = QueueCorpus::new(OnDiskCorpus::new(&rand, PathBuf::from("fancy/path")));
|
||||||
let t: Rc<_> = Testcase::with_filename(BytesInput::new(vec![0 as u8; 4]), PathBuf::from("fancyfile")).into();
|
let t: Rc<_> =
|
||||||
|
Testcase::with_filename(BytesInput::new(vec![0 as u8; 4]), "fancyfile".into()).into();
|
||||||
q.add(t);
|
q.add(t);
|
||||||
let filename = q
|
let filename = q
|
||||||
.next()
|
.next()
|
||||||
@ -387,6 +390,6 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.to_owned()
|
.to_owned()
|
||||||
);
|
);
|
||||||
assert_eq!(filename, PathBuf::from("fancyfile"));
|
assert_eq!(filename, "fancyfile");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,9 +7,12 @@ use alloc::rc::Rc;
|
|||||||
use core::cell::RefCell;
|
use core::cell::RefCell;
|
||||||
use core::convert::Into;
|
use core::convert::Into;
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
|
#[cfg(feature = "std")]
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
|
#[cfg(feature = "std")]
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::path::{Path, PathBuf};
|
#[cfg(feature = "std")]
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
// TODO: Give example
|
// TODO: Give example
|
||||||
/// Metadata for a testcase
|
/// Metadata for a testcase
|
||||||
@ -18,6 +21,24 @@ pub trait TestcaseMetadata {
|
|||||||
fn name(&self) -> &'static str;
|
fn name(&self) -> &'static str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
pub trait Testcase<I, T>
|
||||||
|
where
|
||||||
|
I: Input,
|
||||||
|
T: TestcaseMetadata,
|
||||||
|
{
|
||||||
|
|
||||||
|
fn input(&mut self) -> Option<I>
|
||||||
|
|
||||||
|
input: Option<I>,
|
||||||
|
/// Filename, if this testcase is backed by a file in the filesystem
|
||||||
|
filename: Option<String>,
|
||||||
|
/// Map of metadatas associated with this testcase
|
||||||
|
metadatas: HashMap<&'static str, Box<dyn TestcaseMetadata>>,
|
||||||
|
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
pub enum FileBackedTestcase<I, P> {
|
pub enum FileBackedTestcase<I, P> {
|
||||||
/// A testcase on disk, not yet loaded
|
/// A testcase on disk, not yet loaded
|
||||||
Stored { filename: P },
|
Stored { filename: P },
|
||||||
@ -109,7 +130,7 @@ where
|
|||||||
/// The input of this testcase
|
/// The input of this testcase
|
||||||
input: Option<I>,
|
input: Option<I>,
|
||||||
/// Filename, if this testcase is backed by a file in the filesystem
|
/// Filename, if this testcase is backed by a file in the filesystem
|
||||||
filename: Option<PathBuf>,
|
filename: Option<String>,
|
||||||
/// Map of metadatas associated with this testcase
|
/// Map of metadatas associated with this testcase
|
||||||
metadatas: HashMap<&'static str, Box<dyn TestcaseMetadata>>,
|
metadatas: HashMap<&'static str, Box<dyn TestcaseMetadata>>,
|
||||||
}
|
}
|
||||||
@ -133,7 +154,7 @@ where
|
|||||||
// TODO: Implement cache to disk
|
// TODO: Implement cache to disk
|
||||||
match self.input.as_ref() {
|
match self.input.as_ref() {
|
||||||
Some(i) => Ok(i),
|
Some(i) => Ok(i),
|
||||||
None => Err(AflError::NotImplemented("load_input".to_string())),
|
None => Err(AflError::NotImplemented("load_input".into())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,15 +172,15 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the filename, if any
|
/// Get the filename, if any
|
||||||
pub fn filename(&self) -> &Option<PathBuf> {
|
pub fn filename(&self) -> &Option<String> {
|
||||||
&self.filename
|
&self.filename
|
||||||
}
|
}
|
||||||
/// Get the filename, if any (mutable)
|
/// Get the filename, if any (mutable)
|
||||||
pub fn filename_mut(&mut self) -> &mut Option<PathBuf> {
|
pub fn filename_mut(&mut self) -> &mut Option<String> {
|
||||||
&mut self.filename
|
&mut self.filename
|
||||||
}
|
}
|
||||||
/// Set the filename
|
/// Set the filename
|
||||||
pub fn set_filename(&mut self, filename: Option<PathBuf>) {
|
pub fn set_filename(&mut self, filename: Option<String>) {
|
||||||
self.filename = filename;
|
self.filename = filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,7 +204,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new Testcase instace given an input and a filename
|
/// Create a new Testcase instace given an input and a filename
|
||||||
pub fn with_filename(input: I, filename: PathBuf) -> Self {
|
pub fn with_filename(input: I, filename: String) -> Self {
|
||||||
Testcase {
|
Testcase {
|
||||||
input: Some(input),
|
input: Some(input),
|
||||||
filename: Some(filename),
|
filename: Some(filename),
|
||||||
|
@ -103,7 +103,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_engine() {
|
fn test_engine() {
|
||||||
let rand: Rc<_> = DefaultRand::preseeded().into();
|
let rand: Rc<_> = DefaultRand::new(0).into();
|
||||||
|
|
||||||
let mut corpus = InMemoryCorpus::<BytesInput, _>::new(&rand);
|
let mut corpus = InMemoryCorpus::<BytesInput, _>::new(&rand);
|
||||||
let testcase = Testcase::new(BytesInput::new(vec![0; 4])).into();
|
let testcase = Testcase::new(BytesInput::new(vec![0; 4])).into();
|
||||||
|
@ -67,11 +67,11 @@ where
|
|||||||
self.observers.push(observer);
|
self.observers.push(observer);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn observers(&self) -> &Vec<Box<dyn Observer>> {
|
fn observers(&self) -> &[Box<dyn Observer>] {
|
||||||
&self.observers
|
&self.observers
|
||||||
}
|
}
|
||||||
|
|
||||||
fn feedbacks(&self) -> &Vec<Box<dyn Feedback<I>>> {
|
fn feedbacks(&self) -> &[Box<dyn Feedback<I>>] {
|
||||||
&self.feedbacks
|
&self.feedbacks
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,10 +211,10 @@ mod tests {
|
|||||||
|
|
||||||
impl Observer for Nopserver {
|
impl Observer for Nopserver {
|
||||||
fn reset(&mut self) -> Result<(), AflError> {
|
fn reset(&mut self) -> Result<(), AflError> {
|
||||||
Err(AflError::Unknown("Nop reset, testing only".to_string()))
|
Err(AflError::Unknown("Nop reset, testing only".into()))
|
||||||
}
|
}
|
||||||
fn post_exec(&mut self) -> Result<(), AflError> {
|
fn post_exec(&mut self) -> Result<(), AflError> {
|
||||||
Err(AflError::Unknown("Nop exec, testing only".to_string()))
|
Err(AflError::Unknown("Nop exec, testing only".into()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,6 @@
|
|||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
pub mod inmemory;
|
pub mod inmemory;
|
||||||
|
|
||||||
use alloc::rc::Rc;
|
|
||||||
use core::cell::RefCell;
|
|
||||||
|
|
||||||
use crate::corpus::Testcase;
|
|
||||||
use crate::corpus::TestcaseMetadata;
|
use crate::corpus::TestcaseMetadata;
|
||||||
use crate::feedbacks::Feedback;
|
use crate::feedbacks::Feedback;
|
||||||
use crate::inputs::Input;
|
use crate::inputs::Input;
|
||||||
@ -37,13 +33,13 @@ where
|
|||||||
fn add_observer(&mut self, observer: Box<dyn Observer>);
|
fn add_observer(&mut self, observer: Box<dyn Observer>);
|
||||||
|
|
||||||
/// Get the linked observers
|
/// Get the linked observers
|
||||||
fn observers(&self) -> &Vec<Box<dyn Observer>>;
|
fn observers(&self) -> &[Box<dyn Observer>];
|
||||||
|
|
||||||
/// Adds a feedback
|
/// Adds a feedback
|
||||||
fn add_feedback(&mut self, feedback: Box<dyn Feedback<I>>);
|
fn add_feedback(&mut self, feedback: Box<dyn Feedback<I>>);
|
||||||
|
|
||||||
/// Returns vector of feebacks
|
/// Returns vector of feebacks
|
||||||
fn feedbacks(&self) -> &Vec<Box<dyn Feedback<I>>>;
|
fn feedbacks(&self) -> &[Box<dyn Feedback<I>>];
|
||||||
|
|
||||||
/// Returns vector of feebacks (mutable)
|
/// Returns vector of feebacks (mutable)
|
||||||
fn feedbacks_mut(&mut self) -> &mut Vec<Box<dyn Feedback<I>>>;
|
fn feedbacks_mut(&mut self) -> &mut Vec<Box<dyn Feedback<I>>>;
|
||||||
@ -66,12 +62,6 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
if rate_acc >= 25 {
|
if rate_acc >= 25 {
|
||||||
let new_entry = Rc::new(RefCell::new(Testcase::<I>::new(input.clone())));
|
|
||||||
for meta in metadatas {
|
|
||||||
new_entry.borrow_mut().add_metadata(meta);
|
|
||||||
}
|
|
||||||
//TODO corpus.add(new_entry);
|
|
||||||
|
|
||||||
Ok(true)
|
Ok(true)
|
||||||
} else {
|
} else {
|
||||||
Ok(false)
|
Ok(false)
|
||||||
|
@ -3,6 +3,8 @@ extern crate num;
|
|||||||
|
|
||||||
use core::cell::RefCell;
|
use core::cell::RefCell;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
|
use alloc::vec::*;
|
||||||
use num::Integer;
|
use num::Integer;
|
||||||
|
|
||||||
use crate::corpus::TestcaseMetadata;
|
use crate::corpus::TestcaseMetadata;
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
use core::convert::From;
|
use alloc::borrow::ToOwned;
|
||||||
|
|
||||||
use alloc::rc::Rc;
|
use alloc::rc::Rc;
|
||||||
|
use alloc::vec::Vec;
|
||||||
use core::cell::RefCell;
|
use core::cell::RefCell;
|
||||||
|
use core::convert::From;
|
||||||
|
|
||||||
use crate::inputs::{HasBytesVec, HasTargetBytes, Input};
|
use crate::inputs::{HasBytesVec, HasTargetBytes, Input};
|
||||||
use crate::AflError;
|
use crate::AflError;
|
||||||
@ -20,7 +21,9 @@ impl Input for BytesInput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize(buf: &[u8]) -> Result<Self, AflError> {
|
fn deserialize(buf: &[u8]) -> Result<Self, AflError> {
|
||||||
Ok(Self { bytes: buf.into() })
|
Ok(Self {
|
||||||
|
bytes: buf.to_owned(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,7 +35,7 @@ impl Into<Rc<RefCell<Self>>> for BytesInput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl HasBytesVec for BytesInput {
|
impl HasBytesVec for BytesInput {
|
||||||
fn bytes(&self) -> &Vec<u8> {
|
fn bytes(&self) -> &[u8] {
|
||||||
&self.bytes
|
&self.bytes
|
||||||
}
|
}
|
||||||
fn bytes_mut(&mut self) -> &mut Vec<u8> {
|
fn bytes_mut(&mut self) -> &mut Vec<u8> {
|
||||||
@ -41,7 +44,7 @@ impl HasBytesVec for BytesInput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl HasTargetBytes for BytesInput {
|
impl HasTargetBytes for BytesInput {
|
||||||
fn target_bytes(&self) -> &Vec<u8> {
|
fn target_bytes(&self) -> &[u8] {
|
||||||
&self.bytes
|
&self.bytes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -71,7 +74,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_input() {
|
fn test_input() {
|
||||||
let mut rand = DefaultRand::preseeded();
|
let mut rand = DefaultRand::new(0);
|
||||||
assert_ne!(rand.next(), rand.next());
|
assert_ne!(rand.next(), rand.next());
|
||||||
assert!(rand.below(100) < 100);
|
assert!(rand.below(100) < 100);
|
||||||
assert_eq!(rand.below(1), 0);
|
assert_eq!(rand.below(1), 0);
|
||||||
|
@ -3,15 +3,21 @@ extern crate alloc;
|
|||||||
pub mod bytes;
|
pub mod bytes;
|
||||||
pub use bytes::BytesInput;
|
pub use bytes::BytesInput;
|
||||||
|
|
||||||
|
use alloc::vec::Vec;
|
||||||
use core::clone::Clone;
|
use core::clone::Clone;
|
||||||
use std::io::Write;
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use std::fs::File;
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
#[cfg(feature = "std")]
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::{fs::File, io::Read};
|
|
||||||
|
|
||||||
use crate::AflError;
|
use crate::AflError;
|
||||||
|
|
||||||
/// An input for the target
|
/// An input for the target
|
||||||
pub trait Input: Clone {
|
pub trait Input: Clone {
|
||||||
|
#[cfg(feature = "std")]
|
||||||
/// Write this input to the file
|
/// Write this input to the file
|
||||||
fn to_file<P>(&self, path: P) -> Result<(), AflError>
|
fn to_file<P>(&self, path: P) -> Result<(), AflError>
|
||||||
where
|
where
|
||||||
@ -22,7 +28,15 @@ pub trait Input: Clone {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
/// Write this input to the file
|
||||||
|
fn to_file<P>(&self, string: P) -> Result<(), AflError>
|
||||||
|
where {
|
||||||
|
Err(AflError::NotImplemented("Not suppored in no_std".into()))
|
||||||
|
}
|
||||||
|
|
||||||
/// Load the contents of this input from a file
|
/// Load the contents of this input from a file
|
||||||
|
#[cfg(feature = "std")]
|
||||||
fn from_file<P>(path: P) -> Result<Self, AflError>
|
fn from_file<P>(path: P) -> Result<Self, AflError>
|
||||||
where
|
where
|
||||||
P: AsRef<Path>,
|
P: AsRef<Path>,
|
||||||
@ -33,6 +47,13 @@ pub trait Input: Clone {
|
|||||||
Self::deserialize(&bytes)
|
Self::deserialize(&bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Write this input to the file
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
fn from_file<P>(string: P) -> Result<Self, AflError>
|
||||||
|
where {
|
||||||
|
Err(AflError::NotImplemented("Not suppored in no_std".into()))
|
||||||
|
}
|
||||||
|
|
||||||
/// Serialize this input, for later deserialization.
|
/// Serialize this input, for later deserialization.
|
||||||
/// This is not necessarily the representation to be used by the target
|
/// This is not necessarily the representation to be used by the target
|
||||||
/// Instead, to get bytes for a target, use [HasTargetBytes](afl::inputs::HasTargetBytes).
|
/// Instead, to get bytes for a target, use [HasTargetBytes](afl::inputs::HasTargetBytes).
|
||||||
@ -47,13 +68,13 @@ pub trait Input: Clone {
|
|||||||
/// Instead, it can be used as bytes input for a target
|
/// Instead, it can be used as bytes input for a target
|
||||||
pub trait HasTargetBytes {
|
pub trait HasTargetBytes {
|
||||||
/// Target bytes, that can be written to a target
|
/// Target bytes, that can be written to a target
|
||||||
fn target_bytes(&self) -> &Vec<u8>;
|
fn target_bytes(&self) -> &[u8];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Contains an internal bytes Vector
|
/// Contains an internal bytes Vector
|
||||||
pub trait HasBytesVec {
|
pub trait HasBytesVec {
|
||||||
/// The internal bytes map
|
/// The internal bytes map
|
||||||
fn bytes(&self) -> &Vec<u8>;
|
fn bytes(&self) -> &[u8];
|
||||||
/// The internal bytes map (as mutable borrow)
|
/// The internal bytes map (as mutable borrow)
|
||||||
fn bytes_mut(&mut self) -> &mut Vec<u8>;
|
fn bytes_mut(&mut self) -> &mut Vec<u8>;
|
||||||
}
|
}
|
||||||
|
46
src/lib.rs
46
src/lib.rs
@ -1,3 +1,5 @@
|
|||||||
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
#[macro_use]
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
pub mod corpus;
|
pub mod corpus;
|
||||||
@ -11,32 +13,52 @@ pub mod observers;
|
|||||||
pub mod stages;
|
pub mod stages;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
|
use alloc::string::String;
|
||||||
|
use core::fmt;
|
||||||
|
#[cfg(feature = "std")]
|
||||||
use std::io;
|
use std::io;
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
/// Main error struct for AFL
|
/// Main error struct for AFL
|
||||||
#[derive(Error, Debug)]
|
#[derive(Debug)]
|
||||||
pub enum AflError {
|
pub enum AflError {
|
||||||
#[error("Error in Serialization: `{0}`")]
|
|
||||||
Serialize(String),
|
Serialize(String),
|
||||||
#[error("File IO failed")]
|
#[cfg(feature = "std")]
|
||||||
File(#[from] io::Error),
|
File(io::Error),
|
||||||
#[error("Optional value `{0}` was not set")]
|
|
||||||
EmptyOptional(String),
|
EmptyOptional(String),
|
||||||
#[error("Key `{0}` not in Corpus")]
|
|
||||||
KeyNotFound(String),
|
KeyNotFound(String),
|
||||||
#[error("No items in {0}")]
|
|
||||||
Empty(String),
|
Empty(String),
|
||||||
#[error("All elements have been processed in {0} iterator")]
|
|
||||||
IteratorEnd(String),
|
IteratorEnd(String),
|
||||||
#[error("Not implemented: {0}")]
|
|
||||||
NotImplemented(String),
|
NotImplemented(String),
|
||||||
#[error("Illegal state: {0}")]
|
|
||||||
IllegalState(String),
|
IllegalState(String),
|
||||||
#[error("Unknown error: {0}")]
|
|
||||||
Unknown(String),
|
Unknown(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for AflError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Serialize(s) => write!(f, "Error in Serialization: `{0}`", &s),
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
Self::File(err) => write!(f, "File IO failed: {:?}", &err),
|
||||||
|
Self::EmptyOptional(s) => write!(f, "Optional value `{0}` was not set", &s),
|
||||||
|
Self::KeyNotFound(s) => write!(f, "Key `{0}` not in Corpus", &s),
|
||||||
|
Self::Empty(s) => write!(f, "No items in {0}", &s),
|
||||||
|
Self::IteratorEnd(s) => {
|
||||||
|
write!(f, "All elements have been processed in {0} iterator", &s)
|
||||||
|
}
|
||||||
|
Self::NotImplemented(s) => write!(f, "Not implemented: {0}", &s),
|
||||||
|
Self::IllegalState(s) => write!(f, "Illegal state: {0}", &s),
|
||||||
|
Self::Unknown(s) => write!(f, "Unknown error: {0}", &s),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
impl From<io::Error> for AflError {
|
||||||
|
fn from(err: io::Error) -> Self {
|
||||||
|
Self::File(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -7,6 +7,7 @@ use crate::utils::{HasRand, Rand};
|
|||||||
use crate::AflError;
|
use crate::AflError;
|
||||||
|
|
||||||
use alloc::rc::Rc;
|
use alloc::rc::Rc;
|
||||||
|
use alloc::vec::Vec;
|
||||||
use core::cell::RefCell;
|
use core::cell::RefCell;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
@ -42,7 +43,7 @@ where
|
|||||||
fn schedule(&mut self, _input: &I) -> Result<MutationFunction<C, Self, I>, AflError> {
|
fn schedule(&mut self, _input: &I) -> Result<MutationFunction<C, Self, I>, AflError> {
|
||||||
let count = self.mutations_count() as u64;
|
let count = self.mutations_count() as u64;
|
||||||
if count == 0 {
|
if count == 0 {
|
||||||
return Err(AflError::Empty("no mutations".to_string()));
|
return Err(AflError::Empty("no mutations".into()));
|
||||||
}
|
}
|
||||||
let idx;
|
let idx;
|
||||||
{
|
{
|
||||||
@ -109,7 +110,7 @@ where
|
|||||||
{
|
{
|
||||||
fn mutation_by_idx(&self, index: usize) -> Result<MutationFunction<C, Self, I>, AflError> {
|
fn mutation_by_idx(&self, index: usize) -> Result<MutationFunction<C, Self, I>, AflError> {
|
||||||
if index >= self.mutations.len() {
|
if index >= self.mutations.len() {
|
||||||
return Err(AflError::Unknown("oob".to_string()));
|
return Err(AflError::Unknown("oob".into()));
|
||||||
}
|
}
|
||||||
Ok(self.mutations[index])
|
Ok(self.mutations[index])
|
||||||
}
|
}
|
||||||
@ -172,7 +173,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the first and last diff position between the given vectors, stopping at the min len
|
/// Returns the first and last diff position between the given vectors, stopping at the min len
|
||||||
fn locate_diffs(this: &Vec<u8>, other: &Vec<u8>) -> (i64, i64) {
|
fn locate_diffs(this: &[u8], other: &[u8]) -> (i64, i64) {
|
||||||
let mut first_diff: i64 = -1;
|
let mut first_diff: i64 = -1;
|
||||||
let mut last_diff: i64 = -1;
|
let mut last_diff: i64 = -1;
|
||||||
for (i, (this_el, other_el)) in this.iter().zip(other.iter()).enumerate() {
|
for (i, (this_el, other_el)) in this.iter().zip(other.iter()).enumerate() {
|
||||||
@ -208,7 +209,7 @@ where
|
|||||||
Err(_) => {
|
Err(_) => {
|
||||||
if retry_count == 20 {
|
if retry_count == 20 {
|
||||||
return Err(AflError::Empty(
|
return Err(AflError::Empty(
|
||||||
"No suitable testcase found for splicing".to_owned(),
|
"No suitable testcase found for splicing".into(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
retry_count += 1;
|
retry_count += 1;
|
||||||
@ -231,7 +232,7 @@ where
|
|||||||
break (f, l);
|
break (f, l);
|
||||||
}
|
}
|
||||||
if counter == 20 {
|
if counter == 20 {
|
||||||
return Err(AflError::Empty("No valid diff found".to_owned()));
|
return Err(AflError::Empty("No valid diff found".into()));
|
||||||
}
|
}
|
||||||
counter += 1;
|
counter += 1;
|
||||||
};
|
};
|
||||||
@ -324,10 +325,13 @@ where
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::corpus::{Corpus, InMemoryCorpus};
|
use crate::inputs::BytesInput;
|
||||||
use crate::inputs::{BytesInput, HasBytesVec};
|
|
||||||
use crate::mutators::scheduled::mutation_splice;
|
use crate::mutators::scheduled::mutation_splice;
|
||||||
use crate::utils::{DefaultHasRand, Rand, XKCDRand};
|
use crate::utils::{DefaultHasRand, Rand, XKCDRand};
|
||||||
|
use crate::{
|
||||||
|
corpus::{Corpus, InMemoryCorpus, Testcase},
|
||||||
|
inputs::HasBytesVec,
|
||||||
|
};
|
||||||
use alloc::rc::Rc;
|
use alloc::rc::Rc;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -336,8 +340,8 @@ mod tests {
|
|||||||
let rand: Rc<_> = XKCDRand::new().into();
|
let rand: Rc<_> = XKCDRand::new().into();
|
||||||
let mut has_rand = DefaultHasRand::new(&rand);
|
let mut has_rand = DefaultHasRand::new(&rand);
|
||||||
let mut corpus = InMemoryCorpus::new(&rand);
|
let mut corpus = InMemoryCorpus::new(&rand);
|
||||||
corpus.add_input(BytesInput::new(vec!['a' as u8, 'b' as u8, 'c' as u8]));
|
corpus.add(Testcase::new(BytesInput::new(vec!['a' as u8, 'b' as u8, 'c' as u8])).into());
|
||||||
corpus.add_input(BytesInput::new(vec!['d' as u8, 'e' as u8, 'f' as u8]));
|
corpus.add(Testcase::new(BytesInput::new(vec!['d' as u8, 'e' as u8, 'f' as u8])).into());
|
||||||
|
|
||||||
let testcase_rr = corpus.next().expect("Corpus did not contain entries");
|
let testcase_rr = corpus.next().expect("Corpus did not contain entries");
|
||||||
let mut testcase = testcase_rr.borrow_mut();
|
let mut testcase = testcase_rr.borrow_mut();
|
||||||
@ -346,10 +350,11 @@ mod tests {
|
|||||||
rand.borrow_mut().set_seed(5);
|
rand.borrow_mut().set_seed(5);
|
||||||
mutation_splice(&mut has_rand, &mut corpus, &mut input).unwrap();
|
mutation_splice(&mut has_rand, &mut corpus, &mut input).unwrap();
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
println!("{:?}", input.bytes());
|
println!("{:?}", input.bytes());
|
||||||
|
|
||||||
// The pre-seeded rand should have spliced at position 2.
|
// The pre-seeded rand should have spliced at position 2.
|
||||||
// TODO: Maybe have a fixed rand for this purpose?
|
// TODO: Maybe have a fixed rand for this purpose?
|
||||||
assert_eq!(input.bytes(), &vec!['a' as u8, 'b' as u8, 'f' as u8])
|
assert_eq!(input.bytes(), &['a' as u8, 'b' as u8, 'f' as u8])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
10
src/utils.rs
10
src/utils.rs
@ -5,9 +5,11 @@ use alloc::rc::Rc;
|
|||||||
use core::cell::RefCell;
|
use core::cell::RefCell;
|
||||||
use core::debug_assert;
|
use core::debug_assert;
|
||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
|
||||||
use xxhash_rust::xxh3::xxh3_64_with_seed;
|
use xxhash_rust::xxh3::xxh3_64_with_seed;
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
pub type DefaultRand = Xoshiro256StarRand;
|
pub type DefaultRand = Xoshiro256StarRand;
|
||||||
|
|
||||||
/// Ways to get random around here
|
/// Ways to get random around here
|
||||||
@ -128,6 +130,8 @@ impl Xoshiro256StarRand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a rand instance, pre-seeded with the current time in nanoseconds.
|
/// Creates a rand instance, pre-seeded with the current time in nanoseconds.
|
||||||
|
/// Needs stdlib timer
|
||||||
|
#[cfg(feature = "std")]
|
||||||
pub fn preseeded() -> Self {
|
pub fn preseeded() -> Self {
|
||||||
let seed = SystemTime::now()
|
let seed = SystemTime::now()
|
||||||
.duration_since(UNIX_EPOCH)
|
.duration_since(UNIX_EPOCH)
|
||||||
@ -220,7 +224,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_rand() {
|
fn test_rand() {
|
||||||
let mut rand = DefaultRand::preseeded();
|
let mut rand = DefaultRand::new(0);
|
||||||
assert_ne!(rand.next(), rand.next());
|
assert_ne!(rand.next(), rand.next());
|
||||||
assert!(rand.below(100) < 100);
|
assert!(rand.below(100) < 100);
|
||||||
assert_eq!(rand.below(1), 0);
|
assert_eq!(rand.below(1), 0);
|
||||||
@ -230,7 +234,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_has_rand() {
|
fn test_has_rand() {
|
||||||
let rand = DefaultRand::preseeded().into();
|
let rand = DefaultRand::new(0).into();
|
||||||
let has_rand = DefaultHasRand::new(&rand);
|
let has_rand = DefaultHasRand::new(&rand);
|
||||||
|
|
||||||
assert!(has_rand.rand_below(100) < 100);
|
assert!(has_rand.rand_below(100) < 100);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user