Autodict forkserver (#525)
* Builder for ForkserverExecutor * add * clippy warnings * comment * stash * tmp * change * revert * use_shmem_feature field * change the harness back * wip * wip * revert * works * clippy * Makefile fix * doc * clippy * rename to program * rename, fix, envs * lifetime * arg_input_file * stash * read autodict from forkserver * works * clippy & fmt * fmt * fix * fix * fmt * better harness * arg_input_file_std * rename * fix
This commit is contained in:
parent
9482433e54
commit
9d38fff662
@ -4,7 +4,7 @@ use libafl::{
|
|||||||
current_nanos,
|
current_nanos,
|
||||||
rands::StdRand,
|
rands::StdRand,
|
||||||
shmem::{ShMem, ShMemProvider, StdShMemProvider},
|
shmem::{ShMem, ShMemProvider, StdShMemProvider},
|
||||||
tuples::tuple_list,
|
tuples::{tuple_list, Merge},
|
||||||
AsMutSlice,
|
AsMutSlice,
|
||||||
},
|
},
|
||||||
corpus::{
|
corpus::{
|
||||||
@ -18,10 +18,10 @@ use libafl::{
|
|||||||
fuzzer::{Fuzzer, StdFuzzer},
|
fuzzer::{Fuzzer, StdFuzzer},
|
||||||
inputs::BytesInput,
|
inputs::BytesInput,
|
||||||
monitors::SimpleMonitor,
|
monitors::SimpleMonitor,
|
||||||
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
|
mutators::{scheduled::havoc_mutations, tokens_mutations, StdScheduledMutator, Tokens},
|
||||||
observers::{ConstMapObserver, HitcountsMapObserver, TimeObserver},
|
observers::{ConstMapObserver, HitcountsMapObserver, TimeObserver},
|
||||||
stages::mutational::StdMutationalStage,
|
stages::mutational::StdMutationalStage,
|
||||||
state::{HasCorpus, StdState},
|
state::{HasCorpus, HasMetadata, StdState},
|
||||||
};
|
};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
@ -145,11 +145,13 @@ pub fn main() {
|
|||||||
None => [].to_vec(),
|
None => [].to_vec(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut tokens = Tokens::new();
|
||||||
let forkserver = ForkserverExecutor::builder()
|
let forkserver = ForkserverExecutor::builder()
|
||||||
.program(res.value_of("executable").unwrap().to_string())
|
.program(res.value_of("executable").unwrap().to_string())
|
||||||
.args(&args)
|
.args(&args)
|
||||||
.debug_child(debug_child)
|
.debug_child(debug_child)
|
||||||
.shmem_provider(&mut shmem_provider)
|
.shmem_provider(&mut shmem_provider)
|
||||||
|
.autotokens(&mut tokens)
|
||||||
.build(tuple_list!(time_observer, edges_observer))
|
.build(tuple_list!(time_observer, edges_observer))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -178,8 +180,10 @@ pub fn main() {
|
|||||||
println!("We imported {} inputs from disk.", state.corpus().count());
|
println!("We imported {} inputs from disk.", state.corpus().count());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state.add_metadata(tokens);
|
||||||
|
|
||||||
// Setup a mutational stage with a basic bytes mutator
|
// Setup a mutational stage with a basic bytes mutator
|
||||||
let mutator = StdScheduledMutator::new(havoc_mutations());
|
let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations()));
|
||||||
let mut stages = tuple_list!(StdMutationalStage::new(mutator));
|
let mut stages = tuple_list!(StdMutationalStage::new(mutator));
|
||||||
|
|
||||||
fuzzer
|
fuzzer
|
||||||
|
@ -1,10 +1,17 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
// The following line is needed for shared memeory testcase fuzzing
|
// The following line is needed for shared memeory testcase fuzzing
|
||||||
__AFL_FUZZ_INIT();
|
__AFL_FUZZ_INIT();
|
||||||
|
|
||||||
|
void vuln(char *buf) {
|
||||||
|
if(strcmp(buf, "vuln") == 0) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char **argv){
|
int main(int argc, char **argv){
|
||||||
|
|
||||||
FILE* file = stdin;
|
FILE* file = stdin;
|
||||||
@ -24,7 +31,6 @@ int main(int argc, char **argv){
|
|||||||
|
|
||||||
|
|
||||||
printf("input: %s\n", buf);
|
printf("input: %s\n", buf);
|
||||||
|
|
||||||
if(buf[0] == 'b'){
|
if(buf[0] == 'b'){
|
||||||
if(buf[1] == 'a'){
|
if(buf[1] == 'a'){
|
||||||
if(buf[2] == 'd'){
|
if(buf[2] == 'd'){
|
||||||
@ -32,6 +38,7 @@ int main(int argc, char **argv){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
vuln(buf);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -15,13 +15,14 @@ use std::{
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
bolts::{
|
bolts::{
|
||||||
fs::OutFile,
|
fs::{OutFile, OUTFILE_STD},
|
||||||
os::{dup2, pipes::Pipe},
|
os::{dup2, pipes::Pipe},
|
||||||
shmem::{ShMem, ShMemProvider, StdShMemProvider},
|
shmem::{ShMem, ShMemProvider, StdShMemProvider},
|
||||||
AsMutSlice, AsSlice,
|
AsMutSlice, AsSlice,
|
||||||
},
|
},
|
||||||
executors::{Executor, ExitKind, HasObservers},
|
executors::{Executor, ExitKind, HasObservers},
|
||||||
inputs::{HasTargetBytes, Input},
|
inputs::{HasTargetBytes, Input},
|
||||||
|
mutators::Tokens,
|
||||||
observers::{get_asan_runtime_flags_with_log_path, ASANBacktraceObserver, ObserversTuple},
|
observers::{get_asan_runtime_flags_with_log_path, ASANBacktraceObserver, ObserversTuple},
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
@ -40,6 +41,8 @@ const FORKSRV_FD: i32 = 198;
|
|||||||
const FS_OPT_ENABLED: i32 = 0x80000001_u32 as i32;
|
const FS_OPT_ENABLED: i32 = 0x80000001_u32 as i32;
|
||||||
#[allow(clippy::cast_possible_wrap)]
|
#[allow(clippy::cast_possible_wrap)]
|
||||||
const FS_OPT_SHDMEM_FUZZ: i32 = 0x01000000_u32 as i32;
|
const FS_OPT_SHDMEM_FUZZ: i32 = 0x01000000_u32 as i32;
|
||||||
|
#[allow(clippy::cast_possible_wrap)]
|
||||||
|
const FS_OPT_AUTODICT: i32 = 0x10000000_u32 as i32;
|
||||||
const SHMEM_FUZZ_HDR_SIZE: usize = 4;
|
const SHMEM_FUZZ_HDR_SIZE: usize = 4;
|
||||||
const MAX_FILE: usize = 1024 * 1024;
|
const MAX_FILE: usize = 1024 * 1024;
|
||||||
|
|
||||||
@ -268,6 +271,14 @@ impl Forkserver {
|
|||||||
Ok((rlen, val))
|
Ok((rlen, val))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Read bytes of any length from the st pipe
|
||||||
|
pub fn read_st_size(&mut self, size: usize) -> Result<(usize, Vec<u8>), Error> {
|
||||||
|
let mut buf = vec![0; size];
|
||||||
|
|
||||||
|
let rlen = self.st_pipe.read(&mut buf)?;
|
||||||
|
Ok((rlen, buf))
|
||||||
|
}
|
||||||
|
|
||||||
/// Write to the ctl pipe
|
/// Write to the ctl pipe
|
||||||
pub fn write_ctl(&mut self, val: i32) -> Result<usize, Error> {
|
pub fn write_ctl(&mut self, val: i32) -> Result<usize, Error> {
|
||||||
let slen = self.ctl_pipe.write(&val.to_ne_bytes())?;
|
let slen = self.ctl_pipe.write(&val.to_ne_bytes())?;
|
||||||
@ -536,13 +547,15 @@ pub struct ForkserverExecutorBuilder<'a, SP> {
|
|||||||
program: Option<OsString>,
|
program: Option<OsString>,
|
||||||
arguments: Vec<OsString>,
|
arguments: Vec<OsString>,
|
||||||
envs: Vec<(OsString, OsString)>,
|
envs: Vec<(OsString, OsString)>,
|
||||||
out_filename: Option<OsString>,
|
|
||||||
debug_child: bool,
|
debug_child: bool,
|
||||||
|
autotokens: Option<&'a mut Tokens>,
|
||||||
|
out_filename: Option<OsString>,
|
||||||
shmem_provider: Option<&'a mut SP>,
|
shmem_provider: Option<&'a mut SP>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, SP> ForkserverExecutorBuilder<'a, SP> {
|
impl<'a, SP> ForkserverExecutorBuilder<'a, SP> {
|
||||||
/// Builds `ForkserverExecutor`.
|
/// Builds `ForkserverExecutor`.
|
||||||
|
#[allow(clippy::pedantic)]
|
||||||
pub fn build<I, OT, S>(
|
pub fn build<I, OT, S>(
|
||||||
&mut self,
|
&mut self,
|
||||||
observers: OT,
|
observers: OT,
|
||||||
@ -560,12 +573,24 @@ impl<'a, SP> ForkserverExecutorBuilder<'a, SP> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for item in &self.arguments {
|
for item in &self.arguments {
|
||||||
|
// need special handling for @@
|
||||||
if item == "@@" && use_stdin {
|
if item == "@@" && use_stdin {
|
||||||
use_stdin = false;
|
use_stdin = false;
|
||||||
args.push(out_filename.clone());
|
args.push(out_filename.clone());
|
||||||
|
} else {
|
||||||
|
// if the filename set by arg_input_file matches the item, then set use_stdin to false
|
||||||
|
if let Some(name) = &self.out_filename {
|
||||||
|
if name == item && use_stdin {
|
||||||
|
use_stdin = false;
|
||||||
|
args.push(out_filename.clone());
|
||||||
} else {
|
} else {
|
||||||
args.push(item.clone());
|
args.push(item.clone());
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// default case, just push item into the arguments.
|
||||||
|
args.push(item.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let out_file = OutFile::create(&out_filename)?;
|
let out_file = OutFile::create(&out_filename)?;
|
||||||
@ -614,9 +639,17 @@ impl<'a, SP> ForkserverExecutorBuilder<'a, SP> {
|
|||||||
println!("All right - fork server is up.");
|
println!("All right - fork server is up.");
|
||||||
// If forkserver is responding, we then check if there's any option enabled.
|
// If forkserver is responding, we then check if there's any option enabled.
|
||||||
if status & FS_OPT_ENABLED == FS_OPT_ENABLED {
|
if status & FS_OPT_ENABLED == FS_OPT_ENABLED {
|
||||||
if (status & FS_OPT_SHDMEM_FUZZ == FS_OPT_SHDMEM_FUZZ) & map.is_some() {
|
let mut send_status = FS_OPT_ENABLED;
|
||||||
|
|
||||||
|
if (status & FS_OPT_SHDMEM_FUZZ == FS_OPT_SHDMEM_FUZZ) && map.is_some() {
|
||||||
println!("Using SHARED MEMORY FUZZING feature.");
|
println!("Using SHARED MEMORY FUZZING feature.");
|
||||||
let send_status = FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ;
|
send_status = send_status | FS_OPT_SHDMEM_FUZZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status & FS_OPT_AUTODICT == FS_OPT_AUTODICT) && self.autotokens.is_some() {
|
||||||
|
println!("Using AUTODICT feature");
|
||||||
|
send_status = send_status | FS_OPT_AUTODICT;
|
||||||
|
}
|
||||||
|
|
||||||
let send_len = forkserver.write_ctl(send_status)?;
|
let send_len = forkserver.write_ctl(send_status)?;
|
||||||
if send_len != 4 {
|
if send_len != 4 {
|
||||||
@ -624,14 +657,47 @@ impl<'a, SP> ForkserverExecutorBuilder<'a, SP> {
|
|||||||
"Writing to forkserver failed.".to_string(),
|
"Writing to forkserver failed.".to_string(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (send_status & FS_OPT_AUTODICT) == FS_OPT_AUTODICT {
|
||||||
|
let (read_len, dict_size) = forkserver.read_st()?;
|
||||||
|
if read_len != 4 {
|
||||||
|
return Err(Error::Forkserver(
|
||||||
|
"Reading from forkserver failed.".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if dict_size < 2 || dict_size > 0xffffff {
|
||||||
|
return Err(Error::Forkserver(
|
||||||
|
"Dictionary has an illegal size".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Autodict size {:x}", dict_size);
|
||||||
|
|
||||||
|
let (rlen, buf) = forkserver.read_st_size(dict_size as usize)?;
|
||||||
|
|
||||||
|
if rlen != dict_size as usize {
|
||||||
|
return Err(Error::Forkserver(
|
||||||
|
"Failed to load autodictionary".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(t) = &mut self.autotokens {
|
||||||
|
t.parse_autodict(&buf, dict_size as usize);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
println!("Forkserver Options are not available.");
|
println!("Forkserver Options are not available.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"ForkserverExecutor: program: {:?}, arguments: {:?}, use_stdin: {:?}",
|
||||||
|
target, args, use_stdin
|
||||||
|
);
|
||||||
|
|
||||||
Ok(ForkserverExecutor {
|
Ok(ForkserverExecutor {
|
||||||
target,
|
target,
|
||||||
args,
|
args: self.arguments.clone(),
|
||||||
out_file,
|
out_file,
|
||||||
forkserver,
|
forkserver,
|
||||||
observers,
|
observers,
|
||||||
@ -654,19 +720,20 @@ impl<'a> ForkserverExecutorBuilder<'a, StdShMemProvider> {
|
|||||||
program: None,
|
program: None,
|
||||||
arguments: vec![],
|
arguments: vec![],
|
||||||
envs: vec![],
|
envs: vec![],
|
||||||
out_filename: None,
|
|
||||||
debug_child: false,
|
debug_child: false,
|
||||||
|
autotokens: None,
|
||||||
|
out_filename: None,
|
||||||
shmem_provider: None,
|
shmem_provider: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The harness
|
/// The harness
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn program<O>(mut self, target: O) -> Self
|
pub fn program<O>(mut self, program: O) -> Self
|
||||||
where
|
where
|
||||||
O: AsRef<OsStr>,
|
O: AsRef<OsStr>,
|
||||||
{
|
{
|
||||||
self.program = Some(target.as_ref().to_owned());
|
self.program = Some(program.as_ref().to_owned());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -731,6 +798,13 @@ impl<'a> ForkserverExecutorBuilder<'a, StdShMemProvider> {
|
|||||||
moved
|
moved
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
/// Place the input at this position and set the default filename for the input.
|
||||||
|
pub fn arg_input_file_std(self) -> Self {
|
||||||
|
let moved = self.arg_input_file(OUTFILE_STD);
|
||||||
|
moved
|
||||||
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
/// If `debug_child` is set, the child will print to `stdout`/`stderr`.
|
/// If `debug_child` is set, the child will print to `stdout`/`stderr`.
|
||||||
pub fn debug_child(mut self, debug_child: bool) -> Self {
|
pub fn debug_child(mut self, debug_child: bool) -> Self {
|
||||||
@ -738,6 +812,13 @@ impl<'a> ForkserverExecutorBuilder<'a, StdShMemProvider> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Use autodict?
|
||||||
|
#[must_use]
|
||||||
|
pub fn autotokens(mut self, tokens: &'a mut Tokens) -> Self {
|
||||||
|
self.autotokens = Some(tokens);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Shmem provider for forkserver's shared memory testcase feature.
|
/// Shmem provider for forkserver's shared memory testcase feature.
|
||||||
pub fn shmem_provider<SP: ShMemProvider>(
|
pub fn shmem_provider<SP: ShMemProvider>(
|
||||||
self,
|
self,
|
||||||
@ -747,8 +828,9 @@ impl<'a> ForkserverExecutorBuilder<'a, StdShMemProvider> {
|
|||||||
program: self.program,
|
program: self.program,
|
||||||
arguments: self.arguments,
|
arguments: self.arguments,
|
||||||
envs: self.envs,
|
envs: self.envs,
|
||||||
out_filename: self.out_filename,
|
|
||||||
debug_child: self.debug_child,
|
debug_child: self.debug_child,
|
||||||
|
autotokens: self.autotokens,
|
||||||
|
out_filename: self.out_filename,
|
||||||
shmem_provider: Some(shmem_provider),
|
shmem_provider: Some(shmem_provider),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,6 +76,30 @@ impl Tokens {
|
|||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse autodict section
|
||||||
|
pub fn parse_autodict(&mut self, slice: &[u8], size: usize) {
|
||||||
|
let mut head = 0;
|
||||||
|
loop {
|
||||||
|
if head >= size {
|
||||||
|
// Sanity Check
|
||||||
|
assert!(head == size);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let size = slice[head] as usize;
|
||||||
|
head += 1;
|
||||||
|
if size > 0 {
|
||||||
|
self.add_token(&slice[head..head + size].to_vec());
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
println!(
|
||||||
|
"Token size: {} content: {:x?}",
|
||||||
|
size,
|
||||||
|
&slice[head..head + size].to_vec()
|
||||||
|
);
|
||||||
|
head += size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a token section from a start and an end pointer
|
/// Create a token section from a start and an end pointer
|
||||||
/// Reads from an autotokens section, returning the count of new entries read
|
/// Reads from an autotokens section, returning the count of new entries read
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@ -95,28 +119,8 @@ impl Tokens {
|
|||||||
// println!("size: {}", section_size);
|
// println!("size: {}", section_size);
|
||||||
let slice = from_raw_parts(token_start, section_size);
|
let slice = from_raw_parts(token_start, section_size);
|
||||||
|
|
||||||
let mut head = 0;
|
|
||||||
|
|
||||||
// Now we know the beginning and the end of the token section.. let's parse them into tokens
|
// Now we know the beginning and the end of the token section.. let's parse them into tokens
|
||||||
loop {
|
ret.parse_autodict(slice, section_size);
|
||||||
if head >= section_size {
|
|
||||||
// Sanity Check
|
|
||||||
assert!(head == section_size);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
let size = slice[head] as usize;
|
|
||||||
head += 1;
|
|
||||||
if size > 0 {
|
|
||||||
ret.add_token(&slice[head..head + size].to_vec());
|
|
||||||
/* #[cfg(feature = "std")]
|
|
||||||
println!(
|
|
||||||
"Token size: {} content: {:x?}",
|
|
||||||
size,
|
|
||||||
&slice[head..head + size].to_vec()
|
|
||||||
); */
|
|
||||||
head += size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(ret)
|
Ok(ret)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user