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/afl/Cargo.toml b/afl/Cargo.toml index 328552e0bc..7900609236 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 \ No newline at end of file diff --git a/afl/src/events/llmp.rs b/afl/src/events/llmp.rs index c38d222a50..f52fbac479 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,7 +56,6 @@ use core::{ sync::atomic::{compiler_fence, Ordering}, time::Duration, }; - #[cfg(feature = "std")] use std::{ io::{Read, Write}, @@ -63,12 +63,10 @@ use std::{ thread, }; +use super::shmem::ShMem; use crate::utils::next_pow2; use crate::AflError; -#[cfg(feature = "std")] -use super::shmem_translated::AflShmem; - /// 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 @@ -96,14 +94,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,30 +114,40 @@ 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)] #[repr(C, packed)] @@ -165,7 +176,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()) @@ -177,17 +188,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: &mut 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) + if buf_ptr > (map.page_mut() 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) + <= (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.shmem.map_size - len) as isize) + buf_ptr <= (map.page_mut() as *const u8).offset((map_size - len) as isize) } else { false } @@ -196,17 +208,21 @@ 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, - listener_thread: thread::JoinHandle<()>, - }, + IsBroker { broker: LlmpBroker }, /// A client, connected to the port - IsClient { client: LlmpClient }, + IsClient { client: LlmpClient }, } -impl LlmpConnection { +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)) { @@ -214,11 +230,8 @@ impl LlmpConnection { // 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() { @@ -238,17 +251,14 @@ impl LlmpConnection { /// 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), } } } /// 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, @@ -261,18 +271,7 @@ pub struct LlmpPage { pub messages: [LlmpMsg; 0], } -/// The broker (node 0) -#[derive(Clone)] -#[repr(C)] -pub struct LlmpBroker { - /// 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. @@ -284,7 +283,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, @@ -293,8 +292,14 @@ 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_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 @@ -334,13 +339,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) { - let page = shmem2page(&shmem); +unsafe fn _llmp_page_init(shmem: &mut SH, sender: u32) { + let map_size = shmem.map().len(); + let page = shmem2page_mut(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; @@ -350,15 +356,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: &mut LlmpSharedMap, 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). - 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,15 +388,18 @@ 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 { - if (*map.page()).save_to_unmap == 0 { + for map in self.out_maps.split_last_mut().unwrap().1 { + if (*map.page_mut()).save_to_unmap == 0 { // The broker didn't read this page yet, no more pages to unmap. break; } @@ -405,15 +415,15 @@ 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 page = map.page(); + let mut map = self.out_maps.last_mut().unwrap(); + let page = map.page_mut(); 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(&map, last_msg, EOP_MSG_SIZE)? + llmp_next_msg_ptr_checked(&mut map, last_msg, EOP_MSG_SIZE)? } else { (*page).messages.as_mut_ptr() }; @@ -437,8 +447,8 @@ 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 page = map.page(); + let map = self.out_maps.last_mut().unwrap(); + 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 @@ -474,7 +484,7 @@ impl LlmpSender { } } 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, @@ -488,8 +498,12 @@ impl LlmpSender { 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 @@ -503,8 +517,8 @@ impl LlmpSender { 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; @@ -525,12 +539,9 @@ impl LlmpSender { 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().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", @@ -547,11 +558,14 @@ impl LlmpSender { /// 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 new_map_shmem = LlmpSharedMap::new((*old_map).sender, (*old_map).max_alloc_size)?; - let mut new_map = new_map_shmem.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_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; @@ -561,8 +575,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_slice(); // We never sent a msg on the new buf */ self.last_msg_sent = 0 as *mut LlmpMsg; @@ -602,7 +616,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_mut(); (*msg).tag = LLMP_TAG_UNSET; (*page).size_used -= (*msg).buf_len_padded as usize + size_of::(); } @@ -631,14 +645,17 @@ 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)] 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); @@ -655,7 +672,7 @@ impl LlmpReceiver { } 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::(), )?) @@ -664,21 +681,22 @@ impl LlmpReceiver { // 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. 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; @@ -690,14 +708,16 @@ 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( - &pageinfo_cpy.shm_str, - pageinfo_cpy.map_size, - )?; + self.current_recv_map = + LlmpSharedMap::existing(SH::existing_from_shm_slice( + &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); + #[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(); } @@ -716,7 +736,7 @@ impl LlmpReceiver { /// 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) { @@ -743,7 +763,7 @@ impl LlmpReceiver { Some(msg) => Some(( (*msg).sender, (*msg).tag, - (*msg).as_slice(&self.current_recv_map)?, + (*msg).as_slice(&mut self.current_recv_map)?, )), None => None, }) @@ -758,48 +778,110 @@ impl LlmpReceiver { 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))?; +/// A thin wrapper around a ShMem implementation, with special Llmp funcs +impl LlmpSharedMap +where + SH: 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 shmem, sender); + _llmp_page_init(&mut new_map, sender); } - Ok(Self { shmem }) + Self { shmem: new_map } } - /// 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 }) + /// 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 { + 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. /// 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, @@ -817,7 +899,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, @@ -838,80 +920,13 @@ impl LlmpBroker { (*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; Ok(()) } - /// 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)?; - } - } - } - } - /// The broker walks all pages and looks for changes, then broadcasts them on /// its own shared page, once. #[inline] @@ -939,13 +954,29 @@ impl LlmpBroker { 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 => (), + } } } + /// 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) + } + + #[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( @@ -958,6 +989,7 @@ impl LlmpBroker { 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, @@ -969,23 +1001,26 @@ impl LlmpBroker { // 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 broadcast_str_initial = client_out_map_mem.shm_slice().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; + 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); 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(), - ], + out_maps: vec![LlmpSharedMap::existing( + 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, }; @@ -1032,22 +1067,141 @@ impl LlmpBroker { })) } - /// 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) + /// 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 { + #[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; + + 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; + next_id += 1; + self.llmp_clients.push(LlmpReceiver { + id, + current_recv_map: new_page, + last_msg_recvd: 0 as *mut LlmpMsg, + }); + } + 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 { + // 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 { +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 { + 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, }, @@ -1059,22 +1213,6 @@ impl LlmpClient { }) } - 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); - - 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( - &new_broker_map_str, - LLMP_PREF_INITIAL_MAP_SIZE, - )?)?; - - stream.write(&ret.llmp_out.out_maps.first().unwrap().shmem.shm_str)?; - 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) @@ -1136,35 +1274,58 @@ impl LlmpClient { pub fn recv_buf_blocking(&mut self) -> Result<(u32, u32, &[u8]), AflError> { 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))?; + println!("Connected to port {}", port); + + 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_from_shm_slice( + &new_broker_map_str, + LLMP_PREF_INITIAL_MAP_SIZE, + )?))?; + + stream.write(ret.llmp_out.out_maps.first().unwrap().shmem.shm_slice())?; + Ok(ret) + } } #[cfg(test)] mod tests { + #[cfg(feature = "std")] use std::{thread::sleep, time::Duration}; + #[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() { + 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!"), + let mut client = match LlmpConnection::::on_port(1337).unwrap() { + 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 7a6a0d4c5d..86bd20f63d 100644 --- a/afl/src/events/mod.rs +++ b/afl/src/events/mod.rs @@ -1,8 +1,5 @@ -// 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; @@ -10,9 +7,13 @@ use core::time::Duration; use core::{marker::PhantomData, time}; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; +use shmem::AflShmem; #[cfg(feature = "std")] -use self::llmp::Tag; +use self::{ + llmp::{LlmpClient, Tag}, + shmem::ShMem, +}; use crate::corpus::Corpus; use crate::executors::Executor; use crate::feedbacks::FeedbacksTuple; @@ -708,8 +709,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, @@ -717,16 +717,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, @@ -736,25 +736,55 @@ where 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, + E: Executor, + OT: ObserversTuple, + 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, } } @@ -762,10 +792,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]| { @@ -803,8 +830,8 @@ where } #[cfg(feature = "std")] -impl EventManager - for LlmpEventManager +impl EventManager + for LlmpEventManager where C: Corpus, E: Executor, @@ -812,6 +839,7 @@ where OT: ObserversTuple, I: Input, R: Rand, + SH: ShMem, ST: Stats, //CE: CustomEvent, { diff --git a/afl/src/events/shmem_translated.rs b/afl/src/events/shmem.rs similarity index 58% rename from afl/src/events/shmem_translated.rs rename to afl/src/events/shmem.rs index 3177b0d5b1..bcd92b269c 100644 --- a/afl/src/events/shmem_translated.rs +++ b/afl/src/events/shmem.rs @@ -1,18 +1,30 @@ +//! A generic sharememory region to be used by any functions (queues or feedbacks +// too.) + +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::{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; - //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; } + +#[cfg(feature = "std")] #[derive(Copy, Clone)] #[repr(C)] struct ipc_perm { @@ -29,6 +41,7 @@ struct ipc_perm { pub __glibc_reserved2: c_ulong, } +#[cfg(feature = "std")] #[derive(Copy, Clone)] #[repr(C)] struct shmid_ds { @@ -43,22 +56,102 @@ struct shmid_ds { 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: Sized { + /// Creates a nes variable with the given name, strigified to 20 bytes. + fn existing_from_shm_slice(map_str_bytes: &[u8; 20], map_size: usize) + -> Result; + /// Initialize from a shm_str with fixed len of 20 + 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_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 { + 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_slice(&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 + #[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); + 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 + #[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))?)?; + Self::existing_from_shm_str(&map_shm_str, map_size) + } +} + +#[cfg(feature = "std")] #[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 +#[cfg(feature = "std")] +impl ShMem for AflShmem { + fn existing_from_shm_slice( + map_str_bytes: &[u8; 20], + map_size: usize, + ) -> Result { + unsafe { + let str_bytes = map_str_bytes as *const [u8; 20] as *const libc::c_char; + Self::from_str(CStr::from_ptr(str_bytes), map_size) + } + } + + fn new_map(map_size: usize) -> Result { + Self::new(map_size) + } + + fn shm_slice(&self) -> &[u8; 20] { + &self.shm_str + } + + fn map(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self.map, self.map_size) } + } + + fn map_mut(&mut self) -> &mut [u8] { + unsafe { slice::from_raw_parts_mut(self.map, self.map_size) } + } +} + +#[cfg(feature = "std")] +/// Deinit sharedmaps on drop impl Drop for AflShmem { fn drop(&mut self) { unsafe { @@ -67,6 +160,7 @@ impl Drop for AflShmem { } } +#[cfg(feature = "std")] /// Create an uninitialized shmap const fn afl_shmem_unitialized() -> AflShmem { AflShmem { @@ -77,6 +171,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(); @@ -91,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) }; @@ -111,21 +198,9 @@ impl AflShmem { ))) } } - - /// 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 - ))) - } - } } +#[cfg(feature = "std")] /// Deinitialize this shmem instance unsafe fn afl_shmem_deinit(shm: *mut AflShmem) { if shm.is_null() || (*shm).map.is_null() { @@ -138,6 +213,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 { @@ -171,6 +247,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 { @@ -198,43 +275,24 @@ unsafe fn afl_shmem_by_str(shm: *mut AflShmem, shm_str: &CStr, map_size: usize) 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; +#[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"); } - 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/executors/inmemory.rs b/afl/src/executors/inmemory.rs index af404c0dad..875e2d7f0e 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) } @@ -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::executors::inmemory::CURRENT_INMEMORY_EXECUTOR_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_INMEMORY_EXECUTOR_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,39 +133,63 @@ pub mod unix_signals { #[cfg(feature = "std")] let _ = stdout().flush(); - // TODO: LLMP + 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_INMEMORY_EXECUTOR_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(); + + 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"), @@ -171,19 +203,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 { diff --git a/afl/src/feedbacks/mod.rs b/afl/src/feedbacks/mod.rs index 84e7f8a97f..9500dc4848 100644 --- a/afl/src/feedbacks/mod.rs +++ b/afl/src/feedbacks/mod.rs @@ -190,7 +190,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/lib.rs b/afl/src/lib.rs index 7597b4c746..92a4c2b6fe 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; @@ -28,7 +27,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)] @@ -50,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), } @@ -68,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), } } @@ -88,10 +90,23 @@ impl From for AflError { } } -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - assert_eq!(2 + 2, 4); +#[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)) + } +} + +#[cfg(feature = "std")] +impl From for AflError { + fn from(err: ParseIntError) -> Self { + Self::Unknown(format!("Failed to parse Int: {:?}", err)) } } diff --git a/afl/src/observers/mod.rs b/afl/src/observers/mod.rs index 6520ed200c..545f1373c7 100644 --- a/afl/src/observers/mod.rs +++ b/afl/src/observers/mod.rs @@ -4,7 +4,7 @@ use alloc::string::{String, ToString}; use alloc::vec::Vec; use serde::{Deserialize, Serialize}; -use crate::serde_anymap::ArrayMut; +use crate::serde_anymap::{ArrayMut, Cptr}; use crate::tuples::{MatchNameAndType, MatchType, Named, TupleList}; use crate::AflError; @@ -94,6 +94,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; @@ -108,7 +113,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(()) @@ -206,6 +212,107 @@ 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/libfuzzer/src/lib.rs b/fuzzers/libfuzzer/src/lib.rs index 390dd617ff..56cd01cd32 100644 --- a/fuzzers/libfuzzer/src/lib.rs +++ b/fuzzers/libfuzzer/src/lib.rs @@ -14,6 +14,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}; @@ -110,12 +111,13 @@ pub extern "C" fn afl_libfuzzer_main() { let mut corpus = InMemoryCorpus::new(); let mut generator = RandPrintablesGenerator::new(32); let stats = SimpleStats::new(|s| println!("{}", s)); - let mut mgr = LlmpEventManager::new_on_port(broker_port, stats).unwrap(); + let mut mgr = LlmpEventManager::new_on_port_std(broker_port, 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 = 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 diff --git a/fuzzers/qemufuzzer/src/lib.rs b/fuzzers/qemufuzzer/src/lib.rs index 6e555624ad..c18010f11e 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,11 +66,10 @@ 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));