From 567f981f5f712b88358613460a3f4c70ef6e62e3 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Wed, 13 Jan 2021 19:02:43 +0100 Subject: [PATCH] added descriptions to llmp --- afl/src/events/llmp.rs | 104 ++++++++++++++++++++++++++++++++++++++-- afl/src/events/mod.rs | 22 ++++++++- afl/src/events/shmem.rs | 24 ++++++++++ afl/src/utils.rs | 57 +++++++++++++++++++++- 4 files changed, 201 insertions(+), 6 deletions(-) diff --git a/afl/src/events/llmp.rs b/afl/src/events/llmp.rs index c8217afd28..43446acec1 100644 --- a/afl/src/events/llmp.rs +++ b/afl/src/events/llmp.rs @@ -57,6 +57,7 @@ use core::{ sync::atomic::{compiler_fence, Ordering}, time::Duration, }; +use serde::{Deserialize, Serialize}; #[cfg(feature = "std")] use std::{ env, @@ -65,7 +66,7 @@ use std::{ thread, }; -use super::shmem::ShMem; +use super::shmem::{ShMem, ShMemDescription}; use crate::utils::next_pow2; use crate::AflError; @@ -217,6 +218,16 @@ unsafe fn _llmp_next_msg_ptr(last_msg: *const LlmpMsg) -> *mut LlmpMsg { .offset((*last_msg).buf_len_padded as isize) as *mut LlmpMsg; } +/// Description of a shared map. +/// May be used to restore the map by id. +#[derive(Copy, Clone, Debug, Serialize, Deserialize)] +pub struct LlmpDescription { + /// Info about the SharedMap in use + shmem: ShMemDescription, + /// The last message sent or received, depnding on page type + last_message_offset: Option, +} + #[derive(Copy, Clone, Debug)] /// Result of an LLMP Mesasge hook pub enum LlmpMsgHookResult { @@ -327,6 +338,23 @@ where } } + /// Describe this in a reproducable fashion, if it's a client + pub fn describe(&self) -> Result { + Ok(match self { + LlmpConnection::IsClient { client } => client.describe()?, + _ => todo!("Only client can be described atm."), + }) + } + + /// Recreate an existing client from the stored description + pub fn existing_client_from_description( + description: &LlmpClientDescription, + ) -> Result, AflError> { + Ok(LlmpConnection::IsClient { + client: LlmpClient::existing_client_from_description(description)?, + }) + } + /// Sends the given buffer over this connection, no matter if client or broker. pub fn send_buf(&mut self, tag: Tag, buf: &[u8]) -> Result<(), AflError> { match self { @@ -432,9 +460,6 @@ where #[cfg(feature = "std")] pub fn to_env(&self, env_name: &str) -> Result<(), AflError> { let current_out_map = self.out_maps.last().unwrap(); - // TODO: Make sure somebody else has mapped this - // current_out_map.await_read_blocking(); - current_out_map.shmem.write_to_env(env_name)?; current_out_map.msg_to_env(self.last_msg_sent, env_name) } @@ -731,6 +756,28 @@ where self.send(msg) } } + + // Describe this cient in a way, that it can be restored later with `Self::on_existing_from_description` + pub fn describe(&self) -> Result { + let map = self.out_maps.last().unwrap(); + let last_message_offset = if self.last_msg_sent.is_null() { + None + } else { + Some(map.msg_to_offset(self.last_msg_sent)?) + }; + Ok(LlmpDescription { + shmem: map.shmem.description(), + last_message_offset, + }) + } + + // Create this client on an existing map from the given description. acquired with `self.describe` + pub fn on_existing_from_description(description: &LlmpDescription) -> Result { + Self::on_existing_map( + SH::existing_from_description(&description.shmem)?, + description.last_message_offset, + ) + } } /// Receiving end on a (unidirectional) sharedmap channel @@ -922,6 +969,28 @@ where )) } } + + // Describe this cient in a way, that it can be restored later with `Self::on_existing_from_description` + pub fn describe(&self) -> Result { + let map = &self.current_recv_map; + let last_message_offset = if self.last_msg_recvd.is_null() { + None + } else { + Some(map.msg_to_offset(self.last_msg_recvd)?) + }; + Ok(LlmpDescription { + shmem: map.shmem.description(), + last_message_offset, + }) + } + + // Create this client on an existing map from the given description. acquired with `self.describe` + pub fn on_existing_from_description(description: &LlmpDescription) -> Result { + Self::on_existing_map( + SH::existing_from_description(&description.shmem)?, + description.last_message_offset, + ) + } } /// A page wrapper @@ -1330,6 +1399,15 @@ where } } +/// A restorable client description +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +pub struct LlmpClientDescription { + /// Description of the sender + sender: LlmpDescription, + /// Description of the receiver + receiver: LlmpDescription, +} + /// Client side of LLMP #[derive(Clone, Debug)] pub struct LlmpClient @@ -1380,6 +1458,24 @@ where self.receiver.to_env(&format!("{}_RECEIVER", env_name)) } + /// Describe this client in a way that it can be recreated, for example after crash + fn describe(&self) -> Result { + Ok(LlmpClientDescription { + sender: self.sender.describe()?, + receiver: self.receiver.describe()?, + }) + } + + /// Create an existing client from description + fn existing_client_from_description( + description: &LlmpClientDescription, + ) -> Result { + Ok(Self { + sender: LlmpSender::on_existing_from_description(&description.sender)?, + receiver: LlmpReceiver::on_existing_from_description(&description.receiver)?, + }) + } + /// Waits for the sender to be save to unmap. /// If a receiver is involved on the other side, this function should always be called. pub fn await_save_to_unmap_blocking(&self) { diff --git a/afl/src/events/mod.rs b/afl/src/events/mod.rs index 04fdf701ac..0ac67230ca 100644 --- a/afl/src/events/mod.rs +++ b/afl/src/events/mod.rs @@ -12,7 +12,7 @@ use core::{ use serde::{Deserialize, Serialize}; use self::{ - llmp::{LlmpClient, Tag}, + llmp::{LlmpClient, LlmpClientDescription, Tag}, shmem::ShMem, }; use crate::{ @@ -800,6 +800,25 @@ where }) } + /// Describe the client event mgr's llmp parts in a restorable fashion + pub fn describe(&self) -> Result { + self.llmp.describe() + } + + /// Create an existing client from description + pub fn existing_client_from_description( + description: &LlmpClientDescription, + stats: ST, + ) -> Result { + Ok(Self { + llmp: llmp::LlmpConnection::existing_client_from_description(description)?, + // Inserting a nop-stats element here so rust won't complain. + // In any case, the client won't currently use it. + stats: stats, + phantom: PhantomData, + }) + } + /// A client on an existing map pub fn for_client(client: LlmpClient, stats: ST) -> Self { Self { @@ -860,6 +879,7 @@ where } } + /// Send an event kind via llmp #[inline] fn send_event_kind<'a>(&mut self, event: LLMPEventKind<'a, I>) -> Result<(), AflError> { let serialized = postcard::to_allocvec(&event)?; diff --git a/afl/src/events/shmem.rs b/afl/src/events/shmem.rs index c9024f03b4..a531371a71 100644 --- a/afl/src/events/shmem.rs +++ b/afl/src/events/shmem.rs @@ -6,11 +6,22 @@ pub use shmem::AflShmem; use alloc::string::{String, ToString}; use core::fmt::Debug; +use serde::{Deserialize, Serialize}; #[cfg(feature = "std")] use std::env; use crate::AflError; +/// Description of a shared map. +/// May be used to restore the map by id. +#[derive(Copy, Clone, Debug, Serialize, Deserialize)] +pub struct ShMemDescription { + /// Size of this map + size: usize, + /// of name of this map, as fixed 20 bytes c-string + str_bytes: [u8; 20], +} + /// A Shared map pub trait ShMem: Sized + Debug { /// Creates a new map with the given size @@ -52,6 +63,19 @@ pub trait ShMem: Sized + Debug { /// The actual shared map, mutable fn map_mut(&mut self) -> &mut [u8]; + /// Describe this shared map in a recreatable fashion + fn description(&self) -> ShMemDescription { + ShMemDescription { + size: self.map().len(), + str_bytes: self.shm_slice().clone(), + } + } + + /// Create a map from a map description + fn existing_from_description(description: &ShMemDescription) -> Result { + Self::existing_from_shm_slice(&description.str_bytes, description.size) + } + /// Write this map's config to env #[cfg(feature = "std")] fn write_to_env(&self, env_name: &str) -> Result<(), AflError> { diff --git a/afl/src/utils.rs b/afl/src/utils.rs index 514adb159b..8b38da47a7 100644 --- a/afl/src/utils.rs +++ b/afl/src/utils.rs @@ -8,10 +8,65 @@ use xxhash_rust::xxh3::xxh3_64_with_seed; #[cfg(feature = "std")] use std::time::{SystemTime, UNIX_EPOCH}; -use crate::{corpus::Corpus, engines::State, feedbacks::FeedbacksTuple, inputs::Input, AflError}; +use crate::{ + corpus::Corpus, + engines::State, + events::{shmem::ShMem, LlmpEventManager, Stats}, + feedbacks::FeedbacksTuple, + inputs::Input, + AflError, +}; pub type StdRand = RomuTrioRand; +/// Serialize the current state and corpus during an executiont to bytes. +/// On top, add the current llmp event manager instance to be restored +/// This method is needed when the fuzzer run crashes and has to restart. +pub fn serialize_state_corpus_mgr( + state: &State, + corpus: &C, + mgr: &LlmpEventManager, +) -> Result, AflError> +where + C: Corpus, + FT: FeedbacksTuple, + I: Input, + R: Rand, + SH: ShMem, + ST: Stats, +{ + let mgr_bytes = postcard::to_allocvec(&mgr.describe()?)?; + let state_bytes = postcard::to_allocvec(&state)?; + let corpus_bytes = postcard::to_allocvec(&corpus)?; + Ok(postcard::to_allocvec(&( + state_bytes, + corpus_bytes, + mgr_bytes, + ))?) +} + +/// Deserialize the state and corpus tuple, previously serialized with `serialize_state_corpus(...)` +pub fn deserialize_state_corpus_mgr( + state_corpus_serialized: &[u8], + stats: ST, +) -> Result<(State, C, LlmpEventManager), AflError> +where + C: Corpus, + FT: FeedbacksTuple, + I: Input, + R: Rand, + SH: ShMem, + ST: Stats, +{ + let tuple: (Vec, Vec, Vec) = postcard::from_bytes(&state_corpus_serialized)?; + let client_description = postcard::from_bytes(&tuple.2)?; + Ok(( + postcard::from_bytes(&tuple.0)?, + postcard::from_bytes(&tuple.1)?, + LlmpEventManager::existing_client_from_description(&client_description, stats)?, + )) +} + /// Serialize the current state and corpus during an executiont to bytes. /// This method is needed when the fuzzer run crashes and has to restart. pub fn serialize_state_corpus(