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)
.shmem_provider(shmem_provider)
.monitor(monitor)
.run_client(&mut run_client)
.cores(&options.cores)
.broker_port(options.broker_port)
.stdout_file(Some(&options.stdout))
.remote_broker_addr(options.remote_broker_addr)
.build()
.launch()
.remote_broker_addr(options.remote_broker_addr);
#[cfg(all(unix, feature = "std"))]
{
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;
#[cfg(all(feature = "std", any(windows, not(feature = "fork"))))]
use std::process::Stdio;
#[cfg(all(unix, feature = "std", feature = "fork"))]
#[cfg(all(unix, feature = "std"))]
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"))))]
use libafl_bolts::os::startable_self;
#[cfg(feature = "adaptive_serialization")]
@ -35,7 +37,7 @@ use libafl_bolts::tuples::{Reference, Referenceable};
#[cfg(all(unix, feature = "std", feature = "fork"))]
use libafl_bolts::{
core_affinity::get_core_ids,
os::{dup2, fork, ForkResult},
os::{fork, ForkResult},
};
use libafl_bolts::{
core_affinity::{CoreId, Cores},
@ -102,6 +104,7 @@ where
/// The list of cores to run on
cores: &'a Cores,
/// A file name to write all client output to
#[cfg(all(unix, feature = "std"))]
#[builder(default = None)]
stdout_file: Option<&'a str>,
/// The actual, opened, `stdout_file` - so that we keep it open until the end
@ -110,6 +113,7 @@ where
opened_stdout_file: Option<File>,
/// A file name to write all client stderr output to. If not specified, output is sent to
/// `stdout_file`.
#[cfg(all(unix, feature = "std"))]
#[builder(default = None)]
stderr_file: Option<&'a str>,
/// The actual, opened, `stdout_file` - so that we keep it open until the end
@ -144,15 +148,21 @@ where
S: State,
{
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("broker_port", &self.broker_port)
.field("core", &self.cores)
.field("spawn_broker", &self.spawn_broker)
.field("remote_broker_addr", &self.remote_broker_addr)
.field("remote_broker_addr", &self.remote_broker_addr);
#[cfg(all(unix, feature = "std"))]
{
dbg_struct
.field("stdout_file", &self.stdout_file)
.field("stderr_file", &self.stderr_file)
.finish_non_exhaustive()
.field("stderr_file", &self.stderr_file);
}
dbg_struct.finish_non_exhaustive()
}
}
@ -331,10 +341,6 @@ where
let mut handles = match is_client {
Ok(core_conf) => {
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
let (state, mgr) = RestartingMgr::<EMH, MT, S, SP>::builder()
.shmem_provider(self.shmem_provider.clone())
@ -354,11 +360,6 @@ where
// I am a broker
// 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 num_cores = core_ids.len();
let mut handles = vec![];
@ -366,22 +367,45 @@ where
log::info!("spawning on cores: {:?}", self.cores);
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
for (id, _) in core_ids.iter().enumerate().take(num_cores) {
if self.cores.ids.iter().any(|&x| x == id.into()) {
let stdio = if self.stdout_file.is_some() {
Stdio::inherit()
} else {
Stdio::null()
// Forward own stdio to child processes, if requested by user
let (mut stdout, mut stderr) = (Stdio::null(), Stdio::null());
#[cfg(all(feature = "std", unix))]
{
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());
let mut child = startable_self()?;
let child = (if debug_output {
&mut child
} else {
child.stdout(stdio)
child.stdout(stdout);
child.stderr(stderr)
})
.spawn()?;
handles.push(child);