unix_domain_sockets/ashmem: Add ability to connect over unix domain sockets instead of just TCP; Add ashmem support for android (#19)
* unix_domain_sockets: Added Listener abstraction Tested and TCP is still working * unix_domain_sockets: turn off the unstable feature except on android * unix_domain_sockets: more turn off the unstable feature except on android * unix_domain_sockets: always import UnixListener * unix_domain_sockets: Finished implementation. Tested working on android when both sides are root * unix_domain_sockets: adjust conditional compilation * unix_domain_sockets: formatting * unix_domain_sockets/android: implement ashmem hooks * unix_domain_sockets/android: formatting * unix_domain_sockets: make Listener abstraction public * unix_domain_sockets: add cfg(std) to Listener * unix_domain_sockets: add cfg(std) to imports * unix_domain_sockets: formatting * unix_domain_sockets: Handle SIGTERM, SIGQUIT and SIGINT gracefully and cleanup the unix socket * unix_domain_sockets: formatting * unix_domain_sockets: fix conditional compilation * unix_domain_sockets: use String::default instead of a literal * unix_domain_sockets: socket_name should be an Option<> * fixed build * fmt * fixed warnings * using volatile reads and writes for shutdown flag * reordered compiler fence on write * moved the signal handler method to its own function * readme * moved to HasShmId * unix_domain_sockets: fix warnings * renamed HasShmId to HasFd Co-authored-by: Dominik Maier <domenukk@gmail.com>
This commit is contained in:
parent
a3b22acb04
commit
fde48be53e
@ -74,8 +74,17 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
|
|||||||
|
|
||||||
// The restarting state will spawn the same process again as child, then restarted it each time it crashes.
|
// The restarting state will spawn the same process again as child, then restarted it each time it crashes.
|
||||||
let (state, mut restarting_mgr) =
|
let (state, mut restarting_mgr) =
|
||||||
setup_restarting_mgr::<_, _, UnixShMem, _>(stats, broker_port)
|
match setup_restarting_mgr::<_, _, UnixShMem, _>(stats, broker_port) {
|
||||||
.expect("Failed to setup the restarter".into());
|
Ok(res) => res,
|
||||||
|
Err(err) => match err {
|
||||||
|
Error::ShuttingDown => {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
panic!("Failed to setup the restarter: {}", err);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
// Create an observation channel using the coverage map
|
// Create an observation channel using the coverage map
|
||||||
let edges_observer = HitcountsMapObserver::new(StdMapObserver::new_from_ptr(
|
let edges_observer = HitcountsMapObserver::new(StdMapObserver::new_from_ptr(
|
||||||
|
@ -57,6 +57,7 @@ serde_json = { version = "1.0", optional = true, default-features = false, featu
|
|||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
libc = "0.2" # For (*nix) libc
|
libc = "0.2" # For (*nix) libc
|
||||||
|
nix = "0.20.0"
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
windows = "0.3.1"
|
windows = "0.3.1"
|
||||||
|
@ -96,9 +96,9 @@ fn main() {
|
|||||||
"broker" => {
|
"broker" => {
|
||||||
let mut broker = llmp::LlmpBroker::<UnixShMem>::new().unwrap();
|
let mut broker = llmp::LlmpBroker::<UnixShMem>::new().unwrap();
|
||||||
broker
|
broker
|
||||||
.launch_tcp_listener(
|
.launch_listener(llmp::Listener::Tcp(
|
||||||
std::net::TcpListener::bind(format!("127.0.0.1:{}", port)).unwrap(),
|
std::net::TcpListener::bind(format!("127.0.0.1:{}", port)).unwrap(),
|
||||||
)
|
))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
broker.loop_forever(&mut broker_message_hook, Some(Duration::from_millis(5)))
|
broker.loop_forever(&mut broker_message_hook, Some(Duration::from_millis(5)))
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,7 @@ Then register some clientloops using llmp_broker_register_threaded_clientloop
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use alloc::vec::Vec;
|
use alloc::{string::String, vec::Vec};
|
||||||
use core::{
|
use core::{
|
||||||
cmp::max,
|
cmp::max,
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
@ -64,14 +64,42 @@ use core::{
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use std::{
|
use std::{
|
||||||
env,
|
env, fs,
|
||||||
io::{Read, Write},
|
io::{Read, Write},
|
||||||
|
mem::zeroed,
|
||||||
net::{TcpListener, TcpStream},
|
net::{TcpListener, TcpStream},
|
||||||
thread,
|
thread,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::shmem::{ShMem, ShMemDescription};
|
#[cfg(all(feature = "std", unix))]
|
||||||
use crate::Error;
|
use nix::{
|
||||||
|
cmsg_space,
|
||||||
|
sys::{
|
||||||
|
socket::{recvmsg, sendmsg, ControlMessage, ControlMessageOwned, MsgFlags},
|
||||||
|
uio::IoVec,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
#[cfg(all(feature = "std", unix))]
|
||||||
|
use std::{
|
||||||
|
ffi::CStr,
|
||||||
|
os::unix::{
|
||||||
|
net::{UnixListener, UnixStream},
|
||||||
|
{io::AsRawFd, prelude::RawFd},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(all(feature = "std", unix))]
|
||||||
|
use libc::{
|
||||||
|
c_char, c_int, c_void, malloc, sigaction, sigaltstack, siginfo_t, SA_NODEFER, SA_ONSTACK,
|
||||||
|
SA_SIGINFO, SIGINT, SIGQUIT, SIGTERM,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
bolts::shmem::{ShMem, ShMemDescription},
|
||||||
|
Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::shmem::HasFd;
|
||||||
|
|
||||||
/// We'll start off with 256 megabyte maps per fuzzer client
|
/// We'll start off with 256 megabyte maps per fuzzer client
|
||||||
const LLMP_PREF_INITIAL_MAP_SIZE: usize = 1 << 28;
|
const LLMP_PREF_INITIAL_MAP_SIZE: usize = 1 << 28;
|
||||||
@ -104,6 +132,42 @@ const LLMP_PAGE_HEADER_LEN: usize = size_of::<LlmpPage>();
|
|||||||
/// TAGs used thorughout llmp
|
/// TAGs used thorughout llmp
|
||||||
pub type Tag = u32;
|
pub type Tag = u32;
|
||||||
|
|
||||||
|
/// Abstraction for listeners
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
pub enum Listener {
|
||||||
|
Tcp(TcpListener),
|
||||||
|
Unix(UnixListener),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
pub enum ListenerStream {
|
||||||
|
Tcp(TcpStream, std::net::SocketAddr),
|
||||||
|
Unix(UnixStream, std::os::unix::net::SocketAddr),
|
||||||
|
Empty(),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
impl Listener {
|
||||||
|
fn accept(&self) -> ListenerStream {
|
||||||
|
match self {
|
||||||
|
Listener::Tcp(inner) => match inner.accept() {
|
||||||
|
Ok(res) => ListenerStream::Tcp(res.0, res.1),
|
||||||
|
Err(err) => {
|
||||||
|
dbg!("Ignoring failed accept", err);
|
||||||
|
ListenerStream::Empty()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Listener::Unix(inner) => match inner.accept() {
|
||||||
|
Ok(res) => ListenerStream::Unix(res.0, res.1),
|
||||||
|
Err(err) => {
|
||||||
|
dbg!("Ignoring failed accept", err);
|
||||||
|
ListenerStream::Empty()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Get sharedmem from a page
|
/// Get sharedmem from a page
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn shmem2page_mut<SH: ShMem>(afl_shmem: &mut SH) -> *mut LlmpPage {
|
unsafe fn shmem2page_mut<SH: ShMem>(afl_shmem: &mut SH) -> *mut LlmpPage {
|
||||||
@ -324,7 +388,7 @@ where
|
|||||||
// We got the port. We are the broker! :)
|
// We got the port. We are the broker! :)
|
||||||
dbg!("We're the broker");
|
dbg!("We're the broker");
|
||||||
let mut broker = LlmpBroker::new()?;
|
let mut broker = LlmpBroker::new()?;
|
||||||
let _listener_thread = broker.launch_tcp_listener(listener)?;
|
let _listener_thread = broker.launch_listener(Listener::Tcp(listener))?;
|
||||||
Ok(LlmpConnection::IsBroker { broker })
|
Ok(LlmpConnection::IsBroker { broker })
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@ -368,6 +432,36 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<SH> LlmpConnection<SH>
|
||||||
|
where
|
||||||
|
SH: ShMem + HasFd,
|
||||||
|
{
|
||||||
|
#[cfg(all(feature = "std", unix))]
|
||||||
|
pub fn on_domain_socket(filename: &str) -> Result<Self, Error> {
|
||||||
|
match UnixListener::bind(filename) {
|
||||||
|
Ok(listener) => {
|
||||||
|
dbg!("We're the broker");
|
||||||
|
let mut broker = LlmpBroker::new()?;
|
||||||
|
broker.socket_name = Some(filename.to_string());
|
||||||
|
let _listener_thread = broker.launch_listener(Listener::Unix(listener))?;
|
||||||
|
Ok(LlmpConnection::IsBroker { broker })
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
match e.kind() {
|
||||||
|
std::io::ErrorKind::AddrInUse => {
|
||||||
|
// We are the client :)
|
||||||
|
dbg!("We're the client", e);
|
||||||
|
Ok(LlmpConnection::IsClient {
|
||||||
|
client: LlmpClient::create_attach_to_unix(filename)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => Err(Error::File(e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Contents of the share mem pages, used by llmp internally
|
/// Contents of the share mem pages, used by llmp internally
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
#[repr(C, packed)]
|
#[repr(C, packed)]
|
||||||
@ -1140,8 +1234,17 @@ where
|
|||||||
/// This allows us to intercept messages right in the broker
|
/// This allows us to intercept messages right in the broker
|
||||||
/// This keeps the out map clean.
|
/// This keeps the out map clean.
|
||||||
pub llmp_clients: Vec<LlmpReceiver<SH>>,
|
pub llmp_clients: Vec<LlmpReceiver<SH>>,
|
||||||
|
/// This is the socket name, when unix domain sockets are used.
|
||||||
|
socket_name: Option<String>,
|
||||||
|
/// This flag is used to indicate that shutdown has been requested by the SIGINT and SIGTERM
|
||||||
|
/// handlers
|
||||||
|
shutting_down: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// used to access the current broker in signal handler.
|
||||||
|
#[cfg(all(feature = "std", unix))]
|
||||||
|
static mut CURRENT_BROKER_PTR: *const c_void = ptr::null();
|
||||||
|
|
||||||
/// The broker forwards all messages to its own bus-like broadcast map.
|
/// The broker forwards all messages to its own bus-like broadcast map.
|
||||||
/// It may intercept messages passing through.
|
/// It may intercept messages passing through.
|
||||||
impl<SH> LlmpBroker<SH>
|
impl<SH> LlmpBroker<SH>
|
||||||
@ -1160,6 +1263,8 @@ where
|
|||||||
keep_pages_forever: true,
|
keep_pages_forever: true,
|
||||||
},
|
},
|
||||||
llmp_clients: vec![],
|
llmp_clients: vec![],
|
||||||
|
socket_name: None,
|
||||||
|
shutting_down: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(broker)
|
Ok(broker)
|
||||||
@ -1219,14 +1324,68 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Called from an interrupt: Sets broker `shutting_down` flag to `true`.
|
||||||
|
/// Currently only supported on `std` unix systems.
|
||||||
|
fn shutdown(&mut self) {
|
||||||
|
unsafe { ptr::write_volatile(&mut self.shutting_down, true) };
|
||||||
|
compiler_fence(Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "std", unix))]
|
||||||
|
unsafe fn handle_signal(_sig: c_int, _: siginfo_t, _: c_void) {
|
||||||
|
if !CURRENT_BROKER_PTR.is_null() {
|
||||||
|
let broker = (CURRENT_BROKER_PTR as *mut LlmpBroker<SH>)
|
||||||
|
.as_mut()
|
||||||
|
.unwrap();
|
||||||
|
broker.shutdown();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// For proper cleanup on sigint, we set up a sigint handler
|
||||||
|
#[cfg(all(feature = "std", unix))]
|
||||||
|
unsafe fn setup_sigint_handler(&mut self) {
|
||||||
|
CURRENT_BROKER_PTR = self as *const _ as *const c_void;
|
||||||
|
|
||||||
|
// First, set up our own stack to be used during segfault handling. (and specify `SA_ONSTACK` in `sigaction`)
|
||||||
|
let signal_stack_size = 2 << 22;
|
||||||
|
// TODO: We leak the signal stack. Removing the signal handlers, then freeing this mem on teardown would be more correct.
|
||||||
|
let signal_stack_ptr = malloc(signal_stack_size);
|
||||||
|
if signal_stack_ptr.is_null() {
|
||||||
|
panic!(
|
||||||
|
"Failed to allocate signal stack with {} bytes!",
|
||||||
|
signal_stack_size
|
||||||
|
);
|
||||||
|
}
|
||||||
|
sigaltstack(signal_stack_ptr as _, ptr::null_mut() as _);
|
||||||
|
|
||||||
|
let mut sa: sigaction = zeroed();
|
||||||
|
libc::sigemptyset(&mut sa.sa_mask as *mut libc::sigset_t);
|
||||||
|
sa.sa_flags = SA_NODEFER | SA_SIGINFO | SA_ONSTACK;
|
||||||
|
sa.sa_sigaction = LlmpBroker::<SH>::handle_signal as usize;
|
||||||
|
for (sig, msg) in &[
|
||||||
|
(SIGTERM, "segterm"),
|
||||||
|
(SIGINT, "sigint"),
|
||||||
|
(SIGQUIT, "sigquit"),
|
||||||
|
] {
|
||||||
|
if sigaction(*sig, &mut sa as *mut sigaction, ptr::null_mut()) < 0 {
|
||||||
|
panic!("Could not set up {} handler", &msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Loops infinitely, forwarding and handling all incoming messages from clients.
|
/// Loops infinitely, forwarding and handling all incoming messages from clients.
|
||||||
/// Never returns. Panics on error.
|
/// Never returns. Panics on error.
|
||||||
/// 5 millis of sleep can't hurt to keep busywait not at 100%
|
/// 5 millis of sleep can't hurt to keep busywait not at 100%
|
||||||
pub fn loop_forever<F>(&mut self, on_new_msg: &mut F, sleep_time: Option<Duration>) -> !
|
pub fn loop_forever<F>(&mut self, on_new_msg: &mut F, sleep_time: Option<Duration>)
|
||||||
where
|
where
|
||||||
F: FnMut(u32, Tag, &[u8]) -> Result<LlmpMsgHookResult, Error>,
|
F: FnMut(u32, Tag, &[u8]) -> Result<LlmpMsgHookResult, Error>,
|
||||||
{
|
{
|
||||||
loop {
|
#[cfg(all(feature = "std", unix))]
|
||||||
|
unsafe {
|
||||||
|
self.setup_sigint_handler()
|
||||||
|
};
|
||||||
|
|
||||||
|
while unsafe { !ptr::read_volatile(&self.shutting_down) } {
|
||||||
compiler_fence(Ordering::SeqCst);
|
compiler_fence(Ordering::SeqCst);
|
||||||
self.once(on_new_msg)
|
self.once(on_new_msg)
|
||||||
.expect("An error occurred when brokering. Exiting.");
|
.expect("An error occurred when brokering. Exiting.");
|
||||||
@ -1258,15 +1417,12 @@ where
|
|||||||
let listener = TcpListener::bind(format!("127.0.0.1:{}", port))?;
|
let listener = TcpListener::bind(format!("127.0.0.1:{}", port))?;
|
||||||
// accept connections and process them, spawning a new thread for each one
|
// accept connections and process them, spawning a new thread for each one
|
||||||
println!("Server listening on port {}", port);
|
println!("Server listening on port {}", port);
|
||||||
self.launch_tcp_listener(listener)
|
self.launch_listener(Listener::Tcp(listener))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
/// Launches a thread using a tcp listener socket, on which new clients may connect to this broker
|
/// Launches a thread using a listener socket, on which new clients may connect to this broker
|
||||||
pub fn launch_tcp_listener(
|
pub fn launch_listener(&mut self, listener: Listener) -> Result<thread::JoinHandle<()>, Error> {
|
||||||
&mut self,
|
|
||||||
listener: TcpListener,
|
|
||||||
) -> Result<thread::JoinHandle<()>, Error> {
|
|
||||||
// Later in the execution, after the initial map filled up,
|
// Later in the execution, after the initial map filled up,
|
||||||
// the current broacast map will will point to a different map.
|
// the current broacast map will will point to a different map.
|
||||||
// However, the original map is (as of now) never freed, new clients will start
|
// However, the original map is (as of now) never freed, new clients will start
|
||||||
@ -1298,43 +1454,108 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let (mut stream, addr) = match listener.accept() {
|
match listener.accept() {
|
||||||
Ok(res) => res,
|
ListenerStream::Tcp(mut stream, addr) => {
|
||||||
Err(e) => {
|
dbg!("New connection", addr, stream.peer_addr().unwrap());
|
||||||
dbg!("Ignoring failed accept", e);
|
match stream.write(&broadcast_str_initial) {
|
||||||
continue;
|
Ok(_) => {} // fire & forget
|
||||||
|
Err(e) => {
|
||||||
|
dbg!("Could not send to shmap to client", e);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let mut new_client_map_str: [u8; 20] = Default::default();
|
||||||
|
match stream.read_exact(&mut new_client_map_str) {
|
||||||
|
Ok(()) => (),
|
||||||
|
Err(e) => {
|
||||||
|
dbg!("Ignoring failed read from client", e);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
unsafe {
|
||||||
|
let msg = new_client_sender
|
||||||
|
.alloc_next(size_of::<LlmpPayloadSharedMapInfo>())
|
||||||
|
.expect("Could not allocate a new message in shared map.");
|
||||||
|
(*msg).tag = LLMP_TAG_NEW_SHM_CLIENT;
|
||||||
|
let pageinfo = (*msg).buf.as_mut_ptr() as *mut LlmpPayloadSharedMapInfo;
|
||||||
|
(*pageinfo).shm_str = new_client_map_str;
|
||||||
|
(*pageinfo).map_size = LLMP_PREF_INITIAL_MAP_SIZE;
|
||||||
|
match new_client_sender.send(msg) {
|
||||||
|
Ok(()) => (),
|
||||||
|
Err(e) => println!("Error forwarding client on map: {:?}", e),
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
ListenerStream::Unix(stream, addr) => unsafe {
|
||||||
dbg!("New connection", addr, stream.peer_addr().unwrap());
|
dbg!("New connection", addr);
|
||||||
match stream.write(&broadcast_str_initial) {
|
|
||||||
Ok(_) => {} // fire & forget
|
|
||||||
Err(e) => {
|
|
||||||
dbg!("Could not send to shmap to client", e);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let mut new_client_map_str: [u8; 20] = Default::default();
|
|
||||||
match stream.read_exact(&mut new_client_map_str) {
|
|
||||||
Ok(()) => (),
|
|
||||||
Err(e) => {
|
|
||||||
dbg!("Ignoring failed read from client", e);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
unsafe {
|
let broadcast_fd_initial: i32 =
|
||||||
let msg = new_client_sender
|
CStr::from_ptr(broadcast_str_initial.as_ptr() as *const c_char)
|
||||||
.alloc_next(size_of::<LlmpPayloadSharedMapInfo>())
|
.to_string_lossy()
|
||||||
.expect("Could not allocate a new message in shared map.");
|
.into_owned()
|
||||||
(*msg).tag = LLMP_TAG_NEW_SHM_CLIENT;
|
.parse()
|
||||||
let pageinfo = (*msg).buf.as_mut_ptr() as *mut LlmpPayloadSharedMapInfo;
|
.expect(&format!(
|
||||||
(*pageinfo).shm_str = new_client_map_str;
|
"ShmId is not a valid int file descriptor: {:?}",
|
||||||
(*pageinfo).map_size = LLMP_PREF_INITIAL_MAP_SIZE;
|
broadcast_str_initial
|
||||||
match new_client_sender.send(msg) {
|
));
|
||||||
Ok(()) => (),
|
|
||||||
Err(e) => println!("Error forwarding client on map: {:?}", e),
|
match sendmsg(
|
||||||
};
|
stream.as_raw_fd(),
|
||||||
}
|
&[IoVec::from_slice(b"\x00")],
|
||||||
|
&[ControlMessage::ScmRights(&[broadcast_fd_initial])],
|
||||||
|
MsgFlags::empty(),
|
||||||
|
None,
|
||||||
|
) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(err) => {
|
||||||
|
dbg!("Error sending fd over stream: {}", err);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut buf = [0u8; 5];
|
||||||
|
let mut cmsgspace = cmsg_space!([RawFd; 1]);
|
||||||
|
let msg = recvmsg(
|
||||||
|
stream.as_raw_fd(),
|
||||||
|
&[IoVec::from_mut_slice(&mut buf[..])],
|
||||||
|
Some(&mut cmsgspace),
|
||||||
|
MsgFlags::empty(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
for cmsg in msg.cmsgs() {
|
||||||
|
if let ControlMessageOwned::ScmRights(fds) = cmsg {
|
||||||
|
for fd in fds {
|
||||||
|
let mut fdstr = [0u8; 20];
|
||||||
|
match write!(&mut fdstr[..], "{}", fd) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(_) => {
|
||||||
|
dbg!("error converting fd to string");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let msg = new_client_sender
|
||||||
|
.alloc_next(size_of::<LlmpPayloadSharedMapInfo>())
|
||||||
|
.expect("Could not allocate a new message in shared map.");
|
||||||
|
(*msg).tag = LLMP_TAG_NEW_SHM_CLIENT;
|
||||||
|
let pageinfo =
|
||||||
|
(*msg).buf.as_mut_ptr() as *mut LlmpPayloadSharedMapInfo;
|
||||||
|
(*pageinfo).shm_str = fdstr;
|
||||||
|
(*pageinfo).map_size = LLMP_PREF_INITIAL_MAP_SIZE;
|
||||||
|
match new_client_sender.send(msg) {
|
||||||
|
Ok(()) => (),
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error forwarding client on map: {:?}", e)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ListenerStream::Empty() => {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
@ -1417,6 +1638,24 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
impl<SH> Drop for LlmpBroker<SH>
|
||||||
|
where
|
||||||
|
SH: ShMem,
|
||||||
|
{
|
||||||
|
fn drop(&mut self) {
|
||||||
|
match &self.socket_name {
|
||||||
|
Some(name) => match fs::remove_file(&name) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(err) => {
|
||||||
|
dbg!("failed to close socket: {}", err);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A restorable client description
|
/// A restorable client description
|
||||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||||
pub struct LlmpClientDescription {
|
pub struct LlmpClientDescription {
|
||||||
@ -1616,6 +1855,73 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `n` clients connect to a broker. They share an outgoing map with the broker,
|
||||||
|
/// and get incoming messages from the shared broker bus
|
||||||
|
/// If the Shm has a fd, we can attach to it.
|
||||||
|
impl<SH> LlmpClient<SH>
|
||||||
|
where
|
||||||
|
SH: ShMem + HasFd,
|
||||||
|
{
|
||||||
|
#[cfg(all(feature = "std", unix))]
|
||||||
|
/// Create a LlmpClient, getting the ID from a given filename
|
||||||
|
pub fn create_attach_to_unix(filename: &str) -> Result<Self, Error> {
|
||||||
|
let stream = UnixStream::connect(filename)?;
|
||||||
|
println!("Connected to socket {}", filename);
|
||||||
|
|
||||||
|
let mut buf = [0u8; 5];
|
||||||
|
let mut cmsgspace = cmsg_space!([RawFd; 1]);
|
||||||
|
let msg = recvmsg(
|
||||||
|
stream.as_raw_fd(),
|
||||||
|
&[IoVec::from_mut_slice(&mut buf[..])],
|
||||||
|
Some(&mut cmsgspace),
|
||||||
|
MsgFlags::empty(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
for cmsg in msg.cmsgs() {
|
||||||
|
if let ControlMessageOwned::ScmRights(fds) = cmsg {
|
||||||
|
for fd in fds {
|
||||||
|
let mut fdstr = [0u8; 20];
|
||||||
|
match write!(&mut fdstr[..], "{}", fd) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(_) => {
|
||||||
|
dbg!("error converting fd to string");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let ret = Self::new(LlmpSharedMap::existing(SH::existing_from_shm_slice(
|
||||||
|
&fdstr,
|
||||||
|
LLMP_PREF_INITIAL_MAP_SIZE,
|
||||||
|
)?))?;
|
||||||
|
|
||||||
|
match sendmsg(
|
||||||
|
stream.as_raw_fd(),
|
||||||
|
&[IoVec::from_slice(b"\x00")],
|
||||||
|
&[ControlMessage::ScmRights(&[ret
|
||||||
|
.sender
|
||||||
|
.out_maps
|
||||||
|
.first()
|
||||||
|
.unwrap()
|
||||||
|
.shmem
|
||||||
|
.shm_id()])],
|
||||||
|
MsgFlags::empty(),
|
||||||
|
None,
|
||||||
|
) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(err) => {
|
||||||
|
dbg!("Error sending fd over stream {}", err);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return Ok(ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
panic!("Didn't receive a file descriptor from the broker!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
|
@ -99,17 +99,27 @@ pub trait ShMem: Sized + Debug {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// shared maps that have an id can use this trait
|
||||||
|
pub trait HasFd {
|
||||||
|
/// Retrieve the id of this shared map
|
||||||
|
fn shm_id(&self) -> i32;
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub mod unix_shmem {
|
pub mod unix_shmem {
|
||||||
|
|
||||||
use core::{mem::size_of, ptr, slice};
|
use core::{mem::size_of, ptr, slice};
|
||||||
use libc::{c_char, c_int, c_long, c_uchar, c_uint, c_ulong, c_ushort, c_void};
|
use libc::{c_char, c_int, c_long, c_uchar, c_uint, c_ulong, c_ushort, c_void};
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
use libc::{off_t, size_t, MAP_SHARED, O_RDWR, PROT_READ, PROT_WRITE};
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
use std::ffi::CString;
|
||||||
|
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
|
||||||
use super::ShMem;
|
use super::{HasFd, ShMem};
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -117,12 +127,119 @@ pub mod unix_shmem {
|
|||||||
fn snprintf(_: *mut c_char, _: c_ulong, _: *const c_char, _: ...) -> c_int;
|
fn snprintf(_: *mut c_char, _: c_ulong, _: *const c_char, _: ...) -> c_int;
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
fn strncpy(_: *mut c_char, _: *const c_char, _: c_ulong) -> *mut c_char;
|
fn strncpy(_: *mut c_char, _: *const c_char, _: c_ulong) -> *mut c_char;
|
||||||
#[cfg(feature = "std")]
|
#[cfg(all(feature = "std", not(target_os = "android")))]
|
||||||
fn shmctl(__shmid: c_int, __cmd: c_int, __buf: *mut shmid_ds) -> c_int;
|
fn shmctl(__shmid: c_int, __cmd: c_int, __buf: *mut shmid_ds) -> c_int;
|
||||||
#[cfg(feature = "std")]
|
#[cfg(all(feature = "std", not(target_os = "android")))]
|
||||||
fn shmget(__key: c_int, __size: c_ulong, __shmflg: c_int) -> c_int;
|
fn shmget(__key: c_int, __size: c_ulong, __shmflg: c_int) -> c_int;
|
||||||
#[cfg(feature = "std")]
|
#[cfg(all(feature = "std", not(target_os = "android")))]
|
||||||
fn shmat(__shmid: c_int, __shmaddr: *const c_void, __shmflg: c_int) -> *mut c_void;
|
fn shmat(__shmid: c_int, __shmaddr: *const c_void, __shmflg: c_int) -> *mut c_void;
|
||||||
|
#[cfg(all(feature = "std", target_os = "android"))]
|
||||||
|
fn ioctl(fd: c_int, request: c_long, ...) -> c_int;
|
||||||
|
#[cfg(all(feature = "std", target_os = "android"))]
|
||||||
|
fn open(path: *const c_char, oflag: c_int, ...) -> c_int;
|
||||||
|
#[cfg(all(feature = "std", target_os = "android"))]
|
||||||
|
fn close(fd: c_int) -> c_int;
|
||||||
|
#[cfg(all(feature = "std", target_os = "android"))]
|
||||||
|
fn mmap(
|
||||||
|
addr: *mut c_void,
|
||||||
|
len: size_t,
|
||||||
|
prot: c_int,
|
||||||
|
flags: c_int,
|
||||||
|
fd: c_int,
|
||||||
|
offset: off_t,
|
||||||
|
) -> *mut c_void;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
struct ashmem_pin {
|
||||||
|
pub offset: c_uint,
|
||||||
|
pub len: c_uint,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
const ASHMEM_GET_SIZE: c_long = 0x00007704;
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
const ASHMEM_UNPIN: c_long = 0x40087708;
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
const ASHMEM_SET_NAME: c_long = 0x41007701;
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
const ASHMEM_SET_SIZE: c_long = 0x40087703;
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
const ASHMEM_DEVICE: &str = "/dev/ashmem";
|
||||||
|
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
unsafe fn shmctl(__shmid: c_int, __cmd: c_int, _buf: *mut shmid_ds) -> c_int {
|
||||||
|
print!("shmctl(__shmid: {})\n", __shmid);
|
||||||
|
if __cmd == 0 {
|
||||||
|
let length = ioctl(__shmid, ASHMEM_GET_SIZE);
|
||||||
|
|
||||||
|
let ap = ashmem_pin {
|
||||||
|
offset: 0,
|
||||||
|
len: length as u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
let ret = ioctl(__shmid, ASHMEM_UNPIN, &ap);
|
||||||
|
close(__shmid);
|
||||||
|
ret
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
unsafe fn shmget(__key: c_int, __size: c_ulong, __shmflg: c_int) -> c_int {
|
||||||
|
let path = CString::new(ASHMEM_DEVICE).expect("CString::new failed!");
|
||||||
|
let fd = open(path.as_ptr(), O_RDWR);
|
||||||
|
|
||||||
|
let mut ourkey: [c_char; 20] = [0; 20];
|
||||||
|
snprintf(
|
||||||
|
ourkey.as_mut_ptr() as *mut c_char,
|
||||||
|
size_of::<[c_char; 20]>() as c_ulong,
|
||||||
|
b"%d\x00" as *const u8 as *const c_char,
|
||||||
|
__key,
|
||||||
|
);
|
||||||
|
|
||||||
|
print!("ourkey: {:?}\n", ourkey);
|
||||||
|
if ioctl(fd, ASHMEM_SET_NAME, &ourkey) != 0 {
|
||||||
|
close(fd);
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
if ioctl(fd, ASHMEM_SET_SIZE, __size) != 0 {
|
||||||
|
close(fd);
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
print!("shmget returns {}\n", fd);
|
||||||
|
fd
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
unsafe fn shmat(__shmid: c_int, __shmaddr: *const c_void, __shmflg: c_int) -> *mut c_void {
|
||||||
|
print!("shmat(__shmid: {})\n", __shmid);
|
||||||
|
|
||||||
|
let size = ioctl(__shmid, ASHMEM_GET_SIZE);
|
||||||
|
if size < 0 {
|
||||||
|
return 0 as *mut c_void;
|
||||||
|
}
|
||||||
|
|
||||||
|
let ptr = mmap(
|
||||||
|
0 as *mut c_void,
|
||||||
|
size as usize,
|
||||||
|
PROT_READ | PROT_WRITE,
|
||||||
|
MAP_SHARED,
|
||||||
|
__shmid,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
if ptr == usize::MAX as *mut c_void {
|
||||||
|
return 0 as *mut c_void;
|
||||||
|
}
|
||||||
|
|
||||||
|
print!("shmat() = {:?}\n", ptr);
|
||||||
|
ptr
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
@ -197,6 +314,12 @@ pub mod unix_shmem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl HasFd for UnixShMem {
|
||||||
|
fn shm_id(&self) -> i32 {
|
||||||
|
self.shm_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Deinit sharedmaps on drop
|
/// Deinit sharedmaps on drop
|
||||||
impl Drop for UnixShMem {
|
impl Drop for UnixShMem {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::bolts::llmp::LlmpSender;
|
use crate::bolts::{llmp::LlmpSender, shmem::HasFd};
|
||||||
use alloc::{string::ToString, vec::Vec};
|
use alloc::{string::ToString, vec::Vec};
|
||||||
use core::{marker::PhantomData, time::Duration};
|
use core::{marker::PhantomData, time::Duration};
|
||||||
use serde::{de::DeserializeOwned, Serialize};
|
use serde::{de::DeserializeOwned, Serialize};
|
||||||
@ -191,6 +191,8 @@ where
|
|||||||
},
|
},
|
||||||
Some(Duration::from_millis(5)),
|
Some(Duration::from_millis(5)),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => Err(Error::IllegalState(
|
_ => Err(Error::IllegalState(
|
||||||
"Called broker loop in the client".into(),
|
"Called broker loop in the client".into(),
|
||||||
@ -298,6 +300,23 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<I, S, SH, ST> LlmpEventManager<I, S, SH, ST>
|
||||||
|
where
|
||||||
|
I: Input,
|
||||||
|
S: IfInteresting<I>,
|
||||||
|
SH: ShMem + HasFd,
|
||||||
|
ST: Stats,
|
||||||
|
{
|
||||||
|
#[cfg(all(feature = "std", unix))]
|
||||||
|
pub fn new_on_domain_socket(stats: ST, filename: &str) -> Result<Self, Error> {
|
||||||
|
Ok(Self {
|
||||||
|
stats: Some(stats),
|
||||||
|
llmp: llmp::LlmpConnection::on_domain_socket(filename)?,
|
||||||
|
phantom: PhantomData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<I, S, SH, ST> EventManager<I, S> for LlmpEventManager<I, S, SH, ST>
|
impl<I, S, SH, ST> EventManager<I, S> for LlmpEventManager<I, S, SH, ST>
|
||||||
where
|
where
|
||||||
I: Input,
|
I: Input,
|
||||||
@ -488,18 +507,28 @@ pub fn setup_restarting_mgr<I, S, SH, ST>(
|
|||||||
where
|
where
|
||||||
I: Input,
|
I: Input,
|
||||||
S: DeserializeOwned + IfInteresting<I>,
|
S: DeserializeOwned + IfInteresting<I>,
|
||||||
SH: ShMem,
|
SH: ShMem + HasFd, // Todo: HasFd is only needed for Android
|
||||||
ST: Stats,
|
ST: Stats,
|
||||||
{
|
{
|
||||||
let mut mgr;
|
let mut mgr;
|
||||||
|
|
||||||
// We start ourself as child process to actually fuzz
|
// We start ourself as child process to actually fuzz
|
||||||
if std::env::var(_ENV_FUZZER_SENDER).is_err() {
|
if std::env::var(_ENV_FUZZER_SENDER).is_err() {
|
||||||
mgr = LlmpEventManager::<I, S, SH, ST>::new_on_port(stats, broker_port)?;
|
let path = std::env::current_dir()?;
|
||||||
|
mgr = if cfg!(target_os = "android") {
|
||||||
|
LlmpEventManager::<I, S, SH, ST>::new_on_domain_socket(
|
||||||
|
stats,
|
||||||
|
&format!("{}/.llmp_socket", path.display()).to_string(),
|
||||||
|
)?
|
||||||
|
} else {
|
||||||
|
LlmpEventManager::<I, S, SH, ST>::new_on_port(stats, broker_port)?
|
||||||
|
};
|
||||||
|
|
||||||
if mgr.is_broker() {
|
if mgr.is_broker() {
|
||||||
// Yep, broker. Just loop here.
|
// Yep, broker. Just loop here.
|
||||||
println!("Doing broker things. Run this tool again to start fuzzing in a client.");
|
println!("Doing broker things. Run this tool again to start fuzzing in a client.");
|
||||||
mgr.broker_loop()?;
|
mgr.broker_loop()?;
|
||||||
|
return Err(Error::ShuttingDown);
|
||||||
} else {
|
} else {
|
||||||
mgr.to_env(_ENV_FUZZER_BROKER_CLIENT_INITIAL);
|
mgr.to_env(_ENV_FUZZER_BROKER_CLIENT_INITIAL);
|
||||||
|
|
||||||
|
@ -215,7 +215,7 @@ pub mod unix_signals {
|
|||||||
si_addr
|
si_addr
|
||||||
);
|
);
|
||||||
// let's yolo-cat the maps for debugging, if possible.
|
// let's yolo-cat the maps for debugging, if possible.
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
match fs::read_to_string("/proc/self/maps") {
|
match fs::read_to_string("/proc/self/maps") {
|
||||||
Ok(maps) => println!("maps:\n{}", maps),
|
Ok(maps) => println!("maps:\n{}", maps),
|
||||||
Err(e) => println!("Couldn't load mappings: {:?}", e),
|
Err(e) => println!("Couldn't load mappings: {:?}", e),
|
||||||
|
@ -66,6 +66,8 @@ pub enum Error {
|
|||||||
IllegalState(String),
|
IllegalState(String),
|
||||||
/// The argument passed to this method or function is not valid
|
/// The argument passed to this method or function is not valid
|
||||||
IllegalArgument(String),
|
IllegalArgument(String),
|
||||||
|
/// Shutting down, not really an error.
|
||||||
|
ShuttingDown,
|
||||||
/// Something else happened
|
/// Something else happened
|
||||||
Unknown(String),
|
Unknown(String),
|
||||||
}
|
}
|
||||||
@ -85,6 +87,7 @@ impl fmt::Display for Error {
|
|||||||
Self::NotImplemented(s) => write!(f, "Not implemented: {0}", &s),
|
Self::NotImplemented(s) => write!(f, "Not implemented: {0}", &s),
|
||||||
Self::IllegalState(s) => write!(f, "Illegal state: {0}", &s),
|
Self::IllegalState(s) => write!(f, "Illegal state: {0}", &s),
|
||||||
Self::IllegalArgument(s) => write!(f, "Illegal argument: {0}", &s),
|
Self::IllegalArgument(s) => write!(f, "Illegal argument: {0}", &s),
|
||||||
|
Self::ShuttingDown => write!(f, "Shutting down!"),
|
||||||
Self::Unknown(s) => write!(f, "Unknown error: {0}", &s),
|
Self::Unknown(s) => write!(f, "Unknown error: {0}", &s),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user