Allow setting stdout_file in non-fork launcher (#2127)

* fix(launcher.rs): hide file output behind appropriate feature flag

discovered while debugging #2111

* fix(launcher.rs): implement stdout/stderr piping for non-forking unix

* hide all accesses to stdout_file in cfg blocks

* Conditionally add stdout_file config in frida_gdiplus
This commit is contained in:
Stefan Zabka 2024-05-02 16:24:36 +02:00 committed by GitHub
parent 2302f1b35c
commit ec944a0ea4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 60 additions and 29 deletions

View File

@ -473,15 +473,22 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
} }
}; };
Launcher::builder() let builder = Launcher::builder()
.configuration(EventConfig::AlwaysUnique) .configuration(EventConfig::AlwaysUnique)
.shmem_provider(shmem_provider) .shmem_provider(shmem_provider)
.monitor(monitor) .monitor(monitor)
.run_client(&mut run_client) .run_client(&mut run_client)
.cores(&options.cores) .cores(&options.cores)
.broker_port(options.broker_port) .broker_port(options.broker_port)
.stdout_file(Some(&options.stdout)) .remote_broker_addr(options.remote_broker_addr);
.remote_broker_addr(options.remote_broker_addr)
.build() #[cfg(all(unix, feature = "std"))]
.launch() {
return builder.stdout_file(Some(&options.stdout)).build().launch();
}
#[cfg(not(all(unix, feature = "std")))]
{
return builder.build().launch();
}
} }

View File

