Convert ShMem into a state-full ShMemProvider and otherwise refactor shmem/llmp (#54)

* shmeme/llmp refactor to convert ShMem into a stateful ShMemProvider
factory.

At the moment we use parking_lot::ReentrantMutex. That may not be
necessary.

* fix merge issue

* formatting

* Fix fuzzer examples for new ShMemProvider

* Fix clippy warnings

* Fix build and clippy for x86_64

* Resolve review comments

* Remove ReentrantMutex and RefCell - they are not needed

* Hopefully fix win32 build

* Fix tests, windows build

* Rename ShMemProvider to ShMem

* Revert "Rename ShMemProvider to ShMem"

This reverts commit eca07c8d7bb3d5e829fecf3f7213c763470a41e9.

* Rename ShMemMapping to ShMem; Test fixes

* Add missing trait to scope

* Fix from_int

* Fix try_into

* Move to alloc::sync::Arc and spin::Mutex to support nostd

* Fix tests

* nostd fixes; Make new() a part of the ShMemProvider trait

* Fix errant ?

* Fix windows

* Fix missing trait

* nostd remove dbg!

* Add Default and Clone to ShMemProvider

* Formatting

* Fix windows

* Get rid of ArcMutex in favor of RefCell

* Rc RefCell

* moved to refs

* SHP->SP

* Use alloc::rc::Rc instead of std::rc::Rc

* Format

* Add setup_restarting_mgr_std which selects the right ShMemProvider; changed fuzzers to use it

* Get rid of unnecessary clone

* Fix clippy error on windows

* Fix nostd

* Fix formatting

* Make StdShmemProvider include ServedShMemProvider

* Get rid of lifetime specifiers now that we are using Rc

* Get rid of unneccesary spin

* Rename ShMemProvider::Mapping to ShMemProvider::Mem

* Formatting

* fix Windows

* Rename DefaultUnixShmem* to CommonUnixShmem*

Co-authored-by: Dominik Maier <domenukk@gmail.com>
This commit is contained in:
s1341 2021-04-16 12:26:06 +03:00 committed by GitHub
parent 6f9a81b799
commit 655d30519b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 1159 additions and 1029 deletions

View File

@ -2,15 +2,12 @@
//! The example harness is built for libpng. //! The example harness is built for libpng.
use libafl::{ use libafl::{
bolts::{ bolts::tuples::{tuple_list, Named},
shmem::UnixShMem,
tuples::{tuple_list, Named},
},
corpus::{ corpus::{
Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus, Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus,
QueueCorpusScheduler, QueueCorpusScheduler,
}, },
events::{setup_restarting_mgr, EventManager}, events::{setup_restarting_mgr_std, EventManager},
executors::{inprocess::InProcessExecutor, Executor, ExitKind, HasObservers}, executors::{inprocess::InProcessExecutor, Executor, ExitKind, HasObservers},
feedbacks::{CrashFeedback, MaxMapFeedback}, feedbacks::{CrashFeedback, MaxMapFeedback},
fuzzer::{Fuzzer, StdFuzzer}, fuzzer::{Fuzzer, StdFuzzer},
@ -25,6 +22,7 @@ use libafl::{
Error, Error,
}; };
use core::cell::RefCell;
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
use frida_gum::instruction_writer::X86Register; use frida_gum::instruction_writer::X86Register;
#[cfg(target_arch = "aarch64")] #[cfg(target_arch = "aarch64")]
@ -34,8 +32,7 @@ use frida_gum::{
stalker::{NoneEventSink, Stalker, Transformer}, stalker::{NoneEventSink, Stalker, Transformer},
}; };
use frida_gum::{Gum, MemoryRange, Module, NativePointer, PageProtection}; use frida_gum::{Gum, MemoryRange, Module, NativePointer, PageProtection};
use std::{env, ffi::c_void, path::PathBuf};
use std::{cell::RefCell, env, ffi::c_void, path::PathBuf};
/// An helper that feeds FridaInProcessExecutor with user-supplied instrumentation /// An helper that feeds FridaInProcessExecutor with user-supplied instrumentation
pub trait FridaHelper<'a> { pub trait FridaHelper<'a> {
@ -399,7 +396,7 @@ unsafe fn fuzz(
// 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.
let (state, mut restarting_mgr) = let (state, mut restarting_mgr) =
match setup_restarting_mgr::<_, _, UnixShMem, _>(stats, broker_port) { match setup_restarting_mgr_std(stats, broker_port) {
Ok(res) => res, Ok(res) => res,
Err(err) => match err { Err(err) => match err {
Error::ShuttingDown => { Error::ShuttingDown => {

View File

@ -4,9 +4,9 @@
use std::{env, path::PathBuf}; use std::{env, path::PathBuf};
use libafl::{ use libafl::{
bolts::{shmem::UnixShMem, tuples::tuple_list}, bolts::tuples::tuple_list,
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus, RandCorpusScheduler}, corpus::{Corpus, InMemoryCorpus, OnDiskCorpus, RandCorpusScheduler},
events::setup_restarting_mgr, events::setup_restarting_mgr_std,
executors::{inprocess::InProcessExecutor, ExitKind}, executors::{inprocess::InProcessExecutor, ExitKind},
feedbacks::{CrashFeedback, MaxMapFeedback}, feedbacks::{CrashFeedback, MaxMapFeedback},
fuzzer::{Fuzzer, StdFuzzer}, fuzzer::{Fuzzer, StdFuzzer},
@ -20,9 +20,11 @@ use libafl::{
Error, Error,
}; };
use libafl_targets::{libfuzzer_initialize, libfuzzer_test_one_input, EDGES_MAP, MAX_EDGES_NUM, CMP_MAP, CMP_MAP_SIZE}; use libafl_targets::{
libfuzzer_initialize, libfuzzer_test_one_input, CMP_MAP, CMP_MAP_SIZE, EDGES_MAP, MAX_EDGES_NUM,
};
const ALLOC_MAP_SIZE: usize = 16*1024; const ALLOC_MAP_SIZE: usize = 16 * 1024;
extern "C" { extern "C" {
static mut libafl_alloc_map: [usize; ALLOC_MAP_SIZE]; static mut libafl_alloc_map: [usize; ALLOC_MAP_SIZE];
} }
@ -53,17 +55,19 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
// 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.
let (state, mut restarting_mgr) = let (state, mut restarting_mgr) =
setup_restarting_mgr::<_, _, UnixShMem, _>(stats, broker_port) setup_restarting_mgr_std(stats, broker_port)
.expect("Failed to setup the restarter".into()); .expect("Failed to setup the restarter".into());
// Create an observation channel using the coverage map // Create an observation channel using the coverage map
let edges_observer = StdMapObserver::new("edges", unsafe { &mut EDGES_MAP }, unsafe { MAX_EDGES_NUM }); let edges_observer =
StdMapObserver::new("edges", unsafe { &mut EDGES_MAP }, unsafe { MAX_EDGES_NUM });
// Create an observation channel using the cmp map // Create an observation channel using the cmp map
let cmps_observer = StdMapObserver::new("cmps", unsafe { &mut CMP_MAP }, CMP_MAP_SIZE); let cmps_observer = StdMapObserver::new("cmps", unsafe { &mut CMP_MAP }, CMP_MAP_SIZE);
// Create an observation channel using the allocations map // Create an observation channel using the allocations map
let allocs_observer = StdMapObserver::new("allocs", unsafe { &mut libafl_alloc_map }, ALLOC_MAP_SIZE); let allocs_observer =
StdMapObserver::new("allocs", unsafe { &mut libafl_alloc_map }, ALLOC_MAP_SIZE);
// If not restarting, create a State from scratch // If not restarting, create a State from scratch
let mut state = state.unwrap_or_else(|| { let mut state = state.unwrap_or_else(|| {

View File

@ -5,12 +5,12 @@ use core::time::Duration;
use std::{env, path::PathBuf}; use std::{env, path::PathBuf};
use libafl::{ use libafl::{
bolts::{shmem::StdShMem, tuples::tuple_list}, bolts::tuples::tuple_list,
corpus::{ corpus::{
Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus, Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus,
QueueCorpusScheduler, QueueCorpusScheduler,
}, },
events::setup_restarting_mgr, events::setup_restarting_mgr_std,
executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor}, executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor},
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
fuzzer::{Fuzzer, StdFuzzer}, fuzzer::{Fuzzer, StdFuzzer},
@ -52,7 +52,7 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
// 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.
let (state, mut restarting_mgr) = let (state, mut restarting_mgr) =
match setup_restarting_mgr::<_, _, StdShMem, _>(stats, broker_port) { match setup_restarting_mgr_std(stats, broker_port) {
Ok(res) => res, Ok(res) => res,
Err(err) => match err { Err(err) => match err {
Error::ShuttingDown => { Error::ShuttingDown => {

View File

@ -3,15 +3,19 @@ This shows how llmp can be used directly, without libafl abstractions
*/ */
extern crate alloc; extern crate alloc;
use alloc::rc::Rc;
#[cfg(all(unix, feature = "std"))] #[cfg(all(unix, feature = "std"))]
use core::{convert::TryInto, time::Duration}; use core::{cell::RefCell, convert::TryInto, time::Duration};
#[cfg(all(unix, feature = "std"))] #[cfg(all(unix, feature = "std"))]
use std::{thread, time}; use std::{thread, time};
use libafl::bolts::llmp::Tag; use libafl::bolts::llmp::Tag;
#[cfg(all(unix, feature = "std"))] #[cfg(all(unix, feature = "std"))]
use libafl::{ use libafl::{
bolts::{llmp, shmem::UnixShMem}, bolts::{
llmp,
shmem::{ShMemProvider, StdShMemProvider},
},
Error, Error,
}; };
@ -21,7 +25,8 @@ const _TAG_1MEG_V1: Tag = 0xB1111161;
#[cfg(all(unix, feature = "std"))] #[cfg(all(unix, feature = "std"))]
fn adder_loop(port: u16) -> ! { fn adder_loop(port: u16) -> ! {
let mut client = llmp::LlmpClient::<UnixShMem>::create_attach_to_tcp(port).unwrap(); let shmem_provider = Rc::new(RefCell::new(StdShMemProvider::new()));
let mut client = llmp::LlmpClient::create_attach_to_tcp(&shmem_provider, port).unwrap();
let mut last_result: u32 = 0; let mut last_result: u32 = 0;
let mut current_result: u32 = 0; let mut current_result: u32 = 0;
loop { loop {
@ -63,7 +68,11 @@ fn adder_loop(port: u16) -> ! {
#[cfg(all(unix, feature = "std"))] #[cfg(all(unix, feature = "std"))]
fn large_msg_loop(port: u16) -> ! { fn large_msg_loop(port: u16) -> ! {
let mut client = llmp::LlmpClient::<UnixShMem>::create_attach_to_tcp(port).unwrap(); let mut client = llmp::LlmpClient::create_attach_to_tcp(
&Rc::new(RefCell::new(StdShMemProvider::new())),
port,
)
.unwrap();
let meg_buf = [1u8; 1 << 20]; let meg_buf = [1u8; 1 << 20];
@ -124,7 +133,8 @@ fn main() {
match mode.as_str() { match mode.as_str() {
"broker" => { "broker" => {
let mut broker = llmp::LlmpBroker::<UnixShMem>::new().unwrap(); let mut broker =
llmp::LlmpBroker::new(&Rc::new(RefCell::new(StdShMemProvider::new()))).unwrap();
broker broker
.launch_listener(llmp::Listener::Tcp( .launch_listener(llmp::Listener::Tcp(
std::net::TcpListener::bind(format!("127.0.0.1:{}", port)).unwrap(), std::net::TcpListener::bind(format!("127.0.0.1:{}", port)).unwrap(),
@ -133,7 +143,11 @@ fn main() {
broker.loop_forever(&mut broker_message_hook, Some(Duration::from_millis(5))) broker.loop_forever(&mut broker_message_hook, Some(Duration::from_millis(5)))
} }
"ctr" => { "ctr" => {
let mut client = llmp::LlmpClient::<UnixShMem>::create_attach_to_tcp(port).unwrap(); let mut client = llmp::LlmpClient::create_attach_to_tcp(
&Rc::new(RefCell::new(StdShMemProvider::new())),
port,
)
.unwrap();
let mut counter: u32 = 0; let mut counter: u32 = 0;
loop { loop {
counter = counter.wrapping_add(1); counter = counter.wrapping_add(1);

File diff suppressed because it is too large Load Diff

View File

@ -5,12 +5,18 @@ and forwards them over unix domain sockets.
*/ */
use crate::{ use crate::{
bolts::shmem::{ShMem, ShMemDescription, UnixShMem}, bolts::shmem::{
unix_shmem::ashmem::{AshmemShMem, AshmemShMemProvider},
ShMem, ShMemDescription, ShMemId, ShMemProvider,
},
Error, Error,
}; };
use hashbrown::HashMap; use hashbrown::HashMap;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::io::{Read, Write}; use std::{
io::{Read, Write},
sync::{Arc, Condvar, Mutex},
};
#[cfg(all(feature = "std", unix))] #[cfg(all(feature = "std", unix))]
use nix::poll::{poll, PollFd, PollFlags}; use nix::poll::{poll, PollFd, PollFlags};
@ -18,8 +24,8 @@ use nix::poll::{poll, PollFd, PollFlags};
#[cfg(all(feature = "std", unix))] #[cfg(all(feature = "std", unix))]
use std::{ use std::{
os::unix::{ os::unix::{
io::{AsRawFd, RawFd},
net::{UnixListener, UnixStream}, net::{UnixListener, UnixStream},
{io::AsRawFd, prelude::RawFd},
}, },
thread, thread,
}; };
@ -27,30 +33,43 @@ use std::{
#[cfg(all(unix, feature = "std"))] #[cfg(all(unix, feature = "std"))]
use uds::{UnixListenerExt, UnixSocketAddr, UnixStreamExt}; use uds::{UnixListenerExt, UnixSocketAddr, UnixStreamExt};
#[derive(Debug)]
/// The Sharedmem backed by a `ShmemService`a
pub struct ServedShMem {
stream: UnixStream,
shmem: Option<UnixShMem>,
slice: Option<[u8; 20]>,
fd: Option<RawFd>,
}
const ASHMEM_SERVER_NAME: &str = "@ashmem_server"; const ASHMEM_SERVER_NAME: &str = "@ashmem_server";
impl ServedShMem { #[derive(Debug)]
/// Create a new ServedShMem and connect to the ashmem server. pub struct ServedShMemProvider {
pub fn connect(name: &str) -> Self { stream: UnixStream,
Self { inner: AshmemShMemProvider,
stream: UnixStream::connect_to_unix_addr(&UnixSocketAddr::from_abstract(name).unwrap()) }
.expect("Failed to connect to the ashmem server"),
shmem: None, #[derive(Clone, Debug)]
slice: None, pub struct ServedShMem {
fd: None, inner: AshmemShMem,
} server_fd: i32,
}
impl ShMem for ServedShMem {
fn id(&self) -> ShMemId {
let client_id = self.inner.id();
ShMemId::from_string(&format!("{}:{}", self.server_fd, client_id.to_string()))
} }
fn len(&self) -> usize {
self.inner.len()
}
fn map(&self) -> &[u8] {
self.inner.map()
}
fn map_mut(&mut self) -> &mut [u8] {
self.inner.map_mut()
}
}
impl ServedShMemProvider {
/// Send a request to the server, and wait for a response /// Send a request to the server, and wait for a response
fn send_receive(&mut self, request: AshmemRequest) -> ([u8; 20], RawFd) { #[allow(clippy::similar_names)] // id and fd
fn send_receive(&mut self, request: AshmemRequest) -> (i32, i32) {
let body = postcard::to_allocvec(&request).unwrap(); let body = postcard::to_allocvec(&request).unwrap();
let header = (body.len() as u32).to_be_bytes(); let header = (body.len() as u32).to_be_bytes();
@ -66,63 +85,62 @@ impl ServedShMem {
self.stream self.stream
.recv_fds(&mut shm_slice, &mut fd_buf) .recv_fds(&mut shm_slice, &mut fd_buf)
.expect("Did not receive a response"); .expect("Did not receive a response");
(shm_slice, fd_buf[0])
let server_id = ShMemId::from_slice(&shm_slice);
let server_id_str = server_id.to_string();
let server_fd: i32 = server_id_str.parse().unwrap();
(server_fd, fd_buf[0])
} }
} }
impl ShMem for ServedShMem { impl Default for ServedShMemProvider {
fn new_map(map_size: usize) -> Result<Self, crate::Error> { fn default() -> Self {
let mut res = Self::connect(ASHMEM_SERVER_NAME); Self::new()
let (shm_slice, fd) = res.send_receive(AshmemRequest::NewMap(map_size)); }
if fd == -1 { }
Err(Error::IllegalState(
"Could not allocate from the ashmem server".to_string(), impl Clone for ServedShMemProvider {
)) fn clone(&self) -> Self {
} else { Self::new()
res.slice = Some(shm_slice); }
res.fd = Some(fd); }
res.shmem = Some(
UnixShMem::existing_from_shm_slice(&shm_slice, map_size) impl ShMemProvider for ServedShMemProvider {
.expect("Failed to create the UnixShMem"), type Mem = ServedShMem;
);
Ok(res) /// Connect to the server and return a new ServedShMemProvider
fn new() -> Self {
Self {
stream: UnixStream::connect_to_unix_addr(
&UnixSocketAddr::new(ASHMEM_SERVER_NAME).unwrap(),
)
.expect("Unable to open connection to ashmem service"),
inner: AshmemShMemProvider::new(),
} }
} }
fn new_map(&mut self, map_size: usize) -> Result<Self::Mem, crate::Error> {
let (server_fd, client_fd) = self.send_receive(AshmemRequest::NewMap(map_size));
fn existing_from_shm_slice( Ok(ServedShMem {
map_str_bytes: &[u8; 20], inner: self
map_size: usize, .inner
) -> Result<Self, crate::Error> { .from_id_and_size(ShMemId::from_string(&format!("{}", client_fd)), map_size)?,
let mut res = Self::connect(ASHMEM_SERVER_NAME); server_fd,
let (shm_slice, fd) = res.send_receive(AshmemRequest::ExistingMap(ShMemDescription { })
size: map_size,
str_bytes: *map_str_bytes,
}));
if fd == -1 {
Err(Error::IllegalState(
"Could not allocate from the ashmem server".to_string(),
))
} else {
res.slice = Some(shm_slice);
res.fd = Some(fd);
res.shmem = Some(
UnixShMem::existing_from_shm_slice(&shm_slice, map_size)
.expect("Failed to create the UnixShMem"),
);
Ok(res)
}
} }
fn shm_slice(&self) -> &[u8; 20] { fn from_id_and_size(&mut self, id: ShMemId, size: usize) -> Result<Self::Mem, Error> {
self.slice.as_ref().unwrap() let parts = id.to_string().split(':').collect::<Vec<&str>>();
} let server_id_str = parts.get(0).unwrap();
let (server_fd, client_fd) = self.send_receive(AshmemRequest::ExistingMap(
fn map(&self) -> &[u8] { ShMemDescription::from_string_and_size(server_id_str, size),
self.shmem.as_ref().unwrap().map() ));
} Ok(ServedShMem {
inner: self
fn map_mut(&mut self) -> &mut [u8] { .inner
self.shmem.as_mut().unwrap().map_mut() .from_id_and_size(ShMemId::from_string(&format!("{}", client_fd)), size)?,
server_fd,
})
} }
} }
@ -138,13 +156,20 @@ pub enum AshmemRequest {
} }
#[derive(Debug)] #[derive(Debug)]
pub struct AshmemClient { struct AshmemClient {
unix_socket_file: String, stream: UnixStream,
}
impl AshmemClient {
fn new(stream: UnixStream) -> Self {
Self { stream }
}
} }
#[derive(Debug)] #[derive(Debug)]
pub struct AshmemService { pub struct AshmemService {
maps: HashMap<[u8; 20], UnixShMem>, provider: AshmemShMemProvider,
maps: Vec<AshmemShMem>,
} }
impl AshmemService { impl AshmemService {
@ -152,116 +177,145 @@ impl AshmemService {
#[must_use] #[must_use]
fn new() -> Self { fn new() -> Self {
AshmemService { AshmemService {
maps: HashMap::new(), provider: AshmemShMemProvider::new(),
maps: Vec::new(),
} }
} }
/// Read and handle the client request, send the answer over unix fd. /// Read and handle the client request, send the answer over unix fd.
fn handle_client(&mut self, stream: &mut UnixStream) -> Result<(), Error> { fn handle_client(&mut self, client: &mut AshmemClient) -> Result<(), Error> {
// Always receive one be u32 of size, then the command. // Always receive one be u32 of size, then the command.
let mut size_bytes = [0u8; 4]; let mut size_bytes = [0u8; 4];
stream.read_exact(&mut size_bytes)?; client.stream.read_exact(&mut size_bytes)?;
let size = u32::from_be_bytes(size_bytes); let size = u32::from_be_bytes(size_bytes);
let mut bytes = vec![]; let mut bytes = vec![];
bytes.resize(size as usize, 0u8); bytes.resize(size as usize, 0u8);
stream client
.stream
.read_exact(&mut bytes) .read_exact(&mut bytes)
.expect("Failed to read message body"); .expect("Failed to read message body");
let request: AshmemRequest = postcard::from_bytes(&bytes)?; let request: AshmemRequest = postcard::from_bytes(&bytes)?;
// Handle the client request // Handle the client request
let (shmem_slice, fd): ([u8; 20], RawFd) = match request { let mapping = match request {
AshmemRequest::NewMap(map_size) => match UnixShMem::new(map_size) { AshmemRequest::NewMap(map_size) => self.provider.new_map(map_size)?,
Err(e) => {
println!("Error allocating shared map {:?}", e);
([0; 20], -1)
}
Ok(map) => {
let res = (*map.shm_slice(), map.shm_id);
self.maps.insert(*map.shm_slice(), map);
res
}
},
AshmemRequest::ExistingMap(description) => { AshmemRequest::ExistingMap(description) => {
match self.maps.get(&description.str_bytes) { self.provider.from_description(description)?
None => {
println!("Error finding shared map {:?}", description);
([0; 20], -1)
}
Some(map) => (*map.shm_slice(), map.shm_id),
}
} }
AshmemRequest::Deregister(_) => { AshmemRequest::Deregister(_) => {
return Ok(()); return Ok(());
} }
}; };
stream.send_fds(&shmem_slice, &[fd])?; let id = mapping.id();
let server_fd: i32 = id.to_string().parse().unwrap();
client
.stream
.send_fds(&id.to_string().as_bytes(), &[server_fd])?;
self.maps.push(mapping);
Ok(()) Ok(())
} }
/// Create a new AshmemService, then listen and service incoming connections in a new thread. /// Create a new AshmemService, then listen and service incoming connections in a new thread.
pub fn start() -> Result<thread::JoinHandle<()>, Error> { pub fn start() -> Result<thread::JoinHandle<Result<(), Error>>, Error> {
Ok(thread::spawn(move || { #[allow(clippy::mutex_atomic)]
Self::new().listen(ASHMEM_SERVER_NAME).unwrap() let syncpair = Arc::new((Mutex::new(false), Condvar::new()));
})) let childsyncpair = Arc::clone(&syncpair);
let join_handle =
thread::spawn(move || Self::new().listen(ASHMEM_SERVER_NAME, childsyncpair));
let (lock, cvar) = &*syncpair;
let mut started = lock.lock().unwrap();
while !*started {
started = cvar.wait(started).unwrap();
}
Ok(join_handle)
} }
/// Listen on a filename (or abstract name) for new connections and serve them. This function /// Listen on a filename (or abstract name) for new connections and serve them. This function
/// should not return. /// should not return.
fn listen(&mut self, filename: &str) -> Result<(), Error> { fn listen(
let listener = UnixListener::bind_unix_addr(&UnixSocketAddr::new(filename)?)?; &mut self,
let mut clients: HashMap<RawFd, (UnixStream, UnixSocketAddr)> = HashMap::new(); filename: &str,
let mut poll_fds: HashMap<RawFd, PollFd> = HashMap::new(); syncpair: Arc<(Mutex<bool>, Condvar)>,
) -> Result<(), Error> {
poll_fds.insert( let listener = if let Ok(listener) =
UnixListener::bind_unix_addr(&UnixSocketAddr::new(filename)?)
{
listener
} else {
let (lock, cvar) = &*syncpair;
*lock.lock().unwrap() = true;
cvar.notify_one();
return Err(Error::Unknown(
"The server appears to already be running. We are probably a client".to_string(),
));
};
let mut clients: HashMap<RawFd, AshmemClient> = HashMap::new();
let mut poll_fds: Vec<PollFd> = vec![PollFd::new(
listener.as_raw_fd(), listener.as_raw_fd(),
PollFd::new(listener.as_raw_fd(), PollFlags::POLLIN), PollFlags::POLLIN | PollFlags::POLLRDNORM | PollFlags::POLLRDBAND,
); )];
let (lock, cvar) = &*syncpair;
*lock.lock().unwrap() = true;
cvar.notify_one();
loop { loop {
let mut fds_to_poll: Vec<PollFd> = poll_fds.values().copied().collect(); match poll(&mut poll_fds, -1) {
let fd = match poll(&mut fds_to_poll, -1) { Ok(num_fds) if num_fds > 0 => (),
Ok(fd) => fd, Ok(_) => continue,
Err(e) => { Err(e) => {
println!("Error polling for activity: {:?}", e); println!("Error polling for activity: {:?}", e);
continue; continue;
} }
}; };
if fd == listener.as_raw_fd() { let copied_poll_fds: Vec<PollFd> = poll_fds.iter().copied().collect();
let (stream, addr) = match listener.accept_unix_addr() { for poll_fd in copied_poll_fds {
Ok(stream_val) => stream_val, let revents = poll_fd.revents().expect("revents should not be None");
Err(e) => { let raw_polled_fd =
println!("Error accepting client: {:?}", e); unsafe { *((&poll_fd as *const PollFd) as *const libc::pollfd) }.fd;
continue; if revents.contains(PollFlags::POLLHUP) {
} poll_fds.remove(poll_fds.iter().position(|item| *item == poll_fd).unwrap());
}; clients.remove(&raw_polled_fd);
} else if revents.contains(PollFlags::POLLIN) {
if clients.contains_key(&raw_polled_fd) {
let mut client = clients.get_mut(&raw_polled_fd).unwrap();
match self.handle_client(&mut client) {
Ok(()) => (),
Err(e) => {
dbg!("Ignoring failed read from client", e, poll_fd);
continue;
}
};
} else {
let (stream, addr) = match listener.accept_unix_addr() {
Ok(stream_val) => stream_val,
Err(e) => {
println!("Error accepting client: {:?}", e);
continue;
}
};
println!("Recieved connection from {:?}", addr); println!("Recieved connection from {:?}", addr);
let pollfd = PollFd::new(stream.as_raw_fd(), PollFlags::POLLIN); let pollfd = PollFd::new(
poll_fds.insert(stream.as_raw_fd(), pollfd); stream.as_raw_fd(),
clients PollFlags::POLLIN | PollFlags::POLLRDNORM | PollFlags::POLLRDBAND,
.insert(stream.as_raw_fd(), (stream, addr)) );
.as_ref() poll_fds.push(pollfd);
.unwrap(); let mut client = AshmemClient::new(stream);
} else if poll_fds match self.handle_client(&mut client) {
.get(&fd) Ok(()) => (),
.unwrap() Err(e) => {
.revents() dbg!("Ignoring failed read from client", e);
.unwrap() }
.contains(PollFlags::POLLHUP) };
{ clients.insert(client.stream.as_raw_fd(), client);
poll_fds.remove(&fd);
clients.remove(&fd);
} else {
let (stream, _addr) = clients.get_mut(&fd).unwrap();
match self.handle_client(stream) {
Ok(()) => (),
Err(e) => {
dbg!("Ignoring failed read from client", e);
continue;
} }
}; } else {
//println!("Unknown revents flags: {:?}", revents);
}
} }
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
//! LLMP-backed event manager for scalable multi-processed fuzzing //! LLMP-backed event manager for scalable multi-processed fuzzing
use alloc::{string::ToString, vec::Vec}; use alloc::{rc::Rc, string::ToString, vec::Vec};
use core::{marker::PhantomData, time::Duration}; use core::{cell::RefCell, marker::PhantomData, time::Duration};
use serde::{de::DeserializeOwned, Serialize}; use serde::{de::DeserializeOwned, Serialize};
#[cfg(feature = "std")] #[cfg(feature = "std")]
@ -17,12 +17,16 @@ use crate::utils::startable_self;
use crate::utils::{fork, ForkResult}; use crate::utils::{fork, ForkResult};
#[cfg(all(feature = "std", unix))] #[cfg(all(feature = "std", unix))]
use crate::bolts::shmem::UnixShMem; use crate::bolts::shmem::UnixShMemProvider;
#[cfg(all(feature = "std", target_os = "android"))]
use crate::bolts::os::ashmem_server::AshmemService;
#[cfg(feature = "std")]
use crate::bolts::shmem::StdShMemProvider;
use crate::{ use crate::{
bolts::{ bolts::{
llmp::{self, LlmpClient, LlmpClientDescription, LlmpSender, Tag}, llmp::{self, LlmpClient, LlmpClientDescription, LlmpSender, Tag},
shmem::ShMem, shmem::ShMemProvider,
}, },
corpus::CorpusScheduler, corpus::CorpusScheduler,
events::{BrokerEventResult, Event, EventManager}, events::{BrokerEventResult, Event, EventManager},
@ -46,23 +50,23 @@ const LLMP_TAG_EVENT_TO_BOTH: llmp::Tag = 0x2B0741;
const _LLMP_TAG_RESTART: llmp::Tag = 0x8357A87; const _LLMP_TAG_RESTART: llmp::Tag = 0x8357A87;
const _LLMP_TAG_NO_RESTART: llmp::Tag = 0x57A7EE71; const _LLMP_TAG_NO_RESTART: llmp::Tag = 0x57A7EE71;
#[derive(Clone, Debug)] #[derive(Debug)]
pub struct LlmpEventManager<I, S, SH, ST> pub struct LlmpEventManager<I, S, SP, ST>
where where
I: Input, I: Input,
S: IfInteresting<I>, S: IfInteresting<I>,
SH: ShMem, SP: ShMemProvider + 'static,
ST: Stats, ST: Stats,
//CE: CustomEvent<I>, //CE: CustomEvent<I>,
{ {
stats: Option<ST>, stats: Option<ST>,
llmp: llmp::LlmpConnection<SH>, llmp: llmp::LlmpConnection<SP>,
phantom: PhantomData<(I, S)>, phantom: PhantomData<(I, S)>,
} }
#[cfg(feature = "std")] #[cfg(feature = "std")]
#[cfg(unix)] #[cfg(unix)]
impl<I, S, ST> LlmpEventManager<I, S, UnixShMem, ST> impl<I, S, ST> LlmpEventManager<I, S, UnixShMemProvider, ST>
where where
I: Input, I: Input,
S: IfInteresting<I>, S: IfInteresting<I>,
@ -72,10 +76,14 @@ where
/// If the port is not yet bound, it will act as broker /// If the port is not yet bound, it will act as broker
/// Else, it will act as client. /// Else, it will act as client.
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub fn new_on_port_std(stats: ST, port: u16) -> Result<Self, Error> { pub fn new_on_port_std(
shmem_provider: &Rc<RefCell<UnixShMemProvider>>,
stats: ST,
port: u16,
) -> Result<Self, Error> {
Ok(Self { Ok(Self {
stats: Some(stats), stats: Some(stats),
llmp: llmp::LlmpConnection::on_port(port)?, llmp: llmp::LlmpConnection::on_port(shmem_provider, port)?,
phantom: PhantomData, phantom: PhantomData,
}) })
} }
@ -83,16 +91,19 @@ where
/// If a client respawns, it may reuse the existing connection, previously stored by LlmpClient::to_env /// If a client respawns, it may reuse the existing connection, previously stored by LlmpClient::to_env
/// Std uses UnixShMem. /// Std uses UnixShMem.
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub fn existing_client_from_env_std(env_name: &str) -> Result<Self, Error> { pub fn existing_client_from_env_std(
Self::existing_client_from_env(env_name) shmem_provider: &Rc<RefCell<UnixShMemProvider>>,
env_name: &str,
) -> Result<Self, Error> {
Self::existing_client_from_env(shmem_provider, env_name)
} }
} }
impl<I, S, SH, ST> Drop for LlmpEventManager<I, S, SH, ST> impl<I, S, SP, ST> Drop for LlmpEventManager<I, S, SP, ST>
where where
I: Input, I: Input,
S: IfInteresting<I>, S: IfInteresting<I>,
SH: ShMem, SP: ShMemProvider,
ST: Stats, ST: Stats,
{ {
/// LLMP clients will have to wait until their pages are mapped by somebody. /// LLMP clients will have to wait until their pages are mapped by somebody.
@ -101,32 +112,39 @@ where
} }
} }
impl<I, S, SH, ST> LlmpEventManager<I, S, SH, ST> impl<I, S, SP, ST> LlmpEventManager<I, S, SP, ST>
where where
I: Input, I: Input,
S: IfInteresting<I>, S: IfInteresting<I>,
SH: ShMem, SP: ShMemProvider,
ST: Stats, ST: Stats,
{ {
/// Create llmp on a port /// Create llmp on a port
/// If the port is not yet bound, it will act as broker /// If the port is not yet bound, it will act as broker
/// Else, it will act as client. /// Else, it will act as client.
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub fn new_on_port(stats: ST, port: u16) -> Result<Self, Error> { pub fn new_on_port(
shmem_provider: &Rc<RefCell<SP>>,
stats: ST,
port: u16,
) -> Result<Self, Error> {
Ok(Self { Ok(Self {
stats: Some(stats), stats: Some(stats),
llmp: llmp::LlmpConnection::on_port(port)?, llmp: llmp::LlmpConnection::on_port(shmem_provider, port)?,
phantom: PhantomData, phantom: PhantomData,
}) })
} }
/// If a client respawns, it may reuse the existing connection, previously stored by LlmpClient::to_env /// If a client respawns, it may reuse the existing connection, previously stored by LlmpClient::to_env
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub fn existing_client_from_env(env_name: &str) -> Result<Self, Error> { pub fn existing_client_from_env(
shmem_provider: &Rc<RefCell<SP>>,
env_name: &str,
) -> Result<Self, Error> {
Ok(Self { Ok(Self {
stats: None, stats: None,
llmp: llmp::LlmpConnection::IsClient { llmp: llmp::LlmpConnection::IsClient {
client: LlmpClient::on_existing_from_env(env_name)?, client: LlmpClient::on_existing_from_env(shmem_provider, env_name)?,
}, },
// Inserting a nop-stats element here so rust won't complain. // Inserting a nop-stats element here so rust won't complain.
// In any case, the client won't currently use it. // In any case, the client won't currently use it.
@ -141,26 +159,21 @@ where
/// Create an existing client from description /// Create an existing client from description
pub fn existing_client_from_description( pub fn existing_client_from_description(
shmem_provider: &Rc<RefCell<SP>>,
description: &LlmpClientDescription, description: &LlmpClientDescription,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
Ok(Self { Ok(Self {
stats: None, stats: None,
llmp: llmp::LlmpConnection::existing_client_from_description(description)?, llmp: llmp::LlmpConnection::existing_client_from_description(
shmem_provider,
description,
)?,
// Inserting a nop-stats element here so rust won't complain. // Inserting a nop-stats element here so rust won't complain.
// In any case, the client won't currently use it. // In any case, the client won't currently use it.
phantom: PhantomData, phantom: PhantomData,
}) })
} }
/// A client on an existing map
pub fn for_client(client: LlmpClient<SH>) -> Self {
Self {
stats: None,
llmp: llmp::LlmpConnection::IsClient { client },
phantom: PhantomData,
}
}
/// Write the config for a client eventmgr to env vars, a new client can reattach using existing_client_from_env /// Write the config for a client eventmgr to env vars, a new client can reattach using existing_client_from_env
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub fn to_env(&self, env_name: &str) { pub fn to_env(&self, env_name: &str) {
@ -309,29 +322,11 @@ where
} }
} }
#[cfg(all(feature = "std", unix))] impl<I, S, SP, ST> EventManager<I, S> for LlmpEventManager<I, S, SP, ST>
impl<I, S, SH, ST> LlmpEventManager<I, S, SH, ST>
where where
I: Input, I: Input,
S: IfInteresting<I>, S: IfInteresting<I>,
SH: ShMem, SP: ShMemProvider,
ST: Stats,
{
#[cfg(all(feature = "std", unix))]
pub fn new_on_domain_socket(stats: ST, filename: &str) -> Result<Self, Error> {
Ok(Self {
stats: Some(stats),
llmp: llmp::LlmpConnection::on_domain_socket(filename)?,
phantom: PhantomData,
})
}
}
impl<I, S, SH, ST> EventManager<I, S> for LlmpEventManager<I, S, SH, ST>
where
I: Input,
S: IfInteresting<I>,
SH: ShMem,
ST: Stats, //CE: CustomEvent<I>, ST: Stats, //CE: CustomEvent<I>,
{ {
/// The llmp client needs to wait until a broker mapped all pages, before shutting down. /// The llmp client needs to wait until a broker mapped all pages, before shutting down.
@ -388,14 +383,14 @@ where
/// Serialize the current state and corpus during an executiont to bytes. /// Serialize the current state and corpus during an executiont to bytes.
/// On top, add the current llmp event manager instance to be restored /// 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. /// This method is needed when the fuzzer run crashes and has to restart.
pub fn serialize_state_mgr<I, S, SH, ST>( pub fn serialize_state_mgr<I, S, SP, ST>(
state: &S, state: &S,
mgr: &LlmpEventManager<I, S, SH, ST>, mgr: &LlmpEventManager<I, S, SP, ST>,
) -> Result<Vec<u8>, Error> ) -> Result<Vec<u8>, Error>
where where
I: Input, I: Input,
S: Serialize + IfInteresting<I>, S: Serialize + IfInteresting<I>,
SH: ShMem, SP: ShMemProvider,
ST: Stats, ST: Stats,
{ {
Ok(postcard::to_allocvec(&(&state, &mgr.describe()?))?) Ok(postcard::to_allocvec(&(&state, &mgr.describe()?))?)
@ -403,43 +398,44 @@ where
/// Deserialize the state and corpus tuple, previously serialized with `serialize_state_corpus(...)` /// Deserialize the state and corpus tuple, previously serialized with `serialize_state_corpus(...)`
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
pub fn deserialize_state_mgr<I, S, SH, ST>( pub fn deserialize_state_mgr<I, S, SP, ST>(
shmem_provider: &Rc<RefCell<SP>>,
state_corpus_serialized: &[u8], state_corpus_serialized: &[u8],
) -> Result<(S, LlmpEventManager<I, S, SH, ST>), Error> ) -> Result<(S, LlmpEventManager<I, S, SP, ST>), Error>
where where
I: Input, I: Input,
S: DeserializeOwned + IfInteresting<I>, S: DeserializeOwned + IfInteresting<I>,
SH: ShMem, SP: ShMemProvider,
ST: Stats, ST: Stats,
{ {
let tuple: (S, _) = postcard::from_bytes(&state_corpus_serialized)?; let tuple: (S, _) = postcard::from_bytes(&state_corpus_serialized)?;
Ok(( Ok((
tuple.0, tuple.0,
LlmpEventManager::existing_client_from_description(&tuple.1)?, LlmpEventManager::existing_client_from_description(shmem_provider, &tuple.1)?,
)) ))
} }
/// A manager that can restart on the fly, storing states in-between (in `on_resatrt`) /// A manager that can restart on the fly, storing states in-between (in `on_resatrt`)
#[derive(Clone, Debug)] #[derive(Debug)]
pub struct LlmpRestartingEventManager<I, S, SH, ST> pub struct LlmpRestartingEventManager<I, S, SP, ST>
where where
I: Input, I: Input,
S: IfInteresting<I>, S: IfInteresting<I>,
SH: ShMem, SP: ShMemProvider + 'static,
ST: Stats, ST: Stats,
//CE: CustomEvent<I>, //CE: CustomEvent<I>,
{ {
/// The embedded llmp event manager /// The embedded llmp event manager
llmp_mgr: LlmpEventManager<I, S, SH, ST>, llmp_mgr: LlmpEventManager<I, S, SP, ST>,
/// The sender to serialize the state for the next runner /// The sender to serialize the state for the next runner
sender: LlmpSender<SH>, sender: LlmpSender<SP>,
} }
impl<I, S, SH, ST> EventManager<I, S> for LlmpRestartingEventManager<I, S, SH, ST> impl<I, S, SP, ST> EventManager<I, S> for LlmpRestartingEventManager<I, S, SP, ST>
where where
I: Input, I: Input,
S: IfInteresting<I> + Serialize, S: IfInteresting<I> + Serialize,
SH: ShMem, SP: ShMemProvider,
ST: Stats, //CE: CustomEvent<I>, ST: Stats, //CE: CustomEvent<I>,
{ {
/// The llmp client needs to wait until a broker mapped all pages, before shutting down. /// The llmp client needs to wait until a broker mapped all pages, before shutting down.
@ -484,29 +480,53 @@ const _ENV_FUZZER_RECEIVER: &str = &"_AFL_ENV_FUZZER_RECEIVER";
/// The llmp (2 way) connection from a fuzzer to the broker (broadcasting all other fuzzer messages) /// The llmp (2 way) connection from a fuzzer to the broker (broadcasting all other fuzzer messages)
const _ENV_FUZZER_BROKER_CLIENT_INITIAL: &str = &"_AFL_ENV_FUZZER_BROKER_CLIENT"; const _ENV_FUZZER_BROKER_CLIENT_INITIAL: &str = &"_AFL_ENV_FUZZER_BROKER_CLIENT";
impl<I, S, SH, ST> LlmpRestartingEventManager<I, S, SH, ST> impl<I, S, SP, ST> LlmpRestartingEventManager<I, S, SP, ST>
where where
I: Input, I: Input,
S: IfInteresting<I>, S: IfInteresting<I>,
SH: ShMem, SP: ShMemProvider,
ST: Stats, //CE: CustomEvent<I>, ST: Stats, //CE: CustomEvent<I>,
{ {
/// Create a new runner, the executed child doing the actual fuzzing. /// Create a new runner, the executed child doing the actual fuzzing.
pub fn new(llmp_mgr: LlmpEventManager<I, S, SH, ST>, sender: LlmpSender<SH>) -> Self { pub fn new(llmp_mgr: LlmpEventManager<I, S, SP, ST>, sender: LlmpSender<SP>) -> Self {
Self { llmp_mgr, sender } Self { llmp_mgr, sender }
} }
/// Get the sender /// Get the sender
pub fn sender(&self) -> &LlmpSender<SH> { pub fn sender(&self) -> &LlmpSender<SP> {
&self.sender &self.sender
} }
/// Get the sender (mut) /// Get the sender (mut)
pub fn sender_mut(&mut self) -> &mut LlmpSender<SH> { pub fn sender_mut(&mut self) -> &mut LlmpSender<SP> {
&mut self.sender &mut self.sender
} }
} }
#[cfg(feature = "std")]
#[allow(clippy::type_complexity)]
pub fn setup_restarting_mgr_std<I, S, ST>(
//mgr: &mut LlmpEventManager<I, S, SH, ST>,
stats: ST,
broker_port: u16,
) -> Result<
(
Option<S>,
LlmpRestartingEventManager<I, S, StdShMemProvider, ST>,
),
Error,
>
where
I: Input,
S: DeserializeOwned + IfInteresting<I>,
ST: Stats,
{
#[cfg(target_os = "android")]
AshmemService::start().expect("Error starting Ashmem Service");
setup_restarting_mgr(StdShMemProvider::new(), stats, broker_port)
}
/// A restarting state is a combination of restarter and runner, that can be used on systems without `fork`. /// A restarting state is a combination of restarter and runner, that can be used on systems without `fork`.
/// The restarter will start a new process each time the child crashes or timeouts. /// The restarter will start a new process each time the child crashes or timeouts.
#[cfg(feature = "std")] #[cfg(feature = "std")]
@ -515,30 +535,25 @@ where
clippy::type_complexity, clippy::type_complexity,
clippy::similar_names clippy::similar_names
)] // for { mgr = LlmpEventManager... } )] // for { mgr = LlmpEventManager... }
pub fn setup_restarting_mgr<I, S, SH, ST>( pub fn setup_restarting_mgr<I, S, SP, ST>(
shmem_provider: SP,
//mgr: &mut LlmpEventManager<I, S, SH, ST>, //mgr: &mut LlmpEventManager<I, S, SH, ST>,
stats: ST, stats: ST,
broker_port: u16, broker_port: u16,
) -> Result<(Option<S>, LlmpRestartingEventManager<I, S, SH, ST>), Error> ) -> Result<(Option<S>, LlmpRestartingEventManager<I, S, SP, ST>), Error>
where where
I: Input, I: Input,
S: DeserializeOwned + IfInteresting<I>, S: DeserializeOwned + IfInteresting<I>,
SH: ShMem, SP: ShMemProvider,
ST: Stats, ST: Stats,
{ {
let mut mgr; let shmem_provider = Rc::new(RefCell::new(shmem_provider));
let mut mgr =
LlmpEventManager::<I, S, SP, ST>::new_on_port(&shmem_provider, stats, broker_port)?;
// We start ourself as child process to actually fuzz // We start ourself as child process to actually fuzz
let (sender, mut receiver) = if std::env::var(_ENV_FUZZER_SENDER).is_err() { let (sender, mut receiver, shmem_provider) = if std::env::var(_ENV_FUZZER_SENDER).is_err() {
#[cfg(target_os = "android")]
{
mgr = LlmpEventManager::<I, S, SH, ST>::new_on_domain_socket(stats, "\x00llmp_socket")?;
};
#[cfg(not(target_os = "android"))]
{
mgr = LlmpEventManager::<I, S, SH, ST>::new_on_port(stats, broker_port)?
};
if mgr.is_broker() { if mgr.is_broker() {
// Yep, broker. Just loop here. // Yep, broker. Just loop here.
println!("Doing broker things. Run this tool again to start fuzzing in a client."); println!("Doing broker things. Run this tool again to start fuzzing in a client.");
@ -550,11 +565,14 @@ where
mgr.to_env(_ENV_FUZZER_BROKER_CLIENT_INITIAL); mgr.to_env(_ENV_FUZZER_BROKER_CLIENT_INITIAL);
// First, create a channel from the fuzzer (sender) to us (receiver) to report its state for restarts. // First, create a channel from the fuzzer (sender) to us (receiver) to report its state for restarts.
let sender = LlmpSender::new(0, false)?; let sender = { LlmpSender::new(&shmem_provider, 0, false)? };
let receiver = LlmpReceiver::on_existing_map(
SH::clone_ref(&sender.out_maps.last().unwrap().shmem)?, let map = {
None, shmem_provider
)?; .borrow_mut()
.clone_ref(&sender.out_maps.last().unwrap().shmem)?
};
let receiver = LlmpReceiver::on_existing_map(shmem_provider.clone(), map, None)?;
// Store the information to a map. // Store the information to a map.
sender.to_env(_ENV_FUZZER_SENDER)?; sender.to_env(_ENV_FUZZER_SENDER)?;
receiver.to_env(_ENV_FUZZER_RECEIVER)?; receiver.to_env(_ENV_FUZZER_RECEIVER)?;
@ -568,7 +586,7 @@ where
#[cfg(unix)] #[cfg(unix)]
let _ = match unsafe { fork() }? { let _ = match unsafe { fork() }? {
ForkResult::Parent(handle) => handle.status(), ForkResult::Parent(handle) => handle.status(),
ForkResult::Child => break (sender, receiver), ForkResult::Child => break (sender, receiver, shmem_provider),
}; };
// On windows, we spawn ourself again // On windows, we spawn ourself again
@ -585,20 +603,29 @@ where
} else { } else {
// We are the newly started fuzzing instance, first, connect to our own restore map. // We are the newly started fuzzing instance, first, connect to our own restore map.
// A sender and a receiver for single communication // A sender and a receiver for single communication
// Clone so we get a new connection to the AshmemServer if we are using
// ServedShMemProvider
let shmem_provider = Rc::new(RefCell::new(shmem_provider.borrow_mut().clone()));
( (
LlmpSender::<SH>::on_existing_from_env(_ENV_FUZZER_SENDER)?, LlmpSender::on_existing_from_env(&shmem_provider, _ENV_FUZZER_SENDER)?,
LlmpReceiver::<SH>::on_existing_from_env(_ENV_FUZZER_RECEIVER)?, LlmpReceiver::on_existing_from_env(&shmem_provider, _ENV_FUZZER_RECEIVER)?,
shmem_provider,
) )
}; };
println!("We're a client, let's fuzz :)"); println!("We're a client, let's fuzz :)");
for (var, val) in std::env::vars() {
println!("ENV VARS: {:?}: {:?}", var, val);
}
// If we're restarting, deserialize the old state. // If we're restarting, deserialize the old state.
let (state, mut mgr) = match receiver.recv_buf()? { let (state, mut mgr) = match receiver.recv_buf()? {
None => { None => {
println!("First run. Let's set it all up"); println!("First run. Let's set it all up");
// Mgr to send and receive msgs from/to all other fuzzer instances // Mgr to send and receive msgs from/to all other fuzzer instances
let client_mgr = LlmpEventManager::<I, S, SH, ST>::existing_client_from_env( let client_mgr = LlmpEventManager::<I, S, SP, ST>::existing_client_from_env(
&shmem_provider,
_ENV_FUZZER_BROKER_CLIENT_INITIAL, _ENV_FUZZER_BROKER_CLIENT_INITIAL,
)?; )?;
@ -607,7 +634,8 @@ where
// Restoring from a previous run, deserialize state and corpus. // Restoring from a previous run, deserialize state and corpus.
Some((_sender, _tag, msg)) => { Some((_sender, _tag, msg)) => {
println!("Subsequent run. Let's load all data from shmem (received {} bytes from previous instance)", msg.len()); println!("Subsequent run. Let's load all data from shmem (received {} bytes from previous instance)", msg.len());
let (state, mgr): (S, LlmpEventManager<I, S, SH, ST>) = deserialize_state_mgr(&msg)?; let (state, mgr): (S, LlmpEventManager<I, S, SP, ST>) =
deserialize_state_mgr(&shmem_provider, &msg)?;
(Some(state), LlmpRestartingEventManager::new(mgr, sender)) (Some(state), LlmpRestartingEventManager::new(mgr, sender))
} }