Debug output for forkserver (#413)

* usability fixes for forkserver

* don't call target_bytes twice in TimeoutForkserverExecutor

* don't call target_bytes twice in ForkserverExecutor
This commit is contained in:
Andrea Fioraldi 2021-12-10 14:52:23 +01:00 committed by GitHub
parent 4aa6550bf2
commit b4c2551544
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 43 additions and 30 deletions

View File

@ -195,15 +195,22 @@ impl Forkserver {
out_filefd: RawFd, out_filefd: RawFd,
use_stdin: bool, use_stdin: bool,
memlimit: u64, memlimit: u64,
debug_output: bool,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
let mut st_pipe = Pipe::new().unwrap(); let mut st_pipe = Pipe::new().unwrap();
let mut ctl_pipe = Pipe::new().unwrap(); let mut ctl_pipe = Pipe::new().unwrap();
let (stdout, stderr) = if debug_output {
(Stdio::inherit(), Stdio::inherit())
} else {
(Stdio::null(), Stdio::null())
};
match Command::new(target) match Command::new(target)
.args(args) .args(args)
.stdin(Stdio::null()) .stdin(Stdio::null())
.stdout(Stdio::null()) .stdout(stdout)
.stderr(Stdio::null()) .stderr(stderr)
.env("LD_BIND_LAZY", "1") .env("LD_BIND_LAZY", "1")
.setlimit(memlimit) .setlimit(memlimit)
.setsid() .setsid()
@ -364,12 +371,13 @@ where
match &mut self.executor.map_mut() { match &mut self.executor.map_mut() {
Some(map) => { Some(map) => {
let size = input.target_bytes().as_slice().len(); let target_bytes = input.target_bytes();
let size = target_bytes.as_slice().len();
let size_in_bytes = size.to_ne_bytes(); let size_in_bytes = size.to_ne_bytes();
// The first four bytes tells the size of the shmem. // The first four bytes tells the size of the shmem.
map.map_mut()[..4].copy_from_slice(&size_in_bytes[..4]); map.map_mut()[..4].copy_from_slice(&size_in_bytes[..4]);
map.map_mut()[SHMEM_FUZZ_HDR_SIZE..(SHMEM_FUZZ_HDR_SIZE + size)] map.map_mut()[SHMEM_FUZZ_HDR_SIZE..(SHMEM_FUZZ_HDR_SIZE + size)]
.copy_from_slice(input.target_bytes().as_slice()); .copy_from_slice(target_bytes.as_slice());
} }
None => { None => {
self.executor self.executor
@ -466,6 +474,16 @@ where
arguments: &[String], arguments: &[String],
use_shmem_testcase: bool, use_shmem_testcase: bool,
observers: OT, observers: OT,
) -> Result<Self, Error> {
Self::with_debug(target, arguments, use_shmem_testcase, observers, false)
}
pub fn with_debug(
target: String,
arguments: &[String],
use_shmem_testcase: bool,
observers: OT,
debug_output: bool,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
let mut args = Vec::<String>::new(); let mut args = Vec::<String>::new();
let mut use_stdin = true; let mut use_stdin = true;
@ -500,6 +518,7 @@ where
out_file.as_raw_fd(), out_file.as_raw_fd(),
use_stdin, use_stdin,
0, 0,
debug_output,
)?; )?;
let (rlen, status) = forkserver.read_st()?; // Initial handshake, read 4-bytes hello message from the forkserver. let (rlen, status) = forkserver.read_st()?; // Initial handshake, read 4-bytes hello message from the forkserver.
@ -573,12 +592,13 @@ where
// Write to testcase // Write to testcase
match &mut self.map { match &mut self.map {
Some(map) => { Some(map) => {
let size = input.target_bytes().as_slice().len(); let target_bytes = input.target_bytes();
let size = target_bytes.as_slice().len();
let size_in_bytes = size.to_ne_bytes(); let size_in_bytes = size.to_ne_bytes();
// The first four bytes tells the size of the shmem. // The first four bytes tells the size of the shmem.
map.map_mut()[..4].copy_from_slice(&size_in_bytes[..4]); map.map_mut()[..4].copy_from_slice(&size_in_bytes[..4]);
map.map_mut()[SHMEM_FUZZ_HDR_SIZE..(SHMEM_FUZZ_HDR_SIZE + size)] map.map_mut()[SHMEM_FUZZ_HDR_SIZE..(SHMEM_FUZZ_HDR_SIZE + size)]
.copy_from_slice(input.target_bytes().as_slice()); .copy_from_slice(target_bytes.as_slice());
} }
None => { None => {
self.out_file.write_buf(input.target_bytes().as_slice()); self.out_file.write_buf(input.target_bytes().as_slice());

View File

@ -31,10 +31,10 @@ use libafl::{
use crate::{CORPUS_CACHE_SIZE, DEFAULT_TIMEOUT_SECS}; use crate::{CORPUS_CACHE_SIZE, DEFAULT_TIMEOUT_SECS};
pub const MAP_SIZE: usize = 65536; pub const DEFAULT_MAP_SIZE: usize = 65536;
#[derive(TypedBuilder)] #[derive(TypedBuilder)]
pub struct ForkserverBytesCoverageSugar<'a> { pub struct ForkserverBytesCoverageSugar<'a, const MAP_SIZE: usize> {
/// Laucher configuration (default is random) /// Laucher configuration (default is random)
#[builder(default = None, setter(strip_option))] #[builder(default = None, setter(strip_option))]
configuration: Option<String>, configuration: Option<String>,
@ -66,13 +66,13 @@ pub struct ForkserverBytesCoverageSugar<'a> {
#[builder(default = false)] #[builder(default = false)]
/// Use shared mem testcase delivery /// Use shared mem testcase delivery
shmem_testcase: bool, shmem_testcase: bool,
// Coverage map size #[builder(default = false)]
//#[builder(default = MAP_SIZE)] /// Print target program output
//map_size: usize, debug_output: bool,
} }
#[allow(clippy::similar_names)] #[allow(clippy::similar_names)]
impl<'a> ForkserverBytesCoverageSugar<'a> { impl<'a, const MAP_SIZE: usize> ForkserverBytesCoverageSugar<'a, MAP_SIZE> {
#[allow(clippy::too_many_lines, clippy::similar_names)] #[allow(clippy::too_many_lines, clippy::similar_names)]
pub fn run(&mut self) { pub fn run(&mut self) {
let conf = match self.configuration.as_ref() { let conf = match self.configuration.as_ref() {
@ -166,11 +166,12 @@ impl<'a> ForkserverBytesCoverageSugar<'a> {
// Create the executor for an in-process function with one observer for edge coverage and one for the execution time // Create the executor for an in-process function with one observer for edge coverage and one for the execution time
let mut executor = TimeoutForkserverExecutor::new( let mut executor = TimeoutForkserverExecutor::new(
ForkserverExecutor::new( ForkserverExecutor::with_debug(
self.program.clone(), self.program.clone(),
self.arguments, self.arguments,
self.shmem_testcase, self.shmem_testcase,
tuple_list!(edges_observer, time_observer), tuple_list!(edges_observer, time_observer),
self.debug_output,
) )
.expect("Failed to create the executor."), .expect("Failed to create the executor."),
timeout, timeout,
@ -248,16 +249,14 @@ impl<'a> ForkserverBytesCoverageSugar<'a> {
} }
} }
/*
#[cfg(feature = "python")] #[cfg(feature = "python")]
pub mod pybind { pub mod pybind {
use crate::inmemory; use crate::forkserver;
use pyo3::prelude::*; use pyo3::prelude::*;
use pyo3::types::PyBytes;
use std::path::PathBuf; use std::path::PathBuf;
#[pyclass(unsendable)] #[pyclass(unsendable)]
struct InMemoryBytesCoverageSugar { struct ForkserverBytesCoverageSugar {
input_dirs: Vec<PathBuf>, input_dirs: Vec<PathBuf>,
output_dir: PathBuf, output_dir: PathBuf,
broker_port: u16, broker_port: u16,
@ -265,7 +264,7 @@ pub mod pybind {
} }
#[pymethods] #[pymethods]
impl InMemoryBytesCoverageSugar { impl ForkserverBytesCoverageSugar {
#[new] #[new]
fn new( fn new(
input_dirs: Vec<PathBuf>, input_dirs: Vec<PathBuf>,
@ -282,27 +281,21 @@ pub mod pybind {
} }
#[allow(clippy::needless_pass_by_value)] #[allow(clippy::needless_pass_by_value)]
pub fn run(&self, harness: PyObject) { pub fn run(&self, program: String, arguments: Vec<String>) {
inmemory::InMemoryBytesCoverageSugar::builder() forkserver::ForkserverBytesCoverageSugar::<{ forkserver::DEFAULT_MAP_SIZE }>::builder()
.input_dirs(&self.input_dirs) .input_dirs(&self.input_dirs)
.output_dir(self.output_dir.clone()) .output_dir(self.output_dir.clone())
.broker_port(self.broker_port) .broker_port(self.broker_port)
.cores(&self.cores) .cores(&self.cores)
.harness(|buf| { .program(program)
Python::with_gil(|py| -> PyResult<()> { .arguments(&arguments)
let args = (PyBytes::new(py, buf),); // TODO avoid copy
harness.call1(py, args)?;
Ok(())
})
.unwrap();
})
.build() .build()
.run(); .run();
} }
} }
pub fn register(_py: Python, m: &PyModule) -> PyResult<()> { pub fn register(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_class::<InMemoryBytesCoverageSugar>()?; m.add_class::<ForkserverBytesCoverageSugar>()?;
Ok(()) Ok(())
} }
}*/ }