Add mute_inprocess_target fn, SimpleFdLogger::set_logger, and more (#1754)

* Add mute_inprocess_target fn, SimpleFdLogger::set_logger, set_error_print_panic_hook

* Trying to fix #1753

* typo

* More fix

* Fix test?

* more testcase fixes
This commit is contained in:
Dominik Maier 2024-01-01 23:14:59 +01:00 committed by GitHub
parent 38e16fbade
commit a07563def0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 235 additions and 104 deletions

View File

@ -34,11 +34,11 @@ windows_alias = "unsupported"
script_runner = "@shell"
script='''
timeout 30s ${CARGO_TARGET_DIR}/${PROFILE_DIR}/${FUZZER_NAME} >fuzz_stdout.log || true
if [ -z "$(grep "objectives: 1" fuzz_stdout.log)" ]; then
if grep -qa "objectives: 1" fuzz_stdout.log; then
echo "Fuzzer is working"
else
echo "Fuzzer does not generate any testcases or any crashes"
exit 1
else
echo "Fuzzer is working"
fi
'''
dependencies = [ "fuzzer" ]

View File

@ -90,11 +90,11 @@ script='''
rm -rf libafl_unix_shmem_server || true
LD_PRELOAD=$CARGO_TARGET_DIR/${PROFILE_DIR}/libfrida_executable_fuzzer.so ./libpng-harness -i corpus -o out -H ./libpng-harness > fuzz_stdout.log &
sleep 10s && pkill libpng-harness
if [ -z "$(grep "corpus: 30" fuzz_stdout.log)" ]; then
if grep -qa "corpus: 30" fuzz_stdout.log; then
echo "Fuzzer is working"
else
echo "Fuzzer does not generate any testcases or any crashes"
exit 1
else
echo "Fuzzer is working"
fi
'''
dependencies = [ "fuzzer", "harness" ]

View File

@ -111,11 +111,11 @@ script_runner = "@shell"
script='''
rm -rf libafl_unix_shmem_server || true
timeout 30s ./${FUZZER_NAME} -F LLVMFuzzerTestOneInput -H ./libpng-harness.so -l ./libpng-harness.so >fuzz_stdout.log 2>/dev/null || true
if [ -z "$(grep "corpus: 30" fuzz_stdout.log)" ]; then
if grep -qa "corpus: 30" fuzz_stdout.log; then
echo "Fuzzer is working"
else
echo "Fuzzer does not generate any testcases or any crashes"
exit 1
else
echo "Fuzzer is working"
fi
'''
dependencies = [ "fuzzer", "harness" ]

View File

@ -83,11 +83,11 @@ mkdir in || true
echo a > in/a
# Allow sigterm as exit code
timeout 31s ./${FUZZER_NAME} -o out -i in >fuzz_stdout.log || true
if [ -z "$(grep "objectives: 1" fuzz_stdout.log)" ]; then
if grep -qa "objectives: 1" fuzz_stdout.log; then
echo "Fuzzer is working"
else
echo "Fuzzer does not generate any testcases or any crashes"
exit 1
else
echo "Fuzzer is working"
fi
rm -rf out || true
rm -rf in || true

View File

@ -381,12 +381,12 @@ fn fuzz(
println!("We imported {} inputs from disk.", state.corpus().count());
}
// Remove target ouput (logs still survive)
// Remove target output (logs still survive)
#[cfg(unix)]
{
let null_fd = file_null.as_raw_fd();
dup2(null_fd, io::stdout().as_raw_fd())?;
if !std::env::var("LIBAFL_FUZZBENCH_DEBUG").is_ok() {
if std::env::var("LIBAFL_FUZZBENCH_DEBUG").is_err() {
dup2(null_fd, io::stderr().as_raw_fd())?;
}
}

View File

@ -369,7 +369,7 @@ fn fuzz(
// The order of the stages matter!
let mut stages = tuple_list!(calibration, tracing, i2s, power);
// Remove target ouput (logs still survive)
// Remove target output (logs still survive)
#[cfg(unix)]
{
let null_fd = file_null.as_raw_fd();

View File

@ -387,7 +387,7 @@ fn fuzz(
// The order of the stages matter!
let mut stages = tuple_list!(calibration, tracing, i2s, power);
// Remove target ouput (logs still survive)
// Remove target output (logs still survive)
#[cfg(unix)]
{
let null_fd = file_null.as_raw_fd();

View File

@ -85,11 +85,11 @@ echo a > in/a
# Allow sigterm as exit code
timeout 31s ./${FUZZER_NAME} -o out -i in >fuzz_stdout.log || true
cat fuzz_stdout.log
if [ -z "$(grep "objectives: 1" fuzz_stdout.log)" ]; then
if grep -qa "objectives: 1" fuzz_stdout.log; then
echo "Fuzzer is working"
else
echo "Fuzzer does not generate any testcases or any crashes"
exit 1
else
echo "Fuzzer is working"
fi
rm -rf out || true
rm -rf in || true

View File

@ -448,7 +448,7 @@ fn fuzz_binary(
println!("We imported {} inputs from disk.", state.corpus().count());
}
// Remove target ouput (logs still survive)
// Remove target output (logs still survive)
#[cfg(unix)]
{
let null_fd = file_null.as_raw_fd();
@ -675,7 +675,7 @@ fn fuzz_text(
println!("We imported {} inputs from disk.", state.corpus().count());
}
// Remove target ouput (logs still survive)
// Remove target output (logs still survive)
#[cfg(unix)]
{
let null_fd = file_null.as_raw_fd();

View File

@ -102,11 +102,11 @@ rm -rf libafl_unix_shmem_server || true
(timeout 31s ./${FUZZER_NAME} >fuzz_stdout.log 2>/dev/null || true) &
sleep 0.2
timeout 30s ./${FUZZER_NAME} >/dev/null 2>/dev/null || true
if [ -z "$(grep "corpus: 30" fuzz_stdout.log)" ]; then
if grep -qa "corpus: 30" fuzz_stdout.log; then
echo "Fuzzer is working"
else
echo "Fuzzer does not generate any testcases or any crashes"
exit 1
else
echo "Fuzzer is working"
fi
'''
dependencies = [ "fuzzer" ]

View File

@ -164,11 +164,11 @@ rm -rf libafl_unix_shmem_server || true
(timeout 31s ./${FUZZER_NAME} >fuzz_stdout.log 2>/dev/null || true) &
sleep 0.2
timeout 30s ./${FUZZER_NAME} >/dev/null 2>/dev/null || true
if [ -z "$(grep "corpus: 30" fuzz_stdout.log)" ]; then
if grep -qa "corpus: 30" fuzz_stdout.log; then
echo "Fuzzer is working"
else
echo "Fuzzer does not generate any testcases or any crashes"
exit 1
else
echo "Fuzzer is working"
fi
'''
dependencies = [ "fuzzer" ]

View File

@ -99,11 +99,11 @@ script_runner = "@shell"
script='''
rm -rf libafl_unix_shmem_server || true
timeout 31s ./${FUZZER_NAME} --cores 0 --input ./corpus >fuzz_stdout.log 2>/dev/null || true
if [ -z "$(grep "corpus: 30" fuzz_stdout.log)" ]; then
if grep -qa "corpus: 30" fuzz_stdout.log; then
echo "Fuzzer is working"
else
echo "Fuzzer does not generate any testcases or any crashes"
exit 1
else
echo "Fuzzer is working"
fi
'''
dependencies = [ "fuzzer" ]

View File

@ -99,11 +99,11 @@ script_runner = "@shell"
script='''
rm -rf libafl_unix_shmem_server || true
timeout 31s ./${FUZZER_NAME} --cores 0 --input ./corpus 2>/dev/null >fuzz_stdout.log || true
if [ -z "$(grep "corpus: 30" fuzz_stdout.log)" ]; then
if grep -qa "corpus: 30" fuzz_stdout.log; then
echo "Fuzzer is working"
else
echo "Fuzzer does not generate any testcases or any crashes"
exit 1
else
echo "Fuzzer is working"
fi
'''
dependencies = [ "fuzzer" ]

View File

@ -164,11 +164,11 @@ rm -rf libafl_unix_shmem_server || true
timeout 31s ./${FUZZER_NAME} >fuzz_stdout.log &
sleep 0.2
timeout 30s ./${FUZZER_NAME} >/dev/null 2>/dev/null || true
if [ -z "$(grep "corpus: 30" fuzz_stdout.log)" ]; then
if grep -qa "corpus: 30" fuzz_stdout.log; then
echo "Fuzzer is working"
else
echo "Fuzzer does not generate any testcases or any crashes"
exit 1
else
echo "Fuzzer is working"
fi
'''
dependencies = [ "fuzzer" ]

View File

@ -99,11 +99,11 @@ script_runner = "@shell"
script='''
rm -rf libafl_unix_shmem_server || true
timeout 31s ./${FUZZER_NAME} --cores 0 --input ./corpus >fuzz_stdout.log 2>/dev/null || true
if [ -z "$(grep "corpus: 30" fuzz_stdout.log)" ]; then
if grep -qa "corpus: 30" fuzz_stdout.log; then
echo "Fuzzer is working"
else
echo "Fuzzer does not generate any testcases or any crashes"
exit 1
else
echo "Fuzzer is working"
fi
'''
dependencies = [ "fuzzer" ]

View File

@ -100,11 +100,11 @@ script_runner = "@shell"
script='''
rm -rf libafl_unix_shmem_server || true
timeout 31s ./${FUZZER_NAME}.coverage --broker-port 21337 --cores 0 --input ./corpus 2>/dev/null >fuzz_stdout.log || true
if [ -z "$(grep "corpus: 30" fuzz_stdout.log)" ]; then
if grep -qa "corpus: 30" fuzz_stdout.log; then
echo "Fuzzer is working"
else
echo "Fuzzer does not generate any testcases or any crashes"
exit 1
else
echo "Fuzzer is working"
fi
'''
dependencies = [ "fuzzer" ]

View File

@ -105,11 +105,11 @@ rm -rf corpus/ || true
mkdir corpus/ || true
cp seeds/* corpus/ || true
timeout 31s ./${FUZZER_NAME} --cores 0 --input ./corpus 2>/dev/null >fuzz_stdout.log || true
if [ -z "$(grep "corpus: 30" fuzz_stdout.log)" ]; then
if grep -qa "corpus: 30" fuzz_stdout.log; then
echo "Fuzzer is working"
else
echo "Fuzzer does not generate any testcases or any crashes"
exit 1
else
echo "Fuzzer is working"
fi
'''
dependencies = [ "fuzzer" ]

View File

@ -164,11 +164,11 @@ rm -rf libafl_unix_shmem_server || true
(timeout 31s ./${FUZZER_NAME} >fuzz_stdout.log 2>/dev/null || true) &
sleep 0.2
timeout 30s ./${FUZZER_NAME} >/dev/null 2>/dev/null || true
if [ -z "$(grep "corpus: 30" fuzz_stdout.log)" ]; then
if grep -qa "corpus: 30" fuzz_stdout.log; then
echo "Fuzzer is working"
else
echo "Fuzzer does not generate any testcases or any crashes"
exit 1
else
echo "Fuzzer is working"
fi
'''
dependencies = [ "fuzzer" ]

View File

@ -65,11 +65,11 @@ rm -rf libafl_unix_shmem_server || true
(timeout 31s ./${FUZZER_NAME} >fuzz_stdout.log 2>/dev/null || true) &
sleep 0.2
timeout 30s ./${FUZZER_NAME} >/dev/null 2>/dev/null || true
if [ -z "$(grep "corpus: 30" fuzz_stdout.log)" ]; then
if grep -qa "corpus: 30" fuzz_stdout.log; then
echo "Fuzzer is working"
else
echo "Fuzzer does not generate any testcases or any crashes"
exit 1
else
echo "Fuzzer is working"
fi
'''
dependencies = [ "fuzzer" ]

View File

@ -107,11 +107,11 @@ script_runner = "@shell"
script='''
rm -rf libafl_unix_shmem_server || true
timeout 31s ./${FUZZER_NAME} --cores 0 >fuzz_stdout.log 2>/dev/null || true
if [ -z "$(grep "corpus: 8" fuzz_stdout.log)" ]; then
if grep -qa "corpus: 8" fuzz_stdout.log; then
echo "Fuzzer is working"
else
echo "Fuzzer does not generate any testcases or any crashes"
exit 1
else
echo "Fuzzer is working"
fi
'''
dependencies = [ "fuzzer" ]

View File

@ -95,10 +95,18 @@ where
/// A file name to write all client output to
#[builder(default = None)]
stdout_file: Option<&'a str>,
/// The actual, opened, stdout_file - so that we keep it open until the end
#[cfg(all(unix, feature = "std", feature = "fork"))]
#[builder(setter(skip), default = None)]
opened_stdout_file: Option<File>,
/// A file name to write all client stderr output to. If not specified, output is sent to
/// `stdout_file`.
#[builder(default = None)]
stderr_file: Option<&'a str>,
/// The actual, opened, stdout_file - so that we keep it open until the end
#[cfg(all(unix, feature = "std", feature = "fork"))]
#[builder(setter(skip), default = None)]
opened_stderr_file: Option<File>,
/// The `ip:port` address of another broker to connect our new broker to for multi-machine
/// clusters.
#[builder(default = None)]
@ -166,12 +174,10 @@ where
log::info!("spawning on cores: {:?}", self.cores);
#[cfg(feature = "std")]
let stdout_file = self
self.opened_stdout_file = self
.stdout_file
.map(|filename| File::create(filename).unwrap());
#[cfg(feature = "std")]
let stderr_file = self
self.opened_stderr_file = self
.stderr_file
.map(|filename| File::create(filename).unwrap());
@ -204,9 +210,9 @@ where
#[cfg(feature = "std")]
if !debug_output {
if let Some(file) = stdout_file {
if let Some(file) = &self.opened_stdout_file {
dup2(file.as_raw_fd(), libc::STDOUT_FILENO)?;
if let Some(stderr) = stderr_file {
if let Some(stderr) = &self.opened_stderr_file {
dup2(stderr.as_raw_fd(), libc::STDERR_FILENO)?;
} else {
dup2(file.as_raw_fd(), libc::STDERR_FILENO)?;
@ -423,12 +429,21 @@ where
/// A file name to write all client output to
#[builder(default = None)]
stdout_file: Option<&'a str>,
/// The actual, opened, stdout_file - so that we keep it open until the end
#[cfg(all(unix, feature = "std", feature = "fork"))]
#[builder(setter(skip), default = None)]
opened_stdout_file: Option<File>,
/// A file name to write all client stderr output to. If not specified, output is sent to
/// `stdout_file`.
#[builder(default = None)]
stderr_file: Option<&'a str>,
/// The actual, opened, stdout_file - so that we keep it open until the end
#[cfg(all(unix, feature = "std", feature = "fork"))]
#[builder(setter(skip), default = None)]
opened_stderr_file: Option<File>,
/// The `ip:port` address of another broker to connect our new broker to for multi-machine
/// clusters.
#[builder(default = None)]
remote_broker_addr: Option<SocketAddr>,
/// If this launcher should spawn a new `broker` on `[Self::broker_port]` (default).
@ -503,10 +518,10 @@ where
log::info!("spawning on cores: {:?}", self.cores);
let stdout_file = self
self.opened_stdout_file = self
.stdout_file
.map(|filename| File::create(filename).unwrap());
let stderr_file = self
self.opened_stderr_file = self
.stderr_file
.map(|filename| File::create(filename).unwrap());
@ -556,9 +571,9 @@ where
std::thread::sleep(std::time::Duration::from_millis(index * 10));
if !debug_output {
if let Some(file) = stdout_file {
if let Some(file) = &self.opened_stdout_file {
dup2(file.as_raw_fd(), libc::STDOUT_FILENO)?;
if let Some(stderr) = stderr_file {
if let Some(stderr) = &self.opened_stderr_file {
dup2(stderr.as_raw_fd(), libc::STDERR_FILENO)?;
} else {
dup2(file.as_raw_fd(), libc::STDERR_FILENO)?;

View File

@ -154,7 +154,7 @@ impl ConfigTarget for Command {
if memlimit == 0 {
return self;
}
// SAFETY
// # Safety
// This method does not do shady pointer foo.
// It merely call libc functions.
let func = move || {
@ -181,7 +181,7 @@ impl ConfigTarget for Command {
}
Ok(())
};
// # SAFETY
// # Safety
// This calls our non-shady function from above.
unsafe { self.pre_exec(func) }
}
@ -547,7 +547,7 @@ where
self.executor.shmem_mut().is_some(),
"The uses_shmem_testcase() bool can only exist when a map is set"
);
// # SAFETY
// # Safety
// Struct can never be created when uses_shmem_testcase is true and map is none.
let map = unsafe { self.executor.shmem_mut().as_mut().unwrap_unchecked() };
let target_bytes = input.target_bytes();
@ -1237,7 +1237,7 @@ where
self.map.is_some(),
"The uses_shmem_testcase bool can only exist when a map is set"
);
// # SAFETY
// # Safety
// Struct can never be created when uses_shmem_testcase is true and map is none.
let map = unsafe { self.map.as_mut().unwrap_unchecked() };
let target_bytes = input.target_bytes();

View File

@ -1184,7 +1184,8 @@ pub mod pybind {
EM: EventFirer<State = PythonStdState>,
OT: ObserversTuple<PythonStdState>,
{
// SAFETY: We use this observer in Python ony when the ObserverTuple is PythonObserversTuple
// # Safety
// We use this observer in Python ony when the ObserverTuple is PythonObserversTuple
let dont_look_at_this: &PythonObserversTuple =
unsafe { &*(observers as *const OT as *const PythonObserversTuple) };
let dont_look_at_this2: &PythonEventManager =
@ -1217,7 +1218,8 @@ pub mod pybind {
where
OT: ObserversTuple<PythonStdState>,
{
// SAFETY: We use this observer in Python ony when the ObserverTuple is PythonObserversTuple
// # Safety
// We use this observer in Python ony when the ObserverTuple is PythonObserversTuple
let dont_look_at_this: &PythonObserversTuple =
unsafe { &*(observers as *const OT as *const PythonObserversTuple) };
Python::with_gil(|py| -> PyResult<()> {

View File

@ -97,6 +97,7 @@ extern crate std;
#[macro_use]
#[doc(hidden)]
pub extern crate alloc;
#[cfg(feature = "ctor")]
#[doc(hidden)]
pub use ctor::ctor;
@ -159,6 +160,8 @@ pub mod bolts_prelude {
pub use super::{cpu::*, os::*};
}
#[cfg(all(unix, feature = "std"))]
use alloc::boxed::Box;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
#[cfg(all(not(feature = "xxh3"), feature = "alloc"))]
@ -170,15 +173,17 @@ use std::time::{SystemTime, UNIX_EPOCH};
#[cfg(all(unix, feature = "std"))]
use std::{
fs::File,
io::Write,
io::{stderr, stdout, Write},
mem,
os::fd::{FromRawFd, RawFd},
os::fd::{AsRawFd, FromRawFd, RawFd},
panic,
};
// There's a bug in ahash that doesn't let it build in `alloc` without once_cell right now.
// TODO: re-enable once <https://github.com/tkaitchuck/aHash/issues/155> is resolved.
#[cfg(all(not(feature = "xxh3"), feature = "alloc"))]
use ahash::RandomState;
use log::SetLoggerError;
use serde::{Deserialize, Serialize};
#[cfg(feature = "xxh3")]
use xxhash_rust::xxh3::xxh3_64;
@ -570,6 +575,13 @@ impl From<TryFromSliceError> for Error {
}
}
impl From<SetLoggerError> for Error {
#[allow(unused_variables)]
fn from(err: SetLoggerError) -> Self {
Self::illegal_state(format!("Failed to register logger: {err:?}"))
}
}
#[cfg(windows)]
impl From<windows::core::Error> for Error {
#[allow(unused_variables)]
@ -795,6 +807,10 @@ pub static LIBAFL_STDERR_LOGGER: SimpleStderrLogger = SimpleStderrLogger::new();
#[cfg(feature = "std")]
pub static LIBAFL_STDOUT_LOGGER: SimpleStdoutLogger = SimpleStdoutLogger::new();
/// A logger we can use log to raw fds.
#[cfg(all(unix, feature = "std"))]
static mut LIBAFL_RAWFD_LOGGER: SimpleFdLogger = unsafe { SimpleFdLogger::new(1) };
/// A simple logger struct that logs to stdout when used with [`log::set_logger`].
#[derive(Debug)]
#[cfg(feature = "std")]
@ -817,8 +833,8 @@ impl SimpleStdoutLogger {
/// register stdout logger
pub fn set_logger() -> Result<(), Error> {
log::set_logger(&LIBAFL_STDOUT_LOGGER)
.map_err(|_| Error::unknown("Failed to register logger"))
log::set_logger(&LIBAFL_STDOUT_LOGGER)?;
Ok(())
}
}
@ -863,8 +879,8 @@ impl SimpleStderrLogger {
/// register stderr logger
pub fn set_logger() -> Result<(), Error> {
log::set_logger(&LIBAFL_STDERR_LOGGER)
.map_err(|_| Error::unknown("Failed to register logger"))
log::set_logger(&LIBAFL_STDERR_LOGGER)?;
Ok(())
}
}
@ -912,6 +928,22 @@ impl SimpleFdLogger {
pub unsafe fn set_fd(&mut self, fd: RawFd) {
self.fd = fd;
}
/// Register this logger, logging to the given `fd`
///
/// # Safety
/// This function may not be called multiple times concurrently.
/// The passed-in `fd` has to be a legal file descriptor to log to.
pub unsafe fn set_logger(log_fd: RawFd) -> Result<(), Error> {
// # Safety
// The passed-in `fd` has to be a legal file descriptor to log to.
// We also access a shared variable here.
unsafe {
LIBAFL_RAWFD_LOGGER.set_fd(log_fd);
log::set_logger(&LIBAFL_RAWFD_LOGGER)?;
}
Ok(())
}
}
#[cfg(all(feature = "std", unix))]
@ -937,6 +969,42 @@ impl log::Log for SimpleFdLogger {
fn flush(&self) {}
}
/// Closes `stdout` and `stderr` and returns a new `stdout` and `stderr`
/// to be used in the fuzzer for further logging.
///
/// # Safety
/// The function is arguably safe, but it might have undesirable side effects since it closes `stdout` and `stderr`.
#[cfg(all(unix, feature = "std"))]
pub unsafe fn dup_and_mute_outputs() -> Result<(RawFd, RawFd), Error> {
let old_stdout = stdout().as_raw_fd();
let old_stderr = stderr().as_raw_fd();
let null_fd = crate::os::null_fd()?;
let new_stdout = crate::os::dup(old_stdout)?;
let new_stderr = crate::os::dup(old_stderr)?;
crate::os::dup2(null_fd, old_stdout)?;
crate::os::dup2(null_fd, old_stderr)?;
Ok((new_stdout, new_stderr))
}
/// Set up an error print hook that will
///
/// # Safety
/// Will fail if `new_stderr` is not a valid file descriptor.
/// May not be called multiple times concurrently.
#[cfg(all(unix, feature = "std"))]
pub unsafe fn set_error_print_panic_hook(new_stderr: RawFd) {
// Make sure potential errors get printed to the correct (non-closed) stderr
panic::set_hook(Box::new(move |panic_info| {
let mut f = unsafe { File::from_raw_fd(new_stderr) };
writeln!(f, "{panic_info}",)
.unwrap_or_else(|err| println!("Failed to log to fd {new_stderr}: {err}"));
std::mem::forget(f);
}));
}
#[cfg(feature = "python")]
#[allow(missing_docs)]
pub mod pybind {
@ -1083,19 +1151,16 @@ pub mod pybind {
mod tests {
#[cfg(all(feature = "std", unix))]
use crate::SimpleFdLogger;
#[cfg(all(feature = "std", unix))]
pub static mut LOGGER: SimpleFdLogger = unsafe { SimpleFdLogger::new(1) };
use crate::LIBAFL_RAWFD_LOGGER;
#[test]
#[cfg(all(unix, feature = "std"))]
fn test_logger() {
use std::{io::stdout, os::fd::AsRawFd};
unsafe { LOGGER.fd = stdout().as_raw_fd() };
unsafe { LIBAFL_RAWFD_LOGGER.fd = stdout().as_raw_fd() };
unsafe {
log::set_logger(&LOGGER).unwrap();
log::set_logger(&LIBAFL_RAWFD_LOGGER).unwrap();
}
log::set_max_level(log::LevelFilter::Debug);
log::info!("Test");

View File

@ -1,9 +1,6 @@
//! Operating System specific abstractions
//!
#[cfg(feature = "std")]
use std::{env, process::Command};
#[cfg(any(unix, all(windows, feature = "std")))]
use crate::Error;
@ -17,7 +14,15 @@ pub mod unix_signals;
pub mod pipes;
#[cfg(all(unix, feature = "std"))]
use std::ffi::CString;
use alloc::borrow::Cow;
#[cfg(all(unix, feature = "std"))]
use core::ffi::CStr;
#[cfg(feature = "std")]
use std::{env, process::Command};
#[cfg(all(unix, feature = "std"))]
use std::{ffi::CString, os::fd::RawFd};
#[cfg(all(unix, feature = "std"))]
use std::{fs::File, os::fd::AsRawFd, sync::OnceLock};
// Allow a few extra features we need for the whole module
#[cfg(all(windows, feature = "std"))]
@ -27,6 +32,10 @@ pub mod windows_exceptions;
#[cfg(unix)]
use libc::pid_t;
/// A file that we keep open, pointing to /dev/null
#[cfg(all(feature = "std", unix))]
static NULL_FILE: OnceLock<File> = OnceLock::new();
/// Child Process Handle
#[cfg(unix)]
#[derive(Debug)]
@ -91,11 +100,51 @@ pub fn startable_self() -> Result<Command, Error> {
Ok(startable)
}
/// "Safe" wrapper around dup2
/// "Safe" wrapper around `dup`, duplicating the given file descriptor
///
/// # Safety
/// The fd need to be a legal fd.
#[cfg(all(unix, feature = "std"))]
pub fn dup2(fd: i32, device: i32) -> Result<(), Error> {
pub fn dup(fd: RawFd) -> Result<RawFd, Error> {
match unsafe { libc::dup(fd) } {
-1 => Err(Error::file(std::io::Error::last_os_error())),
new_fd => Ok(new_fd),
}
}
/// "Safe" wrapper around dup2
///
/// # Safety
/// The fds need to be legal fds.
#[cfg(all(unix, feature = "std"))]
pub fn dup2(fd: RawFd, device: RawFd) -> Result<(), Error> {
match unsafe { libc::dup2(fd, device) } {
-1 => Err(Error::file(std::io::Error::last_os_error())),
_ => Ok(()),
}
}
/// Gets the stringified version of the last `errno`.
/// This is roughly equivalent to `strerror(errno)` in C.
#[cfg(all(unix, feature = "std"))]
#[must_use]
pub fn last_error_str<'a>() -> Option<Cow<'a, str>> {
std::io::Error::last_os_error().raw_os_error().map(|errno| {
// # Safety
//
// Calling the `strerror` libc functions with the correct `errno`
unsafe { CStr::from_ptr(libc::strerror(errno)).to_string_lossy() }
})
}
/// Get a file descriptor ([`RawFd`]) pointing to "/dev/null"
#[cfg(all(unix, feature = "std"))]
pub fn null_fd() -> Result<RawFd, Error> {
// We don't care about opening the file twice here - races are ok.
if let Some(file) = NULL_FILE.get() {
Ok(file.as_raw_fd())
} else {
let null_file = File::open("/dev/null")?;
Ok(NULL_FILE.get_or_init(move || null_file).as_raw_fd())
}
}

View File

@ -192,7 +192,8 @@ where
#[allow(clippy::cast_possible_truncation)] // we cannot have more than usize elements..
let hash = (self.build_hasher.hash_one(location) % usize::MAX as u64) as usize;
let val = unsafe {
// SAFETY: the index is modulo by the length, therefore it is always in bounds
// # Safety
// The index is modulo by the length, therefore it is always in bounds
let len = self.hitcounts_map.len();
self.hitcounts_map
.as_mut_slice()

View File

@ -1,10 +1,11 @@
use core::ffi::c_int;
#[cfg(unix)]
use std::io::Write;
use std::io::{stderr, stdout, Write};
use std::{
fmt::Debug,
fs::File,
net::TcpListener,
os::fd::AsRawFd,
str::FromStr,
time::{SystemTime, UNIX_EPOCH},
};
@ -35,23 +36,21 @@ use crate::{feedbacks::LibfuzzerCrashCauseMetadata, fuzz_with, options::Libfuzze
fn destroy_output_fds(options: &LibfuzzerOptions) {
#[cfg(unix)]
{
use std::os::fd::AsRawFd;
use libafl_bolts::os::{dup2, null_fd};
let null_fd = null_fd().unwrap();
let stdout_fd = stdout().as_raw_fd();
let stderr_fd = stderr().as_raw_fd();
if options.tui() {
let file_null = File::open("/dev/null").unwrap();
unsafe {
libc::dup2(file_null.as_raw_fd(), 1);
libc::dup2(file_null.as_raw_fd(), 2);
}
dup2(null_fd, stdout_fd).unwrap();
dup2(null_fd, stderr_fd).unwrap();
} else if options.close_fd_mask() != 0 {
let file_null = File::open("/dev/null").unwrap();
unsafe {
if options.close_fd_mask() & 1 != 0 {
libc::dup2(file_null.as_raw_fd(), 1);
}
if options.close_fd_mask() & 2 != 0 {
libc::dup2(file_null.as_raw_fd(), 2);
if options.close_fd_mask() & u8::try_from(stderr_fd).unwrap() != 0 {
dup2(null_fd, stdout_fd).unwrap();
}
if options.close_fd_mask() & u8::try_from(stderr_fd).unwrap() != 0 {
dup2(null_fd, stderr_fd).unwrap();
}
}
}