libafl_libfuzzer 0.16.0

libFuzzer shim which uses LibAFL with common defaults
use alloc::rc::Rc;
use core::{cell::RefCell, fmt::Debug};
use std::borrow::Cow;

use libafl::{
    Error, HasMetadata, alloc,
    corpus::Testcase,
    executors::ExitKind,
    feedbacks::{Feedback, MinMapFeedback, StateInitializer},
    inputs::{BytesInput, Input},
    state::HasCorpus,
};
use libafl_bolts::{Named, impl_serdeany, tuples::MatchNameRef};
use libafl_targets::OomFeedback;
use serde::{Deserialize, Serialize};

use crate::{observers::MappedEdgeMapObserver, options::ArtifactPrefix};

#[derive(Debug)]
pub struct LibfuzzerKeepFeedback {
    keep: Rc<RefCell<bool>>,
}

impl LibfuzzerKeepFeedback {
    pub fn new() -> Self {
        Self {
            keep: Rc::new(RefCell::new(false)),
        }
    }

    pub fn keep(&self) -> Rc<RefCell<bool>> {
        self.keep.clone()
    }
}

impl Named for LibfuzzerKeepFeedback {
    fn name(&self) -> &Cow<'static, str> {
        static NAME: Cow<'static, str> = Cow::Borrowed("libfuzzer-keep");
        &NAME
    }
}

impl<S> StateInitializer<S> for LibfuzzerKeepFeedback {}

impl<EM, I, OT, S> Feedback<EM, I, OT, S> for LibfuzzerKeepFeedback
where
    S: HasCorpus<I>,
{
    fn is_interesting(
        &mut self,
        _state: &mut S,
        _manager: &mut EM,
        _input: &I,
        _observers: &OT,
        _exit_kind: &ExitKind,
    ) -> Result<bool, Error> {
        Ok(*self.keep.borrow())
    }

    #[cfg(feature = "track_hit_feedbacks")]
    fn last_result(&self) -> Result<bool, Error> {
        Ok(*self.keep.borrow())
    }
}

#[derive(Deserialize, Serialize, Debug)]
pub struct LibfuzzerCrashCauseMetadata {
    kind: ExitKind,
}

impl_serdeany!(LibfuzzerCrashCauseMetadata);

impl LibfuzzerCrashCauseMetadata {
    pub fn kind(&self) -> ExitKind {
        self.kind
    }
}

#[derive(Debug)]
pub struct LibfuzzerCrashCauseFeedback {
    artifact_prefix: ArtifactPrefix,
    exit_kind: ExitKind,
}

impl LibfuzzerCrashCauseFeedback {
    pub fn new(artifact_prefix: ArtifactPrefix) -> Self {
        Self {
            artifact_prefix,
            exit_kind: ExitKind::Ok,
        }
    }
}

impl Named for LibfuzzerCrashCauseFeedback {
    fn name(&self) -> &Cow<'static, str> {
        static NAME: Cow<'static, str> = Cow::Borrowed("crash-cause");
        &NAME
    }
}

impl LibfuzzerCrashCauseFeedback {
    fn set_filename<I: Input>(&self, prefix: &str, testcase: &mut Testcase<I>) {
        let base = if let Some(filename) = testcase.filename() {
            filename.clone()
        } else {
            testcase.input().as_ref().unwrap().generate_name(None)
        };
        let file_path = self.artifact_prefix.dir().join(format!(
            "{}{prefix}-{base}",
            self.artifact_prefix.filename_prefix()
        ));
        *testcase.file_path_mut() = Some(file_path);
    }
}

impl<S> StateInitializer<S> for LibfuzzerCrashCauseFeedback {}

impl<EM, OT, S> Feedback<EM, BytesInput, OT, S> for LibfuzzerCrashCauseFeedback
where
    OT: MatchNameRef,
{
    fn is_interesting(
        &mut self,
        _state: &mut S,
        _manager: &mut EM,
        _input: &BytesInput,
        _observers: &OT,
        exit_kind: &ExitKind,
    ) -> Result<bool, Error> {
        self.exit_kind = *exit_kind;
        Ok(false)
    }
    #[cfg(feature = "track_hit_feedbacks")]
    fn last_result(&self) -> Result<bool, Error> {
        Ok(false)
    }

    fn append_metadata(
        &mut self,
        _state: &mut S,
        _manager: &mut EM,
        _observers: &OT,
        testcase: &mut Testcase<BytesInput>,
    ) -> Result<(), Error> {
        match self.exit_kind {
            ExitKind::Crash | ExitKind::Oom if OomFeedback::oomed() => {
                self.set_filename("oom", testcase);
                testcase.add_metadata(LibfuzzerCrashCauseMetadata {
                    kind: ExitKind::Oom,
                });
            }
            ExitKind::Crash => {
                self.set_filename("crash", testcase);
                testcase.add_metadata(LibfuzzerCrashCauseMetadata {
                    kind: ExitKind::Crash,
                });
            }
            ExitKind::Timeout => {
                self.set_filename("timeout", testcase);
                testcase.add_metadata(LibfuzzerCrashCauseMetadata {
                    kind: ExitKind::Timeout,
                });
            }
            _ => {
                self.set_filename("uncategorized", testcase);
                testcase.add_metadata(LibfuzzerCrashCauseMetadata {
                    kind: self.exit_kind,
                });
            }
        }
        Ok(())
    }
}

pub type ShrinkMapFeedback<C, O, T> = MinMapFeedback<C, MappedEdgeMapObserver<O, T>>;