@ -25,9 +25,11 @@ use core::{
use std::net::SocketAddr; use std::net::SocketAddr;
#[cfg(all(feature = "std", any(windows, not(feature = "fork"))))] #[cfg(all(feature = "std", any(windows, not(feature = "fork"))))]
use std::process::Stdio; use std::process::Stdio;
#[cfg(all(unix, feature = "std", feature = "fork"))] #[cfg(all(unix, feature = "std"))]
use std::{fs::File, os::unix::io::AsRawFd}; use std::{fs::File, os::unix::io::AsRawFd};
#[cfg(all(unix, feature = "std"))]
use libafl_bolts::os::dup2;
#[cfg(all(feature = "std", any(windows, not(feature = "fork"))))] #[cfg(all(feature = "std", any(windows, not(feature = "fork"))))]
use libafl_bolts::os::startable_self; use libafl_bolts::os::startable_self;
#[cfg(feature = "adaptive_serialization")] #[cfg(feature = "adaptive_serialization")]
@ -35,7 +37,7 @@ use libafl_bolts::tuples::{Reference, Referenceable};
#[cfg(all(unix, feature = "std", feature = "fork"))] #[cfg(all(unix, feature = "std", feature = "fork"))]
use libafl_bolts::{ use libafl_bolts::{
core_affinity::get_core_ids, core_affinity::get_core_ids,
os::{dup2, fork, ForkResult}, os::{fork, ForkResult},
}; };
use libafl_bolts::{ use libafl_bolts::{
core_affinity::{CoreId, Cores}, core_affinity::{CoreId, Cores},
@ -102,6 +104,7 @@ where
/// The list of cores to run on /// The list of cores to run on
cores: &'a Cores, cores: &'a Cores,
/// A file name to write all client output to /// A file name to write all client output to
#[cfg(all(unix, feature = "std"))]
#[builder(default = None)] #[builder(default = None)]
stdout_file: Option<&'a str>, stdout_file: Option<&'a str>,
/// The actual, opened, `stdout_file` - so that we keep it open until the end /// The actual, opened, `stdout_file` - so that we keep it open until the end
@ -110,6 +113,7 @@ where
opened_stdout_file: Option<File>, opened_stdout_file: Option<File>,
/// A file name to write all client stderr output to. If not specified, output is sent to /// A file name to write all client stderr output to. If not specified, output is sent to
/// `stdout_file`. /// `stdout_file`.
#[cfg(all(unix, feature = "std"))]
#[builder(default = None)] #[builder(default = None)]
stderr_file: Option<&'a str>, stderr_file: Option<&'a str>,
/// The actual, opened, `stdout_file` - so that we keep it open until the end /// The actual, opened, `stdout_file` - so that we keep it open until the end
@ -144,15 +148,21 @@ where
S: State, S: State,
{ {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("Launcher") let mut dbg_struct = f.debug_struct("Launcher");
dbg_struct
.field("configuration", &self.configuration) .field("configuration", &self.configuration)
.field("broker_port", &self.broker_port) .field("broker_port", &self.broker_port)
.field("core", &self.cores) .field("core", &self.cores)
.field("spawn_broker", &self.spawn_broker) .field("spawn_broker", &self.spawn_broker)
.field("remote_broker_addr", &self.remote_broker_addr) .field("remote_broker_addr", &self.remote_broker_addr);
.field("stdout_file", &self.stdout_file) #[cfg(all(unix, feature = "std"))]
.field("stderr_file", &self.stderr_file) {
.finish_non_exhaustive() dbg_struct
.field("stdout_file", &self.stdout_file)
.field("stderr_file", &self.stderr_file);
}
dbg_struct.finish_non_exhaustive()
} }
} }
@ -331,10 +341,6 @@ where
let mut handles = match is_client { let mut handles = match is_client {
Ok(core_conf) => { Ok(core_conf) => {
let core_id = core_conf.parse()?; let core_id = core_conf.parse()?;
// TODO: silence stdout and stderr for clients
// let debug_output = std::env::var(LIBAFL_DEBUG_OUTPUT).is_ok();
// the actual client. do the fuzzing // the actual client. do the fuzzing
let (state, mgr) = RestartingMgr::<EMH, MT, S, SP>::builder() let (state, mgr) = RestartingMgr::<EMH, MT, S, SP>::builder()
.shmem_provider(self.shmem_provider.clone()) .shmem_provider(self.shmem_provider.clone())
@ -354,11 +360,6 @@ where
// I am a broker // I am a broker
// before going to the broker loop, spawn n clients // before going to the broker loop, spawn n clients
#[cfg(windows)]
if self.stdout_file.is_some() {
log::info!("Child process file stdio is not supported on Windows yet. Dumping to stdout instead...");
}
let core_ids = core_affinity::get_core_ids().unwrap(); let core_ids = core_affinity::get_core_ids().unwrap();
let num_cores = core_ids.len(); let num_cores = core_ids.len();
let mut handles = vec![]; let mut handles = vec![];
@ -366,22 +367,45 @@ where
log::info!("spawning on cores: {:?}", self.cores); log::info!("spawning on cores: {:?}", self.cores);
let debug_output = std::env::var("LIBAFL_DEBUG_OUTPUT").is_ok(); let debug_output = std::env::var("LIBAFL_DEBUG_OUTPUT").is_ok();
#[cfg(all(feature = "std", unix))]
{
// Set own stdout and stderr as set by the user
if !debug_output {
let opened_stdout_file = self
.stdout_file
.map(|filename| File::create(filename).unwrap());
let opened_stderr_file = self
.stderr_file
.map(|filename| File::create(filename).unwrap());
if let Some(file) = opened_stdout_file {
dup2(file.as_raw_fd(), libc::STDOUT_FILENO)?;
if let Some(stderr) = opened_stderr_file {
dup2(stderr.as_raw_fd(), libc::STDERR_FILENO)?;
} else {
dup2(file.as_raw_fd(), libc::STDERR_FILENO)?;
}
}
}
}
//spawn clients //spawn clients
for (id, _) in core_ids.iter().enumerate().take(num_cores) { for (id, _) in core_ids.iter().enumerate().take(num_cores) {
if self.cores.ids.iter().any(|&x| x == id.into()) { if self.cores.ids.iter().any(|&x| x == id.into()) {
let stdio = if self.stdout_file.is_some() { // Forward own stdio to child processes, if requested by user
Stdio::inherit() let (mut stdout, mut stderr) = (Stdio::null(), Stdio::null());
} else { #[cfg(all(feature = "std", unix))]
Stdio::null() {
}; if self.stdout_file.is_some() || self.stderr_file.is_some() {
stdout = Stdio::inherit();
stderr = Stdio::inherit();
};
}
std::env::set_var(_AFL_LAUNCHER_CLIENT, id.to_string()); std::env::set_var(_AFL_LAUNCHER_CLIENT, id.to_string());
let mut child = startable_self()?; let mut child = startable_self()?;
let child = (if debug_output { let child = (if debug_output {
&mut child &mut child
} else { } else {
child.stdout(stdio) child.stdout(stdout);
child.stderr(stderr)
}) })
.spawn()?; .spawn()?;
handles.push(child); handles.push(child);