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.
|
||||
let (state, mut restarting_mgr) =
|
||||
setup_restarting_mgr::<_, _, UnixShMem, _>(stats, broker_port)
|
||||
.expect("Failed to setup the restarter".into());
|
||||
match setup_restarting_mgr::<_, _, UnixShMem, _>(stats, broker_port) {
|
||||
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
|
||||
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]
|
||||
libc = "0.2" # For (*nix) libc
|
||||
nix = "0.20.0"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
windows = "0.3.1"
|
||||
|
@ -96,9 +96,9 @@ fn main() {
|
||||
"broker" => {
|
||||
let mut broker = llmp::LlmpBroker::<UnixShMem>::new().unwrap();
|
||||
broker
|
||||
.launch_tcp_listener(
|
||||
.launch_listener(llmp::Listener::Tcp(
|
||||
std::net::TcpListener::bind(format!("127.0.0.1:{}", port)).unwrap(),
|
||||
)
|
||||
))
|
||||
.unwrap();
|
||||
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::{
|
||||
cmp::max,
|
||||
fmt::Debug,
|
||||
@ -64,14 +64,42 @@ use core::{
|
||||
use serde::{Deserialize, Serialize};
|
||||
#[cfg(feature = "std")]
|
||||
use std::{
|
||||
env,
|
||||
env, fs,
|
||||
io::{Read, Write},
|
||||
mem::zeroed,
|
||||
net::{TcpListener, TcpStream},
|
||||
thread,
|
||||
};
|
||||
|
||||
use super::shmem::{ShMem, ShMemDescription};
|
||||
use crate::Error;
|
||||
#[cfg(all(feature = "std", unix))]
|
||||
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
|
||||
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
|
||||
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
|
||||
#[inline]
|
||||
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! :)
|
||||
dbg!("We're the broker");
|
||||
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 })
|
||||
}
|
||||
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
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(C, packed)]
|
||||
@ -1140,8 +1234,17 @@ where
|
||||
/// This allows us to intercept messages right in the broker
|
||||
/// This keeps the out map clean.
|
||||
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.
|
||||
/// It may intercept messages passing through.
|
||||
impl<SH> LlmpBroker<SH>
|
||||
@ -1160,6 +1263,8 @@ where
|
||||
keep_pages_forever: true,
|
||||
},
|
||||
llmp_clients: vec![],
|
||||
socket_name: None,
|
||||
shutting_down: false,
|
||||
};
|
||||
|
||||
Ok(broker)
|
||||
@ -1219,14 +1324,68 @@ where
|
||||
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.
|
||||
/// Never returns. Panics on error.
|
||||
/// 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
|
||||
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);
|
||||
self.once(on_new_msg)
|
||||
.expect("An error occurred when brokering. Exiting.");
|
||||
@ -1258,15 +1417,12 @@ where
|
||||
let listener = TcpListener::bind(format!("127.0.0.1:{}", port))?;
|
||||
// accept connections and process them, spawning a new thread for each one
|
||||
println!("Server listening on port {}", port);
|
||||
self.launch_tcp_listener(listener)
|
||||
self.launch_listener(Listener::Tcp(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,
|
||||
listener: TcpListener,
|
||||
) -> Result<thread::JoinHandle<()>, Error> {
|
||||
/// Launches a thread using a listener socket, on which new clients may connect to this broker
|
||||
pub fn launch_listener(&mut self, listener: Listener) -> Result<thread::JoinHandle<()>, Error> {
|
||||
// Later in the execution, after the initial map filled up,
|
||||
// the current broacast map will will point to a different map.
|
||||
// However, the original map is (as of now) never freed, new clients will start
|
||||
@ -1298,43 +1454,108 @@ where
|
||||
};
|
||||
|
||||
loop {
|
||||
let (mut stream, addr) = match listener.accept() {
|
||||
Ok(res) => res,
|
||||
Err(e) => {
|
||||
dbg!("Ignoring failed accept", e);
|
||||
continue;
|
||||
match listener.accept() {
|
||||
ListenerStream::Tcp(mut stream, addr) => {
|
||||
dbg!("New connection", addr, stream.peer_addr().unwrap());
|
||||
match stream.write(&broadcast_str_initial) {
|
||||
Ok(_) => {} // fire & forget
|
||||
Err(e) => {
|
||||
dbg!("Could not send to shmap to client", e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let mut new_client_map_str: [u8; 20] = Default::default();
|
||||
match stream.read_exact(&mut new_client_map_str) {
|
||||
Ok(()) => (),
|
||||
Err(e) => {
|
||||
dbg!("Ignoring failed read from client", e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
unsafe {
|
||||
let msg = new_client_sender
|
||||
.alloc_next(size_of::<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),
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
dbg!("New connection", addr, stream.peer_addr().unwrap());
|
||||
match stream.write(&broadcast_str_initial) {
|
||||
Ok(_) => {} // fire & forget
|
||||
Err(e) => {
|
||||
dbg!("Could not send to shmap to client", e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let mut new_client_map_str: [u8; 20] = Default::default();
|
||||
match stream.read_exact(&mut new_client_map_str) {
|
||||
Ok(()) => (),
|
||||
Err(e) => {
|
||||
dbg!("Ignoring failed read from client", e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
ListenerStream::Unix(stream, addr) => unsafe {
|
||||
dbg!("New connection", addr);
|
||||
|
||||
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),
|
||||
};
|
||||
}
|
||||
let broadcast_fd_initial: i32 =
|
||||
CStr::from_ptr(broadcast_str_initial.as_ptr() as *const c_char)
|
||||
.to_string_lossy()
|
||||
.into_owned()
|
||||
.parse()
|
||||
.expect(&format!(
|
||||
"ShmId is not a valid int file descriptor: {:?}",
|
||||
broadcast_str_initial
|
||||
));
|
||||
|
||||
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
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
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)]
|
||||
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(feature = "std")]
|
||||
pub mod unix_shmem {
|
||||
|
||||
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};
|
||||
#[cfg(target_os = "android")]
|
||||
use libc::{off_t, size_t, MAP_SHARED, O_RDWR, PROT_READ, PROT_WRITE};
|
||||
use std::ffi::CStr;
|
||||
#[cfg(target_os = "android")]
|
||||
use std::ffi::CString;
|
||||
|
||||
use crate::Error;
|
||||
|
||||
use super::ShMem;
|
||||
use super::{HasFd, ShMem};
|
||||
|
||||
#[cfg(unix)]
|
||||
extern "C" {
|
||||
@ -117,12 +127,119 @@ pub mod unix_shmem {
|
||||
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;
|
||||
#[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;
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg(all(feature = "std", not(target_os = "android")))]
|
||||
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;
|
||||
#[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)]
|
||||
@ -197,6 +314,12 @@ pub mod unix_shmem {
|
||||
}
|
||||
}
|
||||
|
||||
impl HasFd for UnixShMem {
|
||||
fn shm_id(&self) -> i32 {
|
||||
self.shm_id
|
||||
}
|
||||
}
|
||||
|
||||
/// Deinit sharedmaps on drop
|
||||
impl Drop for UnixShMem {
|
||||
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 core::{marker::PhantomData, time::Duration};
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
@ -191,6 +191,8 @@ where
|
||||
},
|
||||
Some(Duration::from_millis(5)),
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(Error::IllegalState(
|
||||
"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>
|
||||
where
|
||||
I: Input,
|
||||
@ -488,18 +507,28 @@ pub fn setup_restarting_mgr<I, S, SH, ST>(
|
||||
where
|
||||
I: Input,
|
||||
S: DeserializeOwned + IfInteresting<I>,
|
||||
SH: ShMem,
|
||||
SH: ShMem + HasFd, // Todo: HasFd is only needed for Android
|
||||
ST: Stats,
|
||||
{
|
||||
let mut mgr;
|
||||
|
||||
// We start ourself as child process to actually fuzz
|
||||
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() {
|
||||
// Yep, broker. Just loop here.
|
||||
println!("Doing broker things. Run this tool again to start fuzzing in a client.");
|
||||
mgr.broker_loop()?;
|
||||
return Err(Error::ShuttingDown);
|
||||
} else {
|
||||
mgr.to_env(_ENV_FUZZER_BROKER_CLIENT_INITIAL);
|
||||
|
||||
|
@ -215,7 +215,7 @@ pub mod unix_signals {
|
||||
si_addr
|
||||
);
|
||||
// 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") {
|
||||
Ok(maps) => println!("maps:\n{}", maps),
|
||||
Err(e) => println!("Couldn't load mappings: {:?}", e),
|
||||
|
@ -66,6 +66,8 @@ pub enum Error {
|
||||
IllegalState(String),
|
||||
/// The argument passed to this method or function is not valid
|
||||
IllegalArgument(String),
|
||||
/// Shutting down, not really an error.
|
||||
ShuttingDown,
|
||||
/// Something else happened
|
||||
Unknown(String),
|
||||
}
|
||||
@ -85,6 +87,7 @@ impl fmt::Display for Error {
|
||||
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::ShuttingDown => write!(f, "Shutting down!"),
|
||||
Self::Unknown(s) => write!(f, "Unknown error: {0}", &s),
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user