use alloc::string::String;
#[cfg(feature = "track_hit_feedbacks")]
use alloc::{borrow::Cow, vec::Vec};
use core::{
cell::{Ref, RefMut},
time::Duration,
};
#[cfg(feature = "std")]
use std::path::PathBuf;
use libafl_bolts::{HasLen, serdeany::SerdeAnyMap};
use serde::{Deserialize, Serialize};
use super::Corpus;
use crate::{Error, HasMetadata, corpus::CorpusId};
pub trait HasTestcase<I> {
fn testcase(&self, id: CorpusId) -> Result<Ref<'_, Testcase<I>>, Error>;
fn testcase_mut(&self, id: CorpusId) -> Result<RefMut<'_, Testcase<I>>, Error>;
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Testcase<I> {
input: Option<I>,
filename: Option<String>,
#[cfg(feature = "std")]
file_path: Option<PathBuf>,
metadata: SerdeAnyMap,
#[cfg(feature = "std")]
metadata_path: Option<PathBuf>,
exec_time: Option<Duration>,
cached_len: Option<usize>,
scheduled_count: usize,
executions: u64,
corpus_id: Option<CorpusId>,
parent_id: Option<CorpusId>,
disabled: bool,
objectives_found: usize,
#[cfg(feature = "track_hit_feedbacks")]
hit_feedbacks: Vec<Cow<'static, str>>,
#[cfg(feature = "track_hit_feedbacks")]
hit_objectives: Vec<Cow<'static, str>>,
}
impl<I> HasMetadata for Testcase<I> {
#[inline]
fn metadata_map(&self) -> &SerdeAnyMap {
&self.metadata
}
#[inline]
fn metadata_map_mut(&mut self) -> &mut SerdeAnyMap {
&mut self.metadata
}
}
impl<I> Testcase<I> {
pub fn load_input<C: Corpus<I>>(&mut self, corpus: &C) -> Result<&I, Error> {
corpus.load_input_into(self)?;
Ok(self.input.as_ref().unwrap())
}
#[inline]
pub fn input(&self) -> &Option<I> {
&self.input
}
#[inline]
pub fn input_mut(&mut self) -> &mut Option<I> {
&mut self.input
}
#[inline]
pub fn set_input(&mut self, input: I) {
self.input = Some(input);
}
#[inline]
pub fn filename(&self) -> &Option<String> {
&self.filename
}
#[inline]
pub fn filename_mut(&mut self) -> &mut Option<String> {
&mut self.filename
}
#[inline]
#[cfg(feature = "std")]
pub fn file_path(&self) -> &Option<PathBuf> {
&self.file_path
}
#[inline]
#[cfg(feature = "std")]
pub fn file_path_mut(&mut self) -> &mut Option<PathBuf> {
&mut self.file_path
}
#[inline]
#[cfg(feature = "std")]
pub fn metadata_path(&self) -> &Option<PathBuf> {
&self.metadata_path
}
#[inline]
#[cfg(feature = "std")]
pub fn metadata_path_mut(&mut self) -> &mut Option<PathBuf> {
&mut self.metadata_path
}
#[inline]
pub fn executions(&self) -> &u64 {
&self.executions
}
#[inline]
pub fn executions_mut(&mut self) -> &mut u64 {
&mut self.executions
}
#[inline]
pub fn set_executions(&mut self, executions: u64) {
self.executions = executions;
}
#[inline]
pub fn exec_time(&self) -> &Option<Duration> {
&self.exec_time
}
#[inline]
pub fn exec_time_mut(&mut self) -> &mut Option<Duration> {
&mut self.exec_time
}
#[inline]
pub fn set_exec_time(&mut self, time: Duration) {
self.exec_time = Some(time);
}
#[inline]
pub fn scheduled_count(&self) -> usize {
self.scheduled_count
}
#[inline]
pub fn set_scheduled_count(&mut self, scheduled_count: usize) {
self.scheduled_count = scheduled_count;
}
#[inline]
pub fn disabled(&mut self) -> bool {
self.disabled
}
#[inline]
pub fn set_disabled(&mut self, disabled: bool) {
self.disabled = disabled;
}
#[inline]
pub fn corpus_id(&self) -> Option<CorpusId> {
self.corpus_id
}
#[inline]
pub fn set_corpus_id(&mut self, id: Option<CorpusId>) {
self.corpus_id = id;
}
#[inline]
#[cfg(feature = "track_hit_feedbacks")]
pub fn hit_feedbacks(&self) -> &Vec<Cow<'static, str>> {
&self.hit_feedbacks
}
#[inline]
#[cfg(feature = "track_hit_feedbacks")]
pub fn hit_feedbacks_mut(&mut self) -> &mut Vec<Cow<'static, str>> {
&mut self.hit_feedbacks
}
#[inline]
#[cfg(feature = "track_hit_feedbacks")]
pub fn hit_objectives(&self) -> &Vec<Cow<'static, str>> {
&self.hit_objectives
}
#[inline]
#[cfg(feature = "track_hit_feedbacks")]
pub fn hit_objectives_mut(&mut self) -> &mut Vec<Cow<'static, str>> {
&mut self.hit_objectives
}
#[inline]
pub fn new(input: I) -> Self {
Self {
input: Some(input),
filename: None,
#[cfg(feature = "std")]
file_path: None,
metadata: SerdeAnyMap::default(),
#[cfg(feature = "std")]
metadata_path: None,
exec_time: None,
cached_len: None,
executions: 0,
scheduled_count: 0,
corpus_id: None,
parent_id: None,
disabled: false,
objectives_found: 0,
#[cfg(feature = "track_hit_feedbacks")]
hit_feedbacks: Vec::new(),
#[cfg(feature = "track_hit_feedbacks")]
hit_objectives: Vec::new(),
}
}
pub fn with_parent_id(input: I, parent_id: CorpusId) -> Self {
Testcase {
input: Some(input),
filename: None,
#[cfg(feature = "std")]
file_path: None,
metadata: SerdeAnyMap::default(),
#[cfg(feature = "std")]
metadata_path: None,
exec_time: None,
cached_len: None,
executions: 0,
scheduled_count: 0,
corpus_id: None,
parent_id: Some(parent_id),
disabled: false,
objectives_found: 0,
#[cfg(feature = "track_hit_feedbacks")]
hit_feedbacks: Vec::new(),
#[cfg(feature = "track_hit_feedbacks")]
hit_objectives: Vec::new(),
}
}
#[inline]
pub fn with_filename(input: I, filename: String) -> Self {
Self {
input: Some(input),
filename: Some(filename),
#[cfg(feature = "std")]
file_path: None,
metadata: SerdeAnyMap::default(),
#[cfg(feature = "std")]
metadata_path: None,
exec_time: None,
cached_len: None,
executions: 0,
scheduled_count: 0,
corpus_id: None,
parent_id: None,
disabled: false,
objectives_found: 0,
#[cfg(feature = "track_hit_feedbacks")]
hit_feedbacks: Vec::new(),
#[cfg(feature = "track_hit_feedbacks")]
hit_objectives: Vec::new(),
}
}
#[must_use]
pub fn parent_id(&self) -> Option<CorpusId> {
self.parent_id
}
pub fn set_parent_id(&mut self, parent_id: CorpusId) {
self.parent_id = Some(parent_id);
}
pub fn set_parent_id_optional(&mut self, parent_id: Option<CorpusId>) {
self.parent_id = parent_id;
}
pub fn objectives_found(&self) -> usize {
self.objectives_found
}
pub fn found_objective(&mut self) {
self.objectives_found = self.objectives_found.saturating_add(1);
}
}
impl<I> Default for Testcase<I> {
#[inline]
fn default() -> Self {
Testcase {
input: None,
filename: None,
metadata: SerdeAnyMap::new(),
exec_time: None,
cached_len: None,
scheduled_count: 0,
corpus_id: None,
parent_id: None,
#[cfg(feature = "std")]
file_path: None,
#[cfg(feature = "std")]
metadata_path: None,
disabled: false,
executions: 0,
objectives_found: 0,
#[cfg(feature = "track_hit_feedbacks")]
hit_feedbacks: Vec::new(),
#[cfg(feature = "track_hit_feedbacks")]
hit_objectives: Vec::new(),
}
}
}
impl<I> Testcase<I>
where
I: HasLen,
{
#[inline]
pub fn cached_len(&mut self) -> Option<usize> {
self.cached_len
}
pub fn load_len<C: Corpus<I>>(&mut self, corpus: &C) -> Result<usize, Error> {
match &self.input {
Some(i) => {
let l = i.len();
self.cached_len = Some(l);
Ok(l)
}
None => {
if let Some(l) = self.cached_len {
Ok(l)
} else {
corpus.load_input_into(self)?;
self.load_len(corpus)
}
}
}
}
}
impl<I> From<I> for Testcase<I> {
fn from(input: I) -> Self {
Testcase::new(input)
}
}
#[derive(Serialize, Deserialize, Clone, Debug)]
#[cfg_attr(
any(not(feature = "serdeany_autoreg"), miri),
expect(clippy::unsafe_derive_deserialize)
)] pub struct SchedulerTestcaseMetadata {
bitmap_size: u64,
handicap: u64,
depth: u64,
n_fuzz_entry: usize,
cycle_and_time: (Duration, usize),
}
impl SchedulerTestcaseMetadata {
#[must_use]
pub fn new(depth: u64) -> Self {
Self {
bitmap_size: 0,
handicap: 0,
depth,
n_fuzz_entry: 0,
cycle_and_time: (Duration::default(), 0),
}
}
#[must_use]
pub fn with_n_fuzz_entry(depth: u64, n_fuzz_entry: usize) -> Self {
Self {
bitmap_size: 0,
handicap: 0,
depth,
n_fuzz_entry,
cycle_and_time: (Duration::default(), 0),
}
}
#[inline]
#[must_use]
pub fn bitmap_size(&self) -> u64 {
self.bitmap_size
}
#[inline]
pub fn set_bitmap_size(&mut self, val: u64) {
self.bitmap_size = val;
}
#[inline]
#[must_use]
pub fn handicap(&self) -> u64 {
self.handicap
}
#[inline]
pub fn set_handicap(&mut self, val: u64) {
self.handicap = val;
}
#[inline]
#[must_use]
pub fn depth(&self) -> u64 {
self.depth
}
#[inline]
pub fn set_depth(&mut self, val: u64) {
self.depth = val;
}
#[inline]
#[must_use]
pub fn n_fuzz_entry(&self) -> usize {
self.n_fuzz_entry
}
#[inline]
pub fn set_n_fuzz_entry(&mut self, val: usize) {
self.n_fuzz_entry = val;
}
#[inline]
#[must_use]
pub fn cycle_and_time(&self) -> (Duration, usize) {
self.cycle_and_time
}
#[inline]
pub fn set_cycle_and_time(&mut self, cycle_and_time: (Duration, usize)) {
self.cycle_and_time = cycle_and_time;
}
}
libafl_bolts::impl_serdeany!(SchedulerTestcaseMetadata);
#[cfg(feature = "std")]
impl<I> Drop for Testcase<I> {
fn drop(&mut self) {
if let Some(filename) = &self.filename {
let mut path = PathBuf::from(filename);
let lockname = format!(".{}.lafl_lock", path.file_name().unwrap().to_str().unwrap());
path.set_file_name(lockname);
let _ = std::fs::remove_file(path);
}
}
}