From a07563def0f9b224fe6bb12bb2634f52028a4513 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Mon, 1 Jan 2024 23:14:59 +0100 Subject: [PATCH] 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 --- .../Makefile.toml | 6 +- fuzzers/frida_executable_libpng/Makefile.toml | 6 +- fuzzers/frida_libpng/Makefile.toml | 6 +- fuzzers/fuzzbench/Makefile.toml | 6 +- fuzzers/fuzzbench/src/lib.rs | 4 +- fuzzers/fuzzbench_fork_qemu/src/fuzzer.rs | 2 +- fuzzers/fuzzbench_qemu/src/fuzzer.rs | 2 +- fuzzers/fuzzbench_text/Makefile.toml | 6 +- fuzzers/fuzzbench_text/src/lib.rs | 4 +- fuzzers/libfuzzer_libmozjpeg/Makefile.toml | 6 +- fuzzers/libfuzzer_libpng/Makefile.toml | 6 +- .../libfuzzer_libpng_accounting/Makefile.toml | 6 +- .../Makefile.toml | 6 +- fuzzers/libfuzzer_libpng_cmin/Makefile.toml | 6 +- fuzzers/libfuzzer_libpng_ctx/Makefile.toml | 6 +- .../libfuzzer_libpng_launcher/Makefile.toml | 6 +- .../libfuzzer_libpng_norestart/Makefile.toml | 6 +- .../Makefile.toml | 6 +- fuzzers/libfuzzer_stb_image/Makefile.toml | 6 +- fuzzers/nautilus_sync/Makefile.toml | 6 +- libafl/src/events/launcher.rs | 35 +++++--- libafl/src/executors/forkserver.rs | 8 +- libafl/src/feedbacks/mod.rs | 6 +- libafl_bolts/src/lib.rs | 89 ++++++++++++++++--- libafl_bolts/src/os/mod.rs | 61 +++++++++++-- .../symcc_runtime/src/filter/coverage.rs | 3 +- .../libafl_libfuzzer_runtime/src/fuzz.rs | 29 +++--- 27 files changed, 235 insertions(+), 104 deletions(-) diff --git a/fuzzers/baby_fuzzer_swap_differential/Makefile.toml b/fuzzers/baby_fuzzer_swap_differential/Makefile.toml index d3d3cc59b7..08b52d4b27 100644 --- a/fuzzers/baby_fuzzer_swap_differential/Makefile.toml +++ b/fuzzers/baby_fuzzer_swap_differential/Makefile.toml @@ -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" ] diff --git a/fuzzers/frida_executable_libpng/Makefile.toml b/fuzzers/frida_executable_libpng/Makefile.toml index bed1b4bdd1..92868e51c7 100644 --- a/fuzzers/frida_executable_libpng/Makefile.toml +++ b/fuzzers/frida_executable_libpng/Makefile.toml @@ -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" ] diff --git a/fuzzers/frida_libpng/Makefile.toml b/fuzzers/frida_libpng/Makefile.toml index c15301f7c9..1df01fd2c2 100644 --- a/fuzzers/frida_libpng/Makefile.toml +++ b/fuzzers/frida_libpng/Makefile.toml @@ -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" ] diff --git a/fuzzers/fuzzbench/Makefile.toml b/fuzzers/fuzzbench/Makefile.toml index 788681f82b..3cc7164e4a 100644 --- a/fuzzers/fuzzbench/Makefile.toml +++ b/fuzzers/fuzzbench/Makefile.toml @@ -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 diff --git a/fuzzers/fuzzbench/src/lib.rs b/fuzzers/fuzzbench/src/lib.rs index 705c1bebb5..2522331087 100644 --- a/fuzzers/fuzzbench/src/lib.rs +++ b/fuzzers/fuzzbench/src/lib.rs @@ -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())?; } } diff --git a/fuzzers/fuzzbench_fork_qemu/src/fuzzer.rs b/fuzzers/fuzzbench_fork_qemu/src/fuzzer.rs index c4431a4dfd..71dfd945bd 100644 --- a/fuzzers/fuzzbench_fork_qemu/src/fuzzer.rs +++ b/fuzzers/fuzzbench_fork_qemu/src/fuzzer.rs @@ -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(); diff --git a/fuzzers/fuzzbench_qemu/src/fuzzer.rs b/fuzzers/fuzzbench_qemu/src/fuzzer.rs index cc16a9d29a..38f98de552 100644 --- a/fuzzers/fuzzbench_qemu/src/fuzzer.rs +++ b/fuzzers/fuzzbench_qemu/src/fuzzer.rs @@ -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(); diff --git a/fuzzers/fuzzbench_text/Makefile.toml b/fuzzers/fuzzbench_text/Makefile.toml index 8d00253099..cbf7f0bdfc 100644 --- a/fuzzers/fuzzbench_text/Makefile.toml +++ b/fuzzers/fuzzbench_text/Makefile.toml @@ -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 diff --git a/fuzzers/fuzzbench_text/src/lib.rs b/fuzzers/fuzzbench_text/src/lib.rs index 7f32121773..31973c4cff 100644 --- a/fuzzers/fuzzbench_text/src/lib.rs +++ b/fuzzers/fuzzbench_text/src/lib.rs @@ -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(); diff --git a/fuzzers/libfuzzer_libmozjpeg/Makefile.toml b/fuzzers/libfuzzer_libmozjpeg/Makefile.toml index f056143696..8d70fcf4bf 100644 --- a/fuzzers/libfuzzer_libmozjpeg/Makefile.toml +++ b/fuzzers/libfuzzer_libmozjpeg/Makefile.toml @@ -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" ] diff --git a/fuzzers/libfuzzer_libpng/Makefile.toml b/fuzzers/libfuzzer_libpng/Makefile.toml index 9bccdea6aa..94430ed162 100644 --- a/fuzzers/libfuzzer_libpng/Makefile.toml +++ b/fuzzers/libfuzzer_libpng/Makefile.toml @@ -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" ] diff --git a/fuzzers/libfuzzer_libpng_accounting/Makefile.toml b/fuzzers/libfuzzer_libpng_accounting/Makefile.toml index cfca00e671..b37c152b71 100644 --- a/fuzzers/libfuzzer_libpng_accounting/Makefile.toml +++ b/fuzzers/libfuzzer_libpng_accounting/Makefile.toml @@ -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" ] diff --git a/fuzzers/libfuzzer_libpng_centralized/Makefile.toml b/fuzzers/libfuzzer_libpng_centralized/Makefile.toml index 2f08b0ad0d..fe8768420b 100644 --- a/fuzzers/libfuzzer_libpng_centralized/Makefile.toml +++ b/fuzzers/libfuzzer_libpng_centralized/Makefile.toml @@ -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" ] diff --git a/fuzzers/libfuzzer_libpng_cmin/Makefile.toml b/fuzzers/libfuzzer_libpng_cmin/Makefile.toml index c4e4d10df8..1df326c8f0 100644 --- a/fuzzers/libfuzzer_libpng_cmin/Makefile.toml +++ b/fuzzers/libfuzzer_libpng_cmin/Makefile.toml @@ -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" ] diff --git a/fuzzers/libfuzzer_libpng_ctx/Makefile.toml b/fuzzers/libfuzzer_libpng_ctx/Makefile.toml index 43fb7af391..fb2da9e0fa 100644 --- a/fuzzers/libfuzzer_libpng_ctx/Makefile.toml +++ b/fuzzers/libfuzzer_libpng_ctx/Makefile.toml @@ -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" ] diff --git a/fuzzers/libfuzzer_libpng_launcher/Makefile.toml b/fuzzers/libfuzzer_libpng_launcher/Makefile.toml index 8797f7c795..5b30335d80 100644 --- a/fuzzers/libfuzzer_libpng_launcher/Makefile.toml +++ b/fuzzers/libfuzzer_libpng_launcher/Makefile.toml @@ -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" ] diff --git a/fuzzers/libfuzzer_libpng_norestart/Makefile.toml b/fuzzers/libfuzzer_libpng_norestart/Makefile.toml index b6131ba94f..88cac0c2e1 100644 --- a/fuzzers/libfuzzer_libpng_norestart/Makefile.toml +++ b/fuzzers/libfuzzer_libpng_norestart/Makefile.toml @@ -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" ] diff --git a/fuzzers/libfuzzer_libpng_tcp_manager/Makefile.toml b/fuzzers/libfuzzer_libpng_tcp_manager/Makefile.toml index 9bccdea6aa..94430ed162 100644 --- a/fuzzers/libfuzzer_libpng_tcp_manager/Makefile.toml +++ b/fuzzers/libfuzzer_libpng_tcp_manager/Makefile.toml @@ -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" ] diff --git a/fuzzers/libfuzzer_stb_image/Makefile.toml b/fuzzers/libfuzzer_stb_image/Makefile.toml index 0d0f2216f5..5c88c2a497 100644 --- a/fuzzers/libfuzzer_stb_image/Makefile.toml +++ b/fuzzers/libfuzzer_stb_image/Makefile.toml @@ -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" ] diff --git a/fuzzers/nautilus_sync/Makefile.toml b/fuzzers/nautilus_sync/Makefile.toml index 1cafa6c680..df846ec169 100644 --- a/fuzzers/nautilus_sync/Makefile.toml +++ b/fuzzers/nautilus_sync/Makefile.toml @@ -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" ] diff --git a/libafl/src/events/launcher.rs b/libafl/src/events/launcher.rs index 5cc85c4bdd..18181c46b8 100644 --- a/libafl/src/events/launcher.rs +++ b/libafl/src/events/launcher.rs @@ -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, /// 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, /// 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, /// 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, /// The `ip:port` address of another broker to connect our new broker to for multi-machine /// clusters. + #[builder(default = None)] remote_broker_addr: Option, /// 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)?; diff --git a/libafl/src/executors/forkserver.rs b/libafl/src/executors/forkserver.rs index 389279e035..449de22f48 100644 --- a/libafl/src/executors/forkserver.rs +++ b/libafl/src/executors/forkserver.rs @@ -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(); diff --git a/libafl/src/feedbacks/mod.rs b/libafl/src/feedbacks/mod.rs index cefec5bfce..eaa094aa5a 100644 --- a/libafl/src/feedbacks/mod.rs +++ b/libafl/src/feedbacks/mod.rs @@ -1184,7 +1184,8 @@ pub mod pybind { EM: EventFirer, OT: ObserversTuple, { - // 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, { - // 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<()> { diff --git a/libafl_bolts/src/lib.rs b/libafl_bolts/src/lib.rs index 2e7ad3105c..b0a9d5cca5 100644 --- a/libafl_bolts/src/lib.rs +++ b/libafl_bolts/src/lib.rs @@ -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 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 for Error { } } +impl From for Error { + #[allow(unused_variables)] + fn from(err: SetLoggerError) -> Self { + Self::illegal_state(format!("Failed to register logger: {err:?}")) + } +} + #[cfg(windows)] impl From 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"); diff --git a/libafl_bolts/src/os/mod.rs b/libafl_bolts/src/os/mod.rs index 99142f5b16..2af85e2651 100644 --- a/libafl_bolts/src/os/mod.rs +++ b/libafl_bolts/src/os/mod.rs @@ -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 = OnceLock::new(); + /// Child Process Handle #[cfg(unix)] #[derive(Debug)] @@ -91,11 +100,51 @@ pub fn startable_self() -> Result { 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 { + 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> { + 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 { + // 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()) + } +} diff --git a/libafl_concolic/symcc_runtime/src/filter/coverage.rs b/libafl_concolic/symcc_runtime/src/filter/coverage.rs index 8d5c1cc56e..ff96f5b0f0 100644 --- a/libafl_concolic/symcc_runtime/src/filter/coverage.rs +++ b/libafl_concolic/symcc_runtime/src/filter/coverage.rs @@ -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() diff --git a/libafl_libfuzzer/libafl_libfuzzer_runtime/src/fuzz.rs b/libafl_libfuzzer/libafl_libfuzzer_runtime/src/fuzz.rs index ff1f9a01ff..4ca5c5c12d 100644 --- a/libafl_libfuzzer/libafl_libfuzzer_runtime/src/fuzz.rs +++ b/libafl_libfuzzer/libafl_libfuzzer_runtime/src/fuzz.rs @@ -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(); } } }