From ec944a0ea445d4d7a47df55fb6f3d8c419f2ed4b Mon Sep 17 00:00:00 2001 From: Stefan Zabka Date: Thu, 2 May 2024 16:24:36 +0200 Subject: [PATCH] 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 --- fuzzers/frida_gdiplus/src/fuzzer.rs | 17 +++++-- libafl/src/events/launcher.rs | 72 +++++++++++++++++++---------- 2 files changed, 60 insertions(+), 29 deletions(-) diff --git a/fuzzers/frida_gdiplus/src/fuzzer.rs b/fuzzers/frida_gdiplus/src/fuzzer.rs index 3a692f13e3..de2b3399bb 100644 --- a/fuzzers/frida_gdiplus/src/fuzzer.rs +++ b/fuzzers/frida_gdiplus/src/fuzzer.rs @@ -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(); + } } diff --git a/libafl/src/events/launcher.rs b/libafl/src/events/launcher.rs index d79ec55f2a..d7ad4317d4 100644 --- a/libafl/src/events/launcher.rs +++ b/libafl/src/events/launcher.rs @@ -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, /// 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("stdout_file", &self.stdout_file) - .field("stderr_file", &self.stderr_file) - .finish_non_exhaustive() + .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); + } + + 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::::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);