* wip forking

* fixed build

* fixed build

* import cleanup

* more fork

* added windows ci

* fmt

* no_std fixes

* windows

* unix build fixed

* ignoring tests on windows

* fixed windows tests
This commit is contained in:
Dominik Maier 2021-03-03 17:06:17 +01:00 committed by GitHub
parent 8238d65cac
commit eaa3dc786b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 161 additions and 40 deletions

View File

@ -14,6 +14,14 @@ jobs:
run: cargo build --verbose run: cargo build --verbose
- name: Test - name: Test
run: cargo test --verbose run: cargo test --verbose
windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- name: Windows Build
run: cargo build --verbose
- name: Windows Test
run: cargo test --verbose
all-features: all-features:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:

View File

@ -1,12 +1,19 @@
// build.rs // build.rs
use std::env; use std::{
use std::path::Path; env,
use std::process::Command; path::Path,
process::{exit, Command},
};
const LIBMOZJPEG_URL: &str = "https://github.com/mozilla/mozjpeg/archive/v4.0.3.tar.gz"; const LIBMOZJPEG_URL: &str = "https://github.com/mozilla/mozjpeg/archive/v4.0.3.tar.gz";
fn main() { fn main() {
if cfg!(windows) {
println!("cargo:warning=Skipping libmozjpeg example on Windows");
exit(0);
}
let out_dir = env::var_os("OUT_DIR").unwrap(); let out_dir = env::var_os("OUT_DIR").unwrap();
let cwd = env::current_dir().unwrap().to_string_lossy().to_string(); let cwd = env::current_dir().unwrap().to_string_lossy().to_string();
let out_dir = out_dir.to_string_lossy().to_string(); let out_dir = out_dir.to_string_lossy().to_string();

View File

@ -3,6 +3,7 @@
use std::{env, path::PathBuf}; use std::{env, path::PathBuf};
#[cfg(unix)]
use libafl::{ use libafl::{
bolts::{shmem::UnixShMem, tuples::tuple_list}, bolts::{shmem::UnixShMem, tuples::tuple_list},
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus, RandCorpusScheduler}, corpus::{Corpus, InMemoryCorpus, OnDiskCorpus, RandCorpusScheduler},
@ -22,6 +23,7 @@ use libafl::{
}; };
/// We will interact with a C++ target, so use external c functionality /// We will interact with a C++ target, so use external c functionality
#[cfg(unix)]
extern "C" { extern "C" {
/// int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) /// int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
fn LLVMFuzzerTestOneInput(data: *const u8, size: usize) -> i32; fn LLVMFuzzerTestOneInput(data: *const u8, size: usize) -> i32;
@ -35,6 +37,7 @@ extern "C" {
} }
/// The wrapped harness function, calling out to the LLVM-style harness /// The wrapped harness function, calling out to the LLVM-style harness
#[cfg(unix)]
fn harness<E, I>(_executor: &E, buf: &[u8]) -> ExitKind fn harness<E, I>(_executor: &E, buf: &[u8]) -> ExitKind
where where
E: Executor<I>, E: Executor<I>,
@ -65,7 +68,14 @@ pub fn main() {
.expect("An error occurred while fuzzing"); .expect("An error occurred while fuzzing");
} }
/// Not supported on windows right now
#[cfg(windows)]
fn fuzz(_corpus_dirs: Vec<PathBuf>, _objective_dir: PathBuf, _broker_port: u16) -> Result<(), ()> {
todo!("Example not supported on Windows");
}
/// The actual fuzzer /// The actual fuzzer
#[cfg(unix)]
fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) -> Result<(), Error> { fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) -> Result<(), Error> {
// 'While the stats are state, they are usually used in the broker - which is likely never restarted // 'While the stats are state, they are usually used in the broker - which is likely never restarted
let stats = SimpleStats::new(|s| println!("{}", s)); let stats = SimpleStats::new(|s| println!("{}", s));

View File

@ -1,13 +1,20 @@
// build.rs // build.rs
use std::env; use std::{
use std::path::Path; env,
use std::process::Command; path::Path,
process::{exit, Command},
};
const LIBPNG_URL: &str = const LIBPNG_URL: &str =
"https://deac-fra.dl.sourceforge.net/project/libpng/libpng16/1.6.37/libpng-1.6.37.tar.xz"; "https://deac-fra.dl.sourceforge.net/project/libpng/libpng16/1.6.37/libpng-1.6.37.tar.xz";
fn main() { fn main() {
if cfg!(windows) {
println!("cargo:warning=Skipping libpng example on Windows");
exit(0);
}
let out_dir = env::var_os("OUT_DIR").unwrap(); let out_dir = env::var_os("OUT_DIR").unwrap();
let cwd = env::current_dir().unwrap().to_string_lossy().to_string(); let cwd = env::current_dir().unwrap().to_string_lossy().to_string();
let out_dir = out_dir.to_string_lossy().to_string(); let out_dir = out_dir.to_string_lossy().to_string();

View File

@ -3,6 +3,7 @@
use std::{env, path::PathBuf}; use std::{env, path::PathBuf};
#[cfg(unix)]
use libafl::{ use libafl::{
bolts::{shmem::UnixShMem, tuples::tuple_list}, bolts::{shmem::UnixShMem, tuples::tuple_list},
corpus::{ corpus::{
@ -24,6 +25,7 @@ use libafl::{
}; };
/// We will interact with a C++ target, so use external c functionality /// We will interact with a C++ target, so use external c functionality
#[cfg(unix)]
extern "C" { extern "C" {
/// int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) /// int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
fn LLVMFuzzerTestOneInput(data: *const u8, size: usize) -> i32; fn LLVMFuzzerTestOneInput(data: *const u8, size: usize) -> i32;
@ -37,6 +39,7 @@ extern "C" {
} }
/// The wrapped harness function, calling out to the LLVM-style harness /// The wrapped harness function, calling out to the LLVM-style harness
#[cfg(unix)]
fn harness<E, I>(_executor: &E, buf: &[u8]) -> ExitKind fn harness<E, I>(_executor: &E, buf: &[u8]) -> ExitKind
where where
E: Executor<I>, E: Executor<I>,
@ -67,7 +70,14 @@ pub fn main() {
.expect("An error occurred while fuzzing"); .expect("An error occurred while fuzzing");
} }
/// Not supported on windows right now
#[cfg(windows)]
fn fuzz(_corpus_dirs: Vec<PathBuf>, _objective_dir: PathBuf, _broker_port: u16) -> Result<(), ()> {
todo!("Example not supported on Windows");
}
/// The actual fuzzer /// The actual fuzzer
#[cfg(unix)]
fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) -> Result<(), Error> { fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) -> Result<(), Error> {
// 'While the stats are state, they are usually used in the broker - which is likely never restarted // 'While the stats are state, they are usually used in the broker - which is likely never restarted
let stats = SimpleStats::new(|s| println!("{}", s)); let stats = SimpleStats::new(|s| println!("{}", s));

View File

@ -3,17 +3,21 @@ This shows how llmp can be used directly, without libafl abstractions
*/ */
extern crate alloc; extern crate alloc;
#[cfg(all(unix, feature = "std"))]
use core::{convert::TryInto, time::Duration}; use core::{convert::TryInto, time::Duration};
#[cfg(all(unix, feature = "std"))]
use std::{thread, time}; use std::{thread, time};
#[cfg(all(unix, feature = "std"))]
use libafl::{ use libafl::{
bolts::{llmp, shmem::UnixShMem}, bolts::{llmp, shmem::UnixShMem},
Error, Error,
}; };
const TAG_SIMPLE_U32_V1: u32 = 0x51300321; const _TAG_SIMPLE_U32_V1: u32 = 0x51300321;
const TAG_MATH_RESULT_V1: u32 = 0x77474331; const _TAG_MATH_RESULT_V1: u32 = 0x77474331;
#[cfg(all(unix, feature = "std"))]
fn adder_loop(port: u16) -> ! { fn adder_loop(port: u16) -> ! {
let mut client = llmp::LlmpClient::<UnixShMem>::create_attach_to_tcp(port).unwrap(); let mut client = llmp::LlmpClient::<UnixShMem>::create_attach_to_tcp(port).unwrap();
let mut last_result: u32 = 0; let mut last_result: u32 = 0;
@ -27,7 +31,7 @@ fn adder_loop(port: u16) -> ! {
}; };
msg_counter += 1; msg_counter += 1;
match tag { match tag {
TAG_SIMPLE_U32_V1 => { _TAG_SIMPLE_U32_V1 => {
current_result = current_result =
current_result.wrapping_add(u32::from_le_bytes(buf.try_into().unwrap())); current_result.wrapping_add(u32::from_le_bytes(buf.try_into().unwrap()));
} }
@ -42,7 +46,7 @@ fn adder_loop(port: u16) -> ! {
); );
client client
.send_buf(TAG_MATH_RESULT_V1, &current_result.to_le_bytes()) .send_buf(_TAG_MATH_RESULT_V1, &current_result.to_le_bytes())
.unwrap(); .unwrap();
last_result = current_result; last_result = current_result;
} }
@ -51,13 +55,14 @@ fn adder_loop(port: u16) -> ! {
} }
} }
#[cfg(all(unix, feature = "std"))]
fn broker_message_hook( fn broker_message_hook(
client_id: u32, client_id: u32,
tag: llmp::Tag, tag: llmp::Tag,
message: &[u8], message: &[u8],
) -> Result<llmp::LlmpMsgHookResult, Error> { ) -> Result<llmp::LlmpMsgHookResult, Error> {
match tag { match tag {
TAG_SIMPLE_U32_V1 => { _TAG_SIMPLE_U32_V1 => {
println!( println!(
"Client {:?} sent message: {:?}", "Client {:?} sent message: {:?}",
client_id, client_id,
@ -65,7 +70,7 @@ fn broker_message_hook(
); );
Ok(llmp::LlmpMsgHookResult::ForwardToClients) Ok(llmp::LlmpMsgHookResult::ForwardToClients)
} }
TAG_MATH_RESULT_V1 => { _TAG_MATH_RESULT_V1 => {
println!( println!(
"Adder Client has this current result: {:?}", "Adder Client has this current result: {:?}",
u32::from_le_bytes(message.try_into().unwrap()) u32::from_le_bytes(message.try_into().unwrap())
@ -79,6 +84,12 @@ fn broker_message_hook(
} }
} }
#[cfg(not(unix))]
fn main() {
todo!("LLMP is not yet supported on this platform.");
}
#[cfg(unix)]
fn main() { fn main() {
/* The main node has a broker, and a few worker threads */ /* The main node has a broker, and a few worker threads */
@ -108,7 +119,7 @@ fn main() {
loop { loop {
counter = counter.wrapping_add(1); counter = counter.wrapping_add(1);
client client
.send_buf(TAG_SIMPLE_U32_V1, &counter.to_le_bytes()) .send_buf(_TAG_SIMPLE_U32_V1, &counter.to_le_bytes())
.unwrap(); .unwrap();
println!("CTR Client writing {}", counter); println!("CTR Client writing {}", counter);
thread::sleep(Duration::from_secs(1)) thread::sleep(Duration::from_secs(1))

View File

@ -74,7 +74,6 @@ use std::{
#[cfg(all(feature = "std", unix))] #[cfg(all(feature = "std", unix))]
use nix::{ use nix::{
cmsg_space, cmsg_space,
mem::zeroed,
sys::{ sys::{
socket::{recvmsg, sendmsg, ControlMessage, ControlMessageOwned, MsgFlags}, socket::{recvmsg, sendmsg, ControlMessage, ControlMessageOwned, MsgFlags},
uio::IoVec, uio::IoVec,
@ -83,7 +82,9 @@ use nix::{
#[cfg(all(feature = "std", unix))] #[cfg(all(feature = "std", unix))]
use std::{ use std::{
ffi::CStr, ffi::CStr,
mem::zeroed,
os::unix::{ os::unix::{
self,
net::{UnixListener, UnixStream}, net::{UnixListener, UnixStream},
{io::AsRawFd, prelude::RawFd}, {io::AsRawFd, prelude::RawFd},
}, },
@ -1330,6 +1331,7 @@ where
/// Called from an interrupt: Sets broker `shutting_down` flag to `true`. /// Called from an interrupt: Sets broker `shutting_down` flag to `true`.
/// Currently only supported on `std` unix systems. /// Currently only supported on `std` unix systems.
#[cfg(all(feature = "std", unix))]
fn shutdown(&mut self) { fn shutdown(&mut self) {
unsafe { ptr::write_volatile(&mut self.shutting_down, true) }; unsafe { ptr::write_volatile(&mut self.shutting_down, true) };
compiler_fence(Ordering::SeqCst); compiler_fence(Ordering::SeqCst);
@ -1928,22 +1930,20 @@ where
} }
#[cfg(test)] #[cfg(test)]
#[cfg(all(unix, feature = "std"))]
mod tests { mod tests {
#[cfg(feature = "std")]
use std::{thread::sleep, time::Duration}; use std::{thread::sleep, time::Duration};
#[cfg(feature = "std")]
use super::{ use super::{
LlmpClient, LlmpClient,
LlmpConnection::{self, IsBroker, IsClient}, LlmpConnection::{self, IsBroker, IsClient},
LlmpMsgHookResult::ForwardToClients, LlmpMsgHookResult::ForwardToClients,
Tag, Tag,
}; };
#[cfg(feature = "std")]
use crate::bolts::shmem::UnixShMem; use crate::bolts::shmem::UnixShMem;
#[cfg(feature = "std")]
#[test] #[test]
pub fn llmp_connection() { pub fn llmp_connection() {
let mut broker = match LlmpConnection::<UnixShMem>::on_port(1337).unwrap() { let mut broker = match LlmpConnection::<UnixShMem>::on_port(1337).unwrap() {

View File

@ -1,12 +1,10 @@
//! A generic sharememory region to be used by any functions (queues or feedbacks //! A generic sharememory region to be used by any functions (queues or feedbacks
// too.) // too.)
#[cfg(feature = "std")] #[cfg(all(feature = "std", unix))]
#[cfg(unix)]
pub use unix_shmem::UnixShMem; pub use unix_shmem::UnixShMem;
#[cfg(feature = "std")] #[cfg(all(windows, feature = "std"))]
#[cfg(windows)]
pub use shmem::Win32ShMem; pub use shmem::Win32ShMem;
use alloc::string::{String, ToString}; use alloc::string::{String, ToString};
@ -465,10 +463,10 @@ pub mod shmem {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#[cfg(feature = "std")] #[cfg(all(unix, feature = "std"))]
use super::{ShMem, UnixShMem}; use super::{ShMem, UnixShMem};
#[cfg(feature = "std")] #[cfg(all(unix, feature = "std"))]
#[test] #[test]
fn test_str_conversions() { fn test_str_conversions() {
let mut shm_str: [u8; 20] = [0; 20]; let mut shm_str: [u8; 20] = [0; 20];

View File

@ -6,11 +6,13 @@ use serde::{de::DeserializeOwned, Serialize};
#[cfg(feature = "std")] #[cfg(feature = "std")]
use crate::bolts::llmp::LlmpReceiver; use crate::bolts::llmp::LlmpReceiver;
#[cfg(feature = "std")] #[cfg(all(feature = "std", windows))]
use std::{env, process::Command}; use crate::utils::startable_self;
#[cfg(feature = "std")] #[cfg(all(feature = "std", unix))]
#[cfg(unix)] use crate::utils::{fork, ForkResult};
#[cfg(all(feature = "std", unix))]
use crate::bolts::shmem::UnixShMem; use crate::bolts::shmem::UnixShMem;
use crate::{ use crate::{
bolts::{ bolts::{
@ -513,7 +515,7 @@ where
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() { let (sender, mut receiver) = if std::env::var(_ENV_FUZZER_SENDER).is_err() {
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
{ {
let path = std::env::current_dir()?; let path = std::env::current_dir()?;
@ -549,22 +551,32 @@ where
// Client->parent loop // Client->parent loop
loop { loop {
dbg!("Spawning next client (id {})", ctr); dbg!("Spawning next client (id {})", ctr);
Command::new(env::current_exe()?)
.current_dir(env::current_dir()?) // On Unix, we fork (todo: measure if that is actually faster.)
.args(env::args()) #[cfg(unix)]
.status()?; let _ = match unsafe { fork() }? {
ForkResult::Parent(handle) => handle.status(),
ForkResult::Child => break (sender, receiver),
};
// On windows, we spawn ourself again
#[cfg(windows)]
startable_self()?.status()?;
ctr += 1; ctr += 1;
} }
} }
} } else {
// We are the newly started fuzzing instance, first, connect to our own restore map.
// A sender and a receiver for single communication
(
LlmpSender::<SH>::on_existing_from_env(_ENV_FUZZER_SENDER)?,
LlmpReceiver::<SH>::on_existing_from_env(_ENV_FUZZER_RECEIVER)?,
)
};
println!("We're a client, let's fuzz :)"); println!("We're a client, let's fuzz :)");
// We are the fuzzing instance, first, connect to our own restore map.
// A sender and a receiver for single communication
let mut receiver = LlmpReceiver::<SH>::on_existing_from_env(_ENV_FUZZER_RECEIVER)?;
let sender = LlmpSender::<SH>::on_existing_from_env(_ENV_FUZZER_SENDER)?;
// If we're restarting, deserialize the old state. // If we're restarting, deserialize the old state.
let (state, mut mgr) = match receiver.recv_buf()? { let (state, mut mgr) = match receiver.recv_buf()? {
None => { None => {

View File

@ -4,8 +4,19 @@ use core::{cell::RefCell, debug_assert, fmt::Debug, time};
use serde::{de::DeserializeOwned, Deserialize, Serialize}; use serde::{de::DeserializeOwned, Deserialize, Serialize};
use xxhash_rust::xxh3::xxh3_64_with_seed; use xxhash_rust::xxh3::xxh3_64_with_seed;
#[cfg(unix)]
use alloc::string::ToString;
#[cfg(unix)]
use libc::pid_t;
use crate::Error;
#[cfg(feature = "std")] #[cfg(feature = "std")]
use std::time::{SystemTime, UNIX_EPOCH}; use std::{
env,
process::Command,
time::{SystemTime, UNIX_EPOCH},
};
pub trait AsSlice<T> { pub trait AsSlice<T> {
/// Convert to a slice /// Convert to a slice
@ -378,6 +389,53 @@ impl XKCDRand {
} }
} }
/// Child Process Handle
#[cfg(unix)]
pub struct ChildHandle {
pid: pid_t,
}
#[cfg(unix)]
impl ChildHandle {
/// Block until the child exited and the status code becomes available
pub fn status(&self) -> i32 {
let mut status = -1;
unsafe {
libc::waitpid(self.pid, &mut status, 0);
}
status
}
}
#[cfg(unix)]
/// The ForkResult
pub enum ForkResult {
Parent(ChildHandle),
Child,
}
/// Unix has forks.
#[cfg(unix)]
pub unsafe fn fork() -> Result<ForkResult, Error> {
let pid = libc::fork();
if pid < 0 {
Err(Error::Unknown("Fork failed".to_string()))
} else if pid == 0 {
Ok(ForkResult::Child)
} else {
Ok(ForkResult::Parent(ChildHandle { pid }))
}
}
/// Executes the current process from the beginning, as subprocess.
/// use `start_self.status()?` to wait for the child
#[cfg(feature = "std")]
pub fn startable_self() -> Result<Command, Error> {
let mut startable = Command::new(env::current_exe()?);
startable.current_dir(env::current_dir()?).args(env::args());
Ok(startable)
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
//use xxhash_rust::xxh3::xxh3_64_with_seed; //use xxhash_rust::xxh3::xxh3_64_with_seed;