From 2ed6583041ff1d9b91416a0975e28f11322412f6 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Wed, 8 Mar 2023 19:21:17 +0100 Subject: [PATCH] CI: Run miri tests (#1130) * Fixes/ignores for miri support * linux * fix doctest for miri * fix docs * fix UB in baby_fuzzer * no custom allocator in miri --- .github/workflows/build_and_test.yml | 6 ++++- .gitignore | 3 ++- fuzzers/baby_fuzzer/src/main.rs | 8 +++---- .../baby_fuzzer_swap_differential/src/main.rs | 2 ++ libafl/src/bolts/core_affinity.rs | 13 ++++++++--- libafl/src/bolts/llmp.rs | 11 +++++----- libafl/src/bolts/minibsod.rs | 1 + libafl/src/bolts/os/unix_signals.rs | 2 ++ libafl/src/bolts/shmem.rs | 1 + libafl/src/bolts/staterestore.rs | 1 + libafl/src/events/llmp.rs | 10 ++++----- libafl/src/events/simple.rs | 9 ++++---- libafl/src/executors/command.rs | 2 ++ libafl/src/executors/forkserver.rs | 1 + libafl/src/executors/inprocess.rs | 9 +++++++- libafl/src/lib.rs | 3 +++ libafl/src/mutators/mutations.rs | 22 +++++++++++++++---- libafl_cc/src/cfg.rs | 2 ++ libafl_cc/src/clang.rs | 1 + 19 files changed, 78 insertions(+), 29 deletions(-) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 2d55ff37df..6fe6f6c780 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -68,7 +68,7 @@ jobs: - name: get clang version run: command -v llvm-config && clang -v - name: Add nightly rustfmt and clippy - run: rustup toolchain install nightly --component rustfmt --component clippy --allow-downgrade + run: rustup toolchain install nightly --component rustfmt --component clippy --component miri --allow-downgrade - uses: actions/checkout@v3 - uses: Swatinem/rust-cache@v2 @@ -98,6 +98,10 @@ jobs: run: cargo build --verbose - name: Build examples run: cargo build --examples --verbose + + # --- miri undefined behavior test -- + - name: Run miri tests + run: RUST_BACKTRACE=1 MIRIFLAGS="-Zmiri-disable-isolation" cargo +nightly miri test ubuntu-check: runs-on: ubuntu-22.04 diff --git a/.gitignore b/.gitignore index 9a6553edf8..82151577a9 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ vendor *.obj .cur_input +.cur_input_* .venv crashes @@ -53,4 +54,4 @@ __pycache__ **/libxml2-*.tar.gz libafl_nyx/QEMU-Nyx -libafl_nyx/packer \ No newline at end of file +libafl_nyx/packer diff --git a/fuzzers/baby_fuzzer/src/main.rs b/fuzzers/baby_fuzzer/src/main.rs index da08be3f35..a60ab8ba9e 100644 --- a/fuzzers/baby_fuzzer/src/main.rs +++ b/fuzzers/baby_fuzzer/src/main.rs @@ -1,6 +1,6 @@ -use std::path::PathBuf; #[cfg(windows)] use std::ptr::write_volatile; +use std::{path::PathBuf, ptr::write}; #[cfg(feature = "tui")] use libafl::monitors::tui::TuiMonitor; @@ -24,10 +24,11 @@ use libafl::{ /// Coverage map with explicit assignments due to the lack of instrumentation static mut SIGNALS: [u8; 16] = [0; 16]; +static mut SIGNALS_PTR: *mut u8 = unsafe { SIGNALS.as_mut_ptr() }; /// Assign a signal to the signals map fn signals_set(idx: usize) { - unsafe { SIGNALS[idx] = 1 }; + unsafe { write(SIGNALS_PTR.add(idx), 1) }; } #[allow(clippy::similar_names, clippy::manual_assert)] @@ -60,8 +61,7 @@ pub fn main() { }; // Create an observation channel using the signals map - let observer = - unsafe { StdMapObserver::from_mut_ptr("signals", SIGNALS.as_mut_ptr(), SIGNALS.len()) }; + let observer = unsafe { StdMapObserver::from_mut_ptr("signals", SIGNALS_PTR, SIGNALS.len()) }; // Feedback to rate the interestingness of an input let mut feedback = MaxMapFeedback::new(&observer); diff --git a/fuzzers/baby_fuzzer_swap_differential/src/main.rs b/fuzzers/baby_fuzzer_swap_differential/src/main.rs index 739691fd46..5f20250dc9 100644 --- a/fuzzers/baby_fuzzer_swap_differential/src/main.rs +++ b/fuzzers/baby_fuzzer_swap_differential/src/main.rs @@ -25,9 +25,11 @@ use libafl::{ state::{HasSolutions, StdState}, }; use libafl_targets::{DifferentialAFLMapSwapObserver, MAX_EDGES_NUM}; +#[cfg(not(miri))] use mimalloc::MiMalloc; #[global_allocator] +#[cfg(not(miri))] static GLOBAL: MiMalloc = MiMalloc; // bindings to the functions defined in the target diff --git a/libafl/src/bolts/core_affinity.rs b/libafl/src/bolts/core_affinity.rs index 4e129fc7e5..6030a40d71 100644 --- a/libafl/src/bolts/core_affinity.rs +++ b/libafl/src/bolts/core_affinity.rs @@ -5,14 +5,15 @@ //! This example shows how create a thread for each available processor and pin each thread to its corresponding processor. //! //! ```rust +//! # use std::thread; //! use libafl::bolts::core_affinity; //! -//! use std::thread; -//! //! // Retrieve the IDs of all active CPU cores. +//! # #[cfg(not(miri))] //! let core_ids = core_affinity::get_core_ids().unwrap(); //! //! // Create a thread for each active CPU core. +//! # #[cfg(not(miri))] //! let handles = core_ids.into_iter().map(|id| { //! thread::spawn(move || { //! // Pin this thread to a single CPU core. @@ -21,6 +22,7 @@ //! }) //! }).collect::>(); //! +//! # #[cfg(not(miri))] //! for handle in handles.into_iter() { //! handle.join().unwrap(); //! } @@ -307,11 +309,13 @@ mod linux { use super::*; #[test] + #[cfg_attr(miri, ignore)] fn test_linux_get_affinity_mask() { get_affinity_mask().unwrap(); } #[test] + #[cfg_attr(miri, ignore)] fn test_linux_set_for_current() { let ids = get_core_ids().unwrap(); @@ -558,7 +562,7 @@ mod apple { thread_policy_flavor_t, thread_policy_t, thread_t, KERN_SUCCESS, THREAD_AFFINITY_POLICY, THREAD_AFFINITY_POLICY_COUNT, }; - #[cfg(target_arch = "aarch64")] + #[cfg(all(target_arch = "aarch64", not(miri)))] use libc::{pthread_set_qos_class_self_np, qos_class_t::QOS_CLASS_USER_INITIATED}; use super::CoreId; @@ -622,6 +626,7 @@ mod apple { // // Furthermore, this seems to fail on background threads, so we ignore errors (result != 0). + #[cfg(not(miri))] unsafe { let _result = pthread_set_qos_class_self_np(QOS_CLASS_USER_INITIATED, 0); } @@ -902,12 +907,14 @@ mod tests { use super::*; #[test] + #[cfg_attr(miri, ignore)] fn test_get_core_ids() { let set = get_core_ids().unwrap(); assert_eq!(set.len(), usize::from(available_parallelism().unwrap())); } #[test] + #[cfg_attr(miri, ignore)] fn test_set_affinity() { let ids = get_core_ids().unwrap(); diff --git a/libafl/src/bolts/llmp.rs b/libafl/src/bolts/llmp.rs index 05f2e95f35..b2496bc1f5 100644 --- a/libafl/src/bolts/llmp.rs +++ b/libafl/src/bolts/llmp.rs @@ -96,10 +96,10 @@ use serde::{Deserialize, Serialize}; #[cfg(feature = "std")] use crate::bolts::current_time; +#[cfg(all(unix, not(miri)))] +use crate::bolts::os::unix_signals::setup_signal_handler; #[cfg(unix)] -use crate::bolts::os::unix_signals::{ - setup_signal_handler, siginfo_t, ucontext_t, Handler, Signal, -}; +use crate::bolts::os::unix_signals::{siginfo_t, ucontext_t, Handler, Signal}; use crate::{ bolts::{ shmem::{ShMem, ShMemDescription, ShMemId, ShMemProvider}, @@ -2190,7 +2190,7 @@ where { use super::current_milliseconds; - #[cfg(unix)] + #[cfg(all(unix, not(miri)))] if let Err(_e) = unsafe { setup_signal_handler(&mut LLMP_SIGHANDLER_STATE) } { // We can live without a proper ctrl+c signal handler. Print and ignore. log::info!("Failed to setup signal handlers: {_e}"); @@ -2254,7 +2254,7 @@ where where F: FnMut(ClientId, Tag, Flags, &[u8]) -> Result, { - #[cfg(unix)] + #[cfg(all(unix, not(miri)))] if let Err(_e) = unsafe { setup_signal_handler(&mut LLMP_SIGHANDLER_STATE) } { // We can live without a proper ctrl+c signal handler. Print and ignore. log::info!("Failed to setup signal handlers: {_e}"); @@ -3100,6 +3100,7 @@ mod tests { #[test] #[serial] + #[cfg_attr(miri, ignore)] pub fn test_llmp_connection() { #[allow(unused_variables)] let shmem_provider = StdShMemProvider::new().unwrap(); diff --git a/libafl/src/bolts/minibsod.rs b/libafl/src/bolts/minibsod.rs index bcc765aa4e..59f6ae6b6d 100644 --- a/libafl/src/bolts/minibsod.rs +++ b/libafl/src/bolts/minibsod.rs @@ -635,6 +635,7 @@ mod tests { use crate::bolts::{minibsod::dump_registers, os::unix_signals::ucontext}; #[test] + #[cfg_attr(miri, ignore)] pub fn test_dump_registers() { let ucontext = ucontext().unwrap(); let mut writer = BufWriter::new(stdout()); diff --git a/libafl/src/bolts/os/unix_signals.rs b/libafl/src/bolts/os/unix_signals.rs index 9c7cca417d..3e9ecf3f9e 100644 --- a/libafl/src/bolts/os/unix_signals.rs +++ b/libafl/src/bolts/os/unix_signals.rs @@ -380,7 +380,9 @@ unsafe fn handle_signal(sig: c_int, info: siginfo_t, void: *mut c_void) { /// This will allocate a signal stack and set the signal handlers accordingly. /// It is, for example, used in the [`type@crate::executors::InProcessExecutor`] to restart the fuzzer in case of a crash, /// or to handle `SIGINT` in the broker process. +/// /// # Safety +/// /// The signal handlers will be called on any signal. They should (tm) be async safe. /// A lot can go south in signal handling. Be sure you know what you are doing. pub unsafe fn setup_signal_handler(handler: &mut T) -> Result<(), Error> { diff --git a/libafl/src/bolts/shmem.rs b/libafl/src/bolts/shmem.rs index 3b1f0ca9e3..4d0b9ea4a8 100644 --- a/libafl/src/bolts/shmem.rs +++ b/libafl/src/bolts/shmem.rs @@ -1472,6 +1472,7 @@ mod tests { #[test] #[serial] + #[cfg_attr(miri, ignore)] fn test_shmem_service() { let mut provider = StdShMemProvider::new().unwrap(); let mut map = provider.new_shmem(1024).unwrap(); diff --git a/libafl/src/bolts/staterestore.rs b/libafl/src/bolts/staterestore.rs index 6f7ae009e7..70b84f6bb4 100644 --- a/libafl/src/bolts/staterestore.rs +++ b/libafl/src/bolts/staterestore.rs @@ -301,6 +301,7 @@ mod tests { #[test] #[serial] + #[cfg_attr(miri, ignore)] fn test_state_restore() { const TESTMAP_SIZE: usize = 1024; diff --git a/libafl/src/events/llmp.rs b/libafl/src/events/llmp.rs index 91c7022414..164ffc88a9 100644 --- a/libafl/src/events/llmp.rs +++ b/libafl/src/events/llmp.rs @@ -22,6 +22,8 @@ use super::{CustomBufEventResult, CustomBufHandlerFn}; use crate::bolts::core_affinity::CoreId; #[cfg(all(feature = "std", any(windows, not(feature = "fork"))))] use crate::bolts::os::startable_self; +#[cfg(all(unix, feature = "std", not(miri)))] +use crate::bolts::os::unix_signals::setup_signal_handler; #[cfg(all(feature = "std", feature = "fork", unix))] use crate::bolts::os::{fork, ForkResult}; #[cfg(feature = "llmp_compression")] @@ -32,10 +34,7 @@ use crate::bolts::{ #[cfg(feature = "std")] use crate::bolts::{llmp::LlmpConnection, shmem::StdShMemProvider, staterestore::StateRestorer}; #[cfg(all(unix, feature = "std"))] -use crate::{ - bolts::os::unix_signals::setup_signal_handler, - events::{shutdown_handler, SHUTDOWN_SIGHANDLER_DATA}, -}; +use crate::events::{shutdown_handler, SHUTDOWN_SIGHANDLER_DATA}; use crate::{ bolts::{ llmp::{self, LlmpClient, LlmpClientDescription, Tag}, @@ -992,7 +991,7 @@ where } // We setup signal handlers to clean up shmem segments used by state restorer - #[cfg(unix)] + #[cfg(all(unix, not(miri)))] if let Err(_e) = unsafe { setup_signal_handler(&mut SHUTDOWN_SIGHANDLER_DATA) } { // We can live without a proper ctrl+c signal handler. Print and ignore. log::error!("Failed to setup signal handlers: {_e}"); @@ -1474,6 +1473,7 @@ mod tests { #[test] #[serial] + #[cfg_attr(miri, ignore)] fn test_mgr_state_restore() { let rand = StdRand::with_seed(0); diff --git a/libafl/src/events/simple.rs b/libafl/src/events/simple.rs index 80db06a182..610af3cb81 100644 --- a/libafl/src/events/simple.rs +++ b/libafl/src/events/simple.rs @@ -19,13 +19,12 @@ use serde::{de::DeserializeOwned, Serialize}; use super::{CustomBufEventResult, CustomBufHandlerFn, HasCustomBufHandlers, ProgressReporter}; #[cfg(all(feature = "std", any(windows, not(feature = "fork"))))] use crate::bolts::os::startable_self; +#[cfg(all(unix, feature = "std", not(miri)))] +use crate::bolts::os::unix_signals::setup_signal_handler; #[cfg(all(feature = "std", feature = "fork", unix))] use crate::bolts::os::{fork, ForkResult}; #[cfg(all(unix, feature = "std"))] -use crate::{ - bolts::os::unix_signals::setup_signal_handler, - events::{shutdown_handler, SHUTDOWN_SIGHANDLER_DATA}, -}; +use crate::events::{shutdown_handler, SHUTDOWN_SIGHANDLER_DATA}; use crate::{ bolts::ClientId, events::{ @@ -472,7 +471,7 @@ where } // We setup signal handlers to clean up shmem segments used by state restorer - #[cfg(unix)] + #[cfg(all(unix, not(miri)))] if let Err(_e) = unsafe { setup_signal_handler(&mut SHUTDOWN_SIGHANDLER_DATA) } { // We can live without a proper ctrl+c signal handler. Print and ignore. log::error!("Failed to setup signal handlers: {_e}"); diff --git a/libafl/src/executors/command.rs b/libafl/src/executors/command.rs index 0fed13c669..67d0976a53 100644 --- a/libafl/src/executors/command.rs +++ b/libafl/src/executors/command.rs @@ -656,6 +656,7 @@ mod tests { #[test] #[cfg(unix)] + #[cfg_attr(miri, ignore)] fn test_builder() { let mut mgr = SimpleEventManager::new(SimpleMonitor::new(|status| { log::info!("{status}"); @@ -680,6 +681,7 @@ mod tests { #[test] #[cfg(unix)] + #[cfg_attr(miri, ignore)] fn test_parse_afl_cmdline() { use alloc::string::ToString; diff --git a/libafl/src/executors/forkserver.rs b/libafl/src/executors/forkserver.rs index e6b93a792c..4bf423473b 100644 --- a/libafl/src/executors/forkserver.rs +++ b/libafl/src/executors/forkserver.rs @@ -1283,6 +1283,7 @@ mod tests { #[test] #[serial] + #[cfg_attr(miri, ignore)] fn test_forkserver() { const MAP_SIZE: usize = 65536; let bin = OsString::from("echo"); diff --git a/libafl/src/executors/inprocess.rs b/libafl/src/executors/inprocess.rs index cb33f56175..030861c03b 100644 --- a/libafl/src/executors/inprocess.rs +++ b/libafl/src/executors/inprocess.rs @@ -35,7 +35,7 @@ use nix::{ #[cfg(windows)] use windows::Win32::System::Threading::SetThreadStackGuarantee; -#[cfg(unix)] +#[cfg(all(unix, not(miri)))] use crate::bolts::os::unix_signals::setup_signal_handler; #[cfg(all(feature = "std", unix))] use crate::bolts::os::unix_signals::{ucontext_t, Handler, Signal}; @@ -348,10 +348,12 @@ impl InProcessHandlers { Z: HasObjective, { #[cfg(unix)] + #[cfg_attr(miri, allow(unused_variables))] unsafe { let data = &mut GLOBAL_STATE; #[cfg(feature = "std")] unix_signal_handler::setup_panic_hook::(); + #[cfg(not(miri))] setup_signal_handler(data)?; compiler_fence(Ordering::SeqCst); Ok(Self { @@ -1284,9 +1286,11 @@ impl InChildProcessHandlers { where E: HasObservers, { + #[cfg_attr(miri, allow(unused_variables))] unsafe { let data = &mut FORK_EXECUTOR_GLOBAL_DATA; // child_signal_handlers::setup_child_panic_hook::(); + #[cfg(not(miri))] setup_signal_handler(data)?; compiler_fence(Ordering::SeqCst); Ok(Self { @@ -1301,9 +1305,11 @@ impl InChildProcessHandlers { where E: HasObservers, { + #[cfg_attr(miri, allow(unused_variables))] unsafe { let data = &mut FORK_EXECUTOR_GLOBAL_DATA; // child_signal_handlers::setup_child_panic_hook::(); + #[cfg(not(miri))] setup_signal_handler(data)?; compiler_fence(Ordering::SeqCst); Ok(Self { @@ -2075,6 +2081,7 @@ mod tests { #[test] #[serial] + #[cfg_attr(miri, ignore)] #[cfg(all(feature = "std", feature = "fork", unix))] fn test_inprocessfork_exec() { use crate::{ diff --git a/libafl/src/lib.rs b/libafl/src/lib.rs index d13d1a5794..172c8f68f8 100644 --- a/libafl/src/lib.rs +++ b/libafl/src/lib.rs @@ -519,6 +519,9 @@ mod tests { fuzzer .fuzz_one(&mut stages, &mut executor, &mut state, &mut event_manager) .unwrap_or_else(|_| panic!("Error in iter {i}")); + if cfg!(miri) { + break; + } } let state_serialized = postcard::to_allocvec(&state).unwrap(); diff --git a/libafl/src/mutators/mutations.rs b/libafl/src/mutators/mutations.rs index 456533ad41..21c452e782 100644 --- a/libafl/src/mutators/mutations.rs +++ b/libafl/src/mutators/mutations.rs @@ -1253,6 +1253,7 @@ mod tests { } #[test] + #[cfg_attr(miri, ignore)] // testing all mutators would be good but is way too slow. :/ fn test_mutators() { let mut inputs = vec![ BytesInput::new(vec![0x13, 0x37]), @@ -1267,6 +1268,7 @@ mod tests { let mut state = test_state(); let mut mutations = test_mutations(); + for _ in 0..2 { let mut new_testcases = vec![]; for idx in 0..(mutations.len()) { @@ -1294,7 +1296,10 @@ mod tests { let mut state = test_state(); let mut mutator = BytesDeleteMutator::new(); - for _ in 0..100000 { + // If we're running in miri, we have to make this test a _lot_ shorter. + let iters = if cfg!(miri) { 100 } else { 100_000 }; + + for _ in 0..iters { let mut mutated = base.clone(); if mutator.mutate(&mut state, &mut mutated, 0)? == MutationResult::Skipped { continue; @@ -1345,7 +1350,10 @@ mod tests { let mut state = test_state(); let mut mutator = BytesExpandMutator::new(); - for _ in 0..100000 { + // If we're running in miri, we have to make this test a _lot_ shorter. + let iters = if cfg!(miri) { 100 } else { 100_000 }; + + for _ in 0..iters { let mut mutated = base.clone(); if mutator.mutate(&mut state, &mut mutated, 0)? == MutationResult::Skipped { continue; @@ -1390,7 +1398,10 @@ mod tests { let mut state = test_state(); let mut mutator = BytesInsertMutator::new(); - for _ in 0..100000 { + // If we're running in miri, we have to make this test a _lot_ shorter. + let iters = if cfg!(miri) { 100 } else { 100_000 }; + + for _ in 0..iters { let mut mutated = base.clone(); if mutator.mutate(&mut state, &mut mutated, 0)? == MutationResult::Skipped { continue; @@ -1436,7 +1447,10 @@ mod tests { let mut state = test_state(); let mut mutator = BytesRandInsertMutator::new(); - for _ in 0..100000 { + // If we're running in miri, we have to make this test a _lot_ shorter. + let iters = if cfg!(miri) { 100 } else { 100_000 }; + + for _ in 0..iters { let mut mutated = base.clone(); if mutator.mutate(&mut state, &mut mutated, 0)? == MutationResult::Skipped { continue; diff --git a/libafl_cc/src/cfg.rs b/libafl_cc/src/cfg.rs index 55d4b285ff..967349c133 100644 --- a/libafl_cc/src/cfg.rs +++ b/libafl_cc/src/cfg.rs @@ -362,6 +362,7 @@ mod tests { const TEST_GRAPH_STR: &str = "$$main+41864\n$$_ZN7MyClass1VEi+50306\n%%_ZN7MyClass1VEi+50306\n->19123\n%%main+41864\n->52706\n->26911\n%%main+52706\n%%main+26911\n->52706\n->41925\n"; #[test] + #[cfg_attr(miri, ignore)] // Testcase takes long in miri. fn test_basic_cfg_from_str() { let cfg: ControlFlowGraph = ControlFlowGraph::from_content(TEST_GRAPH_STR); let entry = cfg.get_entry("main").unwrap(); @@ -391,6 +392,7 @@ mod tests { } #[test] + #[cfg_attr(miri, ignore)] // Testcase takes too long in miri. :/ fn test_shortest_path() { let cfg: ControlFlowGraph = ControlFlowGraph::from_content(TEST_GRAPH_STR); let distances = cfg.calculate_distances_to_all_edges((41864 >> 1) ^ 26911); diff --git a/libafl_cc/src/clang.rs b/libafl_cc/src/clang.rs index 9ebacbee51..9fb1b40d24 100644 --- a/libafl_cc/src/clang.rs +++ b/libafl_cc/src/clang.rs @@ -470,6 +470,7 @@ mod tests { use crate::{ClangWrapper, CompilerWrapper}; #[test] + #[cfg_attr(miri, ignore)] fn test_clang_version() { if let Err(res) = ClangWrapper::new() .parse_args(&["my-clang", "-v"])