diff --git a/afl/llmp_test/src/main.rs b/afl/llmp_test/src/main.rs index 84d5dd5b5e..88eaa358d5 100644 --- a/afl/llmp_test/src/main.rs +++ b/afl/llmp_test/src/main.rs @@ -56,14 +56,14 @@ unsafe fn broker_message_hook( println!( "Client {:?} sent message: {:?}", client_id, - u32::from_le_bytes((*message).as_slice().try_into().unwrap()) + u32::from_le_bytes((*message).as_slice_unsafe().try_into().unwrap()) ); llmp::LlmpMsgHookResult::ForwardToClients } TAG_MATH_RESULT_V1 => { println!( "Adder Client has this current result: {:?}", - u32::from_le_bytes((*message).as_slice().try_into().unwrap()) + u32::from_le_bytes((*message).as_slice_unsafe().try_into().unwrap()) ); llmp::LlmpMsgHookResult::Handled } diff --git a/afl/src/events/llmp.rs b/afl/src/events/llmp.rs index 614cd77cb5..44a5bd06f1 100644 --- a/afl/src/events/llmp.rs +++ b/afl/src/events/llmp.rs @@ -158,8 +158,38 @@ pub struct LlmpMsg { /// The message we receive impl LlmpMsg { /// Gets the buffer from this message as slice, with the corrent length. - pub fn as_slice(&self) -> &[u8] { - unsafe { slice::from_raw_parts(self.buf.as_ptr(), self.buf_len as usize) } + /// This is unsafe if somebody has access to shared mem pages on the system. + pub unsafe fn as_slice_unsafe(&self) -> &[u8] { + slice::from_raw_parts(self.buf.as_ptr(), self.buf_len as usize) + } + + /// Gets the buffer from this message as slice, with the corrent length. + pub fn as_slice(&self, map: &LlmpSharedMap) -> Result<&[u8], AflError> { + unsafe { + if self.in_map(map) { + Ok(self.as_slice_unsafe()) + } else { + Err(AflError::IllegalState("Current message not in page. The sharedmap get tampered with or we have a BUG.".into())) + } + } + } + + /// Returns true, if the pointer is, indeed, in the page of this shared map. + pub fn in_map(&self, map: &LlmpSharedMap) -> bool { + unsafe { + 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) + { + // 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) + } else { + false + } + } } } @@ -264,6 +294,29 @@ unsafe fn _llmp_page_init(shmem: &mut AflShmem, sender: u32) { ptr::write_volatile(&mut (*page).sender_dead, 0); } +/// 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, + last_msg: *const LlmpMsg, + alloc_size: usize, +) -> Result<*mut LlmpMsg, AflError> { + let page = map.page(); + 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 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 { + Ok(next) + } else { + Err(AflError::IllegalState(format!( + "Inconsistent data on sharedmap, or Bug (next_ptr was {:x}, sharedmap page was {:x})", + next_ptr as usize, page as usize + ))) + } +} + /// Pointer to the message behind the last message #[inline] unsafe fn _llmp_next_msg_ptr(last_msg: *const LlmpMsg) -> *mut LlmpMsg { @@ -297,15 +350,16 @@ impl LlmpSender { /// The normal alloc will fail if there is not enough space for buf_len_padded + EOP /// 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) -> *mut LlmpMsg { - let page = self.out_maps.last().unwrap().page(); + unsafe fn alloc_eop(&mut self) -> Result<*mut LlmpMsg, AflError> { + let map = self.out_maps.last().unwrap(); + 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)); } let mut ret: *mut LlmpMsg = if !last_msg.is_null() { - _llmp_next_msg_ptr(last_msg) + llmp_next_msg_ptr_checked(&map, last_msg, EOP_MSG_SIZE)? } else { (*page).messages.as_mut_ptr() }; @@ -320,7 +374,7 @@ impl LlmpSender { }; (*ret).tag = LLMP_TAG_END_OF_PAGE; (*page).size_used += EOP_MSG_SIZE; - ret + Ok(ret) } /// Intern: Will return a ptr to the next msg buf, or None if map is full. @@ -329,7 +383,8 @@ impl LlmpSender { unsafe fn alloc_next_if_space(&mut self, buf_len: usize) -> Option<*mut LlmpMsg> { let mut buf_len_padded = buf_len; let mut complete_msg_size = llmp_align(size_of::() + buf_len_padded); - let page = self.out_maps.last().unwrap().page(); + let map = self.out_maps.last().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)); */ /* In case we don't have enough space, make sure the next page will be large @@ -376,7 +431,13 @@ impl LlmpSender { /* We're full. */ return None; } - ret = _llmp_next_msg_ptr(last_msg); + ret = match llmp_next_msg_ptr_checked(map, last_msg, complete_msg_size) { + Ok(msg) => msg, + Err(e) => { + dbg!("Unexpected error allocing new msg", e); + return None; + } + }; (*ret).message_id = (*last_msg).message_id + 1 } @@ -441,7 +502,7 @@ impl LlmpSender { (*new_map).max_alloc_size = (*old_map).max_alloc_size; /* On the old map, place a last message linking to the new map for the clients * to consume */ - let mut out: *mut LlmpMsg = self.alloc_eop(); + let mut out: *mut LlmpMsg = self.alloc_eop()?; (*out).sender = (*old_map).sender; let mut end_of_page_msg = (*out).buf.as_mut_ptr() as *mut LlmpPayloadSharedMapInfo; @@ -535,12 +596,20 @@ impl LlmpReceiver { /* Oops! No new message! */ None } else { - Some(_llmp_next_msg_ptr(last_msg)) + // 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, + last_msg, + size_of::(), + )?) }; // Let's see what we go here. match ret { Some(msg) => { + if !(*msg).in_map(&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"), @@ -561,11 +630,16 @@ impl LlmpReceiver { Clone the contents first to be safe (probably fine in rust eitner way). */ let pageinfo_cpy = (*pageinfo).clone(); + // 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, )?; + // 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); // After we mapped the new page, return the next message, if available return self.recv(); @@ -608,7 +682,7 @@ impl LlmpReceiver { pub fn recv_buf(&mut self) -> Result, AflError> { unsafe { Ok(match self.recv()? { - Some(msg) => Some(((*msg).tag, (*msg).as_slice())), + Some(msg) => Some(((*msg).tag, (*msg).as_slice(&self.current_recv_map)?)), None => None, }) } @@ -618,7 +692,7 @@ impl LlmpReceiver { pub fn recv_buf_blocking(&mut self) -> Result<(u32, &[u8]), AflError> { unsafe { let msg = self.recv_blocking()?; - Ok(((*msg).tag, (*msg).as_slice())) + Ok(((*msg).tag, (*msg).as_slice(&self.current_recv_map)?)) } } } diff --git a/afl/src/events/mod.rs b/afl/src/events/mod.rs index 933669fe5a..ba677d2269 100644 --- a/afl/src/events/mod.rs +++ b/afl/src/events/mod.rs @@ -1,5 +1,5 @@ #[cfg(feature = "std")] -mod llmp; +pub mod llmp; #[cfg(feature = "std")] pub mod shmem_translated; @@ -534,10 +534,10 @@ where mod tests { use crate::events::Event; - use crate::serde_anymap::{Ptr, PtrMut}; use crate::inputs::bytes::BytesInput; - use crate::observers::{Observer, StdMapObserver}; use crate::observers::observer_serde::NamedSerdeAnyMap; + use crate::observers::{Observer, StdMapObserver}; + use crate::serde_anymap::{Ptr, PtrMut}; static mut MAP: [u32; 4] = [0; 4]; @@ -563,10 +563,13 @@ mod tests { input: _, observers: obs, } => { - let o = obs.as_ref().get::>(&"key".to_string()).unwrap(); + let o = obs + .as_ref() + .get::>(&"key".to_string()) + .unwrap(); assert_eq!("test".to_string(), *o.name()); - }, - _ => panic!("mistmatch".to_string()) + } + _ => panic!("mistmatch".to_string()), }; } } diff --git a/afl/src/inputs/mod.rs b/afl/src/inputs/mod.rs index 61d19c0a93..7601d3420e 100644 --- a/afl/src/inputs/mod.rs +++ b/afl/src/inputs/mod.rs @@ -24,7 +24,7 @@ pub trait Input: Clone + serde::Serialize + serde::de::DeserializeOwned { { let mut file = File::create(path)?; let serialized = postcard::to_allocvec(self)?; - file.write_all(&serialized); + file.write_all(&serialized)?; Ok(()) } diff --git a/afl/src/serde_anymap.rs b/afl/src/serde_anymap.rs index d0a1b9dd64..6b76508218 100644 --- a/afl/src/serde_anymap.rs +++ b/afl/src/serde_anymap.rs @@ -450,7 +450,7 @@ impl<'a, T: 'a + ?Sized + serde::Serialize> serde::Serialize for Ptr<'a, T> { { match *self { Ptr::Ref(ref r) => se.serialize_some(r), - Ptr::Owned(ref b) => se.serialize_some(b.as_ref()) + Ptr::Owned(ref b) => se.serialize_some(b.as_ref()), } } } @@ -474,7 +474,6 @@ impl<'a, T: Sized> Ptr<'a, T> { Ptr::Owned(v) => v.as_ref(), } } - } pub enum PtrMut<'a, T: 'a + ?Sized> { @@ -489,7 +488,7 @@ impl<'a, T: 'a + ?Sized + serde::Serialize> serde::Serialize for PtrMut<'a, T> { { match *self { PtrMut::Ref(ref r) => se.serialize_some(r), - PtrMut::Owned(ref b) => se.serialize_some(b.as_ref()) + PtrMut::Owned(ref b) => se.serialize_some(b.as_ref()), } } } @@ -522,7 +521,6 @@ impl<'a, T: Sized> PtrMut<'a, T> { } } - pub enum Slice<'a, T: 'a + Sized> { Ref(&'a [T]), Owned(Vec), @@ -535,7 +533,7 @@ impl<'a, T: 'a + Sized + serde::Serialize> serde::Serialize for Slice<'a, T> { { match *self { Slice::Ref(ref r) => se.serialize_some(r), - Slice::Owned(ref b) => se.serialize_some(b.as_slice()) + Slice::Owned(ref b) => se.serialize_some(b.as_slice()), } } } @@ -573,7 +571,7 @@ impl<'a, T: 'a + Sized + serde::Serialize> serde::Serialize for SliceMut<'a, T> { match *self { SliceMut::Ref(ref r) => se.serialize_some(r), - SliceMut::Owned(ref b) => se.serialize_some(b.as_slice()) + SliceMut::Owned(ref b) => se.serialize_some(b.as_slice()), } } }