diff --git a/fuzzers/frida_libpng/src/fuzzer.rs b/fuzzers/frida_libpng/src/fuzzer.rs index b9cc649bbc..1c816c16e1 100644 --- a/fuzzers/frida_libpng/src/fuzzer.rs +++ b/fuzzers/frida_libpng/src/fuzzer.rs @@ -2,15 +2,12 @@ //! The example harness is built for libpng. use libafl::{ - bolts::{ - shmem::UnixShMem, - tuples::{tuple_list, Named}, - }, + bolts::tuples::{tuple_list, Named}, corpus::{ Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus, QueueCorpusScheduler, }, - events::{setup_restarting_mgr, EventManager}, + events::{setup_restarting_mgr_std, EventManager}, executors::{inprocess::InProcessExecutor, Executor, ExitKind, HasObservers}, feedbacks::{CrashFeedback, MaxMapFeedback}, fuzzer::{Fuzzer, StdFuzzer}, @@ -25,6 +22,7 @@ use libafl::{ Error, }; +use core::cell::RefCell; #[cfg(target_arch = "x86_64")] use frida_gum::instruction_writer::X86Register; #[cfg(target_arch = "aarch64")] @@ -34,8 +32,7 @@ use frida_gum::{ stalker::{NoneEventSink, Stalker, Transformer}, }; use frida_gum::{Gum, MemoryRange, Module, NativePointer, PageProtection}; - -use std::{cell::RefCell, env, ffi::c_void, path::PathBuf}; +use std::{env, ffi::c_void, path::PathBuf}; /// An helper that feeds FridaInProcessExecutor with user-supplied instrumentation 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. let (state, mut restarting_mgr) = - match setup_restarting_mgr::<_, _, UnixShMem, _>(stats, broker_port) { + match setup_restarting_mgr_std(stats, broker_port) { Ok(res) => res, Err(err) => match err { Error::ShuttingDown => { diff --git a/fuzzers/libfuzzer_libmozjpeg/src/lib.rs b/fuzzers/libfuzzer_libmozjpeg/src/lib.rs index 14d8556eb5..6c42c3eb47 100644 --- a/fuzzers/libfuzzer_libmozjpeg/src/lib.rs +++ b/fuzzers/libfuzzer_libmozjpeg/src/lib.rs @@ -4,9 +4,9 @@ use std::{env, path::PathBuf}; use libafl::{ - bolts::{shmem::UnixShMem, tuples::tuple_list}, + bolts::tuples::tuple_list, corpus::{Corpus, InMemoryCorpus, OnDiskCorpus, RandCorpusScheduler}, - events::setup_restarting_mgr, + events::setup_restarting_mgr_std, executors::{inprocess::InProcessExecutor, ExitKind}, feedbacks::{CrashFeedback, MaxMapFeedback}, fuzzer::{Fuzzer, StdFuzzer}, @@ -20,9 +20,11 @@ use libafl::{ 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" { static mut libafl_alloc_map: [usize; ALLOC_MAP_SIZE]; } @@ -53,17 +55,19 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // The restarting state will spawn the same process again as child, then restarted it each time it crashes. 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()); // 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 let cmps_observer = StdMapObserver::new("cmps", unsafe { &mut CMP_MAP }, CMP_MAP_SIZE); // 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 let mut state = state.unwrap_or_else(|| { diff --git a/fuzzers/libfuzzer_libpng/src/lib.rs b/fuzzers/libfuzzer_libpng/src/lib.rs index 92cd079c24..0a4a661e9c 100644 --- a/fuzzers/libfuzzer_libpng/src/lib.rs +++ b/fuzzers/libfuzzer_libpng/src/lib.rs @@ -5,12 +5,12 @@ use core::time::Duration; use std::{env, path::PathBuf}; use libafl::{ - bolts::{shmem::StdShMem, tuples::tuple_list}, + bolts::tuples::tuple_list, corpus::{ Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus, QueueCorpusScheduler, }, - events::setup_restarting_mgr, + events::setup_restarting_mgr_std, executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor}, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, @@ -52,7 +52,7 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // The restarting state will spawn the same process again as child, then restarted it each time it crashes. let (state, mut restarting_mgr) = - match setup_restarting_mgr::<_, _, StdShMem, _>(stats, broker_port) { + match setup_restarting_mgr_std(stats, broker_port) { Ok(res) => res, Err(err) => match err { Error::ShuttingDown => { diff --git a/libafl/examples/llmp_test/main.rs b/libafl/examples/llmp_test/main.rs index fcb03d3872..eda837b958 100644 --- a/libafl/examples/llmp_test/main.rs +++ b/libafl/examples/llmp_test/main.rs @@ -3,15 +3,19 @@ This shows how llmp can be used directly, without libafl abstractions */ extern crate alloc; +use alloc::rc::Rc; #[cfg(all(unix, feature = "std"))] -use core::{convert::TryInto, time::Duration}; +use core::{cell::RefCell, convert::TryInto, time::Duration}; #[cfg(all(unix, feature = "std"))] use std::{thread, time}; use libafl::bolts::llmp::Tag; #[cfg(all(unix, feature = "std"))] use libafl::{ - bolts::{llmp, shmem::UnixShMem}, + bolts::{ + llmp, + shmem::{ShMemProvider, StdShMemProvider}, + }, Error, }; @@ -21,7 +25,8 @@ const _TAG_1MEG_V1: Tag = 0xB1111161; #[cfg(all(unix, feature = "std"))] fn adder_loop(port: u16) -> ! { - let mut client = llmp::LlmpClient::::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 current_result: u32 = 0; loop { @@ -63,7 +68,11 @@ fn adder_loop(port: u16) -> ! { #[cfg(all(unix, feature = "std"))] fn large_msg_loop(port: u16) -> ! { - let mut client = llmp::LlmpClient::::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]; @@ -124,7 +133,8 @@ fn main() { match mode.as_str() { "broker" => { - let mut broker = llmp::LlmpBroker::::new().unwrap(); + let mut broker = + llmp::LlmpBroker::new(&Rc::new(RefCell::new(StdShMemProvider::new()))).unwrap(); broker .launch_listener(llmp::Listener::Tcp( 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))) } "ctr" => { - let mut client = llmp::LlmpClient::::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; loop { counter = counter.wrapping_add(1); diff --git a/libafl/src/bolts/llmp.rs b/libafl/src/bolts/llmp.rs index af745e6d2c..2cbadc4655 100644 --- a/libafl/src/bolts/llmp.rs +++ b/libafl/src/bolts/llmp.rs @@ -52,8 +52,9 @@ Then register some clientloops using llmp_broker_register_threaded_clientloop */ -use alloc::{string::String, vec::Vec}; +use alloc::{rc::Rc, string::String, vec::Vec}; use core::{ + cell::RefCell, cmp::max, fmt::Debug, mem::size_of, @@ -64,38 +65,19 @@ use core::{ use serde::{Deserialize, Serialize}; #[cfg(feature = "std")] use std::{ - env, fs, + env, io::{Read, Write}, net::{SocketAddr, TcpListener, TcpStream}, thread, }; -#[cfg(all(feature = "std", unix))] -use nix::{ - cmsg_space, - sys::{ - socket::{recvmsg, sendmsg, ControlMessage, ControlMessageOwned, MsgFlags}, - uio::IoVec, - }, -}; - -#[cfg(all(feature = "std", unix))] -use std::os::unix::{ - self, - net::{UnixListener, UnixStream}, - {io::AsRawFd, prelude::RawFd}, -}; - #[cfg(all(feature = "llmp_debug", feature = "std"))] use backtrace::Backtrace; -#[cfg(all(unix, feature = "std"))] -use uds::{UnixListenerExt, UnixSocketAddr, UnixStreamExt}; - #[cfg(unix)] use crate::bolts::os::unix_signals::{c_void, setup_signal_handler, siginfo_t, Handler, Signal}; use crate::{ - bolts::shmem::{ShMem, ShMemDescription}, + bolts::shmem::{ShMem, ShMemDescription, ShMemId, ShMemProvider}, Error, }; @@ -146,15 +128,11 @@ pub type Tag = u32; #[cfg(feature = "std")] pub enum Listener { Tcp(TcpListener), - #[cfg(unix)] - Unix(UnixListener), } #[cfg(feature = "std")] pub enum ListenerStream { Tcp(TcpStream, SocketAddr), - #[cfg(unix)] - Unix(UnixStream, unix::net::SocketAddr), Empty(), } @@ -169,27 +147,19 @@ impl Listener { ListenerStream::Empty() } }, - #[cfg(unix)] - Listener::Unix(inner) => match inner.accept() { - Ok(res) => ListenerStream::Unix(res.0, res.1), - Err(err) => { - dbg!("Ignoring failed accept", err); - ListenerStream::Empty() - } - }, } } } /// Get sharedmem from a page #[inline] -unsafe fn shmem2page_mut(afl_shmem: &mut SH) -> *mut LlmpPage { +unsafe fn shmem2page_mut(afl_shmem: &mut SHM) -> *mut LlmpPage { afl_shmem.map_mut().as_mut_ptr() as *mut LlmpPage } /// Get sharedmem from a page #[inline] -unsafe fn shmem2page(afl_shmem: &SH) -> *const LlmpPage { +unsafe fn shmem2page(afl_shmem: &SHM) -> *const LlmpPage { afl_shmem.map().as_ptr() as *const LlmpPage } @@ -244,7 +214,7 @@ fn new_map_size(max_alloc: usize) -> usize { /// Initialize a new llmp_page. size should be relative to /// llmp_page->messages -unsafe fn _llmp_page_init(shmem: &mut SH, sender: u32, allow_reinit: bool) { +unsafe fn _llmp_page_init(shmem: &mut SHM, sender: u32, allow_reinit: bool) { let map_size = shmem.map().len(); let page = shmem2page_mut(shmem); if (*page).magic == PAGE_INITIALIZED_MAGIC && !allow_reinit { @@ -268,8 +238,8 @@ unsafe fn _llmp_page_init(shmem: &mut SH, sender: u32, allow_reinit: /// Get the next pointer and make sure it's in the current page, and has enough space. #[inline] -unsafe fn llmp_next_msg_ptr_checked( - map: &mut LlmpSharedMap, +unsafe fn llmp_next_msg_ptr_checked( + map: &mut LlmpSharedMap, last_msg: *const LlmpMsg, alloc_size: usize, ) -> Result<*mut LlmpMsg, Error> { @@ -347,7 +317,7 @@ impl LlmpMsg { /// Gets the buffer from this message as slice, with the corrent length. #[inline] - pub fn as_slice(&self, map: &mut LlmpSharedMap) -> Result<&[u8], Error> { + pub fn as_slice(&self, map: &mut LlmpSharedMap) -> Result<&[u8], Error> { unsafe { if self.in_map(map) { Ok(self.as_slice_unsafe()) @@ -359,7 +329,7 @@ impl LlmpMsg { /// Returns true, if the pointer is, indeed, in the page of this shared map. #[inline] - pub fn in_map(&self, map: &mut LlmpSharedMap) -> bool { + pub fn in_map(&self, map: &mut LlmpSharedMap) -> bool { unsafe { let map_size = map.shmem.map().len(); let buf_ptr = self.buf.as_ptr(); @@ -378,39 +348,40 @@ impl LlmpMsg { } /// An Llmp instance -#[derive(Clone, Debug)] -pub enum LlmpConnection +#[derive(Debug)] +pub enum LlmpConnection where - SH: ShMem, + SP: ShMemProvider + 'static, { /// A broker and a thread using this tcp background thread - IsBroker { broker: LlmpBroker }, + IsBroker { broker: LlmpBroker }, /// A client, connected to the port - IsClient { client: LlmpClient }, + IsClient { client: LlmpClient }, } -impl LlmpConnection +impl LlmpConnection where - SH: ShMem, + SP: ShMemProvider, { #[cfg(feature = "std")] /// Creates either a broker, if the tcp port is not bound, or a client, connected to this port. - pub fn on_port(port: u16) -> Result { + pub fn on_port(shmem_provider: &Rc>, port: u16) -> Result { match TcpListener::bind(format!("127.0.0.1:{}", port)) { Ok(listener) => { // We got the port. We are the broker! :) dbg!("We're the broker"); - let mut broker = LlmpBroker::new()?; + let mut broker = LlmpBroker::new(shmem_provider)?; let _listener_thread = broker.launch_listener(Listener::Tcp(listener))?; Ok(LlmpConnection::IsBroker { broker }) } Err(e) => { + println!("error: {:?}", e); match e.kind() { std::io::ErrorKind::AddrInUse => { // We are the client :) dbg!("We're the client", e); Ok(LlmpConnection::IsClient { - client: LlmpClient::create_attach_to_tcp(port)?, + client: LlmpClient::create_attach_to_tcp(shmem_provider, port)?, }) } _ => Err(Error::File(e)), @@ -419,6 +390,12 @@ where } } + pub fn shmem_provider(&mut self) -> &Rc> { + match self { + LlmpConnection::IsBroker { broker } => &broker.shmem_provider, + LlmpConnection::IsClient { client } => &client.shmem_provider, + } + } /// Describe this in a reproducable fashion, if it's a client pub fn describe(&self) -> Result { Ok(match self { @@ -429,10 +406,11 @@ where /// Recreate an existing client from the stored description pub fn existing_client_from_description( + shmem_provider: &Rc>, description: &LlmpClientDescription, - ) -> Result, Error> { + ) -> Result, Error> { Ok(LlmpConnection::IsClient { - client: LlmpClient::existing_client_from_description(description)?, + client: LlmpClient::existing_client_from_description(shmem_provider, description)?, }) } @@ -445,37 +423,6 @@ where } } -#[cfg(all(unix, feature = "std"))] -impl LlmpConnection -where - SH: ShMem, -{ - #[cfg(all(feature = "std", unix))] - pub fn on_domain_socket(filename: &str) -> Result { - match UnixListener::bind_unix_addr(&UnixSocketAddr::new(filename)?) { - Ok(listener) => { - dbg!("We're the broker"); - let mut broker = LlmpBroker::new()?; - broker.socket_name = Some(filename.to_string()); - let _listener_thread = broker.launch_listener(Listener::Unix(listener))?; - Ok(LlmpConnection::IsBroker { broker }) - } - Err(e) => { - match e.kind() { - std::io::ErrorKind::AddrInUse => { - // We are the client :) - dbg!("We're the client", e); - Ok(LlmpConnection::IsClient { - client: LlmpClient::create_attach_to_unix(filename)?, - }) - } - _ => Err(Error::File(e)), - } - } - } - } -} - /// Contents of the share mem pages, used by llmp internally #[derive(Copy, Clone, Debug)] #[repr(C, packed)] @@ -516,10 +463,10 @@ struct LlmpPayloadSharedMapInfo { } /// Sending end on a (unidirectional) sharedmap channel -#[derive(Clone, Debug)] -pub struct LlmpSender +#[derive(Debug)] +pub struct LlmpSender where - SH: ShMem, + SP: ShMemProvider, { /// ID of this sender. Only used in the broker. pub id: u32, @@ -527,29 +474,37 @@ where /// If null, a new page (just) started. pub last_msg_sent: *const LlmpMsg, /// A vec of page wrappers, each containing an intialized AfShmem - pub out_maps: Vec>, + pub out_maps: Vec>, /// If true, pages will never be pruned. /// The broker uses this feature. /// By keeping the message history around, /// new clients may join at any time in the future. pub keep_pages_forever: bool, + shmem_provider: Rc>, } -/// An actor on the sendin part of the shared map -impl LlmpSender +/// An actor on the sending part of the shared map +impl LlmpSender where - SH: ShMem, + SP: ShMemProvider, { - pub fn new(id: u32, keep_pages_forever: bool) -> Result { + pub fn new( + shmem_provider: &Rc>, + id: u32, + keep_pages_forever: bool, + ) -> Result { Ok(Self { id, last_msg_sent: ptr::null_mut(), out_maps: vec![LlmpSharedMap::new( 0, - SH::new_map(LLMP_CFG_INITIAL_MAP_SIZE)?, + shmem_provider + .borrow_mut() + .new_map(LLMP_CFG_INITIAL_MAP_SIZE)?, )], // drop pages to the broker if it already read them keep_pages_forever, + shmem_provider: shmem_provider.clone(), }) } @@ -565,9 +520,16 @@ where /// Reattach to a vacant out_map, to with a previous sender stored the information in an env before. #[cfg(feature = "std")] - pub fn on_existing_from_env(env_name: &str) -> Result { + pub fn on_existing_from_env( + shmem_provider: &Rc>, + env_name: &str, + ) -> Result { let msg_sent_offset = msg_offset_from_env(env_name)?; - Self::on_existing_map(SH::existing_from_env(env_name)?, msg_sent_offset) + Self::on_existing_map( + shmem_provider.clone(), + shmem_provider.borrow_mut().existing_from_env(env_name)?, + msg_sent_offset, + ) } /// Store the info to this sender to env. @@ -603,7 +565,8 @@ where /// It is essential, that the receiver (or someone else) keeps a pointer to this map /// else reattach will get a new, empty page, from the OS, or fail. pub fn on_existing_map( - current_out_map: SH, + shmem_provider: Rc>, + current_out_map: SP::Mem, last_msg_sent_offset: Option, ) -> Result { let mut out_map = LlmpSharedMap::existing(current_out_map); @@ -618,6 +581,7 @@ where out_maps: vec![out_map], // drop pages to the broker if it already read them keep_pages_forever: false, + shmem_provider, }) } @@ -710,6 +674,8 @@ where #[cfg(all(feature = "llmp_debug", feature = "std"))] dbg!( + page, + (*page), (*page).size_used, complete_msg_size, EOP_MSG_SIZE, @@ -814,8 +780,8 @@ where let shm = self.out_maps.last().unwrap(); println!( "LLMP_DEBUG: End of page reached for map {} with len {}, sending EOP, bt: {:?}", - shm.shmem.shm_str(), - shm.shmem.map().len(), + shm.shmem.id().to_string(), + shm.shmem.len(), bt ); } @@ -828,7 +794,9 @@ where // Create a new shard page. let mut new_map_shmem = LlmpSharedMap::new( (*old_map).sender, - SH::new_map(new_map_size((*old_map).max_alloc_size))?, + self.shmem_provider + .borrow_mut() + .new_map(new_map_size((*old_map).max_alloc_size))?, ); let mut new_map = new_map_shmem.page_mut(); @@ -847,8 +815,8 @@ where (*out).sender = (*old_map).sender; let mut end_of_page_msg = (*out).buf.as_mut_ptr() as *mut LlmpPayloadSharedMapInfo; - (*end_of_page_msg).map_size = new_map_shmem.shmem.map().len(); - (*end_of_page_msg).shm_str = *new_map_shmem.shmem.shm_slice(); + (*end_of_page_msg).map_size = new_map_shmem.shmem.len(); + (*end_of_page_msg).shm_str = *new_map_shmem.shmem.id().as_slice(); /* Send the last msg on the old buf */ self.send(out)?; @@ -940,37 +908,49 @@ where } // Create this client on an existing map from the given description. acquired with `self.describe` - pub fn on_existing_from_description(description: &LlmpDescription) -> Result { + pub fn on_existing_from_description( + shmem_provider: &Rc>, + description: &LlmpDescription, + ) -> Result { Self::on_existing_map( - SH::existing_from_description(&description.shmem)?, + shmem_provider.clone(), + shmem_provider + .borrow_mut() + .from_description(description.shmem)?, description.last_message_offset, ) } } /// Receiving end on a (unidirectional) sharedmap channel -#[derive(Clone, Debug)] -pub struct LlmpReceiver +#[derive(Debug)] +pub struct LlmpReceiver where - SH: ShMem, + SP: ShMemProvider, { pub id: u32, /// Pointer to the last meg this received pub last_msg_recvd: *const LlmpMsg, + /// The shmem provider + pub shmem_provider: Rc>, /// current page. After EOP, this gets replaced with the new one - pub current_recv_map: LlmpSharedMap, + pub current_recv_map: LlmpSharedMap, } /// Receiving end of an llmp channel -impl LlmpReceiver +impl LlmpReceiver where - SH: ShMem, + SP: ShMemProvider, { /// Reattach to a vacant recv_map, to with a previous sender stored the information in an env before. #[cfg(feature = "std")] - pub fn on_existing_from_env(env_name: &str) -> Result { + pub fn on_existing_from_env( + shmem_provider: &Rc>, + env_name: &str, + ) -> Result { Self::on_existing_map( - SH::existing_from_env(env_name)?, + shmem_provider.clone(), + shmem_provider.borrow_mut().existing_from_env(env_name)?, msg_offset_from_env(env_name)?, ) } @@ -988,7 +968,8 @@ where /// It is essential, that the sender (or someone else) keeps a pointer to the sender_map /// else reattach will get a new, empty page, from the OS, or fail. pub fn on_existing_map( - current_sender_map: SH, + shmem_provider: Rc>, + current_sender_map: SP::Mem, last_msg_recvd_offset: Option, ) -> Result { let mut current_recv_map = LlmpSharedMap::existing(current_sender_map); @@ -1001,6 +982,7 @@ where id: 0, current_recv_map, last_msg_recvd, + shmem_provider, }) } @@ -1071,10 +1053,12 @@ where ptr::write_volatile(&mut (*page).save_to_unmap, 1); // Map the new page. The old one should be unmapped by Drop - self.current_recv_map = LlmpSharedMap::existing(SH::existing_from_shm_slice( - &pageinfo_cpy.shm_str, - pageinfo_cpy.map_size, - )?); + self.current_recv_map = LlmpSharedMap::existing( + self.shmem_provider.borrow_mut().from_id_and_size( + ShMemId::from_slice(&pageinfo_cpy.shm_str), + pageinfo_cpy.map_size, + )?, + ); page = self.current_recv_map.page_mut(); // Mark the new page save to unmap also (it's mapped by us, the broker now) ptr::write_volatile(&mut (*page).save_to_unmap, 1); @@ -1082,8 +1066,8 @@ where #[cfg(all(feature = "llmp_debug", feature = "std"))] println!( "LLMP_DEBUG: Got a new recv map {} with len {:?}", - self.current_recv_map.shmem.shm_str(), - self.current_recv_map.shmem.map().len() + self.current_recv_map.shmem.id().to_string(), + self.current_recv_map.shmem.len() ); // After we mapped the new page, return the next message, if available return self.recv(); @@ -1166,9 +1150,15 @@ where } // Create this client on an existing map from the given description. acquired with `self.describe` - pub fn on_existing_from_description(description: &LlmpDescription) -> Result { + pub fn on_existing_from_description( + shmem_provider: &Rc>, + description: &LlmpDescription, + ) -> Result { Self::on_existing_map( - SH::existing_from_description(&description.shmem)?, + shmem_provider.clone(), + shmem_provider + .borrow_mut() + .from_description(description.shmem)?, description.last_message_offset, ) } @@ -1176,29 +1166,29 @@ where /// A page wrapper #[derive(Clone, Debug)] -pub struct LlmpSharedMap +pub struct LlmpSharedMap where - SH: ShMem, + SHM: ShMem, { /// Shmem containg the actual (unsafe) page, /// shared between one LlmpSender and one LlmpReceiver - pub shmem: SH, + pub shmem: SHM, } // TODO: May be obsolete /// The page struct, placed on a shared mem instance. /// A thin wrapper around a ShMem implementation, with special Llmp funcs -impl LlmpSharedMap +impl LlmpSharedMap where - SH: ShMem, + SHM: ShMem, { /// Creates a new page, initializing the passed shared mem struct - pub fn new(sender: u32, mut new_map: SH) -> Self { + pub fn new(sender: u32, mut new_map: SHM) -> Self { #[cfg(all(feature = "llmp_debug", feature = "std"))] println!( "LLMP_DEBUG: Initializing map on {} with size {}", - new_map.shm_str(), - new_map.map().len() + new_map.id().to_string(), + new_map.len() ); unsafe { @@ -1208,7 +1198,7 @@ where } /// Maps and wraps an existing - pub fn existing(existing_map: SH) -> Self { + pub fn existing(existing_map: SHM) -> Self { #[cfg(all(feature = "llmp_debug", feature = "std"))] //{ //#[cfg(debug_assertions)] @@ -1217,8 +1207,8 @@ where //let bt = ""; dbg!( "LLMP_DEBUG: Using existing map {} with size {}", - existing_map.shm_str(), - existing_map.map().len(), + existing_map.id().to_string(), + existing_map.len(), //bt ); //} @@ -1321,22 +1311,24 @@ where } /// The broker (node 0) -#[derive(Clone, Debug)] -pub struct LlmpBroker +#[derive(Debug)] +pub struct LlmpBroker where - SH: ShMem, + SP: ShMemProvider + 'static, { /// Broadcast map from broker to all clients - pub llmp_out: LlmpSender, + pub llmp_out: LlmpSender, /// Users of Llmp can add message handlers in the broker. /// This allows us to intercept messages right in the broker /// This keeps the out map clean. - pub llmp_clients: Vec>, + pub llmp_clients: Vec>, /// This is the socket name, when unix domain sockets are used. socket_name: Option, /// This flag is used to indicate that shutdown has been requested by the SIGINT and SIGTERM /// handlers shutting_down: bool, + /// The ShMemProvider to use + shmem_provider: Rc>, } #[cfg(unix)] @@ -1357,27 +1349,30 @@ impl Handler for LlmpBrokerSignalHandler { /// The broker forwards all messages to its own bus-like broadcast map. /// It may intercept messages passing through. -impl LlmpBroker +impl LlmpBroker where - SH: ShMem, + SP: ShMemProvider, { /// Create and initialize a new llmp_broker - pub fn new() -> Result { - let broker = LlmpBroker { + pub fn new(shmem_provider: &Rc>) -> Result { + Ok(LlmpBroker { llmp_out: LlmpSender { id: 0, last_msg_sent: ptr::null_mut(), - out_maps: vec![LlmpSharedMap::new(0, SH::new_map(new_map_size(0))?)], + out_maps: vec![LlmpSharedMap::new( + 0, + shmem_provider.borrow_mut().new_map(new_map_size(0))?, + )], // Broker never cleans up the pages so that new // clients may join at any time keep_pages_forever: true, + shmem_provider: shmem_provider.clone(), }, llmp_clients: vec![], socket_name: None, shutting_down: false, - }; - - Ok(broker) + shmem_provider: shmem_provider.clone(), + }) } /// Allocate the next message on the outgoing map @@ -1387,7 +1382,7 @@ where /// Registers a new client for the given sharedmap str and size. /// Returns the id of the new client in broker.client_map - pub fn register_client(&mut self, mut client_page: LlmpSharedMap) { + pub fn register_client(&mut self, mut client_page: LlmpSharedMap) { // Tell the client it may unmap this page now. client_page.mark_save_to_unmap(); @@ -1396,6 +1391,7 @@ where id, current_recv_map: client_page, last_msg_recvd: ptr::null_mut(), + shmem_provider: self.shmem_provider.clone(), }); } @@ -1511,27 +1507,41 @@ where let client_out_map_mem = &self.llmp_out.out_maps.first().unwrap().shmem; let broadcast_map_description = postcard::to_allocvec(&client_out_map_mem.description())?; - let client_out_map_mem_fd: i32 = client_out_map_mem.shm_str().parse().unwrap(); let mut incoming_map_description_serialized = vec![0u8; broadcast_map_description.len()]; let llmp_tcp_id = self.llmp_clients.len() as u32; // Tcp out map sends messages from background thread tcp server to foreground client - let tcp_out_map = LlmpSharedMap::new(llmp_tcp_id, SH::new_map(LLMP_CFG_INITIAL_MAP_SIZE)?); - let tcp_out_map_str = tcp_out_map.shmem.shm_str(); - let tcp_out_map_size = tcp_out_map.shmem.map().len(); + let tcp_out_map = LlmpSharedMap::new( + llmp_tcp_id, + self.shmem_provider + .borrow_mut() + .new_map(LLMP_CFG_INITIAL_MAP_SIZE)?, + ); + let shmem_id = tcp_out_map.shmem.id(); + let tcp_out_map_str = *shmem_id.as_slice(); + let tcp_out_map_size = tcp_out_map.shmem.len(); self.register_client(tcp_out_map); + let shmem_provider_clone = self.shmem_provider.borrow_mut().clone(); + Ok(thread::spawn(move || { + let shmem_provider = Rc::new(RefCell::new(shmem_provider_clone)); + // Clone so we get a new connection to the AshmemServer if we are using + // ServedShMemProvider let mut new_client_sender = LlmpSender { id: 0, last_msg_sent: ptr::null_mut(), out_maps: vec![LlmpSharedMap::existing( - SH::existing_from_shm_str(&tcp_out_map_str, tcp_out_map_size).unwrap(), + shmem_provider + .borrow_mut() + .from_id_and_size(ShMemId::from_slice(&tcp_out_map_str), tcp_out_map_size) + .unwrap(), )], // drop pages to the broker if it already read them keep_pages_forever: false, + shmem_provider: shmem_provider.clone(), }; loop { @@ -1562,7 +1572,7 @@ where (*msg).tag = LLMP_TAG_NEW_SHM_CLIENT; let pageinfo = (*msg).buf.as_mut_ptr() as *mut LlmpPayloadSharedMapInfo; - (*pageinfo).shm_str = incoming_map_description.str_bytes; + (*pageinfo).shm_str = *incoming_map_description.id.as_slice(); (*pageinfo).map_size = incoming_map_description.size; match new_client_sender.send(msg) { Ok(()) => (), @@ -1571,65 +1581,6 @@ where } } } - #[cfg(unix)] - ListenerStream::Unix(stream, addr) => unsafe { - dbg!("New connection", addr); - - let broadcast_fd_initial: i32 = client_out_map_mem_fd; - - match sendmsg( - stream.as_raw_fd(), - &[IoVec::from_slice(b"\x00")], - &[ControlMessage::ScmRights(&[broadcast_fd_initial])], - MsgFlags::empty(), - None, - ) { - Ok(_) => {} - Err(err) => { - dbg!("Error sending fd over stream: {}", err); - continue; - } - }; - - let mut buf = [0u8; 5]; - let mut cmsgspace = cmsg_space!([RawFd; 1]); - let msg = recvmsg( - stream.as_raw_fd(), - &[IoVec::from_mut_slice(&mut buf[..])], - Some(&mut cmsgspace), - MsgFlags::empty(), - ) - .unwrap(); - - for cmsg in msg.cmsgs() { - if let ControlMessageOwned::ScmRights(fds) = cmsg { - for fd in fds { - let mut fdstr = [0u8; 20]; - match write!(&mut fdstr[..], "{}", fd) { - Ok(_) => {} - Err(_) => { - dbg!("error converting fd to string"); - } - } - - let msg = new_client_sender - .alloc_next(size_of::()) - .expect("Could not allocate a new message in shared map."); - (*msg).tag = LLMP_TAG_NEW_SHM_CLIENT; - let pageinfo = - (*msg).buf.as_mut_ptr() as *mut LlmpPayloadSharedMapInfo; - (*pageinfo).shm_str = fdstr; - (*pageinfo).map_size = LLMP_CFG_INITIAL_MAP_SIZE; - match new_client_sender.send(msg) { - Ok(()) => (), - Err(e) => { - println!("Error forwarding client on map: {:?}", e) - } - }; - } - } - } - }, ListenerStream::Empty() => { continue; } @@ -1676,7 +1627,10 @@ where } else { let pageinfo = (*msg).buf.as_mut_ptr() as *mut LlmpPayloadSharedMapInfo; - match SH::existing_from_shm_slice(&(*pageinfo).shm_str, (*pageinfo).map_size) { + match self.shmem_provider.borrow_mut().from_id_and_size( + ShMemId::from_slice(&(*pageinfo).shm_str), + (*pageinfo).map_size, + ) { Ok(new_map) => { let mut new_page = LlmpSharedMap::existing(new_map); let id = next_id; @@ -1686,6 +1640,7 @@ where id, current_recv_map: new_page, last_msg_recvd: ptr::null_mut(), + shmem_provider: self.shmem_provider.clone(), }); } Err(e) => { @@ -1716,24 +1671,6 @@ where } } -#[cfg(feature = "std")] -impl Drop for LlmpBroker -where - SH: ShMem, -{ - fn drop(&mut self) { - match &self.socket_name { - Some(name) => match fs::remove_file(&name) { - Ok(_) => {} - Err(err) => { - dbg!("failed to close socket: {}", err); - } - }, - None => {} - } - } -} - /// A restorable client description #[derive(Clone, Copy, Debug, Serialize, Deserialize)] pub struct LlmpClientDescription { @@ -1744,44 +1681,65 @@ pub struct LlmpClientDescription { } /// Client side of LLMP -#[derive(Clone, Debug)] -pub struct LlmpClient +#[derive(Debug)] +pub struct LlmpClient where - SH: ShMem, + SP: ShMemProvider, { + shmem_provider: Rc>, /// Outgoing channel to the broker - pub sender: LlmpSender, + pub sender: LlmpSender, /// Incoming (broker) broadcast map - pub receiver: LlmpReceiver, + pub receiver: LlmpReceiver, } /// `n` clients connect to a broker. They share an outgoing map with the broker, /// and get incoming messages from the shared broker bus -impl LlmpClient +impl LlmpClient where - SH: ShMem, + SP: ShMemProvider, { /// Reattach to a vacant client map. /// It is essential, that the broker (or someone else) kept a pointer to the out_map /// else reattach will get a new, empty page, from the OS, or fail pub fn on_existing_map( - current_out_map: SH, - last_msg_sent_offset: Option, - current_broker_map: SH, + shmem_provider: Rc>, + _current_out_map: SP::Mem, + _last_msg_sent_offset: Option, + current_broker_map: SP::Mem, last_msg_recvd_offset: Option, ) -> Result { Ok(Self { - receiver: LlmpReceiver::on_existing_map(current_broker_map, last_msg_recvd_offset)?, - sender: LlmpSender::on_existing_map(current_out_map, last_msg_sent_offset)?, + receiver: LlmpReceiver::on_existing_map( + shmem_provider.clone(), + current_broker_map.clone(), + last_msg_recvd_offset, + )?, + sender: LlmpSender::on_existing_map( + shmem_provider.clone(), + current_broker_map, + last_msg_recvd_offset, + )?, + shmem_provider, }) } /// Recreate this client from a previous client.to_env #[cfg(feature = "std")] - pub fn on_existing_from_env(env_name: &str) -> Result { + pub fn on_existing_from_env( + shmem_provider: &Rc>, + env_name: &str, + ) -> Result { Ok(Self { - sender: LlmpSender::on_existing_from_env(&format!("{}_SENDER", env_name))?, - receiver: LlmpReceiver::on_existing_from_env(&format!("{}_RECEIVER", env_name))?, + sender: LlmpSender::on_existing_from_env( + shmem_provider, + &format!("{}_SENDER", env_name), + )?, + receiver: LlmpReceiver::on_existing_from_env( + shmem_provider, + &format!("{}_RECEIVER", env_name), + )?, + shmem_provider: shmem_provider.clone(), }) } @@ -1803,11 +1761,16 @@ where /// Create an existing client from description fn existing_client_from_description( + shmem_provider: &Rc>, description: &LlmpClientDescription, ) -> Result { Ok(Self { - sender: LlmpSender::on_existing_from_description(&description.sender)?, - receiver: LlmpReceiver::on_existing_from_description(&description.receiver)?, + sender: LlmpSender::on_existing_from_description(shmem_provider, &description.sender)?, + receiver: LlmpReceiver::on_existing_from_description( + shmem_provider, + &description.receiver, + )?, + shmem_provider: shmem_provider.clone(), }) } @@ -1823,24 +1786,31 @@ where } /// Creates a new LlmpClient - pub fn new(initial_broker_map: LlmpSharedMap) -> Result { + pub fn new( + shmem_provider: &Rc>, + initial_broker_map: LlmpSharedMap, + ) -> Result { Ok(Self { sender: LlmpSender { id: 0, last_msg_sent: ptr::null_mut(), - out_maps: vec![LlmpSharedMap::new( - 0, - SH::new_map(LLMP_CFG_INITIAL_MAP_SIZE)?, - )], + out_maps: vec![LlmpSharedMap::new(0, { + shmem_provider + .borrow_mut() + .new_map(LLMP_CFG_INITIAL_MAP_SIZE)? + })], // drop pages to the broker if it already read them keep_pages_forever: false, + shmem_provider: shmem_provider.clone(), }, receiver: LlmpReceiver { id: 0, current_recv_map: initial_broker_map, last_msg_recvd: ptr::null_mut(), + shmem_provider: shmem_provider.clone(), }, + shmem_provider: shmem_provider.clone(), }) } @@ -1917,20 +1887,27 @@ where #[cfg(feature = "std")] /// Creates a new LlmpClient, reading the map id and len from env - pub fn create_using_env(env_var: &str) -> Result { - Self::new(LlmpSharedMap::existing(SH::existing_from_env(env_var)?)) + pub fn create_using_env( + shmem_provider: &Rc>, + env_var: &str, + ) -> Result { + let map = LlmpSharedMap::existing(shmem_provider.borrow_mut().existing_from_env(env_var)?); + Self::new(shmem_provider, map) } #[cfg(feature = "std")] /// Create a LlmpClient, getting the ID from a given port - pub fn create_attach_to_tcp(port: u16) -> Result { + pub fn create_attach_to_tcp( + shmem_provider: &Rc>, + port: u16, + ) -> Result { let mut stream = TcpStream::connect(format!("127.0.0.1:{}", port))?; println!("Connected to port {}", port); // First, get the serialized description size by serializing a dummy. let dummy_description = ShMemDescription { size: 0, - str_bytes: Default::default(), + id: ShMemId::default(), }; let mut new_broker_map_str = postcard::to_allocvec(&dummy_description)?; @@ -1938,9 +1915,12 @@ where let broker_map_description: ShMemDescription = postcard::from_bytes(&new_broker_map_str)?; - let ret = Self::new(LlmpSharedMap::existing(SH::existing_from_description( - &broker_map_description, - )?))?; + let map = LlmpSharedMap::existing( + shmem_provider + .borrow_mut() + .from_description(broker_map_description)?, + ); + let ret = Self::new(shmem_provider, map)?; let own_map_description_bytes = postcard::to_allocvec(&ret.sender.out_maps.first().unwrap().shmem.description())?; @@ -1949,80 +1929,11 @@ where } } -/// `n` clients connect to a broker. They share an outgoing map with the broker, -/// and get incoming messages from the shared broker bus -/// If the Shm has a fd, we can attach to it. -#[cfg(all(unix, feature = "std"))] -impl LlmpClient -where - SH: ShMem, -{ - #[cfg(all(unix, feature = "std"))] - /// Create a LlmpClient, getting the ID from a given filename - pub fn create_attach_to_unix(filename: &str) -> Result { - let stream = UnixStream::connect_to_unix_addr(&UnixSocketAddr::new(filename).unwrap())?; - println!("Connected to socket {}", filename); - - let mut buf = [0u8; 5]; - let mut cmsgspace = cmsg_space!([RawFd; 1]); - let msg = recvmsg( - stream.as_raw_fd(), - &[IoVec::from_mut_slice(&mut buf[..])], - Some(&mut cmsgspace), - MsgFlags::empty(), - ) - .unwrap(); - - for cmsg in msg.cmsgs() { - if let ControlMessageOwned::ScmRights(fds) = cmsg { - for fd in fds { - let mut fdstr = [0u8; 20]; - match write!(&mut fdstr[..], "{}", fd) { - Ok(_) => {} - Err(_) => { - dbg!("error converting fd to string"); - } - } - - let ret = Self::new(LlmpSharedMap::existing(SH::existing_from_shm_slice( - &fdstr, - LLMP_CFG_INITIAL_MAP_SIZE, - )?))?; - - match sendmsg( - stream.as_raw_fd(), - &[IoVec::from_slice(b"\x00")], - &[ControlMessage::ScmRights(&[ret - .sender - .out_maps - .first() - .unwrap() - .shmem - .shm_str() - .parse() - .unwrap()])], - MsgFlags::empty(), - None, - ) { - Ok(_) => {} - Err(err) => { - dbg!("Error sending fd over stream {}", err); - continue; - } - }; - return Ok(ret); - } - } - } - - panic!("Didn't receive a file descriptor from the broker!"); - } -} - #[cfg(test)] #[cfg(all(unix, feature = "std"))] mod tests { + use alloc::rc::Rc; use std::{thread::sleep, time::Duration}; use super::{ @@ -2032,17 +1943,20 @@ mod tests { Tag, }; - use crate::bolts::shmem::UnixShMem; + use crate::bolts::shmem::{ShMemProvider, StdShMemProvider}; + + use core::cell::RefCell; #[test] pub fn llmp_connection() { - let mut broker = match LlmpConnection::::on_port(1337).unwrap() { + let shmem_provider = Rc::new(RefCell::new(StdShMemProvider::new())); + let mut broker = match LlmpConnection::on_port(&shmem_provider, 1337).unwrap() { IsClient { client: _ } => panic!("Could not bind to port as broker"), IsBroker { broker } => broker, }; // Add the first client (2nd, actually, because of the tcp listener client) - let mut client = match LlmpConnection::::on_port(1337).unwrap() { + let mut client = match LlmpConnection::on_port(&shmem_provider, 1337).unwrap() { IsBroker { broker: _ } => panic!("Second connect should be a client!"), IsClient { client } => client, }; @@ -2066,7 +1980,7 @@ mod tests { } /* recreate the client from env, check if it still works */ - client = LlmpClient::::on_existing_from_env("_ENV_TEST").unwrap(); + client = LlmpClient::on_existing_from_env(&shmem_provider, "_ENV_TEST").unwrap(); client.send_buf(tag, &arr).unwrap(); diff --git a/libafl/src/bolts/os/ashmem_server.rs b/libafl/src/bolts/os/ashmem_server.rs index e5b544c8d4..b5aee980e6 100644 --- a/libafl/src/bolts/os/ashmem_server.rs +++ b/libafl/src/bolts/os/ashmem_server.rs @@ -5,12 +5,18 @@ and forwards them over unix domain sockets. */ use crate::{ - bolts::shmem::{ShMem, ShMemDescription, UnixShMem}, + bolts::shmem::{ + unix_shmem::ashmem::{AshmemShMem, AshmemShMemProvider}, + ShMem, ShMemDescription, ShMemId, ShMemProvider, + }, Error, }; use hashbrown::HashMap; use serde::{Deserialize, Serialize}; -use std::io::{Read, Write}; +use std::{ + io::{Read, Write}, + sync::{Arc, Condvar, Mutex}, +}; #[cfg(all(feature = "std", unix))] use nix::poll::{poll, PollFd, PollFlags}; @@ -18,8 +24,8 @@ use nix::poll::{poll, PollFd, PollFlags}; #[cfg(all(feature = "std", unix))] use std::{ os::unix::{ + io::{AsRawFd, RawFd}, net::{UnixListener, UnixStream}, - {io::AsRawFd, prelude::RawFd}, }, thread, }; @@ -27,30 +33,43 @@ use std::{ #[cfg(all(unix, feature = "std"))] use uds::{UnixListenerExt, UnixSocketAddr, UnixStreamExt}; -#[derive(Debug)] -/// The Sharedmem backed by a `ShmemService`a -pub struct ServedShMem { - stream: UnixStream, - shmem: Option, - slice: Option<[u8; 20]>, - fd: Option, -} const ASHMEM_SERVER_NAME: &str = "@ashmem_server"; -impl ServedShMem { - /// Create a new ServedShMem and connect to the ashmem server. - pub fn connect(name: &str) -> Self { - Self { - stream: UnixStream::connect_to_unix_addr(&UnixSocketAddr::from_abstract(name).unwrap()) - .expect("Failed to connect to the ashmem server"), - shmem: None, - slice: None, - fd: None, - } +#[derive(Debug)] +pub struct ServedShMemProvider { + stream: UnixStream, + inner: AshmemShMemProvider, +} + +#[derive(Clone, Debug)] +pub struct ServedShMem { + 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 - 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 header = (body.len() as u32).to_be_bytes(); @@ -66,63 +85,62 @@ impl ServedShMem { self.stream .recv_fds(&mut shm_slice, &mut fd_buf) .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 { - fn new_map(map_size: usize) -> Result { - let mut res = Self::connect(ASHMEM_SERVER_NAME); - 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(), - )) - } 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) +impl Default for ServedShMemProvider { + fn default() -> Self { + Self::new() + } +} + +impl Clone for ServedShMemProvider { + fn clone(&self) -> Self { + Self::new() + } +} + +impl ShMemProvider for ServedShMemProvider { + type Mem = ServedShMem; + + /// 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 { + let (server_fd, client_fd) = self.send_receive(AshmemRequest::NewMap(map_size)); - fn existing_from_shm_slice( - map_str_bytes: &[u8; 20], - map_size: usize, - ) -> Result { - let mut res = Self::connect(ASHMEM_SERVER_NAME); - 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) - } + Ok(ServedShMem { + inner: self + .inner + .from_id_and_size(ShMemId::from_string(&format!("{}", client_fd)), map_size)?, + server_fd, + }) } - fn shm_slice(&self) -> &[u8; 20] { - self.slice.as_ref().unwrap() - } - - fn map(&self) -> &[u8] { - self.shmem.as_ref().unwrap().map() - } - - fn map_mut(&mut self) -> &mut [u8] { - self.shmem.as_mut().unwrap().map_mut() + fn from_id_and_size(&mut self, id: ShMemId, size: usize) -> Result { + let parts = id.to_string().split(':').collect::>(); + let server_id_str = parts.get(0).unwrap(); + let (server_fd, client_fd) = self.send_receive(AshmemRequest::ExistingMap( + ShMemDescription::from_string_and_size(server_id_str, size), + )); + Ok(ServedShMem { + inner: self + .inner + .from_id_and_size(ShMemId::from_string(&format!("{}", client_fd)), size)?, + server_fd, + }) } } @@ -138,13 +156,20 @@ pub enum AshmemRequest { } #[derive(Debug)] -pub struct AshmemClient { - unix_socket_file: String, +struct AshmemClient { + stream: UnixStream, +} + +impl AshmemClient { + fn new(stream: UnixStream) -> Self { + Self { stream } + } } #[derive(Debug)] pub struct AshmemService { - maps: HashMap<[u8; 20], UnixShMem>, + provider: AshmemShMemProvider, + maps: Vec, } impl AshmemService { @@ -152,116 +177,145 @@ impl AshmemService { #[must_use] fn new() -> Self { AshmemService { - maps: HashMap::new(), + provider: AshmemShMemProvider::new(), + maps: Vec::new(), } } /// 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. 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 mut bytes = vec![]; bytes.resize(size as usize, 0u8); - stream + client + .stream .read_exact(&mut bytes) .expect("Failed to read message body"); let request: AshmemRequest = postcard::from_bytes(&bytes)?; // Handle the client request - let (shmem_slice, fd): ([u8; 20], RawFd) = match request { - AshmemRequest::NewMap(map_size) => match UnixShMem::new(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 - } - }, + let mapping = match request { + AshmemRequest::NewMap(map_size) => self.provider.new_map(map_size)?, AshmemRequest::ExistingMap(description) => { - match self.maps.get(&description.str_bytes) { - None => { - println!("Error finding shared map {:?}", description); - ([0; 20], -1) - } - Some(map) => (*map.shm_slice(), map.shm_id), - } + self.provider.from_description(description)? } AshmemRequest::Deregister(_) => { 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(()) } /// Create a new AshmemService, then listen and service incoming connections in a new thread. - pub fn start() -> Result, Error> { - Ok(thread::spawn(move || { - Self::new().listen(ASHMEM_SERVER_NAME).unwrap() - })) + pub fn start() -> Result>, Error> { + #[allow(clippy::mutex_atomic)] + 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 /// should not return. - fn listen(&mut self, filename: &str) -> Result<(), Error> { - let listener = UnixListener::bind_unix_addr(&UnixSocketAddr::new(filename)?)?; - let mut clients: HashMap = HashMap::new(); - let mut poll_fds: HashMap = HashMap::new(); - - poll_fds.insert( + fn listen( + &mut self, + filename: &str, + syncpair: Arc<(Mutex, Condvar)>, + ) -> Result<(), Error> { + 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 = HashMap::new(); + let mut poll_fds: Vec = vec![PollFd::new( 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 { - let mut fds_to_poll: Vec = poll_fds.values().copied().collect(); - let fd = match poll(&mut fds_to_poll, -1) { - Ok(fd) => fd, + match poll(&mut poll_fds, -1) { + Ok(num_fds) if num_fds > 0 => (), + Ok(_) => continue, Err(e) => { println!("Error polling for activity: {:?}", e); continue; } }; - if fd == listener.as_raw_fd() { - let (stream, addr) = match listener.accept_unix_addr() { - Ok(stream_val) => stream_val, - Err(e) => { - println!("Error accepting client: {:?}", e); - continue; - } - }; + let copied_poll_fds: Vec = poll_fds.iter().copied().collect(); + for poll_fd in copied_poll_fds { + let revents = poll_fd.revents().expect("revents should not be None"); + let raw_polled_fd = + unsafe { *((&poll_fd as *const PollFd) as *const libc::pollfd) }.fd; + 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); - let pollfd = PollFd::new(stream.as_raw_fd(), PollFlags::POLLIN); - poll_fds.insert(stream.as_raw_fd(), pollfd); - clients - .insert(stream.as_raw_fd(), (stream, addr)) - .as_ref() - .unwrap(); - } else if poll_fds - .get(&fd) - .unwrap() - .revents() - .unwrap() - .contains(PollFlags::POLLHUP) - { - 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; + println!("Recieved connection from {:?}", addr); + let pollfd = PollFd::new( + stream.as_raw_fd(), + PollFlags::POLLIN | PollFlags::POLLRDNORM | PollFlags::POLLRDBAND, + ); + poll_fds.push(pollfd); + let mut client = AshmemClient::new(stream); + match self.handle_client(&mut client) { + Ok(()) => (), + Err(e) => { + dbg!("Ignoring failed read from client", e); + } + }; + clients.insert(client.stream.as_raw_fd(), client); } - }; + } else { + //println!("Unknown revents flags: {:?}", revents); + } } } } diff --git a/libafl/src/bolts/shmem.rs b/libafl/src/bolts/shmem.rs index b2f43c4344..930dc38c24 100644 --- a/libafl/src/bolts/shmem.rs +++ b/libafl/src/bolts/shmem.rs @@ -2,21 +2,38 @@ // too.) #[cfg(all(feature = "std", unix))] -pub use unix_shmem::UnixShMem; +pub use unix_shmem::{UnixShMem, UnixShMemProvider}; #[cfg(all(feature = "std", unix))] -pub type StdShMem = UnixShMem; +pub type OsShMemProvider = UnixShMemProvider; +#[cfg(all(feature = "std", unix))] +pub type OsShMem = UnixShMem; #[cfg(all(windows, feature = "std"))] -pub use win32_shmem::Win32ShMem; +pub use win32_shmem::{Win32ShMem, Win32ShMemProvider}; #[cfg(all(windows, feature = "std"))] -pub type StdShMem = Win32ShMem; +pub type OsShMemProvider = Win32ShMemProvider; +#[cfg(all(windows, feature = "std"))] +pub type OsShMem = Win32ShMem; + +#[cfg(target_os = "android")] +use crate::bolts::os::ashmem_server::{ServedShMem, ServedShMemProvider}; +#[cfg(target_os = "android")] +pub type StdShMemProvider = ServedShMemProvider; +#[cfg(target_os = "android")] +pub type StdShMem = ServedShMem; + +#[cfg(all(feature = "std", not(target_os = "android")))] +pub type StdShMemProvider = OsShMemProvider; +#[cfg(all(feature = "std", not(target_os = "android")))] +pub type StdShMem = OsShMem; -use alloc::string::{String, ToString}; use core::fmt::Debug; use serde::{Deserialize, Serialize}; #[cfg(feature = "std")] use std::env; +use alloc::string::ToString; + use crate::Error; /// Description of a shared map. @@ -25,419 +42,526 @@ use crate::Error; pub struct ShMemDescription { /// Size of this map pub size: usize, - /// of name of this map, as fixed 20 bytes c-string - pub str_bytes: [u8; 20], + /// Id of this map + pub id: ShMemId, } -/// A Shared map -pub trait ShMem: Sized + Debug { - /// Creates a new map with the given size - fn new_map(map_size: usize) -> Result; +impl ShMemDescription { + pub fn from_string_and_size(string: &str, size: usize) -> Self { + Self { + size, + id: ShMemId::from_string(string), + } + } +} - /// Creates a new reference to the same map - fn clone_ref(old_ref: &Self) -> Result { - Self::existing_from_shm_slice(old_ref.shm_slice(), old_ref.map().len()) +/// An id associated with a given shared memory mapping (ShMem), which can be used to +/// establish shared-mappings between proccesses. +#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash, Default)] +pub struct ShMemId { + id: [u8; 20], +} + +impl ShMemId { + /// Create a new id from a fixed-size string + pub fn from_slice(slice: &[u8; 20]) -> Self { + Self { id: *slice } } - /// Creates a nes variable with the given name, strigified to 20 bytes. - fn existing_from_shm_slice(map_str_bytes: &[u8; 20], map_size: usize) -> Result; + /// Create a new id from an int + pub fn from_int(val: i32) -> Self { + Self::from_string(&val.to_string()) + } - /// Initialize from a shm_str with fixed len of 20 - fn existing_from_shm_str(shm_str: &str, map_size: usize) -> Result { + /// Create a new id from a string + pub fn from_string(val: &str) -> Self { let mut slice: [u8; 20] = [0; 20]; - for (i, val) in shm_str.as_bytes().iter().enumerate() { + for (i, val) in val.as_bytes().iter().enumerate() { slice[i] = *val; } - Self::existing_from_shm_slice(&slice, map_size) + Self { id: slice } } - /// The string to identify this shm - fn shm_str(&self) -> String { - let bytes = self.shm_slice(); - let eof_pos = bytes.iter().position(|&c| c == 0).unwrap(); - alloc::str::from_utf8(&bytes[..eof_pos]) - .unwrap() - .to_string() + /// Get the id as a fixed-length slice + pub fn as_slice(&self) -> &[u8; 20] { + &self.id } - /// Let's just fix this to a large enough buf - fn shm_slice(&self) -> &[u8; 20]; + /// Get a string representation of this id + pub fn to_string(&self) -> &str { + let eof_pos = self.id.iter().position(|&c| c == 0).unwrap(); + alloc::str::from_utf8(&self.id[..eof_pos]).unwrap() + } + + /// Get an integer representation of this id + pub fn to_int(&self) -> i32 { + let id: i32 = self.to_string().parse().unwrap(); + id + } +} + +pub trait ShMem: Sized + Debug + Clone { + /// Get the id of this shared memory mapping + fn id(&self) -> ShMemId; + + /// Get the size of this mapping + fn len(&self) -> usize; + + /// Check if the mapping is empty + fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Get the description of the shared memory mapping + fn description(&self) -> ShMemDescription { + ShMemDescription { + size: self.len(), + id: self.id(), + } + } /// The actual shared map, in memory fn map(&self) -> &[u8]; /// 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(), - } - } - - /// 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<(), Error> { - let map_size = self.map().len(); + let map_size = self.len(); let map_size_env = format!("{}_SIZE", env_name); - env::set_var(env_name, self.shm_str()); + env::set_var(env_name, self.id().to_string()); env::set_var(map_size_env, format!("{}", map_size)); Ok(()) } +} + +pub trait ShMemProvider: Send + Clone + Default { + type Mem: ShMem; + + /// Create a new instance of the provider + fn new() -> Self; + + /// Create a new shared memory mapping + fn new_map(&mut self, map_size: usize) -> Result; + + /// Get a mapping given its id and size + fn from_id_and_size(&mut self, id: ShMemId, size: usize) -> Result; + + /// Get a mapping given a description + fn from_description(&mut self, description: ShMemDescription) -> Result { + self.from_id_and_size(description.id, description.size) + } + + fn clone_ref(&mut self, mapping: &Self::Mem) -> Result { + self.from_id_and_size(mapping.id(), mapping.len()) + } /// Reads an existing map config from env vars, then maps it #[cfg(feature = "std")] - fn existing_from_env(env_name: &str) -> Result { + fn existing_from_env(&mut self, env_name: &str) -> Result { let map_shm_str = env::var(env_name)?; let map_size = str::parse::(&env::var(format!("{}_SIZE", env_name))?)?; - Self::existing_from_shm_str(&map_shm_str, map_size) + self.from_description(ShMemDescription::from_string_and_size( + &map_shm_str, + map_size, + )) } } #[cfg(all(unix, feature = "std"))] pub mod unix_shmem { - use core::{mem::size_of, ptr, slice}; - use libc::{c_char, c_int, c_long, c_uchar, c_uint, c_ulong, c_ushort, c_void}; #[cfg(target_os = "android")] - use libc::{off_t, size_t, MAP_SHARED, O_RDWR, PROT_READ, PROT_WRITE}; - use std::ffi::CStr; + pub type UnixShMemProvider = ashmem::AshmemShMemProvider; #[cfg(target_os = "android")] - use std::ffi::CString; + pub type UnixShMem = ashmem::AshmemShMem; + #[cfg(not(target_os = "android"))] + pub type UnixShMemProvider = default::CommonUnixShMemProvider; + #[cfg(not(target_os = "android"))] + pub type UnixShMem = ashmem::AshmemShMem; - use crate::Error; + #[cfg(all(unix, feature = "std", not(target_os = "android")))] + mod default { + use core::{ptr, slice}; + use libc::{c_int, c_long, c_uchar, c_uint, c_ulong, c_ushort, c_void}; - use super::ShMem; + use crate::Error; - #[cfg(unix)] - extern "C" { - #[cfg(feature = "std")] - fn snprintf(_: *mut c_char, _: c_ulong, _: *const c_char, _: ...) -> c_int; - #[cfg(feature = "std")] - fn strncpy(_: *mut c_char, _: *const c_char, _: c_ulong) -> *mut c_char; - #[cfg(all(feature = "std", not(target_os = "android")))] - fn shmctl(__shmid: c_int, __cmd: c_int, __buf: *mut shmid_ds) -> c_int; - #[cfg(all(feature = "std", not(target_os = "android")))] - fn shmget(__key: c_int, __size: c_ulong, __shmflg: c_int) -> c_int; - #[cfg(all(feature = "std", not(target_os = "android")))] - fn shmat(__shmid: c_int, __shmaddr: *const c_void, __shmflg: c_int) -> *mut c_void; - #[cfg(all(feature = "std", target_os = "android"))] - fn ioctl(fd: c_int, request: c_long, ...) -> c_int; - #[cfg(all(feature = "std", target_os = "android"))] - fn open(path: *const c_char, oflag: c_int, ...) -> c_int; - #[cfg(all(feature = "std", target_os = "android"))] - fn close(fd: c_int) -> c_int; - #[cfg(all(feature = "std", target_os = "android"))] - fn mmap( - addr: *mut c_void, - len: size_t, - prot: c_int, - flags: c_int, - fd: c_int, - offset: off_t, - ) -> *mut c_void; + use super::super::{ShMem, ShMemId, ShMemProvider}; - } - - #[cfg(target_os = "android")] - #[derive(Copy, Clone)] - #[repr(C)] - struct ashmem_pin { - pub offset: c_uint, - pub len: c_uint, - } - - #[cfg(target_os = "android")] - const ASHMEM_GET_SIZE: c_long = 0x00007704; - #[cfg(target_os = "android")] - const ASHMEM_UNPIN: c_long = 0x40087708; - #[cfg(target_os = "android")] - const ASHMEM_SET_NAME: c_long = 0x41007701; - #[cfg(target_os = "android")] - const ASHMEM_SET_SIZE: c_long = 0x40087703; - #[cfg(target_os = "android")] - const ASHMEM_DEVICE: &str = "/dev/ashmem"; - - #[cfg(target_os = "android")] - unsafe fn shmctl(__shmid: c_int, __cmd: c_int, _buf: *mut shmid_ds) -> c_int { - if __cmd == 0 { - let length = ioctl(__shmid, ASHMEM_GET_SIZE); - - let ap = ashmem_pin { - offset: 0, - len: length as u32, - }; - - let ret = ioctl(__shmid, ASHMEM_UNPIN, &ap); - close(__shmid); - ret - } else { - 0 - } - } - - #[cfg(target_os = "android")] - unsafe fn shmget(__key: c_int, __size: c_ulong, __shmflg: c_int) -> c_int { - let boot_id = std::fs::read_to_string("/proc/sys/kernel/random/boot_id").unwrap(); - - let path = CString::new(format!("{}{}", ASHMEM_DEVICE, boot_id).trim()) - .expect("CString::new failed!"); - let fd = open(path.as_ptr(), O_RDWR); - - let mut ourkey: [c_char; 20] = [0; 20]; - snprintf( - ourkey.as_mut_ptr() as *mut c_char, - size_of::<[c_char; 20]>() as c_ulong, - b"%d\x00" as *const u8 as *const c_char, - if __key == 0 { fd } else { __key }, - ); - - if ioctl(fd, ASHMEM_SET_NAME, &ourkey) != 0 { - close(fd); - return 0; - }; - - if ioctl(fd, ASHMEM_SET_SIZE, __size) != 0 { - close(fd); - return 0; - }; - - fd - } - - #[cfg(target_os = "android")] - unsafe fn shmat(__shmid: c_int, __shmaddr: *const c_void, __shmflg: c_int) -> *mut c_void { - let size = ioctl(__shmid, ASHMEM_GET_SIZE); - if size < 0 { - return 0 as *mut c_void; + #[cfg(unix)] + #[derive(Copy, Clone)] + #[repr(C)] + struct ipc_perm { + pub __key: c_int, + pub uid: c_uint, + pub gid: c_uint, + pub cuid: c_uint, + pub cgid: c_uint, + pub mode: c_ushort, + pub __pad1: c_ushort, + pub __seq: c_ushort, + pub __pad2: c_ushort, + pub __glibc_reserved1: c_ulong, + pub __glibc_reserved2: c_ulong, } - let ptr = mmap( - 0 as *mut c_void, - size as usize, - PROT_READ | PROT_WRITE, - MAP_SHARED, - __shmid, - 0, - ); - if ptr == usize::MAX as *mut c_void { - return 0 as *mut c_void; + #[cfg(unix)] + #[derive(Copy, Clone)] + #[repr(C)] + struct shmid_ds { + pub shm_perm: ipc_perm, + pub shm_segsz: c_ulong, + pub shm_atime: c_long, + pub shm_dtime: c_long, + pub shm_ctime: c_long, + pub shm_cpid: c_int, + pub shm_lpid: c_int, + pub shm_nattch: c_ulong, + pub __glibc_reserved4: c_ulong, + pub __glibc_reserved5: c_ulong, } - ptr - } + extern "C" { + fn shmctl(__shmid: c_int, __cmd: c_int, __buf: *mut shmid_ds) -> c_int; + fn shmget(__key: c_int, __size: c_ulong, __shmflg: c_int) -> c_int; + fn shmat(__shmid: c_int, __shmaddr: *const c_void, __shmflg: c_int) -> *mut c_void; + } - #[cfg(unix)] - #[derive(Copy, Clone)] - #[repr(C)] - struct ipc_perm { - pub __key: c_int, - pub uid: c_uint, - pub gid: c_uint, - pub cuid: c_uint, - pub cgid: c_uint, - pub mode: c_ushort, - pub __pad1: c_ushort, - pub __seq: c_ushort, - pub __pad2: c_ushort, - pub __glibc_reserved1: c_ulong, - pub __glibc_reserved2: c_ulong, - } - - #[cfg(unix)] - #[derive(Copy, Clone)] - #[repr(C)] - struct shmid_ds { - pub shm_perm: ipc_perm, - pub shm_segsz: c_ulong, - pub shm_atime: c_long, - pub shm_dtime: c_long, - pub shm_ctime: c_long, - pub shm_cpid: c_int, - pub shm_lpid: c_int, - pub shm_nattch: c_ulong, - pub __glibc_reserved4: c_ulong, - pub __glibc_reserved5: c_ulong, - } - - /// The default Sharedmap impl for unix using shmctl & shmget - #[cfg(unix)] - #[derive(Clone, Debug)] - pub struct UnixShMem { - pub shm_str: [u8; 20], - pub shm_id: c_int, - pub map: *mut u8, - pub map_size: usize, - } - - #[cfg(unix)] - impl ShMem for UnixShMem { - fn existing_from_shm_slice( - map_str_bytes: &[u8; 20], + /// The default sharedmap impl for unix using shmctl & shmget + #[derive(Clone, Debug)] + pub struct CommonUnixShMem { + id: ShMemId, + map: *mut u8, map_size: usize, - ) -> Result { - unsafe { - let str_bytes = map_str_bytes as *const [u8; 20] as *const libc::c_char; - Self::from_str(CStr::from_ptr(str_bytes), map_size) + } + + impl CommonUnixShMem { + /// Create a new shared memory mapping, using shmget/shmat + pub fn new(map_size: usize) -> Result { + unsafe { + let os_id = shmget(0, map_size as c_ulong, 0o1000 | 0o2000 | 0o600); + + if os_id < 0_i32 { + return Err(Error::Unknown(format!("Failed to allocate a shared mapping of size {} - check OS limits (i.e shmall, shmmax)", map_size))); + } + + let map = shmat(os_id, ptr::null(), 0) as *mut c_uchar; + + if map == usize::MAX as c_int as *mut c_void as *mut c_uchar || map.is_null() { + shmctl(os_id, 0, ptr::null_mut()); + return Err(Error::Unknown( + "Failed to map the shared mapping".to_string(), + )); + } + + Ok(Self { + id: ShMemId::from_int(os_id), + map, + map_size, + }) + } + } + + /// Get a UnixShMem of the existing shared memory mapping identified by id + pub fn from_id_and_size(id: ShMemId, map_size: usize) -> Result { + unsafe { + let map = shmat(id.to_int(), ptr::null(), 0) as *mut c_uchar; + + if map == usize::MAX as *mut c_void as *mut c_uchar || map.is_null() { + return Err(Error::Unknown( + "Failed to map the shared mapping".to_string(), + )); + } + + Ok(Self { id, map, map_size }) + } } } - fn new_map(map_size: usize) -> Result { - Self::new(map_size) + #[cfg(unix)] + impl ShMem for CommonUnixShMem { + fn id(&self) -> ShMemId { + self.id + } + + fn len(&self) -> usize { + self.map_size + } + + fn map(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self.map, self.map_size) } + } + + fn map_mut(&mut self) -> &mut [u8] { + unsafe { slice::from_raw_parts_mut(self.map, self.map_size) } + } } - fn shm_slice(&self) -> &[u8; 20] { - &self.shm_str + /// Drop implementation for UnixShMem, which cleans up the mapping + #[cfg(unix)] + impl Drop for CommonUnixShMem { + fn drop(&mut self) { + unsafe { + shmctl(self.id.to_int(), 0, ptr::null_mut()); + } + } } - fn map(&self) -> &[u8] { - unsafe { slice::from_raw_parts(self.map, self.map_size) } + /// A ShMemProvider which uses shmget/shmat/shmctl to provide shared memory mappings. + #[cfg(unix)] + #[derive(Clone, Debug)] + pub struct CommonUnixShMemProvider {} + + unsafe impl Send for CommonUnixShMemProvider {} + + #[cfg(unix)] + impl Default for CommonUnixShMemProvider { + fn default() -> Self { + Self::new() + } } - fn map_mut(&mut self) -> &mut [u8] { - unsafe { slice::from_raw_parts_mut(self.map, self.map_size) } - } - } + /// Implement ShMemProvider for UnixShMemProvider + #[cfg(unix)] + impl ShMemProvider for CommonUnixShMemProvider { + type Mem = CommonUnixShMem; - /// Deinit sharedmaps on drop - impl Drop for UnixShMem { - fn drop(&mut self) { - unsafe { - unix_shmem_deinit(self); + fn new() -> Self { + Self {} + } + fn new_map(&mut self, map_size: usize) -> Result { + CommonUnixShMem::new(map_size) + } + + fn from_id_and_size(&mut self, id: ShMemId, size: usize) -> Result { + CommonUnixShMem::from_id_and_size(id, size) } } } - /// Create an uninitialized shmap - #[cfg(unix)] - const fn unix_shmem_unitialized() -> UnixShMem { - UnixShMem { - shm_str: [0; 20], - shm_id: -1, - map: 0 as *mut c_uchar, - map_size: 0, - } - } + #[cfg(all(unix, feature = "std"))] + pub mod ashmem { + use core::slice; + use libc::{ + c_char, c_int, c_long, c_uint, c_void, off_t, size_t, MAP_SHARED, O_RDWR, PROT_READ, + PROT_WRITE, + }; + use std::ffi::CString; - #[cfg(unix)] - impl UnixShMem { - pub fn from_str(shm_str: &CStr, map_size: usize) -> Result { - let mut ret = unix_shmem_unitialized(); - let map = unsafe { unix_shmem_by_str(&mut ret, shm_str, map_size) }; - if !map.is_null() { - Ok(ret) - } else { - Err(Error::Unknown(format!( - "Could not allocate map with id {:?} and size {}", - shm_str, map_size - ))) + use crate::Error; + + use super::super::{ShMem, ShMemId, ShMemProvider}; + + extern "C" { + fn ioctl(fd: c_int, request: c_long, ...) -> c_int; + fn open(path: *const c_char, oflag: c_int, ...) -> c_int; + fn close(fd: c_int) -> c_int; + fn mmap( + addr: *mut c_void, + len: size_t, + prot: c_int, + flags: c_int, + fd: c_int, + offset: off_t, + ) -> *mut c_void; + + } + + /// An ashmem based impl for linux/android + #[cfg(unix)] + #[derive(Clone, Debug)] + pub struct AshmemShMem { + id: ShMemId, + map: *mut u8, + map_size: usize, + } + + #[derive(Copy, Clone)] + #[repr(C)] + struct ashmem_pin { + pub offset: c_uint, + pub len: c_uint, + } + + const ASHMEM_GET_SIZE: c_long = 0x00007704; + const ASHMEM_UNPIN: c_long = 0x40087708; + //const ASHMEM_SET_NAME: c_long = 0x41007701; + const ASHMEM_SET_SIZE: c_long = 0x40087703; + + impl AshmemShMem { + /// Create a new shared memory mapping, using shmget/shmat + pub fn new(map_size: usize) -> Result { + unsafe { + let device_path = CString::new( + if let Ok(boot_id) = + std::fs::read_to_string("/proc/sys/kernel/random/boot_id") + { + format!("{}{}", "/dev/ashmem", boot_id).trim().to_string() + } else { + "/dev/ashmem".to_string() + }, + ) + .unwrap(); + + let fd = open(device_path.as_ptr(), O_RDWR); + if fd == -1 { + return Err(Error::Unknown(format!( + "Failed to open the ashmem device at {:?}", + device_path + ))); + } + + //if ioctl(fd, ASHMEM_SET_NAME, name) != 0 { + //close(fd); + //return Err(Error::Unknown("Failed to set the ashmem mapping's name".to_string())); + //}; + + if ioctl(fd, ASHMEM_SET_SIZE, map_size) != 0 { + close(fd); + return Err(Error::Unknown( + "Failed to set the ashmem mapping's size".to_string(), + )); + }; + + let map = mmap( + std::ptr::null_mut(), + map_size, + PROT_READ | PROT_WRITE, + MAP_SHARED, + fd, + 0, + ); + if map == usize::MAX as *mut c_void { + close(fd); + return Err(Error::Unknown( + "Failed to map the ashmem mapping".to_string(), + )); + } + + Ok(Self { + id: ShMemId::from_string(&format!("{}", fd)), + map: map as *mut u8, + map_size, + }) + } + } + + /// Get a UnixShMem of the existing shared memory mapping identified by id + pub fn from_id_and_size(id: ShMemId, map_size: usize) -> Result { + unsafe { + let fd: i32 = id.to_string().parse().unwrap(); + if ioctl(fd, ASHMEM_GET_SIZE) != map_size as i32 { + return Err(Error::Unknown( + "The mapping's size differs from the requested size".to_string(), + )); + }; + + let map = mmap( + std::ptr::null_mut(), + map_size, + PROT_READ | PROT_WRITE, + MAP_SHARED, + fd, + 0, + ); + if map == usize::MAX as *mut c_void { + close(fd); + return Err(Error::Unknown( + "Failed to map the ashmem mapping".to_string(), + )); + } + + Ok(Self { + id, + map: map as *mut u8, + map_size, + }) + } } } - pub fn new(map_size: usize) -> Result { - let mut ret = unix_shmem_unitialized(); - let map = unsafe { unix_shmem_init(&mut ret, map_size) }; - if map.is_null() { - Err(Error::Unknown(format!( - "Could not allocate map of size {} - check OS limits, (i.e. shmall, shmmax)!", - map_size - ))) - } else { - Ok(ret) + #[cfg(unix)] + impl ShMem for AshmemShMem { + fn id(&self) -> ShMemId { + self.id + } + + fn len(&self) -> usize { + self.map_size + } + + fn map(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self.map, self.map_size) } + } + + fn map_mut(&mut self) -> &mut [u8] { + unsafe { slice::from_raw_parts_mut(self.map, self.map_size) } } } - } - /// Deinitialize this shmem instance - #[allow(clippy::clippy::unnecessary_cast)] // for c_ types - unsafe fn unix_shmem_deinit(shm: *mut UnixShMem) { - if shm.is_null() || (*shm).map.is_null() { - /* Serialized map id */ - // Not set or not initialized; - return; - } - (*shm).shm_str[0_usize] = 0u8; - shmctl((*shm).shm_id, 0 as c_int, ptr::null_mut()); - (*shm).map = ptr::null_mut(); - } + /// Drop implementation for AshmemShMem, which cleans up the mapping + #[cfg(unix)] + impl Drop for AshmemShMem { + fn drop(&mut self) { + unsafe { + //let fd = Self::fd_from_id(self.id).unwrap(); + let fd: i32 = self.id.to_string().parse().unwrap(); - /// Functions to create Shared memory region, for observation channels and - /// opening inputs and stuff. - #[allow(clippy::clippy::unnecessary_cast)] // for c_ types - unsafe fn unix_shmem_init(shm: *mut UnixShMem, map_size: usize) -> *mut c_uchar { - (*shm).map_size = map_size; - (*shm).map = ptr::null_mut(); - (*shm).shm_id = shmget( - 0 as c_int, - map_size as c_ulong, - 0o1000 as c_int | 0o2000 as c_int | 0o600 as c_int, - ); - if (*shm).shm_id < 0 as c_int { - (*shm).shm_str[0] = 0u8; - return ptr::null_mut(); - } - snprintf( - (*shm).shm_str.as_mut_ptr() as *mut c_char, - size_of::<[c_char; 20]>() as c_ulong, - b"%d\x00" as *const u8 as *const c_char, - (*shm).shm_id, - ); - (*shm).shm_str - [(size_of::<[c_char; 20]>() as c_ulong).wrapping_sub(1 as c_ulong) as usize] = 0u8; - (*shm).map = shmat((*shm).shm_id, ptr::null(), 0 as c_int) as *mut c_uchar; - if (*shm).map == -(1 as c_int) as *mut c_void as *mut c_uchar || (*shm).map.is_null() { - shmctl((*shm).shm_id, 0 as c_int, ptr::null_mut()); - (*shm).shm_id = -(1 as c_int); - (*shm).shm_str[0] = 0u8; - return ptr::null_mut(); - } - (*shm).map - } + let length = ioctl(fd, ASHMEM_GET_SIZE); - /// Uses a shmap id string to open a shared map - #[allow(clippy::unnecessary_cast)] // for c_int and c_long - unsafe fn unix_shmem_by_str( - shm: *mut UnixShMem, - shm_str: &CStr, - map_size: usize, - ) -> *mut c_uchar { - if shm.is_null() || shm_str.to_bytes().is_empty() || map_size == 0 { - return ptr::null_mut(); + let ap = ashmem_pin { + offset: 0, + len: length as u32, + }; + + ioctl(fd, ASHMEM_UNPIN, &ap); + close(fd); + } + } } - (*shm).map = ptr::null_mut(); - (*shm).map_size = map_size; - strncpy( - (*shm).shm_str.as_mut_ptr() as *mut c_char, - shm_str.as_ptr() as *const c_char, - (size_of::<[c_char; 20]>() as c_ulong).wrapping_sub(1 as c_ulong), - ); - (*shm).shm_id = shm_str - .to_str() - .unwrap_or_else(|_| panic!("illegal shm_str {:?}", shm_str)) - .parse::() - .unwrap(); - (*shm).map = shmat((*shm).shm_id, ptr::null(), 0 as c_int) as *mut c_uchar; - if (*shm).map == -(1 as c_int) as *mut c_void as *mut c_uchar { - (*shm).map = ptr::null_mut(); - (*shm).map_size = 0; - (*shm).shm_str[0] = 0u8; - return ptr::null_mut(); + + /// A ShMemProvider which uses ashmem to provide shared memory mappings. + #[cfg(unix)] + #[derive(Clone, Debug)] + pub struct AshmemShMemProvider {} + + unsafe impl Send for AshmemShMemProvider {} + + #[cfg(unix)] + impl Default for AshmemShMemProvider { + fn default() -> Self { + Self::new() + } + } + + /// Implement ShMemProvider for AshmemShMemProvider + #[cfg(unix)] + impl ShMemProvider for AshmemShMemProvider { + type Mem = AshmemShMem; + + fn new() -> Self { + Self {} + } + + fn new_map(&mut self, map_size: usize) -> Result { + let mapping = AshmemShMem::new(map_size)?; + Ok(mapping) + } + + fn from_id_and_size(&mut self, id: ShMemId, size: usize) -> Result { + AshmemShMem::from_id_and_size(id, size) + } } - (*shm).map } } #[cfg(all(feature = "std", windows))] pub mod win32_shmem { - use super::ShMem; + use super::{ShMem, ShMemId, ShMemProvider}; use crate::{ bolts::bindings::{ windows::win32::system_services::{ @@ -450,6 +574,7 @@ pub mod win32_shmem { }; use core::{ffi::c_void, ptr, slice}; + use std::convert::TryInto; use uuid::Uuid; const INVALID_HANDLE_VALUE: isize = -1; @@ -458,80 +583,14 @@ pub mod win32_shmem { /// The default Sharedmap impl for windows using shmctl & shmget #[derive(Clone, Debug)] pub struct Win32ShMem { - pub shm_str: [u8; 20], - pub handle: HANDLE, - pub map: *mut u8, - pub map_size: usize, - } - - impl ShMem for Win32ShMem { - fn existing_from_shm_slice( - map_str_bytes: &[u8; 20], - map_size: usize, - ) -> Result { - Self::from_str(map_str_bytes, map_size) - } - - fn new_map(map_size: usize) -> Result { - Self::new(map_size) - } - - fn shm_slice(&self) -> &[u8; 20] { - &self.shm_str - } - - fn map(&self) -> &[u8] { - unsafe { slice::from_raw_parts(self.map, self.map_size) } - } - - fn map_mut(&mut self) -> &mut [u8] { - unsafe { slice::from_raw_parts_mut(self.map, self.map_size) } - } - } - - /// Deinit sharedmaps on drop - impl Drop for Win32ShMem { - fn drop(&mut self) { - unsafe { - UnmapViewOfFile(self.map as *mut c_void); - CloseHandle(self.handle); - } - } + id: ShMemId, + handle: HANDLE, + map: *mut u8, + map_size: usize, } impl Win32ShMem { - pub fn from_str(map_str_bytes: &[u8; 20], map_size: usize) -> Result { - unsafe { - let handle = OpenFileMappingA( - FILE_MAP_ALL_ACCESS, - BOOL(0), - PSTR(map_str_bytes as *const u8 as *mut u8), - ); - if handle == HANDLE(0) { - return Err(Error::Unknown(format!( - "Cannot open shared memory {}", - String::from_utf8_lossy(map_str_bytes) - ))); - } - let map = MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, map_size) as *mut u8; - if map.is_null() { - return Err(Error::Unknown(format!( - "Cannot map shared memory {}", - String::from_utf8_lossy(map_str_bytes) - ))); - } - let mut ret = Self { - shm_str: [0; 20], - handle: handle, - map: map, - map_size: map_size, - }; - ret.shm_str.clone_from_slice(map_str_bytes); - Ok(ret) - } - } - - pub fn new(map_size: usize) -> Result { + fn new_map(map_size: usize) -> Result { unsafe { let uuid = Uuid::new_v4(); let mut map_str = format!("libafl_{}", uuid.to_simple()); @@ -558,39 +617,99 @@ pub mod win32_shmem { String::from_utf8_lossy(map_str_bytes) ))); } - let mut ret = Self { - shm_str: [0; 20], + + Ok(Self { + id: ShMemId::from_slice(&map_str_bytes[0..20].try_into().unwrap()), handle, map, map_size, - }; - ret.shm_str.clone_from_slice(&map_str_bytes[0..20]); - Ok(ret) + }) + } + } + + fn from_id_and_size(id: ShMemId, map_size: usize) -> Result { + unsafe { + let map_str_bytes = id.id; + + let handle = OpenFileMappingA( + FILE_MAP_ALL_ACCESS, + BOOL(0), + PSTR(&map_str_bytes as *const u8 as *mut u8), + ); + if handle == HANDLE(0) { + return Err(Error::Unknown(format!( + "Cannot open shared memory {}", + String::from_utf8_lossy(&map_str_bytes) + ))); + } + let map = MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, map_size) as *mut u8; + if map.is_null() { + return Err(Error::Unknown(format!( + "Cannot map shared memory {}", + String::from_utf8_lossy(&map_str_bytes) + ))); + } + Ok(Self { + id, + handle, + map, + map_size, + }) } } } -} -#[cfg(test)] -mod tests { + impl ShMem for Win32ShMem { + fn id(&self) -> ShMemId { + self.id + } - #[cfg(all(unix, feature = "std"))] - use super::{ShMem, UnixShMem}; + fn len(&self) -> usize { + self.map_size + } - #[cfg(all(unix, feature = "std"))] - #[test] - fn test_str_conversions() { - let mut shm_str: [u8; 20] = [0; 20]; - shm_str[0] = 'A' as u8; - shm_str[1] = 'B' as u8; - shm_str[2] = 'C' as u8; - let faux_shmem = UnixShMem { - shm_id: 0, - shm_str, - map: 0 as *mut u8, - map_size: 20, - }; - let str = faux_shmem.shm_str(); - assert_eq!(str, "ABC"); + fn map(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self.map, self.map_size) } + } + + fn map_mut(&mut self) -> &mut [u8] { + unsafe { slice::from_raw_parts_mut(self.map, self.map_size) } + } + } + + /// Deinit sharedmaps on drop + impl Drop for Win32ShMem { + fn drop(&mut self) { + unsafe { + UnmapViewOfFile(self.map as *mut c_void); + CloseHandle(self.handle); + } + } + } + + /// A ShMemProvider which uses win32 functions to provide shared memory mappings. + #[derive(Clone, Debug)] + pub struct Win32ShMemProvider {} + + impl Default for Win32ShMemProvider { + fn default() -> Self { + Self::new() + } + } + + /// Implement ShMemProvider for Win32ShMemProvider + impl ShMemProvider for Win32ShMemProvider { + type Mem = Win32ShMem; + + fn new() -> Self { + Self {} + } + fn new_map(&mut self, map_size: usize) -> Result { + Win32ShMem::new_map(map_size) + } + + fn from_id_and_size(&mut self, id: ShMemId, size: usize) -> Result { + Win32ShMem::from_id_and_size(id, size) + } } } diff --git a/libafl/src/events/llmp.rs b/libafl/src/events/llmp.rs index ae85aebeb5..e9a8115efe 100644 --- a/libafl/src/events/llmp.rs +++ b/libafl/src/events/llmp.rs @@ -1,7 +1,7 @@ //! LLMP-backed event manager for scalable multi-processed fuzzing -use alloc::{string::ToString, vec::Vec}; -use core::{marker::PhantomData, time::Duration}; +use alloc::{rc::Rc, string::ToString, vec::Vec}; +use core::{cell::RefCell, marker::PhantomData, time::Duration}; use serde::{de::DeserializeOwned, Serialize}; #[cfg(feature = "std")] @@ -17,12 +17,16 @@ use crate::utils::startable_self; use crate::utils::{fork, ForkResult}; #[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::{ bolts::{ llmp::{self, LlmpClient, LlmpClientDescription, LlmpSender, Tag}, - shmem::ShMem, + shmem::ShMemProvider, }, corpus::CorpusScheduler, 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_NO_RESTART: llmp::Tag = 0x57A7EE71; -#[derive(Clone, Debug)] -pub struct LlmpEventManager +#[derive(Debug)] +pub struct LlmpEventManager where I: Input, S: IfInteresting, - SH: ShMem, + SP: ShMemProvider + 'static, ST: Stats, //CE: CustomEvent, { stats: Option, - llmp: llmp::LlmpConnection, + llmp: llmp::LlmpConnection, phantom: PhantomData<(I, S)>, } #[cfg(feature = "std")] #[cfg(unix)] -impl LlmpEventManager +impl LlmpEventManager where I: Input, S: IfInteresting, @@ -72,10 +76,14 @@ where /// If the port is not yet bound, it will act as broker /// Else, it will act as client. #[cfg(feature = "std")] - pub fn new_on_port_std(stats: ST, port: u16) -> Result { + pub fn new_on_port_std( + shmem_provider: &Rc>, + stats: ST, + port: u16, + ) -> Result { Ok(Self { stats: Some(stats), - llmp: llmp::LlmpConnection::on_port(port)?, + llmp: llmp::LlmpConnection::on_port(shmem_provider, port)?, phantom: PhantomData, }) } @@ -83,16 +91,19 @@ where /// If a client respawns, it may reuse the existing connection, previously stored by LlmpClient::to_env /// Std uses UnixShMem. #[cfg(feature = "std")] - pub fn existing_client_from_env_std(env_name: &str) -> Result { - Self::existing_client_from_env(env_name) + pub fn existing_client_from_env_std( + shmem_provider: &Rc>, + env_name: &str, + ) -> Result { + Self::existing_client_from_env(shmem_provider, env_name) } } -impl Drop for LlmpEventManager +impl Drop for LlmpEventManager where I: Input, S: IfInteresting, - SH: ShMem, + SP: ShMemProvider, ST: Stats, { /// LLMP clients will have to wait until their pages are mapped by somebody. @@ -101,32 +112,39 @@ where } } -impl LlmpEventManager +impl LlmpEventManager where I: Input, S: IfInteresting, - SH: ShMem, + SP: ShMemProvider, ST: Stats, { /// Create llmp on a port /// If the port is not yet bound, it will act as broker /// Else, it will act as client. #[cfg(feature = "std")] - pub fn new_on_port(stats: ST, port: u16) -> Result { + pub fn new_on_port( + shmem_provider: &Rc>, + stats: ST, + port: u16, + ) -> Result { Ok(Self { stats: Some(stats), - llmp: llmp::LlmpConnection::on_port(port)?, + llmp: llmp::LlmpConnection::on_port(shmem_provider, port)?, phantom: PhantomData, }) } /// If a client respawns, it may reuse the existing connection, previously stored by LlmpClient::to_env #[cfg(feature = "std")] - pub fn existing_client_from_env(env_name: &str) -> Result { + pub fn existing_client_from_env( + shmem_provider: &Rc>, + env_name: &str, + ) -> Result { Ok(Self { stats: None, 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. // In any case, the client won't currently use it. @@ -141,26 +159,21 @@ where /// Create an existing client from description pub fn existing_client_from_description( + shmem_provider: &Rc>, description: &LlmpClientDescription, ) -> Result { Ok(Self { 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. // In any case, the client won't currently use it. phantom: PhantomData, }) } - /// A client on an existing map - pub fn for_client(client: LlmpClient) -> 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 #[cfg(feature = "std")] pub fn to_env(&self, env_name: &str) { @@ -309,29 +322,11 @@ where } } -#[cfg(all(feature = "std", unix))] -impl LlmpEventManager +impl EventManager for LlmpEventManager where I: Input, S: IfInteresting, - SH: ShMem, - ST: Stats, -{ - #[cfg(all(feature = "std", unix))] - pub fn new_on_domain_socket(stats: ST, filename: &str) -> Result { - Ok(Self { - stats: Some(stats), - llmp: llmp::LlmpConnection::on_domain_socket(filename)?, - phantom: PhantomData, - }) - } -} - -impl EventManager for LlmpEventManager -where - I: Input, - S: IfInteresting, - SH: ShMem, + SP: ShMemProvider, ST: Stats, //CE: CustomEvent, { /// 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. /// 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_mgr( +pub fn serialize_state_mgr( state: &S, - mgr: &LlmpEventManager, + mgr: &LlmpEventManager, ) -> Result, Error> where I: Input, S: Serialize + IfInteresting, - SH: ShMem, + SP: ShMemProvider, ST: Stats, { Ok(postcard::to_allocvec(&(&state, &mgr.describe()?))?) @@ -403,43 +398,44 @@ where /// Deserialize the state and corpus tuple, previously serialized with `serialize_state_corpus(...)` #[allow(clippy::type_complexity)] -pub fn deserialize_state_mgr( +pub fn deserialize_state_mgr( + shmem_provider: &Rc>, state_corpus_serialized: &[u8], -) -> Result<(S, LlmpEventManager), Error> +) -> Result<(S, LlmpEventManager), Error> where I: Input, S: DeserializeOwned + IfInteresting, - SH: ShMem, + SP: ShMemProvider, ST: Stats, { let tuple: (S, _) = postcard::from_bytes(&state_corpus_serialized)?; Ok(( 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`) -#[derive(Clone, Debug)] -pub struct LlmpRestartingEventManager +#[derive(Debug)] +pub struct LlmpRestartingEventManager where I: Input, S: IfInteresting, - SH: ShMem, + SP: ShMemProvider + 'static, ST: Stats, //CE: CustomEvent, { /// The embedded llmp event manager - llmp_mgr: LlmpEventManager, + llmp_mgr: LlmpEventManager, /// The sender to serialize the state for the next runner - sender: LlmpSender, + sender: LlmpSender, } -impl EventManager for LlmpRestartingEventManager +impl EventManager for LlmpRestartingEventManager where I: Input, S: IfInteresting + Serialize, - SH: ShMem, + SP: ShMemProvider, ST: Stats, //CE: CustomEvent, { /// 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) const _ENV_FUZZER_BROKER_CLIENT_INITIAL: &str = &"_AFL_ENV_FUZZER_BROKER_CLIENT"; -impl LlmpRestartingEventManager +impl LlmpRestartingEventManager where I: Input, S: IfInteresting, - SH: ShMem, + SP: ShMemProvider, ST: Stats, //CE: CustomEvent, { /// Create a new runner, the executed child doing the actual fuzzing. - pub fn new(llmp_mgr: LlmpEventManager, sender: LlmpSender) -> Self { + pub fn new(llmp_mgr: LlmpEventManager, sender: LlmpSender) -> Self { Self { llmp_mgr, sender } } /// Get the sender - pub fn sender(&self) -> &LlmpSender { + pub fn sender(&self) -> &LlmpSender { &self.sender } /// Get the sender (mut) - pub fn sender_mut(&mut self) -> &mut LlmpSender { + pub fn sender_mut(&mut self) -> &mut LlmpSender { &mut self.sender } } +#[cfg(feature = "std")] +#[allow(clippy::type_complexity)] +pub fn setup_restarting_mgr_std( + //mgr: &mut LlmpEventManager, + stats: ST, + broker_port: u16, +) -> Result< + ( + Option, + LlmpRestartingEventManager, + ), + Error, +> +where + I: Input, + S: DeserializeOwned + IfInteresting, + 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`. /// The restarter will start a new process each time the child crashes or timeouts. #[cfg(feature = "std")] @@ -515,30 +535,25 @@ where clippy::type_complexity, clippy::similar_names )] // for { mgr = LlmpEventManager... } -pub fn setup_restarting_mgr( +pub fn setup_restarting_mgr( + shmem_provider: SP, //mgr: &mut LlmpEventManager, stats: ST, broker_port: u16, -) -> Result<(Option, LlmpRestartingEventManager), Error> +) -> Result<(Option, LlmpRestartingEventManager), Error> where I: Input, S: DeserializeOwned + IfInteresting, - SH: ShMem, + SP: ShMemProvider, ST: Stats, { - let mut mgr; + let shmem_provider = Rc::new(RefCell::new(shmem_provider)); + + let mut mgr = + LlmpEventManager::::new_on_port(&shmem_provider, stats, broker_port)?; // We start ourself as child process to actually fuzz - let (sender, mut receiver) = if std::env::var(_ENV_FUZZER_SENDER).is_err() { - #[cfg(target_os = "android")] - { - mgr = LlmpEventManager::::new_on_domain_socket(stats, "\x00llmp_socket")?; - }; - #[cfg(not(target_os = "android"))] - { - mgr = LlmpEventManager::::new_on_port(stats, broker_port)? - }; - + let (sender, mut receiver, shmem_provider) = if std::env::var(_ENV_FUZZER_SENDER).is_err() { if mgr.is_broker() { // Yep, broker. Just loop here. 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); // First, create a channel from the fuzzer (sender) to us (receiver) to report its state for restarts. - let sender = LlmpSender::new(0, false)?; - let receiver = LlmpReceiver::on_existing_map( - SH::clone_ref(&sender.out_maps.last().unwrap().shmem)?, - None, - )?; + let sender = { LlmpSender::new(&shmem_provider, 0, false)? }; + + let map = { + 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. sender.to_env(_ENV_FUZZER_SENDER)?; receiver.to_env(_ENV_FUZZER_RECEIVER)?; @@ -568,7 +586,7 @@ where #[cfg(unix)] let _ = match unsafe { fork() }? { ForkResult::Parent(handle) => handle.status(), - ForkResult::Child => break (sender, receiver), + ForkResult::Child => break (sender, receiver, shmem_provider), }; // On windows, we spawn ourself again @@ -585,20 +603,29 @@ where } else { // We are the newly started fuzzing instance, first, connect to our own restore map. // 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::::on_existing_from_env(_ENV_FUZZER_SENDER)?, - LlmpReceiver::::on_existing_from_env(_ENV_FUZZER_RECEIVER)?, + LlmpSender::on_existing_from_env(&shmem_provider, _ENV_FUZZER_SENDER)?, + LlmpReceiver::on_existing_from_env(&shmem_provider, _ENV_FUZZER_RECEIVER)?, + shmem_provider, ) }; 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. let (state, mut mgr) = match receiver.recv_buf()? { None => { println!("First run. Let's set it all up"); // Mgr to send and receive msgs from/to all other fuzzer instances - let client_mgr = LlmpEventManager::::existing_client_from_env( + let client_mgr = LlmpEventManager::::existing_client_from_env( + &shmem_provider, _ENV_FUZZER_BROKER_CLIENT_INITIAL, )?; @@ -607,7 +634,8 @@ where // Restoring from a previous run, deserialize state and corpus. Some((_sender, _tag, msg)) => { println!("Subsequent run. Let's load all data from shmem (received {} bytes from previous instance)", msg.len()); - let (state, mgr): (S, LlmpEventManager) = deserialize_state_mgr(&msg)?; + let (state, mgr): (S, LlmpEventManager) = + deserialize_state_mgr(&shmem_provider, &msg)?; (Some(state), LlmpRestartingEventManager::new(mgr, sender)) }