generic map

This commit is contained in:
Dominik Maier 2020-12-19 05:43:47 +01:00
parent 9362224020
commit 1e611da35b
6 changed files with 161 additions and 482 deletions

View File

@ -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<SH>
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<SH: ShMem>(&self, map: &LlmpSharedMap<SH>) -> Result<&[u8], AflError> {
pub fn as_slice<SH: ShMem>(&self, map: &mut LlmpSharedMap<SH>) -> 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<SH: ShMem>(&self, map: &LlmpSharedMap<SH>) -> bool {
pub fn in_map<SH: ShMem>(&self, map: &mut LlmpSharedMap<SH>) -> 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<SH>
where
@ -226,7 +223,10 @@ where
IsClient { client: LlmpClient<SH> },
}
impl LlmpConnection<AflShmem> {
impl<SH> LlmpConnection<SH>
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<Self, AflError> {
match TcpListener::bind(format!("127.0.0.1:{}", port)) {
@ -268,7 +268,7 @@ impl LlmpConnection<AflShmem> {
}
/// 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<SH>
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<SH: ShMem>(afl_shmem: &SH) -> *mut LlmpPage {
afl_shmem.map().as_mut_ptr() as *mut LlmpPage
unsafe fn shmem2page<SH: ShMem>(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<SH: ShMem>(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<SH: ShMem> (
map: &LlmpSharedMap<SH>,
unsafe fn llmp_next_msg_ptr_checked<SH: ShMem>(
map: &mut LlmpSharedMap<SH>,
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<AflShmem> {
impl<SH> LlmpSender<SH>
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<AflShmem> {
/// 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<AflShmem> {
(*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<AflShmem> {
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::<LlmpMsg>() + 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<AflShmem> {
(*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<AflShmem> {
let old_map = self.out_maps.last_mut().unwrap().page();
// Create a new shard page.
let new_map_shmem = LlmpSharedMap::<AflShmem>::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<AflShmem> {
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::<LlmpMsg>();
}
@ -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::<LlmpMsg>(),
)?)
@ -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<AflShmem> {
/// 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<Self, AflError> {
// 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<Self, AflError> {
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<Self, AflError> {
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<SH> LlmpSharedMap<SH>
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<Self, AflError> {
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<AflShmem> {
impl<SH> LlmpBroker<SH>
where
SH: ShMem,
{
/// Create and initialize a new llmp_broker
pub fn new() -> Result<Self, AflError> {
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<AflShmem> {
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<AflShmem> {
self.llmp_out.send_buf(tag, buf)
}
}
impl LlmpBroker<AflShmem> {
/// 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<AflShmem> {
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::<AflShmem>::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<AflShmem> {
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<AflShmem> {
}))
}
fn map_new_page(shm_str_buf: &[u8; 20], map_size: usize) -> Result<LlmpSharedMap<AflShmem>, 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<F>(
&mut self,
client_id: u32,
on_new_msg: &mut F,
) -> Result<(), AflError>
where
F: FnMut(u32, Tag, &[u8]) -> Result<LlmpMsgHookResult, AflError>,
{
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::<LlmpPayloadSharedMapInfo>() as u64 {
println!("Ignoring broken CLIENT_ADDED msg due to incorrect size. Expected {} but got {}",
(*msg).buf_len_padded,
size_of::<LlmpPayloadSharedMapInfo>()
);
} 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<AflShmem> {
fn map_new_page(shm_str_buf: &[u8; 20], map_size: usize) -> Result<LlmpSharedMap<SH>, AflError> {
panic!("End of page not handled!");
}
impl<SH> LlmpClient<SH>
where
SH: ShMem,
{
/// Creates a new LlmpClient
pub fn new(initial_broker_map: LlmpSharedMap<SH>) -> Result<Self, AflError> {
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<AflShmem> {
self.llmp_in.recv_buf_blocking()
}
/// broker broadcast to its own page for all others to read */
#[inline]
unsafe fn handle_new_msgs<F>(
&mut self,
client_id: u32,
on_new_msg: &mut F,
) -> Result<(), AflError>
where
F: FnMut(u32, Tag, &[u8]) -> Result<LlmpMsgHookResult, AflError>,
{
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::<LlmpPayloadSharedMapInfo>() as u64 {
println!("Ignoring broken CLIENT_ADDED msg due to incorrect size. Expected {} but got {}",
(*msg).buf_len_padded,
size_of::<LlmpPayloadSharedMapInfo>()
);
} 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<AflShmem> {
/// Creates a new LlmpClient, reading the map id and len from env
pub fn create_using_env(env_var: &str) -> Result<Self, AflError> {
let map_str = env::var(env_var)?;
let map_size = str::parse::<usize>(&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<AflShmem> {
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::<AflShmem>::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::<AflShmem>::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);
}
}

View File

@ -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;

View File

@ -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<Self, AflError> {
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<Self, AflError> {
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<Self, AflError> {
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::<i32>()
.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;
}

View File

@ -6,7 +6,6 @@ Welcome to libAFL
#[macro_use]
extern crate alloc;
#[macro_use]
extern crate static_assertions;
@ -107,12 +106,3 @@ impl From<ParseIntError> for AflError {
Self::Unknown(format!("Failed to parse Int: {:?}", err))
}
}
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}

View File

@ -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() {