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:
s1341 2021-03-03 13:31:29 +02:00 committed by GitHub
parent a3b22acb04
commit fde48be53e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 531 additions and 60 deletions

View File

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

View File

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

View File

@ -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)))
} }

View File

@ -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,13 +1454,8 @@ where
}; };
loop { loop {
let (mut stream, addr) = match listener.accept() { match listener.accept() {
Ok(res) => res, ListenerStream::Tcp(mut stream, addr) => {
Err(e) => {
dbg!("Ignoring failed accept", e);
continue;
}
};
dbg!("New connection", addr, stream.peer_addr().unwrap()); dbg!("New connection", addr, stream.peer_addr().unwrap());
match stream.write(&broadcast_str_initial) { match stream.write(&broadcast_str_initial) {
Ok(_) => {} // fire & forget Ok(_) => {} // fire & forget
@ -1321,7 +1472,6 @@ where
continue; continue;
} }
}; };
unsafe { unsafe {
let msg = new_client_sender let msg = new_client_sender
.alloc_next(size_of::<LlmpPayloadSharedMapInfo>()) .alloc_next(size_of::<LlmpPayloadSharedMapInfo>())
@ -1336,6 +1486,77 @@ where
}; };
} }
} }
ListenerStream::Unix(stream, addr) => unsafe {
dbg!("New connection", addr);
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 /// 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 {

View File

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

View File

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

View File

@ -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),

View File

@ -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),
} }
} }