first steps towards respawning

This commit is contained in:
Dominik Maier 2021-01-03 01:58:02 +01:00
parent 669cb1c96b
commit 27a68fa30d
7 changed files with 156 additions and 45 deletions

View File

@ -400,6 +400,19 @@ impl<SH> LlmpSender<SH>
where where
SH: ShMem, SH: ShMem,
{ {
pub fn new(id: u32, keep_pages_forever: bool) -> Result<Self, AflError> {
Ok(Self {
id,
last_msg_sent: 0 as *mut LlmpMsg,
out_maps: vec![LlmpSharedMap::new(
0,
SH::new_map(new_map_size(LLMP_PREF_INITIAL_MAP_SIZE))?,
)],
// drop pages to the broker if it already read them
keep_pages_forever,
})
}
/// Reattach to a vacant out_map, to with a previous sender stored the information in an env before. /// Reattach to a vacant out_map, to with a previous sender stored the information in an env before.
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub fn on_existing_from_env(env_name: &str) -> Result<Self, AflError> { pub fn on_existing_from_env(env_name: &str) -> Result<Self, AflError> {
@ -890,7 +903,7 @@ where
} }
} }
/// Returns the next message, tag, buf, looping until it becomes available /// Returns the next sender, tag, buf, looping until it becomes available
#[inline] #[inline]
pub fn recv_buf_blocking(&mut self) -> Result<(u32, u32, &[u8]), AflError> { pub fn recv_buf_blocking(&mut self) -> Result<(u32, u32, &[u8]), AflError> {
unsafe { unsafe {
@ -912,7 +925,7 @@ where
{ {
/// Shmem containg the actual (unsafe) page, /// Shmem containg the actual (unsafe) page,
/// shared between one LlmpSender and one LlmpReceiver /// shared between one LlmpSender and one LlmpReceiver
shmem: SH, pub shmem: SH,
} }
// TODO: May be obsolete // TODO: May be obsolete

View File

@ -118,7 +118,7 @@ pub trait Stats {
} }
} }
#[derive(Debug)] #[derive(Clone, Debug)]
pub struct SimpleStats<F> pub struct SimpleStats<F>
where where
F: FnMut(String), F: FnMut(String),
@ -743,6 +743,12 @@ where
phantom: PhantomData, phantom: PhantomData,
}) })
} }
/// If a client respawns, it may reuse the existing connection, previously stored by LlmpClient::to_env
/// Std uses AflShmem.
pub fn existing_client_from_env_std(env_name: &str, stats: ST) -> Result<Self, AflError> {
Self::existing_client_from_env(env_name, stats)
}
} }
impl<C, E, OT, FT, I, R, SH, ST> LlmpEventManager<C, E, OT, FT, I, R, SH, ST> impl<C, E, OT, FT, I, R, SH, ST> LlmpEventManager<C, E, OT, FT, I, R, SH, ST>
@ -768,6 +774,19 @@ where
}) })
} }
/// If a client respawns, it may reuse the existing connection, previously stored by LlmpClient::to_env
pub fn existing_client_from_env(env_name: &str, stats: ST) -> Result<Self, AflError> {
Ok(Self {
llmp: llmp::LlmpConnection::IsClient {
client: LlmpClient::on_existing_from_env(env_name)?,
},
// Inserting a nop-stats element here so rust won't complain.
// In any case, the client won't currently use it.
stats: stats,
phantom: PhantomData,
})
}
/// A client on an existing map /// A client on an existing map
pub fn for_client(client: LlmpClient<SH>, stats: ST) -> Self { pub fn for_client(client: LlmpClient<SH>, stats: ST) -> Self {
Self { Self {
@ -777,6 +796,17 @@ where
} }
} }
#[cfg(feature = "std")]
/// Write the config for a client eventmgr to env vars, a new client can reattach using existing_client_from_env
pub fn to_env(&self, env_name: &str) {
match &self.llmp {
llmp::LlmpConnection::IsBroker { broker: _ } => {
todo!("There is probably no use storing the broker to env. Client only for now")
}
llmp::LlmpConnection::IsClient { client } => client.to_env(env_name).unwrap(),
}
}
/// Returns if we are the broker /// Returns if we are the broker
pub fn is_broker(&self) -> bool { pub fn is_broker(&self) -> bool {
match self.llmp { match self.llmp {

View File

@ -60,6 +60,14 @@ struct shmid_ds {
/// A Shared map /// A Shared map
pub trait ShMem: Sized + Debug { pub trait ShMem: Sized + Debug {
/// Creates a new map with the given size
fn new_map(map_size: usize) -> Result<Self, AflError>;
/// Creates a new reference to the same map
fn clone_ref(old_ref: &Self) -> Result<Self, AflError> {
Self::existing_from_shm_slice(old_ref.shm_slice(), old_ref.map().len())
}
/// Creates a nes variable with the given name, strigified to 20 bytes. /// Creates a nes variable with the given name, strigified to 20 bytes.
fn existing_from_shm_slice(map_str_bytes: &[u8; 20], map_size: usize) fn existing_from_shm_slice(map_str_bytes: &[u8; 20], map_size: usize)
-> Result<Self, AflError>; -> Result<Self, AflError>;
@ -73,9 +81,6 @@ pub trait ShMem: Sized + Debug {
Self::existing_from_shm_slice(&slice, map_size) Self::existing_from_shm_slice(&slice, map_size)
} }
/// Creates a new map with the given size
fn new_map(map_size: usize) -> Result<Self, AflError>;
/// The string to identify this shm /// The string to identify this shm
fn shm_str(&self) -> String { fn shm_str(&self) -> String {
let bytes = self.shm_slice(); let bytes = self.shm_slice();

View File

@ -20,9 +20,16 @@ where
I: Input + HasTargetBytes, I: Input + HasTargetBytes,
OT: ObserversTuple, OT: ObserversTuple,
{ {
harness: HarnessFunction<I>, /// The name of this executor instance, to address it from other components
observers: OT,
name: &'static str, name: &'static str,
/// The harness function, being executed for each fuzzing loop execution
harness: HarnessFunction<I>,
/// The observers, observing each run
observers: OT,
/*
/// A special function being called right before the process crashes. It may save state to restore fuzzing after respawn.
on_crash_fn: Option<Box<dyn FnOnce(ExitKind)>>,
*/
} }
impl<I, OT> Executor<I> for InMemoryExecutor<I, OT> impl<I, OT> Executor<I> for InMemoryExecutor<I, OT>
@ -75,11 +82,22 @@ where
I: Input + HasTargetBytes, I: Input + HasTargetBytes,
OT: ObserversTuple, OT: ObserversTuple,
{ {
pub fn new(name: &'static str, harness_fn: HarnessFunction<I>, observers: OT) -> Self { /// Create a new in mem executor.
/// * `name` - the name of this executor (to address it along the way)
/// * `harness_fn` - the harness, executiong the function
/// * `on_crash_fn` - When an in-mem harness crashes, it may safe some state to continue fuzzing later.
/// Do that that in this function. The program will crash afterwards.
/// * `observers` - the observers observing the target during execution
pub fn new(
name: &'static str,
harness_fn: HarnessFunction<I>,
observers: OT, /*on_crash_fn: Option<Box<dyn FnOnce(ExitKind)>*/
) -> Self {
Self { Self {
harness: harness_fn, harness: harness_fn,
observers: observers, //on_crash_fn,
name: name, observers,
name,
} }
} }
} }

View File

@ -1,31 +1,37 @@
#![cfg_attr(not(feature = "std"), no_std)]
#[macro_use] #[macro_use]
extern crate clap; extern crate clap;
extern crate alloc;
use clap::{App, Arg}; use clap::{App, Arg};
use std::env; use std::{env, path::PathBuf, process::Command};
use std::path::PathBuf;
use afl::corpus::Corpus; use afl::{
use afl::corpus::InMemoryCorpus; corpus::{Corpus, InMemoryCorpus},
use afl::engines::Engine; engines::{Engine, Fuzzer, State, StdFuzzer},
use afl::engines::Fuzzer; events::{
use afl::engines::State; llmp::LlmpReceiver,
use afl::engines::StdFuzzer; llmp::LlmpSender,
use afl::events::{LlmpEventManager, SimpleStats}; shmem::{AflShmem, ShMem},
use afl::executors::inmemory::InMemoryExecutor; LlmpEventManager, SimpleStats,
use afl::executors::{Executor, ExitKind}; },
use afl::feedbacks::MaxMapFeedback; executors::{inmemory::InMemoryExecutor, Executor, ExitKind},
use afl::generators::RandPrintablesGenerator; feedbacks::MaxMapFeedback,
use afl::mutators::scheduled::HavocBytesMutator; generators::RandPrintablesGenerator,
use afl::mutators::HasMaxSize; mutators::{scheduled::HavocBytesMutator, HasMaxSize},
use afl::observers::StdMapObserver; observers::StdMapObserver,
use afl::stages::mutational::StdMutationalStage; stages::mutational::StdMutationalStage,
use afl::tuples::tuple_list; tuples::tuple_list,
use afl::utils::StdRand; utils::StdRand,
};
/// The llmp connection from the actual fuzzer to the process supervising it
const ENV_FUZZER_PARENT_SENDER: &str = &"_AFL_ENV_FUZZER_PARENT_SENDER";
/// The llmp (2 way) connection from a fuzzer to the broker (broadcasting all other fuzzer messages)
const ENV_FUZZER_BROKER_CLIENT: &str = &"_AFL_ENV_FUZZER_BROKER_CLIENT";
/// The name of the coverage map observer, to find it again in the observer list
const NAME_COV_MAP: &str = "cov_map";
/// We will interact with a c++ target, so use external c functionality
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;
@ -38,6 +44,7 @@ extern "C" {
static __lafl_max_edges_size: u32; static __lafl_max_edges_size: u32;
} }
/// The wrapped harness function, calling out to the llvm-style libfuzzer harness
fn harness<I>(_executor: &dyn Executor<I>, buf: &[u8]) -> ExitKind { fn harness<I>(_executor: &dyn Executor<I>, buf: &[u8]) -> ExitKind {
unsafe { unsafe {
LLVMFuzzerTestOneInput(buf.as_ptr(), buf.len()); LLVMFuzzerTestOneInput(buf.as_ptr(), buf.len());
@ -45,8 +52,6 @@ fn harness<I>(_executor: &dyn Executor<I>, buf: &[u8]) -> ExitKind {
ExitKind::Ok ExitKind::Ok
} }
const NAME_COV_MAP: &str = "cov_map";
pub fn main() { pub fn main() {
let matches = App::new("libAFLrs fuzzer harness") let matches = App::new("libAFLrs fuzzer harness")
.about("libAFLrs fuzzer harness help options.") .about("libAFLrs fuzzer harness help options.")
@ -109,15 +114,57 @@ pub fn main() {
let mut corpus = InMemoryCorpus::new(); let mut corpus = InMemoryCorpus::new();
let mut generator = RandPrintablesGenerator::new(32); let mut generator = RandPrintablesGenerator::new(32);
let stats = SimpleStats::new(|s| println!("{}", s)); let stats = SimpleStats::new(|s| println!("{}", s));
let mut mgr = LlmpEventManager::new_on_port_std(broker_port, stats).unwrap(); let mut mgr;
if mgr.is_broker() { // We start ourself as child process to actually fuzz
println!("Doing broker things. Run this tool again to start fuzzing in a client."); if std::env::var(ENV_FUZZER_PARENT_SENDER).is_err() {
mgr.broker_loop().unwrap(); // We are either the broker, or the parent of the fuzzing instance
mgr = LlmpEventManager::new_on_port_std(broker_port, stats.clone()).unwrap();
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().unwrap();
} else {
// we are one of the fuzzing instances. Let's launch the fuzzer.
// First, store the mgr to an env so the client can use it
mgr.to_env(ENV_FUZZER_BROKER_CLIENT);
// First, create a channel from the fuzzer (sender) to us (receiver) to report its state for restarts.
let sender = LlmpSender::new(0, false).unwrap();
let mut receiver = LlmpReceiver::on_existing_map(
AflShmem::clone_ref(&sender.out_maps.last().unwrap().shmem).unwrap(),
None,
)
.unwrap();
// Store the information to a map.
sender.to_env(ENV_FUZZER_PARENT_SENDER).unwrap();
loop {
dbg!("Spawning next client");
Command::new(env::current_exe().unwrap())
.current_dir(env::current_dir().unwrap())
.args(env::args())
.status()
.unwrap();
match receiver.recv_buf().unwrap() {
None => panic!("Fuzzer process exited without giving us its result."),
Some((sender, tag, msg)) => {
todo!("Restore this: {}, {}, {:?}", sender, tag, msg);
}
}
}
}
} }
println!("We're a client, let's fuzz :)"); println!("We're a client, let's fuzz :)");
// We are the fuzzing instance
mgr = LlmpEventManager::existing_client_from_env_std(ENV_FUZZER_BROKER_CLIENT, stats).unwrap();
let channel_sender =
LlmpSender::<AflShmem>::on_existing_from_env(ENV_FUZZER_PARENT_SENDER).unwrap();
let edges_observer = let edges_observer =
StdMapObserver::new_from_ptr(&NAME_COV_MAP, unsafe { __lafl_edges_map }, unsafe { StdMapObserver::new_from_ptr(&NAME_COV_MAP, unsafe { __lafl_edges_map }, unsafe {
__lafl_max_edges_size as usize __lafl_max_edges_size as usize
@ -168,6 +215,4 @@ pub fn main() {
fuzzer fuzzer
.fuzz_loop(&mut rand, &mut state, &mut corpus, &mut engine, &mut mgr) .fuzz_loop(&mut rand, &mut state, &mut corpus, &mut engine, &mut mgr)
.expect("Fuzzer fatal error"); .expect("Fuzzer fatal error");
#[cfg(feature = "std")]
println!("OK");
} }

View File

@ -3,12 +3,12 @@
cargo build --release || exit 1 cargo build --release || exit 1
cp ./target/release/libfuzzer ./.libfuzzer_test.elf cp ./target/release/libfuzzer ./.libfuzzer_test.elf
RUST_BACKTRACE=1 taskset -c 0 ./.libfuzzer_test.elf & RUST_BACKTRACE=full taskset -c 0 ./.libfuzzer_test.elf &
test "$!" -gt 0 && { test "$!" -gt 0 && {
usleep 250 usleep 250
RUST_BACKTRACE=1 taskset -c 1 ./.libfuzzer_test.elf & RUST_BACKTRACE=full taskset -c 1 ./.libfuzzer_test.elf &
} }

View File

@ -3,12 +3,12 @@
cargo build --release || exit 1 cargo build --release || exit 1
cp ./target/release/libfuzzer ./.libfuzzer_test.elf cp ./target/release/libfuzzer ./.libfuzzer_test.elf
RUST_BACKTRACE=1 taskset -c 0 ./.libfuzzer_test.elf & RUST_BACKTRACE=full taskset -c 0 ./.libfuzzer_test.elf &
test "$!" -gt 0 && { test "$!" -gt 0 && {
usleep 250 usleep 250
RUST_BACKTRACE=1 taskset -c 1 ./.libfuzzer_test.elf -x a -x b -T5 in1 in2 & RUST_BACKTRACE=full taskset -c 1 ./.libfuzzer_test.elf -x a -x b -T5 in1 in2 &
} }