From 1e315baa56f1510c75924c9c021f2af8d996b700 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Thu, 17 Dec 2020 17:42:04 +0100 Subject: [PATCH 01/13] variable sized map observer --- afl/src/feedbacks/mod.rs | 2 +- afl/src/observers/mod.rs | 106 +++++++++++++++++++++++++++++++++- afl/src/serde_anymap.rs | 85 +++++++++++++++++++++++++-- fuzzers/qemufuzzer/src/lib.rs | 4 +- 4 files changed, 188 insertions(+), 9 deletions(-) diff --git a/afl/src/feedbacks/mod.rs b/afl/src/feedbacks/mod.rs index b7f2fd4e7b..8206a73f5d 100644 --- a/afl/src/feedbacks/mod.rs +++ b/afl/src/feedbacks/mod.rs @@ -189,7 +189,7 @@ where let mut interesting = 0; // TODO optimize let observer = observers.match_name_type::(&self.name).unwrap(); - let size = observer.map().len(); + let size = observer.usable_count(); for i in 0..size { let history = self.history_map[i]; let item = observer.map()[i]; diff --git a/afl/src/observers/mod.rs b/afl/src/observers/mod.rs index adeae619c8..c57643dc0f 100644 --- a/afl/src/observers/mod.rs +++ b/afl/src/observers/mod.rs @@ -2,7 +2,7 @@ extern crate num; use serde::{Deserialize, Serialize}; -use crate::serde_anymap::ArrayMut; +use crate::serde_anymap::{Cptr, ArrayMut}; use crate::tuples::{MatchNameAndType, MatchType, Named, TupleList}; use crate::AflError; @@ -92,6 +92,11 @@ where /// Get the map (mutable) fn map_mut(&mut self) -> &mut [T]; + /// Get the number of usable entries in the map (all by default) + fn usable_count(&self) -> usize { + self.map().len() + } + /// Get the initial value for reset() fn initial(&self) -> T; @@ -106,7 +111,8 @@ where fn reset_map(&mut self) -> Result<(), AflError> { // Normal memset, see https://rust.godbolt.org/z/Trs5hv let initial = self.initial(); - for i in self.map_mut().iter_mut() { + let cnt = self.usable_count(); + for i in self.map_mut()[0..cnt].iter_mut() { *i = initial; } Ok(()) @@ -204,6 +210,102 @@ where } } +#[derive(Serialize, Deserialize)] +#[serde(bound = "T: serde::de::DeserializeOwned")] +pub struct VariableMapObserver +where + T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, +{ + map: ArrayMut, + size: Cptr, + initial: T, + name: String, +} + +impl Observer for VariableMapObserver +where + T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, +{ + #[inline] + fn reset(&mut self) -> Result<(), AflError> { + self.reset_map() + } +} + +impl Named for VariableMapObserver +where + T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, +{ + #[inline] + fn name(&self) -> &str { + self.name.as_str() + } +} + +impl MapObserver for VariableMapObserver +where + T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, +{ + #[inline] + fn map(&self) -> &[T] { + self.map.as_slice() + } + + #[inline] + fn map_mut(&mut self) -> &mut [T] { + self.map.as_mut_slice() + } + + #[inline] + fn usable_count(&self) -> usize { + *self.size.as_ref() + } + + #[inline] + fn initial(&self) -> T { + self.initial + } + + #[inline] + fn initial_mut(&mut self) -> &mut T { + &mut self.initial + } + + #[inline] + fn set_initial(&mut self, initial: T) { + self.initial = initial + } +} + +impl VariableMapObserver +where + T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, +{ + /// Creates a new MapObserver + pub fn new(name: &'static str, map: &'static mut [T], size: &usize) -> Self { + let initial = if map.len() > 0 { map[0] } else { T::default() }; + Self { + map: ArrayMut::Cptr((map.as_mut_ptr(), map.len())), + size: Cptr::Cptr(size as *const _), + name: name.into(), + initial, + } + } + + /// Creates a new MapObserver from a raw pointer + pub fn new_from_ptr(name: &'static str, map_ptr: *mut T, max_len: usize, size_ptr: *const usize) -> Self { + unsafe { + let initial = if max_len > 0 { *map_ptr } else { T::default() }; + VariableMapObserver { + map: ArrayMut::Cptr((map_ptr, max_len)), + size: Cptr::Cptr(size_ptr), + name: name.into(), + initial, + } + } + } +} + /* #[cfg(feature = "std")] #[cfg(test)] diff --git a/afl/src/serde_anymap.rs b/afl/src/serde_anymap.rs index 4cab18cb85..d5993b8c7a 100644 --- a/afl/src/serde_anymap.rs +++ b/afl/src/serde_anymap.rs @@ -629,7 +629,84 @@ impl<'a, T: Sized> SliceMut<'a, T> { } } -pub enum Array { +pub enum Cptr { + Cptr(*const T), + Owned(Box), +} + +impl serde::Serialize for Cptr { + fn serialize(&self, se: S) -> Result + where + S: serde::Serializer, + { + self.as_ref().serialize(se) + } +} + +impl<'de, T: Sized + serde::de::DeserializeOwned> Deserialize<'de> for Cptr +where + Vec: Deserialize<'de>, +{ + fn deserialize(de: D) -> Result + where + D: serde::Deserializer<'de>, + { + Deserialize::deserialize(de).map(Cptr::Owned) + } +} + +impl Cptr { + pub fn as_ref(&self) -> &T { + match self { + Cptr::Cptr(p) => unsafe { p.as_ref().unwrap() }, + Cptr::Owned(v) => v.as_ref(), + } + } +} + +pub enum CptrMut { + Cptr(*mut T), + Owned(Box), +} + +impl serde::Serialize for CptrMut { + fn serialize(&self, se: S) -> Result + where + S: serde::Serializer, + { + self.as_ref().serialize(se) + } +} + +impl<'de, T: Sized + serde::de::DeserializeOwned> Deserialize<'de> for CptrMut +where + Vec: Deserialize<'de>, +{ + fn deserialize(de: D) -> Result + where + D: serde::Deserializer<'de>, + { + Deserialize::deserialize(de).map(CptrMut::Owned) + } +} + +impl CptrMut { + pub fn as_ref(&self) -> &T { + match self { + CptrMut::Cptr(p) => unsafe { p.as_ref().unwrap() }, + CptrMut::Owned(b) => b.as_ref(), + } + } + + pub fn as_mut(&mut self) -> &mut T { + match self { + CptrMut::Cptr(p) => unsafe { p.as_mut().unwrap() }, + CptrMut::Owned(b) => b.as_mut(), + } + } +} + +pub enum Array { Cptr((*const T, usize)), Owned(Vec), } @@ -655,7 +732,7 @@ where } } -impl Array { +impl Array { pub fn as_slice(&self) -> &[T] { match self { Array::Cptr(p) => unsafe { core::slice::from_raw_parts(p.0, p.1) }, @@ -664,7 +741,7 @@ impl Array { } } -pub enum ArrayMut { +pub enum ArrayMut { Cptr((*mut T, usize)), Owned(Vec), } @@ -690,7 +767,7 @@ where } } -impl ArrayMut { +impl ArrayMut { pub fn as_slice(&self) -> &[T] { match self { ArrayMut::Cptr(p) => unsafe { core::slice::from_raw_parts(p.0, p.1) }, diff --git a/fuzzers/qemufuzzer/src/lib.rs b/fuzzers/qemufuzzer/src/lib.rs index 38f83b434a..7454f25d3b 100644 --- a/fuzzers/qemufuzzer/src/lib.rs +++ b/fuzzers/qemufuzzer/src/lib.rs @@ -14,7 +14,7 @@ use afl::feedbacks::MaxMapFeedback; use afl::generators::RandPrintablesGenerator; use afl::mutators::scheduled::HavocBytesMutator; use afl::mutators::HasMaxSize; -use afl::observers::StdMapObserver; +use afl::observers::VariableMapObserver; use afl::stages::mutational::StdMutationalStage; use afl::tuples::tuple_list; use afl::utils::StdRand; @@ -66,7 +66,7 @@ pub extern "C" fn fuzz_main_loop() { } println!("We're a client, let's fuzz :)"); - let edges_observer = StdMapObserver::new_from_ptr(&NAME_COV_MAP, unsafe { fuzz_hitcounts_map.as_mut_ptr() }, unsafe { fuzz_edges_id }); + let edges_observer = VariableMapObserver::new(&NAME_COV_MAP, unsafe { &mut fuzz_hitcounts_map }, unsafe { &fuzz_edges_id }); let edges_feedback = MaxMapFeedback::new_with_observer(&NAME_COV_MAP, &edges_observer); let executor = InMemoryExecutor::new("QEMUFuzzer", harness, tuple_list!(edges_observer)); From 5c15620bd300c1a04b297313e2effab25e8e0dca Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Thu, 17 Dec 2020 18:24:00 +0100 Subject: [PATCH 02/13] qemu fuzzer build.sh --- .gitmodules | 3 +++ fuzzers/qemufuzzer/{test.sh => build.sh} | 5 +++-- fuzzers/qemufuzzer/qemu-fuzz | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 .gitmodules rename fuzzers/qemufuzzer/{test.sh => build.sh} (78%) create mode 160000 fuzzers/qemufuzzer/qemu-fuzz diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000..a0c3ef7ad6 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "fuzzers/qemufuzzer/qemu-fuzz"] + path = fuzzers/qemufuzzer/qemu-fuzz + url = git@github.com:AFLplusplus/qemu-fuzz.git diff --git a/fuzzers/qemufuzzer/test.sh b/fuzzers/qemufuzzer/build.sh similarity index 78% rename from fuzzers/qemufuzzer/test.sh rename to fuzzers/qemufuzzer/build.sh index bb7c64cbdb..f2e5dddf0e 100755 --- a/fuzzers/qemufuzzer/test.sh +++ b/fuzzers/qemufuzzer/build.sh @@ -2,8 +2,9 @@ cargo build --release -cd qemu -git pull +cd qemu-fuzz + +git submodule update ./build_qemu_fuzz.sh ../target/release/libqemufuzzer.a diff --git a/fuzzers/qemufuzzer/qemu-fuzz b/fuzzers/qemufuzzer/qemu-fuzz new file mode 160000 index 0000000000..6f719f6aed --- /dev/null +++ b/fuzzers/qemufuzzer/qemu-fuzz @@ -0,0 +1 @@ +Subproject commit 6f719f6aedc9c199d7b99ef42c532a9a20605ff3 From 93622240200c271a1280d975e91cb2fff2ac9178 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Fri, 18 Dec 2020 01:14:16 +0100 Subject: [PATCH 03/13] shm trait stated --- afl/Cargo.toml | 1 + afl/src/events/llmp.rs | 573 +++++++++++++++++------------ afl/src/events/mod.rs | 3 +- afl/src/events/shmem_translated.rs | 42 ++- afl/src/lib.rs | 23 +- fuzzers/libfuzzer/src/lib.rs | 9 +- 6 files changed, 404 insertions(+), 247 deletions(-) diff --git a/afl/Cargo.toml b/afl/Cargo.toml index 328552e0bc..21cf033946 100644 --- a/afl/Cargo.toml +++ b/afl/Cargo.toml @@ -40,3 +40,4 @@ serde = { version = "1.0", default-features = false, features = ["alloc"] } # se erased-serde = "0.3.12" postcard = { version = "0.5.1", features = ["alloc"] } # no_std compatible serde serialization fromat static_assertions = "1.1.0" +#TODO: for llmp brotli = { version = "3.3.0", default-features = false } # brotli compression diff --git a/afl/src/events/llmp.rs b/afl/src/events/llmp.rs index c38d222a50..ec58bcfdf5 100644 --- a/afl/src/events/llmp.rs +++ b/afl/src/events/llmp.rs @@ -58,6 +58,7 @@ use core::{ #[cfg(feature = "std")] use std::{ + env, io::{Read, Write}, net::{TcpListener, TcpStream}, thread, @@ -67,7 +68,7 @@ use crate::utils::next_pow2; use crate::AflError; #[cfg(feature = "std")] -use super::shmem_translated::AflShmem; +use super::shmem_translated::{AflShmem, ShMem}; /// We'll start off with 256 megabyte maps per fuzzer client const LLMP_PREF_INITIAL_MAP_SIZE: usize = 1 << 28; @@ -96,14 +97,17 @@ pub type Tag = u32; /// Sending end on a (unidirectional) sharedmap channel #[derive(Clone, Debug)] -pub struct LlmpSender { +pub struct LlmpSender +where + SH: ShMem, +{ /// ID of this sender. Only used in the broker. pub id: u32, /// Ref to the last message this sender sent on the last page. /// If null, a new page (just) started. pub last_msg_sent: *mut 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, @@ -113,29 +117,38 @@ pub struct LlmpSender { /// Receiving end on a (unidirectional) sharedmap channel #[derive(Clone, Debug)] -pub struct LlmpReceiver { +pub struct LlmpReceiver +where + SH: ShMem +{ pub id: u32, /// Pointer to the last meg this received pub last_msg_recvd: *mut LlmpMsg, /// current page. After EOP, this gets replaced with the new one - pub current_recv_map: LlmpSharedMap, + pub current_recv_map: LlmpSharedMap, } /// Client side of LLMP #[derive(Clone, Debug)] -pub struct LlmpClient { +pub struct LlmpClient +where + SH: ShMem, +{ /// Outgoing channel to the broker - pub llmp_out: LlmpSender, + pub llmp_out: LlmpSender, /// Incoming (broker) broadcast map - pub llmp_in: LlmpReceiver, + pub llmp_in: LlmpReceiver, } /// A page wrapper #[derive(Clone, Debug)] -pub struct LlmpSharedMap { +pub struct LlmpSharedMap +where + SH: ShMem, +{ /// Shmem containg the actual (unsafe) page, /// shared between one LlmpSender and one LlmpReceiver - shmem: AflShmem, + shmem: SH, } /// Message sent over the "wire" #[derive(Copy, Clone, Debug)] @@ -165,7 +178,7 @@ impl LlmpMsg { /// Gets the buffer from this message as slice, with the corrent length. #[inline] - pub fn as_slice(&self, map: &LlmpSharedMap) -> Result<&[u8], AflError> { + pub fn as_slice(&self, map: &LlmpSharedMap) -> Result<&[u8], AflError> { unsafe { if self.in_map(map) { Ok(self.as_slice_unsafe()) @@ -177,17 +190,18 @@ impl LlmpMsg { /// Returns true, if the pointer is, indeed, in the page of this shared map. #[inline] - pub fn in_map(&self, map: &LlmpSharedMap) -> bool { + pub fn in_map(&self, map: &LlmpSharedMap) -> bool { unsafe { + let map_size = map.shmem.map().len(); let buf_ptr = self.buf.as_ptr(); if buf_ptr > (map.page() as *const u8).offset(size_of::() as isize) && buf_ptr <= (map.page() as *const u8) - .offset((map.shmem.map_size - size_of::() as usize) as isize) + .offset((map_size - size_of::() as usize) as isize) { // The message header is in the page. Continue with checking the body. let len = self.buf_len_padded as usize + size_of::(); - buf_ptr <= (map.page() as *const u8).offset((map.shmem.map_size - len) as isize) + buf_ptr <= (map.page() as *const u8).offset((map_size - len) as isize) } else { false } @@ -195,18 +209,24 @@ impl LlmpMsg { } } + + + /// An Llmp instance -pub enum LlmpConnection { +pub enum LlmpConnection +where + SH: ShMem, +{ /// A broker and a thread using this tcp background thread IsBroker { - broker: LlmpBroker, + broker: LlmpBroker, listener_thread: thread::JoinHandle<()>, }, /// A client, connected to the port - IsClient { client: LlmpClient }, + IsClient { client: LlmpClient }, } -impl LlmpConnection { +impl LlmpConnection { /// 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 { match TcpListener::bind(format!("127.0.0.1:{}", port)) { @@ -264,13 +284,16 @@ pub struct LlmpPage { /// The broker (node 0) #[derive(Clone)] #[repr(C)] -pub struct LlmpBroker { +pub struct LlmpBroker +where + SH: ShMem, +{ /// 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>, } /// Result of an LLMP Mesasge hook @@ -293,8 +316,8 @@ struct LlmpPayloadSharedMapInfo { /// Get sharedmem from a page #[inline] -unsafe fn shmem2page(afl_shmem: &AflShmem) -> *mut LlmpPage { - afl_shmem.map as *mut LlmpPage +unsafe fn shmem2page(afl_shmem: &SH) -> *mut LlmpPage { + afl_shmem.map().as_mut_ptr() as *mut LlmpPage } /// Return, if a msg is contained in the current page @@ -335,7 +358,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 AflShmem, sender: u32) { - let page = shmem2page(&shmem); + let page = shmem2page(shmem); (*page).sender = sender; ptr::write_volatile(&mut (*page).current_msg_id, 0); (*page).max_alloc_size = 0; @@ -350,15 +373,16 @@ unsafe fn _llmp_page_init(shmem: &mut AflShmem, sender: u32) { /// 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: &LlmpSharedMap, +unsafe fn llmp_next_msg_ptr_checked ( + map: &LlmpSharedMap, last_msg: *const LlmpMsg, alloc_size: usize, ) -> Result<*mut LlmpMsg, AflError> { let page = map.page(); + let map_size = map.shmem.map().len(); let msg_begin_min = (page as *const u8).offset(size_of::() as isize); // We still need space for this msg (alloc_size). - let msg_begin_max = (page as *const u8).offset((map.shmem.map_size - alloc_size) as isize); + let msg_begin_max = (page as *const u8).offset((map_size - alloc_size) as isize); let next = _llmp_next_msg_ptr(last_msg); let next_ptr = next as *const u8; if next_ptr >= msg_begin_min && next_ptr <= msg_begin_max { @@ -381,7 +405,7 @@ unsafe fn _llmp_next_msg_ptr(last_msg: *const LlmpMsg) -> *mut LlmpMsg { } /// An actor on the sendin part of the shared map -impl LlmpSender { +impl LlmpSender { /// For non zero-copy, we want to get rid of old pages with duplicate messages in the client /// eventually. This function This funtion sees if we can unallocate older pages. /// The broker would have informed us by setting the save_to_unmap-flag. @@ -550,7 +574,7 @@ impl LlmpSender { let old_map = self.out_maps.last_mut().unwrap().page(); // Create a new shard page. - let new_map_shmem = LlmpSharedMap::new((*old_map).sender, (*old_map).max_alloc_size)?; + let new_map_shmem = LlmpSharedMap::::new((*old_map).sender, (*old_map).max_alloc_size)?; let mut new_map = new_map_shmem.page(); ptr::write_volatile(&mut (*new_map).current_msg_id, (*old_map).current_msg_id); @@ -561,8 +585,8 @@ impl LlmpSender { (*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_size; - (*end_of_page_msg).shm_str = new_map_shmem.shmem.shm_str; + (*end_of_page_msg).map_size = new_map_shmem.shmem.map().len(); + (*end_of_page_msg).shm_str = *new_map_shmem.shmem.shm_str_buf(); // We never sent a msg on the new buf */ self.last_msg_sent = 0 as *mut LlmpMsg; @@ -631,7 +655,10 @@ impl LlmpSender { } /// Receiving end of an llmp channel -impl LlmpReceiver { +impl LlmpReceiver +where + SH: ShMem, +{ // Never inline, to not get some strange effects /// Read next message. #[inline(never)] @@ -690,14 +717,14 @@ impl LlmpReceiver { // Mark the old page save to unmap, in case we didn't so earlier. 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::from_name_slice( + self.current_recv_map = LlmpSharedMap::new_from_shm_str_buf( &pageinfo_cpy.shm_str, pageinfo_cpy.map_size, )?; // 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); - dbg!("Got a new recv map", self.current_recv_map.shmem.shm_str); + dbg!("Got a new recv map", self.current_recv_map.shmem.shm_str()); // After we mapped the new page, return the next message, if available return self.recv(); } @@ -765,7 +792,7 @@ impl LlmpReceiver { } /// The page struct, placed on a shared mem instance. -impl LlmpSharedMap { +impl LlmpSharedMap { /// Creates a new page with minimum prev_max_alloc_size or LLMP_PREF_INITIAL_MAP_SIZE /// returning the initialized shared mem struct pub fn new(sender: u32, min_size: usize) -> Result { @@ -777,12 +804,41 @@ impl LlmpSharedMap { Ok(Self { shmem }) } + /// Initialize from a shm_str with fixed len of 20 + pub fn from_name(shm_str: &str, map_size: usize) -> Result { + let slice: [u8; 20]; + slice.copy_from_slice(shm_str.as_bytes()); + Self::from_name_slice(&slice, map_size) + } + /// Initialize from a shm_str with fixed len of 20 pub fn from_name_slice(shm_str: &[u8; 20], map_size: usize) -> Result { let shmem = AflShmem::from_name_slice(shm_str, map_size)?; // Not initializing the page here - the other side should have done it already! Ok(Self { shmem }) } +} + +impl LlmpSharedMap +where + SH: ShMem, +{ + + /// Initialize from a shm_str with fixed len of 20 + pub fn new_from_shm_str_buf(&self, shm_str: &[u8; 20], map_size: usize) -> Result { + let shmem = self.shmem.new_from_shm_str_buf(shm_str, map_size)?; + // Not initializing the page here - the other side should have done it already! + Ok(Self { shmem }) + } + + pub fn write_to_env(&self, env_name: String) -> Result<(), AflError> { + let map_size = self.shmem.map().len(); + let map_env = self.shmem.shm_str() ; + let map_size_env = format!("{}_SIZE", map_env); + env::set_var(map_env, self.shmem.shm_str()); + env::set_var(map_size_env, format!("{}", map_size)); + Ok(()) + } /// Get the unsafe ptr to this page, situated on the shared map pub unsafe fn page(&self) -> *mut LlmpPage { @@ -792,7 +848,7 @@ impl LlmpSharedMap { /// The broker forwards all messages to its own bus-like broadcast map. /// It may intercept messages passing through. -impl LlmpBroker { +impl LlmpBroker { /// Create and initialize a new llmp_broker pub fn new() -> Result { let broker = LlmpBroker { @@ -817,7 +873,7 @@ impl LlmpBroker { /// 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, client_page: LlmpSharedMap) { + pub fn register_client(&mut self, client_page: LlmpSharedMap) { let id = self.llmp_clients.len() as u32; self.llmp_clients.push(LlmpReceiver { id, @@ -845,6 +901,232 @@ impl LlmpBroker { Ok(()) } + + /// The broker walks all pages and looks for changes, then broadcasts them on + /// its own shared page, once. + #[inline] + pub fn once(&mut self, on_new_msg: &mut F) -> Result<(), AflError> + where + F: FnMut(u32, Tag, &[u8]) -> Result, + { + compiler_fence(Ordering::SeqCst); + for i in 0..self.llmp_clients.len() { + unsafe { + self.handle_new_msgs(i as u32, on_new_msg)?; + } + } + Ok(()) + } + + /// Loops infinitely, forwarding and handling all incoming messages from clients. + /// Never returns. Panics on error. + /// 5 millis of sleep can't hurt to keep busywait not at 100% + pub fn loop_forever(&mut self, on_new_msg: &mut F, sleep_time: Option) -> ! + where + F: FnMut(u32, Tag, &[u8]) -> Result, + { + loop { + compiler_fence(Ordering::SeqCst); + self.once(on_new_msg) + .expect("An error occurred when brokering. Exiting."); + match sleep_time { + Some(time) => thread::sleep(time), + None => (), + } + } + } + + /// Broadcasts the given buf to all lients + pub fn send_buf(&mut self, tag: Tag, buf: &[u8]) -> Result<(), AflError> { + self.llmp_out.send_buf(tag, buf) + } + +} + +impl LlmpBroker { + + + /// Launches a thread using a tcp listener socket, on which new clients may connect to this broker + /// Does so on the given port. + pub fn launch_tcp_listener_on( + &mut self, + port: u16, + ) -> Result, AflError> { + let listener = TcpListener::bind(format!("127.0.0.1:{}", port))?; + // accept connections and process them, spawning a new thread for each one + println!("Server listening on port {}", port); + return self.launch_tcp_listener(listener); + } + + /// Launches a thread using a tcp listener socket, on which new clients may connect to this broker + pub fn launch_tcp_listener( + &mut self, + listener: TcpListener, + ) -> Result, AflError> { + // Later in the execution, after the initial map filled up, + // the current broacast map will will point to a different map. + // However, the original map is (as of now) never freed, new clients will start + // to read from the initial map id. + + let client_out_map_mem = &self.llmp_out.out_maps.first().unwrap().shmem; + let broadcast_str_initial = client_out_map_mem.shm_str_buf().clone(); + + 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, LLMP_PREF_INITIAL_MAP_SIZE)?; + let tcp_out_map_str = tcp_out_map.shmem.shm_str_buf(); + let tcp_out_map_size = tcp_out_map.shmem.map().len(); + self.register_client(tcp_out_map); + + Ok(thread::spawn(move || { + let mut new_client_sender = LlmpSender { + id: 0, + last_msg_sent: 0 as *mut LlmpMsg, + out_maps: vec![ + LlmpSharedMap::from_name_slice(&tcp_out_map_str, tcp_out_map_size).unwrap(), + ], + // drop pages to the broker if it already read them + keep_pages_forever: false, + }; + + loop { + let (mut stream, addr) = match listener.accept() { + Ok(res) => res, + Err(e) => { + dbg!("Ignoring failed accept", e); + continue; + } + }; + dbg!("New connection", addr, stream.peer_addr().unwrap()); + match stream.write(&broadcast_str_initial) { + Ok(_) => {} // fire & forget + Err(e) => { + dbg!("Could not send to shmap to client", e); + continue; + } + }; + let mut new_client_map_str: [u8; 20] = Default::default(); + match stream.read_exact(&mut new_client_map_str) { + Ok(()) => (), + Err(e) => { + dbg!("Ignoring failed read from client", e); + continue; + } + }; + + unsafe { + 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 = new_client_map_str; + (*pageinfo).map_size = LLMP_PREF_INITIAL_MAP_SIZE; + match new_client_sender.send(msg) { + Ok(()) => (), + Err(e) => println!("Error forwarding client on map: {:?}", e), + }; + } + } + })) + } + + fn map_new_page(shm_str_buf: &[u8; 20], map_size: usize) -> Result, AflError> { + LlmpSharedMap::from_name_slice(shm_str_buf, map_size) + } +} + +/// `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 { + + fn map_new_page(shm_str_buf: &[u8; 20], map_size: usize) -> Result, AflError> { + panic!("End of page not handled!"); + } + + /// Creates a new LlmpClient + pub fn new(initial_broker_map: LlmpSharedMap) -> Result { + Ok(Self { + llmp_out: LlmpSender { + id: 0, + last_msg_sent: 0 as *mut LlmpMsg, + out_maps: vec![LlmpSharedMap::new(0, LLMP_PREF_INITIAL_MAP_SIZE)?], + // drop pages to the broker if it already read them + keep_pages_forever: false, + }, + llmp_in: LlmpReceiver { + id: 0, + current_recv_map: initial_broker_map, + last_msg_recvd: 0 as *mut LlmpMsg, + }, + }) + } + + /// Commits a msg to the client's out map + pub unsafe fn send(&mut self, msg: *mut LlmpMsg) -> Result<(), AflError> { + self.llmp_out.send(msg) + } + + /// Allocates a message of the given size, tags it, and sends it off. + pub fn send_buf(&mut self, tag: Tag, buf: &[u8]) -> Result<(), AflError> { + self.llmp_out.send_buf(tag, buf) + } + + /// Informs the broker about a new client in town, with the given map id + pub fn send_client_added_msg( + &mut self, + shm_str: &[u8; 20], + shm_id: usize, + ) -> Result<(), AflError> { + // We write this by hand to get around checks in send_buf + unsafe { + let msg = self + .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 = *shm_str; + (*pageinfo).map_size = shm_id; + self.send(msg) + } + } + + /// A client receives a broadcast message. + /// Returns null if no message is availiable + #[inline] + pub unsafe fn recv(&mut self) -> Result, AflError> { + self.llmp_in.recv() + } + + /// A client blocks/spins until the next message gets posted to the page, + /// then returns that message. + #[inline] + pub unsafe fn recv_blocking(&mut self) -> Result<*mut LlmpMsg, AflError> { + self.llmp_in.recv_blocking() + } + + /// The current page could have changed in recv (EOP) + /// Alloc the next message, internally handling end of page by allocating a new one. + #[inline] + pub unsafe fn alloc_next(&mut self, buf_len: usize) -> Result<*mut LlmpMsg, AflError> { + self.llmp_out.alloc_next(buf_len) + } + + /// Returns the next message, tag, buf, if avaliable, else None + #[inline] + pub fn recv_buf(&mut self) -> Result, AflError> { + self.llmp_in.recv_buf() + } + + /// Receives a buf from the broker, looping until a messages becomes avaliable + #[inline] + pub fn recv_buf_blocking(&mut self) -> Result<(u32, u32, &[u8]), AflError> { + self.llmp_in.recv_buf_blocking() + } + + + /// broker broadcast to its own page for all others to read */ #[inline] unsafe fn handle_new_msgs( @@ -912,153 +1194,20 @@ impl LlmpBroker { } } - /// The broker walks all pages and looks for changes, then broadcasts them on - /// its own shared page, once. - #[inline] - pub fn once(&mut self, on_new_msg: &mut F) -> Result<(), AflError> - where - F: FnMut(u32, Tag, &[u8]) -> Result, - { - compiler_fence(Ordering::SeqCst); - for i in 0..self.llmp_clients.len() { - unsafe { - self.handle_new_msgs(i as u32, on_new_msg)?; - } - } - Ok(()) - } - - /// Loops infinitely, forwarding and handling all incoming messages from clients. - /// Never returns. Panics on error. - /// 5 millis of sleep can't hurt to keep busywait not at 100% - pub fn loop_forever(&mut self, on_new_msg: &mut F, sleep_time: Option) -> ! - where - F: FnMut(u32, Tag, &[u8]) -> Result, - { - loop { - compiler_fence(Ordering::SeqCst); - self.once(on_new_msg) - .expect("An error occurred when brokering. Exiting."); - match sleep_time { - Some(time) => thread::sleep(time), - None => (), - } - } - } - - /// Launches a thread using a tcp listener socket, on which new clients may connect to this broker - /// Does so on the given port. - pub fn launch_tcp_listener_on( - &mut self, - port: u16, - ) -> Result, AflError> { - let listener = TcpListener::bind(format!("127.0.0.1:{}", port))?; - // accept connections and process them, spawning a new thread for each one - println!("Server listening on port {}", port); - return self.launch_tcp_listener(listener); - } - - /// Launches a thread using a tcp listener socket, on which new clients may connect to this broker - pub fn launch_tcp_listener( - &mut self, - listener: TcpListener, - ) -> Result, AflError> { - // Later in the execution, after the initial map filled up, - // the current broacast map will will point to a different map. - // However, the original map is (as of now) never freed, new clients will start - // to read from the initial map id. - - let client_out_map_mem = &self.llmp_out.out_maps.first().unwrap().shmem; - let broadcast_str_initial = client_out_map_mem.shm_str.clone(); - - 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, LLMP_PREF_INITIAL_MAP_SIZE)?; - let tcp_out_map_str = tcp_out_map.shmem.shm_str; - let tcp_out_map_size = tcp_out_map.shmem.map_size; - self.register_client(tcp_out_map); - - Ok(thread::spawn(move || { - let mut new_client_sender = LlmpSender { - id: 0, - last_msg_sent: 0 as *mut LlmpMsg, - out_maps: vec![ - LlmpSharedMap::from_name_slice(&tcp_out_map_str, tcp_out_map_size).unwrap(), - ], - // drop pages to the broker if it already read them - keep_pages_forever: false, - }; - - loop { - let (mut stream, addr) = match listener.accept() { - Ok(res) => res, - Err(e) => { - dbg!("Ignoring failed accept", e); - continue; - } - }; - dbg!("New connection", addr, stream.peer_addr().unwrap()); - match stream.write(&broadcast_str_initial) { - Ok(_) => {} // fire & forget - Err(e) => { - dbg!("Could not send to shmap to client", e); - continue; - } - }; - let mut new_client_map_str: [u8; 20] = Default::default(); - match stream.read_exact(&mut new_client_map_str) { - Ok(()) => (), - Err(e) => { - dbg!("Ignoring failed read from client", e); - continue; - } - }; - - unsafe { - 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 = new_client_map_str; - (*pageinfo).map_size = LLMP_PREF_INITIAL_MAP_SIZE; - match new_client_sender.send(msg) { - Ok(()) => (), - Err(e) => println!("Error forwarding client on map: {:?}", e), - }; - } - } - })) - } - - /// Broadcasts the given buf to all lients - pub fn send_buf(&mut self, tag: Tag, buf: &[u8]) -> Result<(), AflError> { - self.llmp_out.send_buf(tag, buf) - } } -/// `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 { - /// Creates a new LlmpClient - pub fn new(initial_broker_map: LlmpSharedMap) -> Result { - Ok(Self { - llmp_out: LlmpSender { - id: 0, - last_msg_sent: 0 as *mut LlmpMsg, - out_maps: vec![LlmpSharedMap::new(0, LLMP_PREF_INITIAL_MAP_SIZE)?], - // drop pages to the broker if it already read them - keep_pages_forever: false, - }, - llmp_in: LlmpReceiver { - id: 0, - current_recv_map: initial_broker_map, - last_msg_recvd: 0 as *mut LlmpMsg, - }, - }) +impl LlmpClient { + + /// Creates a new LlmpClient, reading the map id and len from env + pub fn create_using_env(env_var: &str) -> Result { + + let map_str = env::var(env_var)?; + let map_size = str::parse::(&env::var(format!("{}_SIZE", env_var))?)?; + Ok(Self::new(LlmpSharedMap::from_name(&map_str, map_size)?)?) + } + /// Create a LlmpClient, getting the ID from a given port pub fn create_attach_to_tcp(port: u16) -> Result { let mut stream = TcpStream::connect(format!("127.0.0.1:{}", port))?; println!("Connected to port {}", port); @@ -1071,71 +1220,10 @@ impl LlmpClient { LLMP_PREF_INITIAL_MAP_SIZE, )?)?; - stream.write(&ret.llmp_out.out_maps.first().unwrap().shmem.shm_str)?; + stream.write(ret.llmp_out.out_maps.first().unwrap().shmem.shm_str_buf())?; Ok(ret) } - /// Commits a msg to the client's out map - pub unsafe fn send(&mut self, msg: *mut LlmpMsg) -> Result<(), AflError> { - self.llmp_out.send(msg) - } - - /// Allocates a message of the given size, tags it, and sends it off. - pub fn send_buf(&mut self, tag: Tag, buf: &[u8]) -> Result<(), AflError> { - self.llmp_out.send_buf(tag, buf) - } - - /// Informs the broker about a new client in town, with the given map id - pub fn send_client_added_msg( - &mut self, - shm_str: &[u8; 20], - shm_id: usize, - ) -> Result<(), AflError> { - // We write this by hand to get around checks in send_buf - unsafe { - let msg = self - .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 = *shm_str; - (*pageinfo).map_size = shm_id; - self.send(msg) - } - } - - /// A client receives a broadcast message. - /// Returns null if no message is availiable - #[inline] - pub unsafe fn recv(&mut self) -> Result, AflError> { - self.llmp_in.recv() - } - - /// A client blocks/spins until the next message gets posted to the page, - /// then returns that message. - #[inline] - pub unsafe fn recv_blocking(&mut self) -> Result<*mut LlmpMsg, AflError> { - self.llmp_in.recv_blocking() - } - - /// The current page could have changed in recv (EOP) - /// Alloc the next message, internally handling end of page by allocating a new one. - #[inline] - pub unsafe fn alloc_next(&mut self, buf_len: usize) -> Result<*mut LlmpMsg, AflError> { - self.llmp_out.alloc_next(buf_len) - } - - /// Returns the next message, tag, buf, if avaliable, else None - #[inline] - pub fn recv_buf(&mut self) -> Result, AflError> { - self.llmp_in.recv_buf() - } - - /// Receives a buf from the broker, looping until a messages becomes avaliable - #[inline] - pub fn recv_buf_blocking(&mut self) -> Result<(u32, u32, &[u8]), AflError> { - self.llmp_in.recv_buf_blocking() - } } #[cfg(test)] @@ -1189,4 +1277,5 @@ mod tests { // We want at least the tcp and sender clients. assert_eq!(broker.llmp_clients.len(), 2); } + } diff --git a/afl/src/events/mod.rs b/afl/src/events/mod.rs index 7a6a0d4c5d..9c2355a4fe 100644 --- a/afl/src/events/mod.rs +++ b/afl/src/events/mod.rs @@ -6,6 +6,7 @@ pub mod shmem_translated; use alloc::string::{String, ToString}; use alloc::vec::Vec; +use shmem_translated::AflShmem; use core::time::Duration; use core::{marker::PhantomData, time}; #[cfg(feature = "std")] @@ -720,7 +721,7 @@ where ST: Stats, //CE: CustomEvent, { - llmp: llmp::LlmpConnection, + llmp: llmp::LlmpConnection, stats: ST, phantom: PhantomData<(C, E, OT, FT, I, R)>, } diff --git a/afl/src/events/shmem_translated.rs b/afl/src/events/shmem_translated.rs index 3177b0d5b1..e4857dd054 100644 --- a/afl/src/events/shmem_translated.rs +++ b/afl/src/events/shmem_translated.rs @@ -1,4 +1,5 @@ use libc::{c_char, c_int, c_long, c_uchar, c_uint, c_ulong, c_ushort, c_void}; +use core::slice; use std::{ffi::CStr, mem::size_of}; use crate::AflError; @@ -50,15 +51,52 @@ const AFL_RET_SUCCESS: c_uint = 0; // A generic sharememory region to be used by any functions (queues or feedbacks // too.) +/// A Shared map +pub trait ShMem { + + /// The string to identify this shm + fn shm_str(&self) -> String; + + /// Let's just fix this to a large enough buf + fn shm_str_buf(&self) -> &[u8; 20]; + + /// The actual shared map, in memory + fn map(&self) -> &[u8]; + + /// The actual shared map, mutable + fn map_mut(&mut self) -> &mut [u8]; + +} + #[derive(Clone, Debug)] pub struct AflShmem { pub shm_str: [u8; 20], pub shm_id: c_int, - pub map: *mut c_uchar, + pub map: *mut u8, pub map_size: usize, } -/// Deinit on drop +impl ShMem for AflShmem { + + fn shm_str(&self) -> String { + unsafe { CStr::from_ptr(self.shm_str.as_ptr() as *const i8) }.to_string_lossy().into() + } + + fn shm_str_buf(&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 AflShmem { fn drop(&mut self) { unsafe { diff --git a/afl/src/lib.rs b/afl/src/lib.rs index 7597b4c746..042f1f7f2c 100644 --- a/afl/src/lib.rs +++ b/afl/src/lib.rs @@ -28,7 +28,7 @@ pub mod utils; use alloc::string::String; use core::fmt; #[cfg(feature = "std")] -use std::io; +use std::{env::VarError, io, num::ParseIntError, string::FromUtf8Error}; /// Main error struct for AFL #[derive(Debug)] @@ -88,6 +88,27 @@ impl From for AflError { } } +#[cfg(feature = "std")] +impl From for AflError { + fn from(err: FromUtf8Error) -> Self { + Self::Unknown(format!("Could not convert byte to utf-8: {:?}", err)) + } +} + +#[cfg(feature = "std")] +impl From for AflError { + fn from(err: VarError) -> Self { + Self::Empty(format!("Could not get env var: {:?}", err)) + } +} + +impl From for AflError { + fn from(err: ParseIntError) -> Self { + Self::Unknown(format!("Failed to parse Int: {:?}", err)) + } +} + + #[cfg(test)] mod tests { #[test] diff --git a/fuzzers/libfuzzer/src/lib.rs b/fuzzers/libfuzzer/src/lib.rs index 38a985038e..8715db2a59 100644 --- a/fuzzers/libfuzzer/src/lib.rs +++ b/fuzzers/libfuzzer/src/lib.rs @@ -45,11 +45,18 @@ pub extern "C" fn afl_libfuzzer_main() { let mut generator = RandPrintablesGenerator::new(32); let stats = SimpleStats::new(|s| println!("{}", s)); + + /// + match LlmpFuzzInstance::from_env("FUZZER_ENV") { + + } + let mut mgr = LlmpEventManager::new_on_port(1337, stats).unwrap(); if mgr.is_broker() { - println!("Doing broker things."); + println!("Doing broker things. Run this tool again to start fuzzing in a client."); mgr.broker_loop().unwrap(); } + println!("We're a client, let's fuzz :)"); let edges_observer = From 7a19cd21c6d4f971a8da889f7c982595bdd7fb0e Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Fri, 18 Dec 2020 09:58:14 +0100 Subject: [PATCH 04/13] cur input global pointer for inmem executor --- afl/src/executors/inmemory.rs | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/afl/src/executors/inmemory.rs b/afl/src/executors/inmemory.rs index af404c0dad..8f55aa4ccc 100644 --- a/afl/src/executors/inmemory.rs +++ b/afl/src/executors/inmemory.rs @@ -7,9 +7,9 @@ use crate::observers::ObserversTuple; use crate::tuples::Named; use crate::AflError; -/// The (unsafe) pointer to the current inmem executor, for the current run. +/// The (unsafe) pointer to the current inmem input, for the current run. /// This is neede for certain non-rust side effects, as well as unix signal handling. -static mut CURRENT_INMEMORY_EXECUTOR_PTR: *const c_void = ptr::null(); +static mut CURRENT_INPUT_PTR: *const c_void = ptr::null(); /// The inmem executor harness type HarnessFunction = fn(&dyn Executor, &[u8]) -> ExitKind; @@ -34,11 +34,11 @@ where fn run_target(&mut self, input: &I) -> Result { let bytes = input.target_bytes(); unsafe { - CURRENT_INMEMORY_EXECUTOR_PTR = self as *const InMemoryExecutor as *const c_void; + CURRENT_INPUT_PTR = input as *const _ as *const c_void; } let ret = (self.harness)(self, bytes.as_slice()); unsafe { - CURRENT_INMEMORY_EXECUTOR_PTR = ptr::null(); + CURRENT_INPUT_PTR = ptr::null(); } Ok(ret) } @@ -101,7 +101,7 @@ pub mod unix_signals { use std::io::{stdout, Write}; // Write brings flush() into scope use std::{mem, process, ptr}; - use crate::executors::inmemory::CURRENT_INMEMORY_EXECUTOR_PTR; + use crate::executors::inmemory::CURRENT_INPUT_PTR; use crate::inputs::Input; pub extern "C" fn libaflrs_executor_inmem_handle_crash( @@ -112,7 +112,7 @@ pub mod unix_signals { I: Input, { unsafe { - if CURRENT_INMEMORY_EXECUTOR_PTR == ptr::null() { + if CURRENT_INPUT_PTR == ptr::null() { println!( "We died accessing addr {}, but are not in client...", info.si_addr() as usize @@ -125,7 +125,9 @@ pub mod unix_signals { #[cfg(feature = "std")] let _ = stdout().flush(); - // TODO: LLMP + let _input = unsafe { + (CURRENT_INPUT_PTR as *const I).as_ref().unwrap() + }; std::process::exit(139); } @@ -139,11 +141,16 @@ pub mod unix_signals { { dbg!("TIMEOUT/SIGUSR2 received"); unsafe { - if CURRENT_INMEMORY_EXECUTOR_PTR == ptr::null() { + if CURRENT_INPUT_PTR == ptr::null() { dbg!("TIMEOUT or SIGUSR2 happened, but currently not fuzzing."); return; } } + + let _input = unsafe { + (CURRENT_INPUT_PTR as *const I).as_ref().unwrap() + }; + // TODO: send LLMP. println!("Timeout in fuzz run."); let _ = stdout().flush(); From 8bd29f855fda984b9c3daa6bd9d8363fba932b19 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Fri, 18 Dec 2020 10:19:35 +0100 Subject: [PATCH 05/13] fire events in signal handlers --- afl/src/executors/inmemory.rs | 91 ++++++++++++++++++++++------------- 1 file changed, 57 insertions(+), 34 deletions(-) diff --git a/afl/src/executors/inmemory.rs b/afl/src/executors/inmemory.rs index 8f55aa4ccc..6a34ae108f 100644 --- a/afl/src/executors/inmemory.rs +++ b/afl/src/executors/inmemory.rs @@ -76,10 +76,6 @@ where OT: ObserversTuple, { pub fn new(name: &'static str, harness_fn: HarnessFunction, observers: OT) -> Self { - #[cfg(feature = "std")] - unsafe { - os_signals::setup_crash_handlers::(); - } Self { harness: harness_fn, observers: observers, @@ -101,23 +97,35 @@ pub mod unix_signals { use std::io::{stdout, Write}; // Write brings flush() into scope use std::{mem, process, ptr}; + use crate::corpus::Corpus; + use crate::events::EventManager; use crate::executors::inmemory::CURRENT_INPUT_PTR; + use crate::executors::Executor; + use crate::feedbacks::FeedbacksTuple; use crate::inputs::Input; + use crate::observers::ObserversTuple; + use crate::utils::Rand; - pub extern "C" fn libaflrs_executor_inmem_handle_crash( + static mut EVENT_MANAGER_PTR: *mut c_void = ptr::null_mut(); + + pub unsafe extern "C" fn libaflrs_executor_inmem_handle_crash( _sig: c_int, info: siginfo_t, _void: c_void, ) where + EM: EventManager, + C: Corpus, + E: Executor, + OT: ObserversTuple, + FT: FeedbacksTuple, I: Input, + R: Rand, { - unsafe { - if CURRENT_INPUT_PTR == ptr::null() { - println!( - "We died accessing addr {}, but are not in client...", - info.si_addr() as usize - ); - } + if CURRENT_INPUT_PTR == ptr::null() { + println!( + "We died accessing addr {}, but are not in client...", + info.si_addr() as usize + ); } #[cfg(feature = "std")] @@ -125,46 +133,61 @@ pub mod unix_signals { #[cfg(feature = "std")] let _ = stdout().flush(); - let _input = unsafe { - (CURRENT_INPUT_PTR as *const I).as_ref().unwrap() - }; + let input = (CURRENT_INPUT_PTR as *const I).as_ref().unwrap(); + let manager = (EVENT_MANAGER_PTR as *mut EM).as_mut().unwrap(); + + manager.crash(input).expect("Error in sending Crash event"); std::process::exit(139); } - pub extern "C" fn libaflrs_executor_inmem_handle_timeout( + pub unsafe extern "C" fn libaflrs_executor_inmem_handle_timeout( _sig: c_int, _info: siginfo_t, _void: c_void, ) where + EM: EventManager, + C: Corpus, + E: Executor, + OT: ObserversTuple, + FT: FeedbacksTuple, I: Input, + R: Rand, { dbg!("TIMEOUT/SIGUSR2 received"); - unsafe { - if CURRENT_INPUT_PTR == ptr::null() { - dbg!("TIMEOUT or SIGUSR2 happened, but currently not fuzzing."); - return; - } + if CURRENT_INPUT_PTR == ptr::null() { + dbg!("TIMEOUT or SIGUSR2 happened, but currently not fuzzing."); + return; } + + let input = (CURRENT_INPUT_PTR as *const I).as_ref().unwrap(); + let manager = (EVENT_MANAGER_PTR as *mut EM).as_mut().unwrap(); - let _input = unsafe { - (CURRENT_INPUT_PTR as *const I).as_ref().unwrap() - }; - + manager.timeout(input).expect("Error in sending Timeout event"); + // TODO: send LLMP. println!("Timeout in fuzz run."); let _ = stdout().flush(); process::abort(); } - pub unsafe fn setup_crash_handlers() + // TODO clearly state that manager should be static (maybe put the 'static lifetime?) + pub unsafe fn setup_crash_handlers(manager: &mut EM) where + EM: EventManager, + C: Corpus, + E: Executor, + OT: ObserversTuple, + FT: FeedbacksTuple, I: Input, + R: Rand, { + EVENT_MANAGER_PTR = manager as *mut _ as *mut c_void; + let mut sa: sigaction = mem::zeroed(); libc::sigemptyset(&mut sa.sa_mask as *mut libc::sigset_t); sa.sa_flags = SA_NODEFER | SA_SIGINFO; - sa.sa_sigaction = libaflrs_executor_inmem_handle_crash:: as usize; + sa.sa_sigaction = libaflrs_executor_inmem_handle_crash:: as usize; for (sig, msg) in &[ (SIGSEGV, "segfault"), (SIGBUS, "sigbus"), @@ -178,19 +201,19 @@ pub mod unix_signals { } } - sa.sa_sigaction = libaflrs_executor_inmem_handle_timeout:: as usize; + sa.sa_sigaction = libaflrs_executor_inmem_handle_timeout:: as usize; if sigaction(SIGUSR2, &mut sa as *mut sigaction, ptr::null_mut()) < 0 { panic!("Could not set up sigusr2 handler for timeouts"); } } } -#[cfg(feature = "std")] -#[cfg(unix)] -use unix_signals as os_signals; -#[cfg(feature = "std")] -#[cfg(not(unix))] -compile_error!("InMemoryExecutor not yet supported on this OS"); +//#[cfg(feature = "std")] +//#[cfg(unix)] +//use unix_signals as os_signals; +//#[cfg(feature = "std")] +//#[cfg(not(unix))] +//compile_error!("InMemoryExecutor not yet supported on this OS"); #[cfg(test)] mod tests { From 1e611da35bf701371a5d8b620450a3501b5984bd Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Sat, 19 Dec 2020 05:43:47 +0100 Subject: [PATCH 06/13] generic map --- afl/Cargo.toml | 2 +- afl/src/events/llmp.rs | 333 +++++++++++++---------------- afl/src/events/mod.rs | 8 +- afl/src/events/shmem_translated.rs | 278 ------------------------ afl/src/lib.rs | 14 +- fuzzers/libfuzzer/src/lib.rs | 8 +- 6 files changed, 161 insertions(+), 482 deletions(-) delete mode 100644 afl/src/events/shmem_translated.rs diff --git a/afl/Cargo.toml b/afl/Cargo.toml index 21cf033946..7900609236 100644 --- a/afl/Cargo.toml +++ b/afl/Cargo.toml @@ -40,4 +40,4 @@ serde = { version = "1.0", default-features = false, features = ["alloc"] } # se erased-serde = "0.3.12" postcard = { version = "0.5.1", features = ["alloc"] } # no_std compatible serde serialization fromat static_assertions = "1.1.0" -#TODO: for llmp brotli = { version = "3.3.0", default-features = false } # brotli compression +#TODO: for llmp brotli = { version = "3.3.0", default-features = false } # brotli compression \ No newline at end of file diff --git a/afl/src/events/llmp.rs b/afl/src/events/llmp.rs index ec58bcfdf5..8fca736d33 100644 --- a/afl/src/events/llmp.rs +++ b/afl/src/events/llmp.rs @@ -58,7 +58,6 @@ use core::{ #[cfg(feature = "std")] use std::{ - env, io::{Read, Write}, net::{TcpListener, TcpStream}, thread, @@ -68,7 +67,7 @@ use crate::utils::next_pow2; use crate::AflError; #[cfg(feature = "std")] -use super::shmem_translated::{AflShmem, ShMem}; +use super::shmem::ShMem; /// We'll start off with 256 megabyte maps per fuzzer client const LLMP_PREF_INITIAL_MAP_SIZE: usize = 1 << 28; @@ -119,7 +118,7 @@ where #[derive(Clone, Debug)] pub struct LlmpReceiver where - SH: ShMem + SH: ShMem, { pub id: u32, /// Pointer to the last meg this received @@ -150,6 +149,7 @@ where /// shared between one LlmpSender and one LlmpReceiver shmem: SH, } + /// Message sent over the "wire" #[derive(Copy, Clone, Debug)] #[repr(C, packed)] @@ -178,7 +178,7 @@ impl LlmpMsg { /// Gets the buffer from this message as slice, with the corrent length. #[inline] - pub fn as_slice(&self, map: &LlmpSharedMap) -> Result<&[u8], AflError> { + pub fn as_slice(&self, map: &mut LlmpSharedMap) -> Result<&[u8], AflError> { unsafe { if self.in_map(map) { Ok(self.as_slice_unsafe()) @@ -190,7 +190,7 @@ impl LlmpMsg { /// Returns true, if the pointer is, indeed, in the page of this shared map. #[inline] - pub fn in_map(&self, map: &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(); @@ -209,9 +209,6 @@ impl LlmpMsg { } } - - - /// An Llmp instance pub enum LlmpConnection where @@ -226,7 +223,10 @@ where IsClient { client: LlmpClient }, } -impl LlmpConnection { +impl LlmpConnection +where + SH: ShMem, +{ /// 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 { match TcpListener::bind(format!("127.0.0.1:{}", port)) { @@ -268,7 +268,7 @@ impl LlmpConnection { } /// Contents of the share mem pages, used by llmp internally -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] #[repr(C, packed)] pub struct LlmpPage { pub sender: u32, @@ -282,7 +282,7 @@ pub struct LlmpPage { } /// The broker (node 0) -#[derive(Clone)] +#[derive(Clone, Debug)] #[repr(C)] pub struct LlmpBroker where @@ -307,7 +307,7 @@ pub enum LlmpMsgHookResult { /// Message payload when a client got added LLMP_TAG_CLIENT_ADDED_V1 */ /// This is an internal message! /// LLMP_TAG_END_OF_PAGE_V1 -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] #[repr(C, packed)] struct LlmpPayloadSharedMapInfo { pub map_size: usize, @@ -316,8 +316,8 @@ struct LlmpPayloadSharedMapInfo { /// Get sharedmem from a page #[inline] -unsafe fn shmem2page(afl_shmem: &SH) -> *mut LlmpPage { - afl_shmem.map().as_mut_ptr() as *mut LlmpPage +unsafe fn shmem2page(afl_shmem: &mut SH) -> *mut LlmpPage { + afl_shmem.map_mut().as_mut_ptr() as *mut LlmpPage } /// Return, if a msg is contained in the current page @@ -357,13 +357,14 @@ 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 AflShmem, sender: u32) { +unsafe fn _llmp_page_init(shmem: &mut SH, sender: u32) { + let map_size = shmem.map().len(); let page = shmem2page(shmem); (*page).sender = sender; ptr::write_volatile(&mut (*page).current_msg_id, 0); (*page).max_alloc_size = 0; // Don't forget to subtract our own header size - (*page).size_total = shmem.map_size - LLMP_PAGE_HEADER_LEN; + (*page).size_total = map_size - LLMP_PAGE_HEADER_LEN; (*page).size_used = 0; (*(*page).messages.as_mut_ptr()).message_id = 0; (*(*page).messages.as_mut_ptr()).tag = LLMP_TAG_UNSET; @@ -373,8 +374,8 @@ unsafe fn _llmp_page_init(shmem: &mut AflShmem, sender: u32) { /// 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: &LlmpSharedMap, +unsafe fn llmp_next_msg_ptr_checked( + map: &mut LlmpSharedMap, last_msg: *const LlmpMsg, alloc_size: usize, ) -> Result<*mut LlmpMsg, AflError> { @@ -405,14 +406,17 @@ unsafe fn _llmp_next_msg_ptr(last_msg: *const LlmpMsg) -> *mut LlmpMsg { } /// An actor on the sendin part of the shared map -impl LlmpSender { +impl LlmpSender +where + SH: ShMem, +{ /// For non zero-copy, we want to get rid of old pages with duplicate messages in the client /// eventually. This function This funtion sees if we can unallocate older pages. /// The broker would have informed us by setting the save_to_unmap-flag. unsafe fn prune_old_pages(&mut self) { // Exclude the current page by splitting of the last element for this iter let mut unmap_until_excl = 0; - for map in self.out_maps.split_last().unwrap().1 { + for map in self.out_maps.split_last_mut().unwrap().1 { if (*map.page()).save_to_unmap == 0 { // The broker didn't read this page yet, no more pages to unmap. break; @@ -429,7 +433,7 @@ impl LlmpSender { /// So if alloc_next fails, create new page if necessary, use this function, /// place EOP, commit EOP, reset, alloc again on the new space. unsafe fn alloc_eop(&mut self) -> Result<*mut LlmpMsg, AflError> { - let map = self.out_maps.last().unwrap(); + let mut map = self.out_maps.last_mut().unwrap(); let page = map.page(); let last_msg = self.last_msg_sent; if (*page).size_used + EOP_MSG_SIZE > (*page).size_total { @@ -437,7 +441,7 @@ impl LlmpSender { (*page).size_used, (*page).size_total)); } let mut ret: *mut LlmpMsg = if !last_msg.is_null() { - llmp_next_msg_ptr_checked(&map, last_msg, EOP_MSG_SIZE)? + llmp_next_msg_ptr_checked(&mut map, last_msg, EOP_MSG_SIZE)? } else { (*page).messages.as_mut_ptr() }; @@ -461,7 +465,7 @@ impl LlmpSender { unsafe fn alloc_next_if_space(&mut self, buf_len: usize) -> Option<*mut LlmpMsg> { let buf_len_padded; let mut complete_msg_size = llmp_align(size_of::() + buf_len); - let map = self.out_maps.last().unwrap(); + let map = self.out_maps.last_mut().unwrap(); let page = map.page(); let last_msg = self.last_msg_sent; /* DBG("XXX complete_msg_size %lu (h: %lu)\n", complete_msg_size, sizeof(llmp_message)); */ @@ -554,7 +558,7 @@ impl LlmpSender { (*msg).message_id )); } - let page = self.out_maps.last().unwrap().page(); + let page = self.out_maps.last_mut().unwrap().page(); if msg.is_null() || !llmp_msg_in_page(page, msg) { return Err(AflError::Unknown(format!( "Llmp Message {:?} is null or not in current page", @@ -574,7 +578,10 @@ impl LlmpSender { let old_map = self.out_maps.last_mut().unwrap().page(); // Create a new shard page. - let new_map_shmem = LlmpSharedMap::::new((*old_map).sender, (*old_map).max_alloc_size)?; + let mut new_map_shmem = LlmpSharedMap::new( + (*old_map).sender, + SH::new_map(new_map_size((*old_map).max_alloc_size))?, + ); let mut new_map = new_map_shmem.page(); ptr::write_volatile(&mut (*new_map).current_msg_id, (*old_map).current_msg_id); @@ -626,7 +633,7 @@ impl LlmpSender { pub unsafe fn cancel_send(&mut self, msg: *mut LlmpMsg) { /* DBG("Client %d cancels send of msg at %p with tag 0x%X and size %ld", client->id, msg, msg->tag, * msg->buf_len_padded); */ - let page = self.out_maps.last().unwrap().page(); + let page = self.out_maps.last_mut().unwrap().page(); (*msg).tag = LLMP_TAG_UNSET; (*page).size_used -= (*msg).buf_len_padded as usize + size_of::(); } @@ -682,7 +689,7 @@ where } else { // We don't know how big the msg wants to be, assert at least the header has space. Some(llmp_next_msg_ptr_checked( - &self.current_recv_map, + &mut self.current_recv_map, last_msg, size_of::(), )?) @@ -691,7 +698,7 @@ where // Let's see what we go here. match ret { Some(msg) => { - if !(*msg).in_map(&self.current_recv_map) { + if !(*msg).in_map(&mut self.current_recv_map) { return Err(AflError::IllegalState("Unexpected message in map (out of map bounds) - bugy client or tampered shared map detedted!".into())); } // Handle special, LLMP internal, messages. @@ -717,10 +724,11 @@ where // Mark the old page save to unmap, in case we didn't so earlier. 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::new_from_shm_str_buf( - &pageinfo_cpy.shm_str, - pageinfo_cpy.map_size, - )?; + self.current_recv_map = + LlmpSharedMap::existing(SH::existing_map_by_shm_bytes( + &pageinfo_cpy.shm_str, + pageinfo_cpy.map_size, + )?); // 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); @@ -770,7 +778,7 @@ where Some(msg) => Some(( (*msg).sender, (*msg).tag, - (*msg).as_slice(&self.current_recv_map)?, + (*msg).as_slice(&mut self.current_recv_map)?, )), None => None, }) @@ -785,77 +793,53 @@ where Ok(( (*msg).sender, (*msg).tag, - (*msg).as_slice(&self.current_recv_map)?, + (*msg).as_slice(&mut self.current_recv_map)?, )) } } } +// TODO: May be obsolete /// The page struct, placed on a shared mem instance. -impl LlmpSharedMap { - /// Creates a new page with minimum prev_max_alloc_size or LLMP_PREF_INITIAL_MAP_SIZE - /// returning the initialized shared mem struct - pub fn new(sender: u32, min_size: usize) -> Result { - // Create a new shard page. - let mut shmem = AflShmem::new(new_map_size(min_size))?; - unsafe { - _llmp_page_init(&mut shmem, sender); - } - Ok(Self { shmem }) - } - - /// Initialize from a shm_str with fixed len of 20 - pub fn from_name(shm_str: &str, map_size: usize) -> Result { - let slice: [u8; 20]; - slice.copy_from_slice(shm_str.as_bytes()); - Self::from_name_slice(&slice, map_size) - } - - /// Initialize from a shm_str with fixed len of 20 - pub fn from_name_slice(shm_str: &[u8; 20], map_size: usize) -> Result { - let shmem = AflShmem::from_name_slice(shm_str, map_size)?; - // Not initializing the page here - the other side should have done it already! - Ok(Self { shmem }) - } -} - +/// A thin wrapper around a ShMem implementation, with special Llmp funcs impl LlmpSharedMap where SH: ShMem, { - - /// Initialize from a shm_str with fixed len of 20 - pub fn new_from_shm_str_buf(&self, shm_str: &[u8; 20], map_size: usize) -> Result { - let shmem = self.shmem.new_from_shm_str_buf(shm_str, map_size)?; - // Not initializing the page here - the other side should have done it already! - Ok(Self { shmem }) + /// Creates a new page, initializing the passed shared mem struct + pub fn new(sender: u32, mut new_map: SH) -> Self { + unsafe { + _llmp_page_init(&mut new_map, sender); + } + Self { shmem: new_map } } - pub fn write_to_env(&self, env_name: String) -> Result<(), AflError> { - let map_size = self.shmem.map().len(); - let map_env = self.shmem.shm_str() ; - let map_size_env = format!("{}_SIZE", map_env); - env::set_var(map_env, self.shmem.shm_str()); - env::set_var(map_size_env, format!("{}", map_size)); - Ok(()) + /// Maps and wraps an existing + pub fn existing(existing_map: SH) -> Self { + Self { + shmem: existing_map, + } } /// Get the unsafe ptr to this page, situated on the shared map - pub unsafe fn page(&self) -> *mut LlmpPage { - shmem2page(&self.shmem) + pub unsafe fn page(&mut self) -> *mut LlmpPage { + shmem2page(&mut self.shmem) } } /// 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, +{ /// Create and initialize a new llmp_broker pub fn new() -> Result { let broker = LlmpBroker { llmp_out: LlmpSender { id: 0, last_msg_sent: ptr::null_mut(), - out_maps: vec![LlmpSharedMap::new(0, 0)?], + out_maps: vec![LlmpSharedMap::new(0, SH::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, @@ -901,7 +885,6 @@ impl LlmpBroker { Ok(()) } - /// The broker walks all pages and looks for changes, then broadcasts them on /// its own shared page, once. #[inline] @@ -941,11 +924,6 @@ impl LlmpBroker { self.llmp_out.send_buf(tag, buf) } -} - -impl LlmpBroker { - - /// Launches a thread using a tcp listener socket, on which new clients may connect to this broker /// Does so on the given port. pub fn launch_tcp_listener_on( @@ -974,8 +952,11 @@ impl LlmpBroker { 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, LLMP_PREF_INITIAL_MAP_SIZE)?; - let tcp_out_map_str = tcp_out_map.shmem.shm_str_buf(); + let tcp_out_map = LlmpSharedMap::new( + llmp_tcp_id, + SH::new_map(new_map_size(LLMP_PREF_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(); self.register_client(tcp_out_map); @@ -983,9 +964,9 @@ impl LlmpBroker { let mut new_client_sender = LlmpSender { id: 0, last_msg_sent: 0 as *mut LlmpMsg, - out_maps: vec![ - LlmpSharedMap::from_name_slice(&tcp_out_map_str, tcp_out_map_size).unwrap(), - ], + out_maps: vec![LlmpSharedMap::existing( + SH::existing_from_name(&tcp_out_map_str, tcp_out_map_size).unwrap(), + )], // drop pages to the broker if it already read them keep_pages_forever: false, }; @@ -1032,26 +1013,91 @@ impl LlmpBroker { })) } - fn map_new_page(shm_str_buf: &[u8; 20], map_size: usize) -> Result, AflError> { - LlmpSharedMap::from_name_slice(shm_str_buf, map_size) + /// broker broadcast to its own page for all others to read */ + #[inline] + unsafe fn handle_new_msgs( + &mut self, + client_id: u32, + on_new_msg: &mut F, + ) -> Result<(), AflError> + where + F: FnMut(u32, Tag, &[u8]) -> Result, + { + let mut next_id = self.llmp_clients.len() as u32; + + // TODO: We could memcpy a range of pending messages, instead of one by one. + loop { + let msg = { + let client = &mut self.llmp_clients[client_id as usize]; + match client.recv()? { + None => { + // We're done handling this client + return Ok(()); + } + Some(msg) => msg, + } + }; + + if (*msg).tag == LLMP_TAG_NEW_SHM_CLIENT { + /* This client informs us about yet another new client + add it to the list! Also, no need to forward this msg. */ + if (*msg).buf_len < size_of::() as u64 { + println!("Ignoring broken CLIENT_ADDED msg due to incorrect size. Expected {} but got {}", + (*msg).buf_len_padded, + size_of::() + ); + } else { + let pageinfo = (*msg).buf.as_mut_ptr() as *mut LlmpPayloadSharedMapInfo; + + match SH::existing_map_by_shm_bytes(&(*pageinfo).shm_str, (*pageinfo).map_size) + { + Ok(new_map) => { + let new_page = LlmpSharedMap::existing(new_map); + let id = next_id; + next_id += 1; + self.llmp_clients.push(LlmpReceiver { + id, + current_recv_map: new_page, + last_msg_recvd: 0 as *mut LlmpMsg, + }); + } + Err(e) => println!("Error adding client! {:?}", e), + }; + } + } else { + // The message is not specifically for use. Let the user handle it, then forward it to the clients, if necessary. + let mut should_forward_msg = true; + + let map = &mut self.llmp_clients[client_id as usize].current_recv_map; + let msg_buf = (*msg).as_slice(map)?; + match (on_new_msg)(client_id, (*msg).tag, msg_buf)? { + LlmpMsgHookResult::Handled => should_forward_msg = false, + _ => (), + } + if should_forward_msg { + self.forward_msg(msg)?; + } + } + } } } /// `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 { - - fn map_new_page(shm_str_buf: &[u8; 20], map_size: usize) -> Result, AflError> { - panic!("End of page not handled!"); - } - +impl LlmpClient +where + SH: ShMem, +{ /// Creates a new LlmpClient pub fn new(initial_broker_map: LlmpSharedMap) -> Result { Ok(Self { llmp_out: LlmpSender { id: 0, last_msg_sent: 0 as *mut LlmpMsg, - out_maps: vec![LlmpSharedMap::new(0, LLMP_PREF_INITIAL_MAP_SIZE)?], + out_maps: vec![LlmpSharedMap::new( + 0, + SH::new_map(new_map_size(LLMP_PREF_INITIAL_MAP_SIZE))?, + )], // drop pages to the broker if it already read them keep_pages_forever: false, }, @@ -1125,86 +1171,9 @@ impl LlmpClient { self.llmp_in.recv_buf_blocking() } - - - /// broker broadcast to its own page for all others to read */ - #[inline] - unsafe fn handle_new_msgs( - &mut self, - client_id: u32, - on_new_msg: &mut F, - ) -> Result<(), AflError> - where - F: FnMut(u32, Tag, &[u8]) -> Result, - { - let mut next_id = self.llmp_clients.len() as u32; - - // TODO: We could memcpy a range of pending messages, instead of one by one. - loop { - let msg = { - let client = &mut self.llmp_clients[client_id as usize]; - match client.recv()? { - None => { - // We're done handling this client - return Ok(()); - } - Some(msg) => msg, - } - }; - - if (*msg).tag == LLMP_TAG_NEW_SHM_CLIENT { - /* This client informs us about yet another new client - add it to the list! Also, no need to forward this msg. */ - if (*msg).buf_len < size_of::() as u64 { - println!("Ignoring broken CLIENT_ADDED msg due to incorrect size. Expected {} but got {}", - (*msg).buf_len_padded, - size_of::() - ); - } else { - let pageinfo = (*msg).buf.as_mut_ptr() as *mut LlmpPayloadSharedMapInfo; - - match LlmpSharedMap::from_name_slice(&(*pageinfo).shm_str, (*pageinfo).map_size) - { - Ok(new_page) => { - let id = next_id; - next_id += 1; - self.llmp_clients.push(LlmpReceiver { - id, - current_recv_map: new_page, - last_msg_recvd: 0 as *mut LlmpMsg, - }); - } - Err(e) => println!("Error adding client! {:?}", e), - }; - } - } else { - // The message is not specifically for use. Let the user handle it, then forward it to the clients, if necessary. - let mut should_forward_msg = true; - - let map = &self.llmp_clients[client_id as usize].current_recv_map; - let msg_buf = (*msg).as_slice(map)?; - match (on_new_msg)(client_id, (*msg).tag, msg_buf)? { - LlmpMsgHookResult::Handled => should_forward_msg = false, - _ => (), - } - if should_forward_msg { - self.forward_msg(msg)?; - } - } - } - } - -} - -impl LlmpClient { - /// Creates a new LlmpClient, reading the map id and len from env pub fn create_using_env(env_var: &str) -> Result { - - let map_str = env::var(env_var)?; - let map_size = str::parse::(&env::var(format!("{}_SIZE", env_var))?)?; - Ok(Self::new(LlmpSharedMap::from_name(&map_str, map_size)?)?) - + Self::new(LlmpSharedMap::existing(SH::existing_from_env(env_var)?)) } /// Create a LlmpClient, getting the ID from a given port @@ -1215,15 +1184,14 @@ impl LlmpClient { let mut new_broker_map_str: [u8; 20] = Default::default(); stream.read_exact(&mut new_broker_map_str)?; - let ret = Self::new(LlmpSharedMap::from_name_slice( + let ret = Self::new(LlmpSharedMap::existing(SH::existing_map_by_shm_bytes( &new_broker_map_str, LLMP_PREF_INITIAL_MAP_SIZE, - )?)?; + )?))?; stream.write(ret.llmp_out.out_maps.first().unwrap().shmem.shm_str_buf())?; Ok(ret) } - } #[cfg(test)] @@ -1231,6 +1199,8 @@ mod tests { use std::{thread::sleep, time::Duration}; + use crate::events::shmem::AflShmem; + use super::{ LlmpConnection::{self, IsBroker, IsClient}, LlmpMsgHookResult::ForwardToClients, @@ -1239,7 +1209,7 @@ mod tests { #[test] pub fn llmp_connection() { - let mut broker = match LlmpConnection::on_port(1337).unwrap() { + let mut broker = match LlmpConnection::::on_port(1337).unwrap() { IsClient { client: _ } => panic!("Could not bind to port as broker"), IsBroker { broker, @@ -1248,7 +1218,7 @@ mod tests { }; // 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(1337).unwrap() { IsBroker { broker: _, listener_thread: _, @@ -1277,5 +1247,4 @@ mod tests { // We want at least the tcp and sender clients. assert_eq!(broker.llmp_clients.len(), 2); } - } diff --git a/afl/src/events/mod.rs b/afl/src/events/mod.rs index 9c2355a4fe..de2059a94b 100644 --- a/afl/src/events/mod.rs +++ b/afl/src/events/mod.rs @@ -1,19 +1,17 @@ -// TODO: llmp can be no_std, if we abstract away page creation -#[cfg(feature = "std")] pub mod llmp; #[cfg(feature = "std")] -pub mod shmem_translated; +pub mod shmem; use alloc::string::{String, ToString}; use alloc::vec::Vec; -use shmem_translated::AflShmem; use core::time::Duration; use core::{marker::PhantomData, time}; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; -#[cfg(feature = "std")] use self::llmp::Tag; +#[cfg(feature = "std")] +use self::shmem::AflShmem; use crate::corpus::Corpus; use crate::executors::Executor; use crate::feedbacks::FeedbacksTuple; diff --git a/afl/src/events/shmem_translated.rs b/afl/src/events/shmem_translated.rs deleted file mode 100644 index e4857dd054..0000000000 --- a/afl/src/events/shmem_translated.rs +++ /dev/null @@ -1,278 +0,0 @@ -use libc::{c_char, c_int, c_long, c_uchar, c_uint, c_ulong, c_ushort, c_void}; -use core::slice; -use std::{ffi::CStr, mem::size_of}; - -use crate::AflError; - -extern "C" { - fn snprintf(_: *mut c_char, _: c_ulong, _: *const c_char, _: ...) -> c_int; - fn strncpy(_: *mut c_char, _: *const c_char, _: c_ulong) -> *mut c_char; - //fn strlen(_: *const c_char) -> c_ulong; - 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; - //fn strtol(_: *const c_char, _: *mut *mut c_char, _: c_int) -> c_long; - fn setenv(__name: *const c_char, __value: *const c_char, __replace: c_int) -> c_int; -} -#[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, -} - -#[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, -} -const AFL_RET_ERRNO: c_uint = 12; -const AFL_RET_NULL_PTR: c_uint = 11; -const AFL_RET_SUCCESS: c_uint = 0; - -// A generic sharememory region to be used by any functions (queues or feedbacks -// too.) - -/// A Shared map -pub trait ShMem { - - /// The string to identify this shm - fn shm_str(&self) -> String; - - /// Let's just fix this to a large enough buf - fn shm_str_buf(&self) -> &[u8; 20]; - - /// The actual shared map, in memory - fn map(&self) -> &[u8]; - - /// The actual shared map, mutable - fn map_mut(&mut self) -> &mut [u8]; - -} - -#[derive(Clone, Debug)] -pub struct AflShmem { - pub shm_str: [u8; 20], - pub shm_id: c_int, - pub map: *mut u8, - pub map_size: usize, -} - -impl ShMem for AflShmem { - - fn shm_str(&self) -> String { - unsafe { CStr::from_ptr(self.shm_str.as_ptr() as *const i8) }.to_string_lossy().into() - } - - fn shm_str_buf(&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 AflShmem { - fn drop(&mut self) { - unsafe { - afl_shmem_deinit(self); - } - } -} - -/// Create an uninitialized shmap -const fn afl_shmem_unitialized() -> AflShmem { - AflShmem { - shm_str: [0; 20], - shm_id: -1, - map: 0 as *mut c_uchar, - map_size: 0, - } -} - -impl AflShmem { - pub fn from_str(shm_str: &CStr, map_size: usize) -> Result { - let mut ret = afl_shmem_unitialized(); - let map = unsafe { afl_shmem_by_str(&mut ret, shm_str, map_size) }; - if map != 0 as *mut u8 { - Ok(ret) - } else { - Err(AflError::Unknown(format!( - "Could not allocate map with id {:?} and size {}", - shm_str, map_size - ))) - } - } - - /// Generate a shared map with a fixed byte array of 20 - pub fn from_name_slice(shm_str: &[u8; 20], map_size: usize) -> Result { - unsafe { - let str_bytes = shm_str as *const [u8; 20] as *const libc::c_char; - Self::from_str(CStr::from_ptr(str_bytes), map_size) - } - } - - pub fn new(map_size: usize) -> Result { - let mut ret = afl_shmem_unitialized(); - let map = unsafe { afl_shmem_init(&mut ret, map_size) }; - if map != 0 as *mut u8 { - Ok(ret) - } else { - Err(AflError::Unknown(format!( - "Could not allocate map of size {}", - map_size - ))) - } - } - - /// Sets this shm id as env variable with the given name - /// Also write the map size as name#_SIZE env - pub fn to_env_var(&self, env_name: &CStr) -> Result<(), AflError> { - if unsafe { afl_shmem_to_env_var(&self, env_name) } == AFL_RET_SUCCESS { - Ok(()) - } else { - Err(AflError::Unknown(format!( - "Could not set env variable {:?}", - env_name - ))) - } - } -} - -/// Deinitialize this shmem instance -unsafe fn afl_shmem_deinit(shm: *mut AflShmem) { - if shm.is_null() || (*shm).map.is_null() { - /* Serialized map id */ - // Not set or not initialized; - return; - } - (*shm).shm_str[0 as usize] = '\u{0}' as u8; - shmctl((*shm).shm_id, 0 as c_int, 0 as *mut shmid_ds); - (*shm).map = 0 as *mut c_uchar; -} - -/// Functions to create Shared memory region, for observation channels and -/// opening inputs and stuff. -unsafe fn afl_shmem_init(shm: *mut AflShmem, map_size: usize) -> *mut c_uchar { - (*shm).map_size = map_size; - (*shm).map = 0 as *mut c_uchar; - (*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] = '\u{0}' as u8; - return 0 as *mut c_uchar; - } - snprintf( - (*shm).shm_str.as_mut_ptr() as *mut i8, - 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_int as c_ulong) as usize] = - '\u{0}' as u8; - (*shm).map = shmat((*shm).shm_id, 0 as *const c_void, 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, 0 as *mut shmid_ds); - (*shm).shm_id = -(1 as c_int); - (*shm).shm_str[0 as c_int as usize] = '\u{0}' as u8; - return 0 as *mut c_uchar; - } - return (*shm).map; -} - -/// Uses a shmap id string to open a shared map -unsafe fn afl_shmem_by_str(shm: *mut AflShmem, shm_str: &CStr, map_size: usize) -> *mut c_uchar { - if shm.is_null() || shm_str.to_bytes().len() == 0 || map_size == 0 { - return 0 as *mut c_uchar; - } - (*shm).map = 0 as *mut c_uchar; - (*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_int as c_ulong), - ); - (*shm).shm_id = shm_str - .to_str() - .expect(&format!("illegal shm_str {:?}", shm_str)) - .parse::() - .unwrap(); - (*shm).map = shmat((*shm).shm_id, 0 as *const c_void, 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 = 0 as *mut c_uchar; - (*shm).map_size = 0; - (*shm).shm_str[0] = '\u{0}' as u8; - return 0 as *mut c_uchar; - } - return (*shm).map; -} - -/// Write sharedmap as env var and the size as name#_SIZE -unsafe fn afl_shmem_to_env_var(shmem: &AflShmem, env_name: &CStr) -> c_uint { - let env_len = env_name.to_bytes().len(); - if env_len == 0 || env_len > 200 || (*shmem).shm_str[0 as c_int as usize] == 0 { - return AFL_RET_NULL_PTR; - } - let mut shm_str: [c_char; 256] = [0; 256]; - snprintf( - shm_str.as_mut_ptr(), - size_of::<[c_char; 256]>() as c_ulong, - b"%d\x00" as *const u8 as *const c_char, - (*shmem).shm_id, - ); - if setenv( - env_name.as_ptr() as *const c_char, - shm_str.as_mut_ptr(), - 1 as c_int, - ) < 0 as c_int - { - return AFL_RET_ERRNO; - } - /* Write the size to env, too */ - let mut size_env_name: [c_char; 256] = [0; 256]; - snprintf( - size_env_name.as_mut_ptr(), - size_of::<[c_char; 256]>() as c_ulong, - b"%s_SIZE\x00" as *const u8 as *const c_char, - env_name, - ); - snprintf( - shm_str.as_mut_ptr(), - size_of::<[c_char; 256]>() as c_ulong, - b"%d\x00" as *const u8 as *const c_char, - (*shmem).shm_id, - ); - if setenv(size_env_name.as_mut_ptr(), shm_str.as_mut_ptr(), 1 as c_int) < 0 as c_int { - return AFL_RET_ERRNO; - } - return AFL_RET_SUCCESS; -} diff --git a/afl/src/lib.rs b/afl/src/lib.rs index 042f1f7f2c..dfdc76cccd 100644 --- a/afl/src/lib.rs +++ b/afl/src/lib.rs @@ -6,7 +6,6 @@ Welcome to libAFL #[macro_use] extern crate alloc; - #[macro_use] extern crate static_assertions; @@ -94,25 +93,16 @@ impl From for AflError { Self::Unknown(format!("Could not convert byte to utf-8: {:?}", err)) } } - + #[cfg(feature = "std")] impl From for AflError { fn from(err: VarError) -> Self { Self::Empty(format!("Could not get env var: {:?}", err)) } } - + impl From for AflError { fn from(err: ParseIntError) -> Self { Self::Unknown(format!("Failed to parse Int: {:?}", err)) } } - - -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - assert_eq!(2 + 2, 4); - } -} diff --git a/fuzzers/libfuzzer/src/lib.rs b/fuzzers/libfuzzer/src/lib.rs index 8715db2a59..f9e2cbca62 100644 --- a/fuzzers/libfuzzer/src/lib.rs +++ b/fuzzers/libfuzzer/src/lib.rs @@ -46,10 +46,10 @@ pub extern "C" fn afl_libfuzzer_main() { let stats = SimpleStats::new(|s| println!("{}", s)); - /// - match LlmpFuzzInstance::from_env("FUZZER_ENV") { - - } + // TODO + /* match LlmpFuzzInstance::from_env("FUZZER_ENV") { + + }*/ let mut mgr = LlmpEventManager::new_on_port(1337, stats).unwrap(); if mgr.is_broker() { From a84256c3bec9e0240cdee1aec93214175720b0c3 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Sat, 19 Dec 2020 05:50:17 +0100 Subject: [PATCH 07/13] added back shmem --- afl/src/events/shmem.rs | 262 ++++++++++++++++++++++++++++++++++ afl/src/executors/inmemory.rs | 10 +- 2 files changed, 268 insertions(+), 4 deletions(-) create mode 100644 afl/src/events/shmem.rs diff --git a/afl/src/events/shmem.rs b/afl/src/events/shmem.rs new file mode 100644 index 0000000000..8c89419deb --- /dev/null +++ b/afl/src/events/shmem.rs @@ -0,0 +1,262 @@ +//! A generic sharememory region to be used by any functions (queues or feedbacks +// too.) + +use core::slice; +use libc::{c_char, c_int, c_long, c_uchar, c_uint, c_ulong, c_ushort, c_void}; +use std::{env, ffi::CStr, mem::size_of}; + +use crate::AflError; + +extern "C" { + fn snprintf(_: *mut c_char, _: c_ulong, _: *const c_char, _: ...) -> c_int; + fn strncpy(_: *mut c_char, _: *const c_char, _: c_ulong) -> *mut c_char; + //fn strlen(_: *const c_char) -> c_ulong; + 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; +} +#[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, +} + +#[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, +} + +/// A Shared map +pub trait ShMem: Sized { + /// Creates a nes variable with the given name, strigified to 20 bytes. + fn existing_map_by_shm_bytes( + map_str_bytes: &[u8; 20], + map_size: usize, + ) -> Result; + + /// Initialize from a shm_str with fixed len of 20 + fn existing_from_name(shm_str: &str, map_size: usize) -> Result { + let mut slice: [u8; 20] = [0; 20]; + for (i, val) in shm_str.as_bytes().iter().enumerate() { + slice[i] = *val; + } + Self::existing_map_by_shm_bytes(&slice, map_size) + } + + /// Creates a new map with the given size + fn new_map(map_size: usize) -> Result; + + /// The string to identify this shm + fn shm_str(&self) -> String; + + /// Let's just fix this to a large enough buf + fn shm_str_buf(&self) -> &[u8; 20]; + + /// The actual shared map, in memory + fn map(&self) -> &[u8]; + + /// The actual shared map, mutable + fn map_mut(&mut self) -> &mut [u8]; + + /// Write this map's config to env + fn write_to_env(&self, env_name: &str) -> Result<(), AflError> { + let map_size = self.map().len(); + let map_size_env = format!("{}_SIZE", env_name); + env::set_var(env_name, self.shm_str()); + env::set_var(map_size_env, format!("{}", map_size)); + Ok(()) + } + + /// Reads an existing map config from env vars, then maps it + fn existing_from_env(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_name(&map_shm_str, map_size) + } +} + +#[derive(Clone, Debug)] +pub struct AflShmem { + pub shm_str: [u8; 20], + pub shm_id: c_int, + pub map: *mut u8, + pub map_size: usize, +} + +impl ShMem for AflShmem { + fn existing_map_by_shm_bytes( + map_str_bytes: &[u8; 20], + map_size: usize, + ) -> Result { + Self::from_name_slice(map_str_bytes, map_size) + } + + fn new_map(map_size: usize) -> Result { + Self::new(map_size) + } + + fn shm_str(&self) -> String { + unsafe { CStr::from_ptr(self.shm_str.as_ptr() as *const i8) } + .to_string_lossy() + .into() + } + + fn shm_str_buf(&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 AflShmem { + fn drop(&mut self) { + unsafe { + afl_shmem_deinit(self); + } + } +} + +/// Create an uninitialized shmap +const fn afl_shmem_unitialized() -> AflShmem { + AflShmem { + shm_str: [0; 20], + shm_id: -1, + map: 0 as *mut c_uchar, + map_size: 0, + } +} + +impl AflShmem { + pub fn from_str(shm_str: &CStr, map_size: usize) -> Result { + let mut ret = afl_shmem_unitialized(); + let map = unsafe { afl_shmem_by_str(&mut ret, shm_str, map_size) }; + if map != 0 as *mut u8 { + Ok(ret) + } else { + Err(AflError::Unknown(format!( + "Could not allocate map with id {:?} and size {}", + shm_str, map_size + ))) + } + } + + /// Generate a shared map with a fixed byte array of 20 + pub fn from_name_slice(shm_str: &[u8; 20], map_size: usize) -> Result { + unsafe { + let str_bytes = shm_str as *const [u8; 20] as *const libc::c_char; + Self::from_str(CStr::from_ptr(str_bytes), map_size) + } + } + + pub fn new(map_size: usize) -> Result { + let mut ret = afl_shmem_unitialized(); + let map = unsafe { afl_shmem_init(&mut ret, map_size) }; + if map != 0 as *mut u8 { + Ok(ret) + } else { + Err(AflError::Unknown(format!( + "Could not allocate map of size {}", + map_size + ))) + } + } +} + +/// Deinitialize this shmem instance +unsafe fn afl_shmem_deinit(shm: *mut AflShmem) { + if shm.is_null() || (*shm).map.is_null() { + /* Serialized map id */ + // Not set or not initialized; + return; + } + (*shm).shm_str[0 as usize] = '\u{0}' as u8; + shmctl((*shm).shm_id, 0 as c_int, 0 as *mut shmid_ds); + (*shm).map = 0 as *mut c_uchar; +} + +/// Functions to create Shared memory region, for observation channels and +/// opening inputs and stuff. +unsafe fn afl_shmem_init(shm: *mut AflShmem, map_size: usize) -> *mut c_uchar { + (*shm).map_size = map_size; + (*shm).map = 0 as *mut c_uchar; + (*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] = '\u{0}' as u8; + return 0 as *mut c_uchar; + } + snprintf( + (*shm).shm_str.as_mut_ptr() as *mut i8, + 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_int as c_ulong) as usize] = + '\u{0}' as u8; + (*shm).map = shmat((*shm).shm_id, 0 as *const c_void, 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, 0 as *mut shmid_ds); + (*shm).shm_id = -(1 as c_int); + (*shm).shm_str[0 as c_int as usize] = '\u{0}' as u8; + return 0 as *mut c_uchar; + } + return (*shm).map; +} + +/// Uses a shmap id string to open a shared map +unsafe fn afl_shmem_by_str(shm: *mut AflShmem, shm_str: &CStr, map_size: usize) -> *mut c_uchar { + if shm.is_null() || shm_str.to_bytes().len() == 0 || map_size == 0 { + return 0 as *mut c_uchar; + } + (*shm).map = 0 as *mut c_uchar; + (*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_int as c_ulong), + ); + (*shm).shm_id = shm_str + .to_str() + .expect(&format!("illegal shm_str {:?}", shm_str)) + .parse::() + .unwrap(); + (*shm).map = shmat((*shm).shm_id, 0 as *const c_void, 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 = 0 as *mut c_uchar; + (*shm).map_size = 0; + (*shm).shm_str[0] = '\u{0}' as u8; + return 0 as *mut c_uchar; + } + return (*shm).map; +} diff --git a/afl/src/executors/inmemory.rs b/afl/src/executors/inmemory.rs index 6a34ae108f..875e2d7f0e 100644 --- a/afl/src/executors/inmemory.rs +++ b/afl/src/executors/inmemory.rs @@ -135,7 +135,7 @@ pub mod unix_signals { let input = (CURRENT_INPUT_PTR as *const I).as_ref().unwrap(); let manager = (EVENT_MANAGER_PTR as *mut EM).as_mut().unwrap(); - + manager.crash(input).expect("Error in sending Crash event"); std::process::exit(139); @@ -162,8 +162,10 @@ pub mod unix_signals { let input = (CURRENT_INPUT_PTR as *const I).as_ref().unwrap(); let manager = (EVENT_MANAGER_PTR as *mut EM).as_mut().unwrap(); - - manager.timeout(input).expect("Error in sending Timeout event"); + + manager + .timeout(input) + .expect("Error in sending Timeout event"); // TODO: send LLMP. println!("Timeout in fuzz run."); @@ -183,7 +185,7 @@ pub mod unix_signals { R: Rand, { EVENT_MANAGER_PTR = manager as *mut _ as *mut c_void; - + let mut sa: sigaction = mem::zeroed(); libc::sigemptyset(&mut sa.sa_mask as *mut libc::sigset_t); sa.sa_flags = SA_NODEFER | SA_SIGINFO; From eb79004f40ee0325a13c72e695a9b441926af4f4 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Sat, 19 Dec 2020 12:46:12 +0100 Subject: [PATCH 08/13] fixed llmp compilation in std --- afl/src/events/llmp.rs | 69 +++++++++++++++++++++++++++++++---------- afl/src/events/mod.rs | 2 +- afl/src/events/shmem.rs | 27 ++++++++++++++-- afl/src/lib.rs | 1 + 4 files changed, 79 insertions(+), 20 deletions(-) diff --git a/afl/src/events/llmp.rs b/afl/src/events/llmp.rs index 8fca736d33..55f84382d8 100644 --- a/afl/src/events/llmp.rs +++ b/afl/src/events/llmp.rs @@ -56,6 +56,8 @@ use core::{ time::Duration, }; +use alloc::vec::Vec; + #[cfg(feature = "std")] use std::{ io::{Read, Write}, @@ -66,7 +68,6 @@ use std::{ use crate::utils::next_pow2; use crate::AflError; -#[cfg(feature = "std")] use super::shmem::ShMem; /// We'll start off with 256 megabyte maps per fuzzer client @@ -209,6 +210,7 @@ impl LlmpMsg { } } +#[cfg(feature = "std")] /// An Llmp instance pub enum LlmpConnection where @@ -223,6 +225,7 @@ where IsClient { client: LlmpClient }, } +#[cfg(feature = "std")] impl LlmpConnection where SH: ShMem, @@ -437,8 +440,8 @@ where let page = map.page(); let last_msg = self.last_msg_sent; if (*page).size_used + EOP_MSG_SIZE > (*page).size_total { - panic!(format!("PROGRAM ABORT : BUG: EOP does not fit in page! page {:?}, size_current {:?}, size_total {:?}", page, - (*page).size_used, (*page).size_total)); + panic!("PROGRAM ABORT : BUG: EOP does not fit in page! page {:?}, size_current {:?}, size_total {:?}", page, + (*page).size_used, (*page).size_total); } let mut ret: *mut LlmpMsg = if !last_msg.is_null() { llmp_next_msg_ptr_checked(&mut map, last_msg, EOP_MSG_SIZE)? @@ -502,7 +505,7 @@ where } } else if (*page).current_msg_id != (*last_msg).message_id { /* Oops, wrong usage! */ - panic!(format!("BUG: The current message never got commited using send! (page->current_msg_id {:?}, last_msg->message_id: {})", (*page).current_msg_id, (*last_msg).message_id)); + panic!("BUG: The current message never got commited using send! (page->current_msg_id {:?}, last_msg->message_id: {})", (*page).current_msg_id, (*last_msg).message_id); } else { buf_len_padded = complete_msg_size - size_of::(); /* DBG("XXX ret %p id %u buf_len_padded %lu complete_msg_size %lu\n", ret, ret->message_id, buf_len_padded, @@ -516,8 +519,12 @@ where ret = match llmp_next_msg_ptr_checked(map, last_msg, complete_msg_size) { Ok(msg) => msg, Err(e) => { + #[cfg(feature = "std")] dbg!("Unexpected error allocing new msg", e); + #[cfg(feature = "std")] return None; + #[cfg(not(feature = "std"))] + panic!(&format!("Unexpected error allocing new msg {:?}", e)); } }; (*ret).message_id = (*last_msg).message_id + 1 @@ -531,8 +538,8 @@ where if last_msg.is_null() && (*page).size_used != 0 || ((ret as usize) - (*page).messages.as_mut_ptr() as usize) != (*page).size_used { - panic!(format!("Allocated new message without calling send() inbetween. ret: {:?}, page: {:?}, complete_msg_size: {:?}, size_used: {:?}, last_msg: {:?}", ret, page, - buf_len_padded, (*page).size_used, last_msg)); + panic!("Allocated new message without calling send() inbetween. ret: {:?}, page: {:?}, complete_msg_size: {:?}, size_used: {:?}, last_msg: {:?}", ret, page, + buf_len_padded, (*page).size_used, last_msg); } (*page).size_used = (*page).size_used + complete_msg_size; (*ret).buf_len_padded = buf_len_padded as u64; @@ -553,10 +560,7 @@ where panic!("Message sent twice!"); } if (*msg).tag == LLMP_TAG_UNSET { - panic!(format!( - "No tag set on message with id {}", - (*msg).message_id - )); + panic!("No tag set on message with id {}", (*msg).message_id); } let page = self.out_maps.last_mut().unwrap().page(); if msg.is_null() || !llmp_msg_in_page(page, msg) { @@ -705,14 +709,15 @@ where match (*msg).tag { LLMP_TAG_UNSET => panic!("BUG: Read unallocated msg"), LLMP_TAG_END_OF_PAGE => { + #[cfg(feature = "std")] dbg!("Got end of page, allocing next"); // Handle end of page if (*msg).buf_len < size_of::() as u64 { - panic!(format!( + panic!( "Illegal message length for EOP (is {}, expected {})", (*msg).buf_len_padded, size_of::() - )); + ); } let pageinfo = (*msg).buf.as_mut_ptr() as *mut LlmpPayloadSharedMapInfo; @@ -732,6 +737,7 @@ where // 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); + #[cfg(feature = "std")] dbg!("Got a new recv map", self.current_recv_map.shmem.shm_str()); // After we mapped the new page, return the next message, if available return self.recv(); @@ -878,7 +884,7 @@ where (*out).buf_len_padded = actual_size; /* We need to replace the message ID with our own */ match self.llmp_out.send(out) { - Err(e) => panic!(format!("Error sending msg: {:?}", e)), + Err(e) => panic!("Error sending msg: {:?}", e), _ => (), }; self.llmp_out.last_msg_sent = out; @@ -912,10 +918,20 @@ where compiler_fence(Ordering::SeqCst); self.once(on_new_msg) .expect("An error occurred when brokering. Exiting."); + + #[cfg(feature = "std")] match sleep_time { Some(time) => thread::sleep(time), None => (), } + + #[cfg(not(feature = "std"))] + match sleep_time { + Some(_) => { + panic!("Cannot sleep on no_std platform"); + } + None => (), + } } } @@ -924,6 +940,7 @@ where self.llmp_out.send_buf(tag, buf) } + #[cfg(feature = "std")] /// Launches a thread using a tcp listener socket, on which new clients may connect to this broker /// Does so on the given port. pub fn launch_tcp_listener_on( @@ -936,6 +953,7 @@ where return self.launch_tcp_listener(listener); } + #[cfg(feature = "std")] /// Launches a thread using a tcp listener socket, on which new clients may connect to this broker pub fn launch_tcp_listener( &mut self, @@ -1042,10 +1060,16 @@ where /* This client informs us about yet another new client add it to the list! Also, no need to forward this msg. */ if (*msg).buf_len < size_of::() as u64 { + #[cfg(feature = "std")] println!("Ignoring broken CLIENT_ADDED msg due to incorrect size. Expected {} but got {}", (*msg).buf_len_padded, size_of::() ); + #[cfg(not(feature = "std"))] + return Err(AflError::Unknown(format!("Broken CLIENT_ADDED msg with incorrect size received. Expected {} but got {}", + (*msg).buf_len_padded, + size_of::() + ))); } else { let pageinfo = (*msg).buf.as_mut_ptr() as *mut LlmpPayloadSharedMapInfo; @@ -1061,7 +1085,15 @@ where last_msg_recvd: 0 as *mut LlmpMsg, }); } - Err(e) => println!("Error adding client! {:?}", e), + Err(e) => { + #[cfg(feature = "std")] + println!("Error adding client! Ignoring: {:?}", e); + #[cfg(not(feature = "std"))] + return Err(AflError::Unknown(format!( + "Error adding client! PANIC! {:?}", + e + ))); + } }; } } else { @@ -1171,11 +1203,13 @@ where self.llmp_in.recv_buf_blocking() } + #[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)?)) } + #[cfg(feature = "std")] /// Create a LlmpClient, getting the ID from a given port pub fn create_attach_to_tcp(port: u16) -> Result { let mut stream = TcpStream::connect(format!("127.0.0.1:{}", port))?; @@ -1197,16 +1231,19 @@ where #[cfg(test)] mod tests { + #[cfg(feature = "std")] use std::{thread::sleep, time::Duration}; - use crate::events::shmem::AflShmem; - + #[cfg(feature = "std")] use super::{ LlmpConnection::{self, IsBroker, IsClient}, LlmpMsgHookResult::ForwardToClients, Tag, }; + #[cfg(feature = "std")] + use crate::events::shmem::AflShmem; + #[cfg(feature = "std")] #[test] pub fn llmp_connection() { let mut broker = match LlmpConnection::::on_port(1337).unwrap() { diff --git a/afl/src/events/mod.rs b/afl/src/events/mod.rs index de2059a94b..805349560a 100644 --- a/afl/src/events/mod.rs +++ b/afl/src/events/mod.rs @@ -1,5 +1,4 @@ pub mod llmp; -#[cfg(feature = "std")] pub mod shmem; use alloc::string::{String, ToString}; @@ -9,6 +8,7 @@ use core::{marker::PhantomData, time}; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; +#[cfg(feature = "std")] use self::llmp::Tag; #[cfg(feature = "std")] use self::shmem::AflShmem; diff --git a/afl/src/events/shmem.rs b/afl/src/events/shmem.rs index 8c89419deb..17f6bda842 100644 --- a/afl/src/events/shmem.rs +++ b/afl/src/events/shmem.rs @@ -1,20 +1,30 @@ //! A generic sharememory region to be used by any functions (queues or feedbacks // too.) -use core::slice; +use alloc::string::String; +#[cfg(feature = "std")] +use core::{mem::size_of, slice}; +#[cfg(feature = "std")] use libc::{c_char, c_int, c_long, c_uchar, c_uint, c_ulong, c_ushort, c_void}; -use std::{env, ffi::CStr, mem::size_of}; +#[cfg(feature = "std")] +use std::{env, ffi::CStr}; use crate::AflError; 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; - //fn strlen(_: *const c_char) -> c_ulong; + #[cfg(feature = "std")] fn shmctl(__shmid: c_int, __cmd: c_int, __buf: *mut shmid_ds) -> c_int; + #[cfg(feature = "std")] fn shmget(__key: c_int, __size: c_ulong, __shmflg: c_int) -> c_int; + #[cfg(feature = "std")] fn shmat(__shmid: c_int, __shmaddr: *const c_void, __shmflg: c_int) -> *mut c_void; } + +#[cfg(feature = "std")] #[derive(Copy, Clone)] #[repr(C)] struct ipc_perm { @@ -31,6 +41,7 @@ struct ipc_perm { pub __glibc_reserved2: c_ulong, } +#[cfg(feature = "std")] #[derive(Copy, Clone)] #[repr(C)] struct shmid_ds { @@ -79,6 +90,7 @@ pub trait ShMem: Sized { fn map_mut(&mut self) -> &mut [u8]; /// Write this map's config to env + #[cfg(feature = "std")] fn write_to_env(&self, env_name: &str) -> Result<(), AflError> { let map_size = self.map().len(); let map_size_env = format!("{}_SIZE", env_name); @@ -88,6 +100,7 @@ pub trait ShMem: Sized { } /// Reads an existing map config from env vars, then maps it + #[cfg(feature = "std")] fn existing_from_env(env_name: &str) -> Result { let map_shm_str = env::var(env_name)?; let map_size = str::parse::(&env::var(format!("{}_SIZE", env_name))?)?; @@ -95,6 +108,7 @@ pub trait ShMem: Sized { } } +#[cfg(feature = "std")] #[derive(Clone, Debug)] pub struct AflShmem { pub shm_str: [u8; 20], @@ -103,6 +117,7 @@ pub struct AflShmem { pub map_size: usize, } +#[cfg(feature = "std")] impl ShMem for AflShmem { fn existing_map_by_shm_bytes( map_str_bytes: &[u8; 20], @@ -134,6 +149,7 @@ impl ShMem for AflShmem { } } +#[cfg(feature = "std")] /// Deinit sharedmaps on drop impl Drop for AflShmem { fn drop(&mut self) { @@ -143,6 +159,7 @@ impl Drop for AflShmem { } } +#[cfg(feature = "std")] /// Create an uninitialized shmap const fn afl_shmem_unitialized() -> AflShmem { AflShmem { @@ -153,6 +170,7 @@ const fn afl_shmem_unitialized() -> AflShmem { } } +#[cfg(feature = "std")] impl AflShmem { pub fn from_str(shm_str: &CStr, map_size: usize) -> Result { let mut ret = afl_shmem_unitialized(); @@ -189,6 +207,7 @@ impl AflShmem { } } +#[cfg(feature = "std")] /// Deinitialize this shmem instance unsafe fn afl_shmem_deinit(shm: *mut AflShmem) { if shm.is_null() || (*shm).map.is_null() { @@ -201,6 +220,7 @@ unsafe fn afl_shmem_deinit(shm: *mut AflShmem) { (*shm).map = 0 as *mut c_uchar; } +#[cfg(feature = "std")] /// Functions to create Shared memory region, for observation channels and /// opening inputs and stuff. unsafe fn afl_shmem_init(shm: *mut AflShmem, map_size: usize) -> *mut c_uchar { @@ -234,6 +254,7 @@ unsafe fn afl_shmem_init(shm: *mut AflShmem, map_size: usize) -> *mut c_uchar { return (*shm).map; } +#[cfg(feature = "std")] /// Uses a shmap id string to open a shared map unsafe fn afl_shmem_by_str(shm: *mut AflShmem, shm_str: &CStr, map_size: usize) -> *mut c_uchar { if shm.is_null() || shm_str.to_bytes().len() == 0 || map_size == 0 { diff --git a/afl/src/lib.rs b/afl/src/lib.rs index dfdc76cccd..25143ab9b9 100644 --- a/afl/src/lib.rs +++ b/afl/src/lib.rs @@ -101,6 +101,7 @@ impl From for AflError { } } +#[cfg(feature = "std")] impl From for AflError { fn from(err: ParseIntError) -> Self { Self::Unknown(format!("Failed to parse Int: {:?}", err)) From ee6d4ab3d2285ca0dc7ddf98b5cfc0b944d67085 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Sat, 19 Dec 2020 13:06:01 +0100 Subject: [PATCH 09/13] code format --- afl/src/events/llmp.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/afl/src/events/llmp.rs b/afl/src/events/llmp.rs index 55f84382d8..9a2616e4e8 100644 --- a/afl/src/events/llmp.rs +++ b/afl/src/events/llmp.rs @@ -48,6 +48,7 @@ Then register some clientloops using llmp_broker_register_threaded_clientloop */ +use alloc::vec::Vec; use core::{ cmp::max, mem::size_of, @@ -55,9 +56,6 @@ use core::{ sync::atomic::{compiler_fence, Ordering}, time::Duration, }; - -use alloc::vec::Vec; - #[cfg(feature = "std")] use std::{ io::{Read, Write}, @@ -65,11 +63,10 @@ use std::{ thread, }; +use super::shmem::ShMem; use crate::utils::next_pow2; use crate::AflError; -use super::shmem::ShMem; - /// We'll start off with 256 megabyte maps per fuzzer client const LLMP_PREF_INITIAL_MAP_SIZE: usize = 1 << 28; /// What byte count to align messages to From 5c7810763d240020f4f045241f13db69ee6dd4b7 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Sat, 19 Dec 2020 15:42:15 +0100 Subject: [PATCH 10/13] tiny refactorings --- afl/src/events/llmp.rs | 15 +++++---- afl/src/events/shmem.rs | 67 +++++++++++++++++++++++++---------------- 2 files changed, 48 insertions(+), 34 deletions(-) diff --git a/afl/src/events/llmp.rs b/afl/src/events/llmp.rs index 9a2616e4e8..e27001c4d2 100644 --- a/afl/src/events/llmp.rs +++ b/afl/src/events/llmp.rs @@ -594,7 +594,7 @@ where 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_str_buf(); + (*end_of_page_msg).shm_str = *new_map_shmem.shmem.shm_slice(); // We never sent a msg on the new buf */ self.last_msg_sent = 0 as *mut LlmpMsg; @@ -727,7 +727,7 @@ 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_map_by_shm_bytes( + LlmpSharedMap::existing(SH::existing_from_shm_slice( &pageinfo_cpy.shm_str, pageinfo_cpy.map_size, )?); @@ -962,7 +962,7 @@ where // to read from the initial map id. let client_out_map_mem = &self.llmp_out.out_maps.first().unwrap().shmem; - let broadcast_str_initial = client_out_map_mem.shm_str_buf().clone(); + let broadcast_str_initial = client_out_map_mem.shm_slice().clone(); let llmp_tcp_id = self.llmp_clients.len() as u32; @@ -980,7 +980,7 @@ where id: 0, last_msg_sent: 0 as *mut LlmpMsg, out_maps: vec![LlmpSharedMap::existing( - SH::existing_from_name(&tcp_out_map_str, tcp_out_map_size).unwrap(), + SH::existing_from_shm_str(&tcp_out_map_str, tcp_out_map_size).unwrap(), )], // drop pages to the broker if it already read them keep_pages_forever: false, @@ -1070,8 +1070,7 @@ where } else { let pageinfo = (*msg).buf.as_mut_ptr() as *mut LlmpPayloadSharedMapInfo; - match SH::existing_map_by_shm_bytes(&(*pageinfo).shm_str, (*pageinfo).map_size) - { + match SH::existing_from_shm_slice(&(*pageinfo).shm_str, (*pageinfo).map_size) { Ok(new_map) => { let new_page = LlmpSharedMap::existing(new_map); let id = next_id; @@ -1215,12 +1214,12 @@ where let mut new_broker_map_str: [u8; 20] = Default::default(); stream.read_exact(&mut new_broker_map_str)?; - let ret = Self::new(LlmpSharedMap::existing(SH::existing_map_by_shm_bytes( + let ret = Self::new(LlmpSharedMap::existing(SH::existing_from_shm_slice( &new_broker_map_str, LLMP_PREF_INITIAL_MAP_SIZE, )?))?; - stream.write(ret.llmp_out.out_maps.first().unwrap().shmem.shm_str_buf())?; + stream.write(ret.llmp_out.out_maps.first().unwrap().shmem.shm_slice())?; Ok(ret) } } diff --git a/afl/src/events/shmem.rs b/afl/src/events/shmem.rs index 17f6bda842..bcd92b269c 100644 --- a/afl/src/events/shmem.rs +++ b/afl/src/events/shmem.rs @@ -60,28 +60,32 @@ struct shmid_ds { /// A Shared map pub trait ShMem: Sized { /// Creates a nes variable with the given name, strigified to 20 bytes. - fn existing_map_by_shm_bytes( - map_str_bytes: &[u8; 20], - map_size: usize, - ) -> Result; + fn existing_from_shm_slice(map_str_bytes: &[u8; 20], map_size: usize) + -> Result; /// Initialize from a shm_str with fixed len of 20 - fn existing_from_name(shm_str: &str, map_size: usize) -> Result { + fn existing_from_shm_str(shm_str: &str, map_size: usize) -> Result { let mut slice: [u8; 20] = [0; 20]; for (i, val) in shm_str.as_bytes().iter().enumerate() { slice[i] = *val; } - Self::existing_map_by_shm_bytes(&slice, map_size) + Self::existing_from_shm_slice(&slice, map_size) } /// Creates a new map with the given size fn new_map(map_size: usize) -> Result; /// The string to identify this shm - fn shm_str(&self) -> String; + 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() + } /// Let's just fix this to a large enough buf - fn shm_str_buf(&self) -> &[u8; 20]; + fn shm_slice(&self) -> &[u8; 20]; /// The actual shared map, in memory fn map(&self) -> &[u8]; @@ -104,7 +108,7 @@ pub trait ShMem: Sized { fn existing_from_env(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_name(&map_shm_str, map_size) + Self::existing_from_shm_str(&map_shm_str, map_size) } } @@ -119,24 +123,21 @@ pub struct AflShmem { #[cfg(feature = "std")] impl ShMem for AflShmem { - fn existing_map_by_shm_bytes( + fn existing_from_shm_slice( map_str_bytes: &[u8; 20], map_size: usize, ) -> Result { - Self::from_name_slice(map_str_bytes, map_size) + 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) + } } fn new_map(map_size: usize) -> Result { Self::new(map_size) } - fn shm_str(&self) -> String { - unsafe { CStr::from_ptr(self.shm_str.as_ptr() as *const i8) } - .to_string_lossy() - .into() - } - - fn shm_str_buf(&self) -> &[u8; 20] { + fn shm_slice(&self) -> &[u8; 20] { &self.shm_str } @@ -185,14 +186,6 @@ impl AflShmem { } } - /// Generate a shared map with a fixed byte array of 20 - pub fn from_name_slice(shm_str: &[u8; 20], map_size: usize) -> Result { - unsafe { - let str_bytes = shm_str as *const [u8; 20] as *const libc::c_char; - Self::from_str(CStr::from_ptr(str_bytes), map_size) - } - } - pub fn new(map_size: usize) -> Result { let mut ret = afl_shmem_unitialized(); let map = unsafe { afl_shmem_init(&mut ret, map_size) }; @@ -281,3 +274,25 @@ unsafe fn afl_shmem_by_str(shm: *mut AflShmem, shm_str: &CStr, map_size: usize) } return (*shm).map; } + +#[cfg(test)] +mod tests { + use super::{AflShmem, ShMem}; + + #[cfg(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 = AflShmem { + shm_id: 0, + shm_str, + map: 0 as *mut u8, + map_size: 20, + }; + let str = faux_shmem.shm_str(); + assert_eq!(str, "ABC"); + } +} From d4b83ec85ecb91f435b987ad60b71e88638615fc Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Sun, 20 Dec 2020 01:03:44 +0100 Subject: [PATCH 11/13] groundwork for restarting clients --- afl/src/events/llmp.rs | 180 ++++++++++++++++++++++++++++------------- afl/src/events/mod.rs | 47 ++++++----- afl/src/lib.rs | 3 + 3 files changed, 154 insertions(+), 76 deletions(-) diff --git a/afl/src/events/llmp.rs b/afl/src/events/llmp.rs index e27001c4d2..f52fbac479 100644 --- a/afl/src/events/llmp.rs +++ b/afl/src/events/llmp.rs @@ -192,14 +192,14 @@ impl LlmpMsg { unsafe { let map_size = map.shmem.map().len(); let buf_ptr = self.buf.as_ptr(); - if buf_ptr > (map.page() as *const u8).offset(size_of::() as isize) + if buf_ptr > (map.page_mut() as *const u8).offset(size_of::() as isize) && buf_ptr - <= (map.page() as *const u8) + <= (map.page_mut() as *const u8) .offset((map_size - size_of::() as usize) as isize) { // The message header is in the page. Continue with checking the body. let len = self.buf_len_padded as usize + size_of::(); - buf_ptr <= (map.page() as *const u8).offset((map_size - len) as isize) + buf_ptr <= (map.page_mut() as *const u8).offset((map_size - len) as isize) } else { false } @@ -207,26 +207,22 @@ impl LlmpMsg { } } -#[cfg(feature = "std")] /// An Llmp instance pub enum LlmpConnection where SH: ShMem, { /// A broker and a thread using this tcp background thread - IsBroker { - broker: LlmpBroker, - listener_thread: thread::JoinHandle<()>, - }, + IsBroker { broker: LlmpBroker }, /// A client, connected to the port IsClient { client: LlmpClient }, } -#[cfg(feature = "std")] impl LlmpConnection where SH: ShMem, { + #[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 { match TcpListener::bind(format!("127.0.0.1:{}", port)) { @@ -234,11 +230,8 @@ where // We got the port. We are the broker! :) dbg!("We're the broker"); let mut broker = LlmpBroker::new()?; - let listener_thread = broker.launch_tcp_listener(listener)?; - Ok(LlmpConnection::IsBroker { - broker, - listener_thread, - }) + let _listener_thread = broker.launch_tcp_listener(listener)?; + Ok(LlmpConnection::IsBroker { broker }) } Err(e) => { match e.kind() { @@ -258,10 +251,7 @@ where /// Sends the given buffer over this connection, no matter if client or broker. pub fn send_buf(&mut self, tag: Tag, buf: &[u8]) -> Result<(), AflError> { match self { - LlmpConnection::IsBroker { - broker, - listener_thread: _, - } => broker.send_buf(tag, buf), + LlmpConnection::IsBroker { broker } => broker.send_buf(tag, buf), LlmpConnection::IsClient { client } => client.send_buf(tag, buf), } } @@ -281,21 +271,7 @@ pub struct LlmpPage { pub messages: [LlmpMsg; 0], } -/// The broker (node 0) -#[derive(Clone, Debug)] -#[repr(C)] -pub struct LlmpBroker -where - SH: ShMem, -{ - /// Broadcast map from broker to all clients - 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>, -} - +#[derive(Copy, Clone, Debug)] /// Result of an LLMP Mesasge hook pub enum LlmpMsgHookResult { /// This has been handled in the broker. No need to forward. @@ -316,10 +292,16 @@ struct LlmpPayloadSharedMapInfo { /// Get sharedmem from a page #[inline] -unsafe fn shmem2page(afl_shmem: &mut SH) -> *mut LlmpPage { +unsafe fn shmem2page_mut(afl_shmem: &mut SH) -> *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 { + afl_shmem.map().as_ptr() as *const LlmpPage +} + /// Return, if a msg is contained in the current page #[inline] unsafe fn llmp_msg_in_page(page: *mut LlmpPage, msg: *mut LlmpMsg) -> bool { @@ -359,7 +341,7 @@ fn new_map_size(max_alloc: usize) -> usize { /// llmp_page->messages unsafe fn _llmp_page_init(shmem: &mut SH, sender: u32) { let map_size = shmem.map().len(); - let page = shmem2page(shmem); + let page = shmem2page_mut(shmem); (*page).sender = sender; ptr::write_volatile(&mut (*page).current_msg_id, 0); (*page).max_alloc_size = 0; @@ -379,7 +361,7 @@ unsafe fn llmp_next_msg_ptr_checked( last_msg: *const LlmpMsg, alloc_size: usize, ) -> Result<*mut LlmpMsg, AflError> { - let page = map.page(); + let page = map.page_mut(); let map_size = map.shmem.map().len(); let msg_begin_min = (page as *const u8).offset(size_of::() as isize); // We still need space for this msg (alloc_size). @@ -417,7 +399,7 @@ where // Exclude the current page by splitting of the last element for this iter let mut unmap_until_excl = 0; for map in self.out_maps.split_last_mut().unwrap().1 { - if (*map.page()).save_to_unmap == 0 { + if (*map.page_mut()).save_to_unmap == 0 { // The broker didn't read this page yet, no more pages to unmap. break; } @@ -434,7 +416,7 @@ where /// place EOP, commit EOP, reset, alloc again on the new space. unsafe fn alloc_eop(&mut self) -> Result<*mut LlmpMsg, AflError> { let mut map = self.out_maps.last_mut().unwrap(); - let page = map.page(); + let page = map.page_mut(); let last_msg = self.last_msg_sent; if (*page).size_used + EOP_MSG_SIZE > (*page).size_total { panic!("PROGRAM ABORT : BUG: EOP does not fit in page! page {:?}, size_current {:?}, size_total {:?}", page, @@ -466,7 +448,7 @@ where let buf_len_padded; let mut complete_msg_size = llmp_align(size_of::() + buf_len); let map = self.out_maps.last_mut().unwrap(); - let page = map.page(); + let page = map.page_mut(); let last_msg = self.last_msg_sent; /* DBG("XXX complete_msg_size %lu (h: %lu)\n", complete_msg_size, sizeof(llmp_message)); */ /* In case we don't have enough space, make sure the next page will be large @@ -559,7 +541,7 @@ where if (*msg).tag == LLMP_TAG_UNSET { panic!("No tag set on message with id {}", (*msg).message_id); } - let page = self.out_maps.last_mut().unwrap().page(); + let page = self.out_maps.last_mut().unwrap().page_mut(); if msg.is_null() || !llmp_msg_in_page(page, msg) { return Err(AflError::Unknown(format!( "Llmp Message {:?} is null or not in current page", @@ -576,14 +558,14 @@ where /// listener about it using a EOP message. unsafe fn handle_out_eop(&mut self) -> Result<(), AflError> { - let old_map = self.out_maps.last_mut().unwrap().page(); + let old_map = self.out_maps.last_mut().unwrap().page_mut(); // 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))?, ); - let mut new_map = new_map_shmem.page(); + let mut new_map = new_map_shmem.page_mut(); ptr::write_volatile(&mut (*new_map).current_msg_id, (*old_map).current_msg_id); (*new_map).max_alloc_size = (*old_map).max_alloc_size; @@ -634,7 +616,7 @@ where pub unsafe fn cancel_send(&mut self, msg: *mut LlmpMsg) { /* DBG("Client %d cancels send of msg at %p with tag 0x%X and size %ld", client->id, msg, msg->tag, * msg->buf_len_padded); */ - let page = self.out_maps.last_mut().unwrap().page(); + let page = self.out_maps.last_mut().unwrap().page_mut(); (*msg).tag = LLMP_TAG_UNSET; (*page).size_used -= (*msg).buf_len_padded as usize + size_of::(); } @@ -673,7 +655,7 @@ where unsafe fn recv(&mut self) -> Result, AflError> { /* DBG("recv %p %p\n", page, last_msg); */ compiler_fence(Ordering::SeqCst); - let page = self.current_recv_map.page(); + let page = self.current_recv_map.page_mut(); let last_msg = self.last_msg_recvd; let current_msg_id = ptr::read_volatile(&mut (*page).current_msg_id); @@ -754,7 +736,7 @@ where /// then returns that message. pub unsafe fn recv_blocking(&mut self) -> Result<*mut LlmpMsg, AflError> { let mut current_msg_id = 0; - let page = self.current_recv_map.page(); + let page = self.current_recv_map.page_mut(); let last_msg = self.last_msg_recvd; if !last_msg.is_null() { if (*last_msg).tag == LLMP_TAG_END_OF_PAGE && !llmp_msg_in_page(page, last_msg) { @@ -825,9 +807,66 @@ where } /// Get the unsafe ptr to this page, situated on the shared map - pub unsafe fn page(&mut self) -> *mut LlmpPage { - shmem2page(&mut self.shmem) + pub unsafe fn page_mut(&mut self) -> *mut LlmpPage { + shmem2page_mut(&mut self.shmem) } + + /// Get the unsafe ptr to this page, situated on the shared map + pub unsafe fn page(&self) -> *const LlmpPage { + shmem2page(&self.shmem) + } + + /// Gets the offset of a message on this here page. + /// Will return IllegalArgument error if msg is not on page. + pub fn msg_to_offset(&mut self, msg: *mut LlmpMsg) -> Result { + unsafe { + let page = self.page_mut(); + if llmp_msg_in_page(page, msg) { + // Cast both sides to u8 arrays, get the offset, then cast the return isize to u64 + Ok((msg as *const u8).offset_from((*page).messages.as_ptr() as *const u8) as u64) + } else { + Err(AflError::IllegalArgument(format!( + "Message (0x{:X}) not in page (0x{:X})", + page as u64, msg as u64 + ))) + } + } + } + + /// Gets this message from this page, at the indicated offset. + /// Will return IllegalArgument error if the offset is out of bounds. + pub fn msg_from_offset(&mut self, offset: u64) -> Result<*mut LlmpMsg, AflError> { + unsafe { + let page = self.page_mut(); + let page_size = self.shmem.map().len() - size_of::(); + if offset as isize > page_size as isize { + Err(AflError::IllegalArgument(format!( + "Msg offset out of bounds (size: {}, requested offset: {})", + page_size, offset + ))) + } else { + Ok( + ((*page).messages.as_mut_ptr() as *mut u8).offset(offset as isize) + as *mut LlmpMsg, + ) + } + } + } +} + +/// The broker (node 0) +#[derive(Clone, Debug)] +#[repr(C)] +pub struct LlmpBroker +where + SH: ShMem, +{ + /// Broadcast map from broker to all clients + 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>, } /// The broker forwards all messages to its own bus-like broadcast map. @@ -1116,6 +1155,43 @@ impl LlmpClient where SH: ShMem, { + /// 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 + pub fn on_existing_map( + current_out_map: SH, + last_msg_sent_offset: Option, + current_broker_map: SH, + last_msg_recvd_offset: Option, + ) -> Result { + let mut out_map = LlmpSharedMap::new(0, current_out_map); + let last_msg_sent = match last_msg_sent_offset { + Some(offset) => out_map.msg_from_offset(offset)?, + None => 0 as *mut LlmpMsg, + }; + + let mut current_recv_map = LlmpSharedMap::new(0, current_broker_map); + let last_msg_recvd = match last_msg_recvd_offset { + Some(offset) => current_recv_map.msg_from_offset(offset)?, + None => 0 as *mut LlmpMsg, + }; + + Ok(Self { + llmp_out: LlmpSender { + id: 0, + last_msg_sent, + out_maps: vec![out_map], + // drop pages to the broker if it already read them + keep_pages_forever: false, + }, + llmp_in: LlmpReceiver { + id: 0, + current_recv_map, + last_msg_recvd, + }, + }) + } + /// Creates a new LlmpClient pub fn new(initial_broker_map: LlmpSharedMap) -> Result { Ok(Self { @@ -1244,18 +1320,12 @@ mod tests { pub fn llmp_connection() { let mut broker = match LlmpConnection::::on_port(1337).unwrap() { IsClient { client: _ } => panic!("Could not bind to port as broker"), - IsBroker { - broker, - listener_thread: _, - } => 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() { - IsBroker { - broker: _, - listener_thread: _, - } => panic!("Second connect should be a client!"), + IsBroker { broker: _ } => panic!("Second connect should be a client!"), IsClient { client } => client, }; diff --git a/afl/src/events/mod.rs b/afl/src/events/mod.rs index 805349560a..6049bb86a1 100644 --- a/afl/src/events/mod.rs +++ b/afl/src/events/mod.rs @@ -9,9 +9,10 @@ use core::{marker::PhantomData, time}; use serde::{Deserialize, Serialize}; #[cfg(feature = "std")] -use self::llmp::Tag; -#[cfg(feature = "std")] -use self::shmem::AflShmem; +use self::{ + llmp::{LlmpClient, Tag}, + shmem::ShMem, +}; use crate::corpus::Corpus; use crate::executors::Executor; use crate::feedbacks::FeedbacksTuple; @@ -707,8 +708,7 @@ const _LLMP_TAG_EVENT_TO_BROKER: llmp::Tag = 0x2B80438; /// Handle in both const LLMP_TAG_EVENT_TO_BOTH: llmp::Tag = 0x2B0741; -#[cfg(feature = "std")] -pub struct LlmpEventManager +pub struct LlmpEventManager where C: Corpus, E: Executor, @@ -716,16 +716,16 @@ where FT: FeedbacksTuple, I: Input, R: Rand, + SH: ShMem, ST: Stats, //CE: CustomEvent, { - llmp: llmp::LlmpConnection, + llmp: llmp::LlmpConnection, stats: ST, phantom: PhantomData<(C, E, OT, FT, I, R)>, } -#[cfg(feature = "std")] -impl LlmpEventManager +impl LlmpEventManager where C: Corpus, E: Executor, @@ -733,27 +733,34 @@ where FT: FeedbacksTuple, I: Input, R: Rand, + SH: ShMem, ST: Stats, { + #[cfg(feature = "std")] /// Create llmp on a port /// If the port is not yet bound, it will act as broker /// Else, it will act as client. pub fn new_on_port(port: u16, stats: ST) -> Result { - let mgr = Self { + Ok(Self { llmp: llmp::LlmpConnection::on_port(port)?, stats: stats, phantom: PhantomData, - }; - Ok(mgr) + }) + } + + /// A client on an existing map + pub fn for_client(client: LlmpClient, stats: ST) -> Self { + Self { + llmp: llmp::LlmpConnection::IsClient { client }, + stats, + phantom: PhantomData, + } } /// Returns if we are the broker pub fn is_broker(&self) -> bool { match self.llmp { - llmp::LlmpConnection::IsBroker { - broker: _, - listener_thread: _, - } => true, + llmp::LlmpConnection::IsBroker { broker: _ } => true, _ => false, } } @@ -761,10 +768,7 @@ where /// Run forever in the broker pub fn broker_loop(&mut self) -> Result<(), AflError> { match &mut self.llmp { - llmp::LlmpConnection::IsBroker { - broker, - listener_thread: _, - } => { + llmp::LlmpConnection::IsBroker { broker } => { let stats = &mut self.stats; broker.loop_forever( &mut |sender_id: u32, tag: Tag, msg: &[u8]| { @@ -802,8 +806,8 @@ where } #[cfg(feature = "std")] -impl EventManager - for LlmpEventManager +impl EventManager + for LlmpEventManager where C: Corpus, E: Executor, @@ -811,6 +815,7 @@ where OT: ObserversTuple, I: Input, R: Rand, + SH: ShMem, ST: Stats, //CE: CustomEvent, { diff --git a/afl/src/lib.rs b/afl/src/lib.rs index 25143ab9b9..92a4c2b6fe 100644 --- a/afl/src/lib.rs +++ b/afl/src/lib.rs @@ -49,6 +49,8 @@ pub enum AflError { NotImplemented(String), /// You're holding it wrong IllegalState(String), + /// The argument passed to this method or function is not valid + IllegalArgument(String), /// Something else happened Unknown(String), } @@ -67,6 +69,7 @@ impl fmt::Display for AflError { } Self::NotImplemented(s) => write!(f, "Not implemented: {0}", &s), Self::IllegalState(s) => write!(f, "Illegal state: {0}", &s), + Self::IllegalArgument(s) => write!(f, "Illegal argument: {0}", &s), Self::Unknown(s) => write!(f, "Unknown error: {0}", &s), } } From d0117755285ce0dd56e53297de061dacf17588d9 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Sun, 20 Dec 2020 01:09:48 +0100 Subject: [PATCH 12/13] libfuzzer fix --- fuzzers/libfuzzer/src/lib.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/fuzzers/libfuzzer/src/lib.rs b/fuzzers/libfuzzer/src/lib.rs index f9e2cbca62..4f088a7356 100644 --- a/fuzzers/libfuzzer/src/lib.rs +++ b/fuzzers/libfuzzer/src/lib.rs @@ -7,6 +7,7 @@ use afl::engines::Engine; use afl::engines::Fuzzer; use afl::engines::State; use afl::engines::StdFuzzer; +use afl::events::shmem::AflShmem; use afl::events::{LlmpEventManager, SimpleStats}; use afl::executors::inmemory::InMemoryExecutor; use afl::executors::{Executor, ExitKind}; @@ -46,12 +47,8 @@ pub extern "C" fn afl_libfuzzer_main() { let stats = SimpleStats::new(|s| println!("{}", s)); - // TODO - /* match LlmpFuzzInstance::from_env("FUZZER_ENV") { - - }*/ - - let mut mgr = LlmpEventManager::new_on_port(1337, stats).unwrap(); + let mut mgr = + LlmpEventManager::<_, _, _, _, _, _, AflShmem, _>::new_on_port(1337, stats).unwrap(); if mgr.is_broker() { println!("Doing broker things. Run this tool again to start fuzzing in a client."); mgr.broker_loop().unwrap(); From 245f4244578db66dadea03fe133e28c80a4b2e07 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Sun, 20 Dec 2020 01:15:10 +0100 Subject: [PATCH 13/13] less noisy --- afl/src/events/mod.rs | 24 ++++++++++++++++++++++++ fuzzers/libfuzzer/src/lib.rs | 3 +-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/afl/src/events/mod.rs b/afl/src/events/mod.rs index 6049bb86a1..86bd20f63d 100644 --- a/afl/src/events/mod.rs +++ b/afl/src/events/mod.rs @@ -7,6 +7,7 @@ use core::time::Duration; use core::{marker::PhantomData, time}; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; +use shmem::AflShmem; #[cfg(feature = "std")] use self::{ @@ -725,6 +726,29 @@ where phantom: PhantomData<(C, E, OT, FT, I, R)>, } +impl LlmpEventManager +where + C: Corpus, + E: Executor, + OT: ObserversTuple, + FT: FeedbacksTuple, + I: Input, + R: Rand, + ST: Stats, +{ + #[cfg(feature = "std")] + /// Create llmp on a port + /// If the port is not yet bound, it will act as broker + /// Else, it will act as client. + pub fn new_on_port_std(stats: ST) -> Result { + Ok(Self { + llmp: llmp::LlmpConnection::on_port(port)?, + stats: stats, + phantom: PhantomData, + }) + } +} + impl LlmpEventManager where C: Corpus, diff --git a/fuzzers/libfuzzer/src/lib.rs b/fuzzers/libfuzzer/src/lib.rs index 4f088a7356..ff80a8f9e6 100644 --- a/fuzzers/libfuzzer/src/lib.rs +++ b/fuzzers/libfuzzer/src/lib.rs @@ -47,8 +47,7 @@ pub extern "C" fn afl_libfuzzer_main() { let stats = SimpleStats::new(|s| println!("{}", s)); - let mut mgr = - LlmpEventManager::<_, _, _, _, _, _, AflShmem, _>::new_on_port(1337, stats).unwrap(); + let mut mgr = LlmpEventManager::new_on_port_std(1337, stats).unwrap(); if mgr.is_broker() { println!("Doing broker things. Run this tool again to start fuzzing in a client."); mgr.broker_loop().unwrap();