Fork (#20)
* 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:
parent
8238d65cac
commit
eaa3dc786b
8
.github/workflows/build_and_test.yml
vendored
8
.github/workflows/build_and_test.yml
vendored
@ -14,6 +14,14 @@ jobs:
|
||||
run: cargo build --verbose
|
||||
- name: Test
|
||||
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:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
@ -1,12 +1,19 @@
|
||||
// build.rs
|
||||
|
||||
use std::env;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
use std::{
|
||||
env,
|
||||
path::Path,
|
||||
process::{exit, Command},
|
||||
};
|
||||
|
||||
const LIBMOZJPEG_URL: &str = "https://github.com/mozilla/mozjpeg/archive/v4.0.3.tar.gz";
|
||||
|
||||
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 cwd = env::current_dir().unwrap().to_string_lossy().to_string();
|
||||
let out_dir = out_dir.to_string_lossy().to_string();
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
use std::{env, path::PathBuf};
|
||||
|
||||
#[cfg(unix)]
|
||||
use libafl::{
|
||||
bolts::{shmem::UnixShMem, tuples::tuple_list},
|
||||
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus, RandCorpusScheduler},
|
||||
@ -22,6 +23,7 @@ use libafl::{
|
||||
};
|
||||
|
||||
/// We will interact with a C++ target, so use external c functionality
|
||||
#[cfg(unix)]
|
||||
extern "C" {
|
||||
/// int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
|
||||
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
|
||||
#[cfg(unix)]
|
||||
fn harness<E, I>(_executor: &E, buf: &[u8]) -> ExitKind
|
||||
where
|
||||
E: Executor<I>,
|
||||
@ -65,7 +68,14 @@ pub fn main() {
|
||||
.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
|
||||
#[cfg(unix)]
|
||||
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
|
||||
let stats = SimpleStats::new(|s| println!("{}", s));
|
||||
|
@ -1,13 +1,20 @@
|
||||
// build.rs
|
||||
|
||||
use std::env;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
use std::{
|
||||
env,
|
||||
path::Path,
|
||||
process::{exit, Command},
|
||||
};
|
||||
|
||||
const LIBPNG_URL: &str =
|
||||
"https://deac-fra.dl.sourceforge.net/project/libpng/libpng16/1.6.37/libpng-1.6.37.tar.xz";
|
||||
|
||||
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 cwd = env::current_dir().unwrap().to_string_lossy().to_string();
|
||||
let out_dir = out_dir.to_string_lossy().to_string();
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
use std::{env, path::PathBuf};
|
||||
|
||||
#[cfg(unix)]
|
||||
use libafl::{
|
||||
bolts::{shmem::UnixShMem, tuples::tuple_list},
|
||||
corpus::{
|
||||
@ -24,6 +25,7 @@ use libafl::{
|
||||
};
|
||||
|
||||
/// We will interact with a C++ target, so use external c functionality
|
||||
#[cfg(unix)]
|
||||
extern "C" {
|
||||
/// int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
|
||||
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
|
||||
#[cfg(unix)]
|
||||
fn harness<E, I>(_executor: &E, buf: &[u8]) -> ExitKind
|
||||
where
|
||||
E: Executor<I>,
|
||||
@ -67,7 +70,14 @@ pub fn main() {
|
||||
.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
|
||||
#[cfg(unix)]
|
||||
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
|
||||
let stats = SimpleStats::new(|s| println!("{}", s));
|
||||
|
@ -3,17 +3,21 @@ This shows how llmp can be used directly, without libafl abstractions
|
||||
*/
|
||||
extern crate alloc;
|
||||
|
||||
#[cfg(all(unix, feature = "std"))]
|
||||
use core::{convert::TryInto, time::Duration};
|
||||
#[cfg(all(unix, feature = "std"))]
|
||||
use std::{thread, time};
|
||||
|
||||
#[cfg(all(unix, feature = "std"))]
|
||||
use libafl::{
|
||||
bolts::{llmp, shmem::UnixShMem},
|
||||
Error,
|
||||
};
|
||||
|
||||
const TAG_SIMPLE_U32_V1: u32 = 0x51300321;
|
||||
const TAG_MATH_RESULT_V1: u32 = 0x77474331;
|
||||
const _TAG_SIMPLE_U32_V1: u32 = 0x51300321;
|
||||
const _TAG_MATH_RESULT_V1: u32 = 0x77474331;
|
||||
|
||||
#[cfg(all(unix, feature = "std"))]
|
||||
fn adder_loop(port: u16) -> ! {
|
||||
let mut client = llmp::LlmpClient::<UnixShMem>::create_attach_to_tcp(port).unwrap();
|
||||
let mut last_result: u32 = 0;
|
||||
@ -27,7 +31,7 @@ fn adder_loop(port: u16) -> ! {
|
||||
};
|
||||
msg_counter += 1;
|
||||
match tag {
|
||||
TAG_SIMPLE_U32_V1 => {
|
||||
_TAG_SIMPLE_U32_V1 => {
|
||||
current_result =
|
||||
current_result.wrapping_add(u32::from_le_bytes(buf.try_into().unwrap()));
|
||||
}
|
||||
@ -42,7 +46,7 @@ fn adder_loop(port: u16) -> ! {
|
||||
);
|
||||
|
||||
client
|
||||
.send_buf(TAG_MATH_RESULT_V1, ¤t_result.to_le_bytes())
|
||||
.send_buf(_TAG_MATH_RESULT_V1, ¤t_result.to_le_bytes())
|
||||
.unwrap();
|
||||
last_result = current_result;
|
||||
}
|
||||
@ -51,13 +55,14 @@ fn adder_loop(port: u16) -> ! {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(unix, feature = "std"))]
|
||||
fn broker_message_hook(
|
||||
client_id: u32,
|
||||
tag: llmp::Tag,
|
||||
message: &[u8],
|
||||
) -> Result<llmp::LlmpMsgHookResult, Error> {
|
||||
match tag {
|
||||
TAG_SIMPLE_U32_V1 => {
|
||||
_TAG_SIMPLE_U32_V1 => {
|
||||
println!(
|
||||
"Client {:?} sent message: {:?}",
|
||||
client_id,
|
||||
@ -65,7 +70,7 @@ fn broker_message_hook(
|
||||
);
|
||||
Ok(llmp::LlmpMsgHookResult::ForwardToClients)
|
||||
}
|
||||
TAG_MATH_RESULT_V1 => {
|
||||
_TAG_MATH_RESULT_V1 => {
|
||||
println!(
|
||||
"Adder Client has this current result: {:?}",
|
||||
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() {
|
||||
/* The main node has a broker, and a few worker threads */
|
||||
|
||||
@ -108,7 +119,7 @@ fn main() {
|
||||
loop {
|
||||
counter = counter.wrapping_add(1);
|
||||
client
|
||||
.send_buf(TAG_SIMPLE_U32_V1, &counter.to_le_bytes())
|
||||
.send_buf(_TAG_SIMPLE_U32_V1, &counter.to_le_bytes())
|
||||
.unwrap();
|
||||
println!("CTR Client writing {}", counter);
|
||||
thread::sleep(Duration::from_secs(1))
|
||||
|
@ -74,7 +74,6 @@ use std::{
|
||||
#[cfg(all(feature = "std", unix))]
|
||||
use nix::{
|
||||
cmsg_space,
|
||||
mem::zeroed,
|
||||
sys::{
|
||||
socket::{recvmsg, sendmsg, ControlMessage, ControlMessageOwned, MsgFlags},
|
||||
uio::IoVec,
|
||||
@ -83,7 +82,9 @@ use nix::{
|
||||
#[cfg(all(feature = "std", unix))]
|
||||
use std::{
|
||||
ffi::CStr,
|
||||
mem::zeroed,
|
||||
os::unix::{
|
||||
self,
|
||||
net::{UnixListener, UnixStream},
|
||||
{io::AsRawFd, prelude::RawFd},
|
||||
},
|
||||
@ -1330,6 +1331,7 @@ where
|
||||
|
||||
/// Called from an interrupt: Sets broker `shutting_down` flag to `true`.
|
||||
/// Currently only supported on `std` unix systems.
|
||||
#[cfg(all(feature = "std", unix))]
|
||||
fn shutdown(&mut self) {
|
||||
unsafe { ptr::write_volatile(&mut self.shutting_down, true) };
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
@ -1928,22 +1930,20 @@ where
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(all(unix, feature = "std"))]
|
||||
mod tests {
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::{thread::sleep, time::Duration};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use super::{
|
||||
LlmpClient,
|
||||
LlmpConnection::{self, IsBroker, IsClient},
|
||||
LlmpMsgHookResult::ForwardToClients,
|
||||
Tag,
|
||||
};
|
||||
#[cfg(feature = "std")]
|
||||
|
||||
use crate::bolts::shmem::UnixShMem;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[test]
|
||||
pub fn llmp_connection() {
|
||||
let mut broker = match LlmpConnection::<UnixShMem>::on_port(1337).unwrap() {
|
||||
|
@ -1,12 +1,10 @@
|
||||
//! A generic sharememory region to be used by any functions (queues or feedbacks
|
||||
// too.)
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg(unix)]
|
||||
#[cfg(all(feature = "std", unix))]
|
||||
pub use unix_shmem::UnixShMem;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg(windows)]
|
||||
#[cfg(all(windows, feature = "std"))]
|
||||
pub use shmem::Win32ShMem;
|
||||
|
||||
use alloc::string::{String, ToString};
|
||||
@ -465,10 +463,10 @@ pub mod shmem {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg(all(unix, feature = "std"))]
|
||||
use super::{ShMem, UnixShMem};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg(all(unix, feature = "std"))]
|
||||
#[test]
|
||||
fn test_str_conversions() {
|
||||
let mut shm_str: [u8; 20] = [0; 20];
|
||||
|
@ -6,11 +6,13 @@ use serde::{de::DeserializeOwned, Serialize};
|
||||
#[cfg(feature = "std")]
|
||||
use crate::bolts::llmp::LlmpReceiver;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::{env, process::Command};
|
||||
#[cfg(all(feature = "std", windows))]
|
||||
use crate::utils::startable_self;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg(unix)]
|
||||
#[cfg(all(feature = "std", unix))]
|
||||
use crate::utils::{fork, ForkResult};
|
||||
|
||||
#[cfg(all(feature = "std", unix))]
|
||||
use crate::bolts::shmem::UnixShMem;
|
||||
use crate::{
|
||||
bolts::{
|
||||
@ -513,7 +515,7 @@ where
|
||||
let mut mgr;
|
||||
|
||||
// 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")]
|
||||
{
|
||||
let path = std::env::current_dir()?;
|
||||
@ -549,22 +551,32 @@ where
|
||||
// Client->parent loop
|
||||
loop {
|
||||
dbg!("Spawning next client (id {})", ctr);
|
||||
Command::new(env::current_exe()?)
|
||||
.current_dir(env::current_dir()?)
|
||||
.args(env::args())
|
||||
.status()?;
|
||||
|
||||
// On Unix, we fork (todo: measure if that is actually faster.)
|
||||
#[cfg(unix)]
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
} 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 :)");
|
||||
|
||||
// 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.
|
||||
let (state, mut mgr) = match receiver.recv_buf()? {
|
||||
None => {
|
||||
|
@ -4,8 +4,19 @@ use core::{cell::RefCell, debug_assert, fmt::Debug, time};
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
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")]
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use std::{
|
||||
env,
|
||||
process::Command,
|
||||
time::{SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
|
||||
pub trait AsSlice<T> {
|
||||
/// 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)]
|
||||
mod tests {
|
||||
//use xxhash_rust::xxh3::xxh3_64_with_seed;
|
||||
|
Loading…
x
Reference in New Issue
Block a user