fuzzcheck/
fuzzer.rs

1use std::any::{Any, TypeId};
2use std::borrow::Borrow;
3use std::collections::hash_map::DefaultHasher;
4use std::collections::HashMap;
5use std::hash::{Hash, Hasher};
6use std::panic::{catch_unwind, AssertUnwindSafe};
7use std::process::exit;
8use std::result::Result;
9
10use fuzzcheck_common::arg::{Arguments, FuzzerCommand};
11use fuzzcheck_common::{FuzzerEvent, FuzzerStats};
12use libc::{SIGABRT, SIGALRM, SIGBUS, SIGFPE, SIGINT, SIGSEGV, SIGTERM, SIGTRAP};
13
14use crate::data_structures::RcSlab;
15use crate::sensors_and_pools::{
16    AndSensorAndPool, NoopSensor, TestFailure, TestFailurePool, TestFailureSensor, UnitPool, TEST_FAILURE,
17};
18use crate::signals_handler::set_signal_handlers;
19use crate::subvalue_provider::{CrossoverSubValueProvider, Generation, SubValueProviderId};
20use crate::traits::{CorpusDelta, Mutator, SaveToStatsFolder, SensorAndPool, Serializer};
21use crate::world::World;
22use crate::{CSVField, SubValueProvider, ToCSV};
23
24static WRITE_STATS_ERROR: &str = "the stats could not be written to the file system";
25static WORLD_NEW_ERROR: &str = "an IO operation failed when setting up the fuzzer";
26static SERIALIZER_FROM_DATA_ERROR: &str = "the file could not be decoded into a valid input";
27static READ_INPUT_FILE_ERROR: &str = "the input file could not be read";
28static SAVE_ARTIFACTS_ERROR: &str = "the artifact could not be saved";
29static UPDATE_CORPUS_ERROR: &str = "the corpus could not be updated on the file system";
30
31static mut DID_FIND_ANY_TEST_FAILURE: bool = false;
32
33/// The result of a fuzz test, if it ends.
34///
35/// It contains two fields:
36/// 1. [`found_test_failure`](Self::found_test_failure) is `true` if the fuzzer found any failing test case
37/// 2. [`reason_for_stopping`](Self::reason_for_stopping) gives the reason why the fuzzer stopped.
38///
39/// If the fuzzer stopped because it found a failing test case, then `reason_for_stopping` has the
40/// value [`ReasonForStopping::TestFailure(T)`](crate::ReasonForStopping::TestFailure) where `T` is the
41/// failing test case.
42#[derive(Debug, Clone)]
43pub struct FuzzingResult<T> {
44    pub found_test_failure: bool,
45    pub reason_for_stopping: ReasonForStopping<T>,
46}
47
48#[derive(Debug, Clone)]
49pub enum ReasonForStopping<T> {
50    TestFailure(T),
51    ExhaustedAllPossibleMutations,
52    MaxIterationsReached,
53    MaxDurationReached,
54}
55
56/// The index to a test case in the fuzzer’s storage.
57#[cfg_attr(feature = "serde_json_serializer", derive(serde::Serialize, serde::Deserialize))]
58#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
59pub struct PoolStorageIndex(usize);
60
61// #[cfg(test)]
62impl PoolStorageIndex {
63    #[coverage(off)]
64    pub fn mock(idx: usize) -> Self {
65        Self(idx)
66    }
67}
68
69enum FuzzerInputIndex<T> {
70    None,
71    Temporary(T),
72    Pool(PoolStorageIndex),
73}
74
75struct FuzzedInputAndSubValueProvider<T, M>
76where
77    T: Clone + 'static,
78    M: Mutator<T>,
79{
80    input: FuzzedInput<T, M>,
81    subvalues: CrossoverSubValueProvider<T, M>,
82}
83
84/**
85 * A struct that stores the value, cache, and mutation step of an input.
86 * It is used for convenience.
87 */
88struct FuzzedInput<T: Clone + 'static, Mut: Mutator<T>> {
89    value: T,
90    cache: Mut::Cache,
91    mutation_step: Mut::MutationStep,
92    generation: Generation,
93}
94impl<T: Clone + 'static, Mut: Mutator<T>> Clone for FuzzedInput<T, Mut> {
95    fn clone(&self) -> Self {
96        Self {
97            value: self.value.clone(),
98            cache: self.cache.clone(),
99            mutation_step: self.mutation_step.clone(),
100            generation: self.generation,
101        }
102    }
103}
104
105impl<T: Clone + 'static, Mut: Mutator<T>> FuzzedInput<T, Mut> {
106    #[coverage(off)]
107    fn new(value: T, cache: Mut::Cache, mutation_step: Mut::MutationStep, generation: Generation) -> Self {
108        Self {
109            value,
110            cache,
111            mutation_step,
112            generation,
113        }
114    }
115
116    #[coverage(off)]
117    fn new_source(&self, m: &Mut, generation: Generation) -> Self {
118        let cache = m.validate_value(&self.value).unwrap();
119        let mutation_step = m.default_mutation_step(&self.value, &cache);
120        Self::new(self.value.clone(), cache, mutation_step, generation)
121    }
122
123    #[coverage(off)]
124    fn complexity(&self, m: &Mut) -> f64 {
125        m.complexity(&self.value, &self.cache)
126    }
127
128    #[coverage(off)]
129    fn mutate(
130        &mut self,
131        m: &Mut,
132        subvalue_provider: &dyn SubValueProvider,
133        max_cplx: f64,
134    ) -> Option<(Mut::UnmutateToken, f64)> {
135        m.ordered_mutate(
136            &mut self.value,
137            &mut self.cache,
138            &mut self.mutation_step,
139            subvalue_provider,
140            max_cplx,
141        )
142    }
143
144    #[coverage(off)]
145    fn unmutate(&mut self, m: &Mut, t: Mut::UnmutateToken) {
146        m.unmutate(&mut self.value, &mut self.cache, t);
147    }
148}
149
150struct FuzzerState<T: Clone + 'static, M: Mutator<T>> {
151    mutator: M,
152    sensor_and_pool: Box<dyn SensorAndPool>,
153    pool_storage: RcSlab<FuzzedInputAndSubValueProvider<T, M>>,
154    /// The step given to the mutator when the fuzzer wants to create a new arbitrary test case
155    arbitrary_step: M::ArbitraryStep,
156    /// The index of the test case that is being tested
157    input_idx: FuzzerInputIndex<FuzzedInput<T, M>>,
158    /// Various statistics about the fuzzer run
159    fuzzer_stats: FuzzerStats,
160
161    settings: Arguments,
162    serializer: Box<dyn Serializer<Value = T>>,
163    /// The world handles effects
164    world: World,
165    rng: fastrand::Rng,
166
167    signal_handler_alt_stack: Option<(*mut u8, std::alloc::Layout)>,
168}
169
170impl<T: Clone + 'static, M: Mutator<T>> Drop for FuzzerState<T, M> {
171    #[coverage(off)]
172    fn drop(&mut self) {
173        unsafe {
174            crate::signals_handler::reset_signal_handlers();
175            if let Some((stack_ptr, stack_layout)) = self.signal_handler_alt_stack {
176                std::alloc::dealloc(stack_ptr, stack_layout);
177            }
178        }
179    }
180}
181
182impl<T: Clone + 'static, M: Mutator<T>> FuzzerState<T, M> {
183    #[coverage(off)]
184    fn get_input<'a>(
185        fuzzer_input_idx: &'a FuzzerInputIndex<FuzzedInput<T, M>>,
186        pool_storage: &'a RcSlab<FuzzedInputAndSubValueProvider<T, M>>,
187    ) -> Option<&'a FuzzedInput<T, M>> {
188        match fuzzer_input_idx {
189            FuzzerInputIndex::None => None,
190            FuzzerInputIndex::Temporary(input) => Some(input),
191            FuzzerInputIndex::Pool(idx) => Some(&pool_storage[idx.0].input),
192        }
193    }
194}
195
196#[coverage(off)]
197fn update_fuzzer_stats(stats: &mut FuzzerStats, world: &mut World) {
198    let microseconds = world.elapsed_time_since_last_checkpoint();
199    let nbr_runs = stats.total_number_of_runs - stats.number_of_runs_since_last_reset_time;
200    let nbr_runs_times_million = nbr_runs * 1_000_000;
201    if microseconds != 0 {
202        stats.exec_per_s = nbr_runs_times_million / microseconds;
203    }
204    if microseconds > 1_000_000 {
205        world.set_checkpoint_instant();
206        stats.number_of_runs_since_last_reset_time = stats.total_number_of_runs;
207    }
208}
209
210impl<T: Clone + 'static, M: Mutator<T>> SaveToStatsFolder for FuzzerState<T, M>
211where
212    Self: 'static,
213{
214    #[coverage(off)]
215    fn save_to_stats_folder(&self) -> Vec<(std::path::PathBuf, Vec<u8>)> {
216        let mut contents = self.sensor_and_pool.save_to_stats_folder();
217        contents.extend(self.world.save_to_stats_folder());
218        contents
219    }
220}
221
222impl<T: Clone + 'static, M: Mutator<T>> FuzzerState<T, M>
223where
224    Self: 'static,
225{
226    #[coverage(off)]
227    fn write_stats(&mut self) -> Result<(), std::io::Error> {
228        self.world.write_stats_content(self.save_to_stats_folder())
229    }
230
231    #[coverage(off)]
232    fn receive_signal(&mut self, signal: i32) -> ! {
233        self.world.report_event(
234            FuzzerEvent::CaughtSignal(signal as i32),
235            Some((&self.fuzzer_stats, self.sensor_and_pool.stats().as_ref())),
236        );
237
238        match signal {
239            SIGABRT | SIGBUS | SIGSEGV | SIGFPE | SIGALRM | SIGTRAP => {
240                if let Some(input) = Self::get_input(&self.input_idx, &self.pool_storage) {
241                    let input = input.new_source(&self.mutator, Generation(0));
242                    let cplx = input.complexity(&self.mutator);
243                    let content = self.serializer.to_data(&input.value);
244                    let _ = self.world.save_artifact(content, cplx, self.serializer.extension());
245                    self.write_stats().expect(WRITE_STATS_ERROR);
246                    exit(TerminationStatus::Crash as i32);
247                } else {
248                    self.world.report_event(
249                        FuzzerEvent::CrashNoInput,
250                        Some((&self.fuzzer_stats, self.sensor_and_pool.stats().as_ref())),
251                    );
252                    exit(TerminationStatus::Crash as i32);
253                }
254            }
255            SIGINT | SIGTERM => {
256                self.write_stats().expect(WRITE_STATS_ERROR);
257                self.world.stop()
258            }
259            _ => exit(TerminationStatus::Unknown as i32),
260        }
261    }
262    #[coverage(off)]
263    fn arbitrary_input(&mut self) -> Option<(FuzzedInput<T, M>, f64)> {
264        if let Some((v, cplx)) = self
265            .mutator
266            .ordered_arbitrary(&mut self.arbitrary_step, self.settings.max_input_cplx)
267        {
268            let cache = self.mutator.validate_value(&v).unwrap();
269            let step = self.mutator.default_mutation_step(&v, &cache);
270            Some((FuzzedInput::new(v, cache, step, Generation(0)), cplx))
271        } else {
272            None
273        }
274    }
275    #[coverage(off)]
276    unsafe fn set_up_signal_handler(&mut self) {
277        let ptr = self as *mut Self;
278        let (stack_ptr, stack_size) = set_signal_handlers(
279            #[coverage(off)]
280            move |sig| (*ptr).receive_signal(sig),
281        );
282        self.signal_handler_alt_stack = Some((stack_ptr, stack_size));
283    }
284}
285
286pub struct Fuzzer<T, M>
287where
288    T: Clone + 'static,
289    M: Mutator<T>,
290    Self: 'static,
291{
292    state: FuzzerState<T, M>,
293    test: Box<dyn Fn(&T) -> bool>,
294}
295
296impl<T, M> Fuzzer<T, M>
297where
298    T: Clone + 'static,
299    M: Mutator<T>,
300    Self: 'static,
301{
302    #[coverage(off)]
303    fn new(
304        test: Box<dyn Fn(&T) -> bool>,
305        mutator: M,
306        serializer: Box<dyn Serializer<Value = T>>,
307        sensor_and_pool: Box<dyn SensorAndPool>,
308        settings: Arguments,
309        world: World,
310    ) -> Self {
311        let arbitrary_step = mutator.default_arbitrary_step();
312        Fuzzer {
313            state: FuzzerState {
314                sensor_and_pool,
315                pool_storage: RcSlab::new(),
316                mutator,
317                arbitrary_step,
318                input_idx: FuzzerInputIndex::None,
319                fuzzer_stats: FuzzerStats::default(),
320                settings,
321                serializer,
322                world,
323                rng: fastrand::Rng::new(),
324                signal_handler_alt_stack: None,
325            },
326            test,
327        }
328    }
329
330    #[coverage(off)]
331    fn test_and_process_input(&mut self, cplx: f64) -> Result<(), ReasonForStopping<T>> {
332        let Fuzzer {
333            state:
334                FuzzerState {
335                    mutator,
336                    sensor_and_pool,
337                    pool_storage,
338                    input_idx,
339                    fuzzer_stats,
340                    serializer,
341                    world,
342                    settings,
343                    ..
344                },
345            test,
346            ..
347        } = self;
348
349        // we have verified in the caller function that there is an input
350        let input = FuzzerState::<T, M>::get_input(input_idx, pool_storage).unwrap();
351
352        std::panic::set_hook(Box::new(
353            #[coverage(off)]
354            move |panic_info| {
355                let mut hasher = DefaultHasher::new();
356                panic_info.location().hash(&mut hasher);
357                unsafe {
358                    TEST_FAILURE = Some(TestFailure {
359                        display: format!("{}", panic_info),
360                        id: hasher.finish(),
361                    });
362                }
363            },
364        ));
365        if settings.detect_infinite_loop {
366            let _old_time_left = unsafe { libc::alarm(1) };
367            // TODO: I think setitimer should be prefered, but libc
368            // doesn't support it on linux, see:
369            // https://github.com/rust-lang/libc/issues/1347#event-3879031340
370
371            // let success = unsafe {
372            // let t = itimerval {
373            //     it_interval: libc::timeval { tv_sec: 0, tv_usec: 0 },
374            //     it_value: libc::timeval { tv_sec: 1, tv_usec: 0 },
375            // };
376            // libc::setitimer(ITIMER_REAL, &t, std::ptr::null_mut())
377            // };
378            // assert!(success == 0);
379        }
380        sensor_and_pool.start_recording();
381        let result = catch_unwind(AssertUnwindSafe(
382            #[coverage(off)]
383            || (test)(input.value.borrow()),
384        ));
385
386        let _ = std::panic::take_hook();
387        let test_failure = match result {
388            Ok(false) => unsafe {
389                TEST_FAILURE = Some(TestFailure {
390                    display: "test function returned false".to_string(),
391                    id: 0,
392                });
393                true
394            },
395            Err(_) => {
396                // the panic handler already changed the value of TEST_FAILURE
397                // so we don't need to do anything
398                true
399            }
400            Ok(true) => false,
401        };
402        if test_failure {
403            unsafe {
404                DID_FIND_ANY_TEST_FAILURE = true;
405            }
406        }
407        sensor_and_pool.stop_recording();
408        if test_failure && self.state.settings.stop_after_first_failure {
409            let serialized_input = serializer.to_data(&input.value);
410            self.state
411                .world
412                .save_artifact(serialized_input, cplx, serializer.extension())
413                .expect(SAVE_ARTIFACTS_ERROR);
414            return Err(ReasonForStopping::TestFailure(input.value.clone()));
415        }
416
417        fuzzer_stats.total_number_of_runs += 1;
418
419        let input_id = PoolStorageIndex(pool_storage.next_slot());
420
421        let deltas = sensor_and_pool.process(input_id, cplx);
422
423        if !deltas.is_empty() {
424            let add_ref_count = deltas.iter().fold(
425                0,
426                #[coverage(off)]
427                |acc, delta| if delta.add { acc + 1 } else { acc },
428            );
429            update_fuzzer_stats(fuzzer_stats, world);
430            let event = CorpusDelta::fuzzer_event(&deltas);
431            let content = if add_ref_count > 0 {
432                serializer.to_data(&input.value)
433            } else {
434                vec![]
435            };
436            world
437                .update_corpus(input_id, content, &deltas, serializer.extension())
438                .expect(UPDATE_CORPUS_ERROR);
439            world.report_event(event, Some((fuzzer_stats, sensor_and_pool.stats().as_ref())));
440            if add_ref_count > 0 {
441                let generation = Generation(fuzzer_stats.total_number_of_runs);
442                let input = input.new_source(mutator, generation);
443                // check that the mutator's handling of the complexity is correct
444                let serialised = String::from_utf8(serializer.to_data(&input.value)).unwrap();
445                assert!(
446                    (input.complexity(mutator) - cplx).abs() < 0.01,
447                    "The mutator used by the fuzz test does not evaluate the complexity of the test cases consistently.
448                    This is a bug in the implementation of {}
449                    =============
450                    
451                    {serialised}
452
453                    =============
454                    ",
455                    std::any::type_name::<M>()
456                );
457
458                let mut subvalues: HashMap<TypeId, Vec<(*const dyn Any, f64)>> = HashMap::default();
459
460                let mut act_on_subvalue = #[coverage(off)]
461                |subvalue: &dyn Any, complexity| {
462                    subvalues
463                        .entry(subvalue.type_id())
464                        .or_default()
465                        .push((subvalue as *const _, complexity));
466                };
467
468                mutator.visit_subvalues(&input.value, &input.cache, &mut act_on_subvalue);
469                let storage_idx_1 = pool_storage.next_slot();
470                let subvalues = CrossoverSubValueProvider::new(
471                    SubValueProviderId {
472                        idx: storage_idx_1,
473                        generation,
474                    },
475                    &input.value,
476                    &input.cache,
477                    mutator,
478                );
479                let stored_input = FuzzedInputAndSubValueProvider { input, subvalues };
480                let storage_idx_2 = pool_storage.insert(stored_input, add_ref_count);
481                assert_eq!(storage_idx_1, storage_idx_2);
482            }
483            for delta in deltas {
484                for r in delta.remove {
485                    pool_storage.remove(r.0);
486                }
487            }
488        }
489
490        Ok(())
491    }
492
493    #[coverage(off)]
494    fn get_input_and_subvalue_provider<'a>(
495        pool_storage: &'a mut RcSlab<FuzzedInputAndSubValueProvider<T, M>>,
496        sensor_and_pool: &mut dyn SensorAndPool,
497        rng: &fastrand::Rng,
498        idx: PoolStorageIndex,
499    ) -> (&'a mut FuzzedInput<T, M>, &'a (impl SubValueProvider + 'a)) {
500        let idx_cross = sensor_and_pool.get_random_index().unwrap();
501
502        if idx == idx_cross || rng.u8(..5) == 0 {
503            let FuzzedInputAndSubValueProvider { input, subvalues } = &mut pool_storage[idx.0];
504            (input, subvalues)
505        } else {
506            // crossover of two different test cases
507            let (input, FuzzedInputAndSubValueProvider { subvalues, .. }) =
508                pool_storage.get_mut_and_ref(idx.0, idx_cross.0).unwrap();
509            (&mut input.input, subvalues)
510        }
511    }
512
513    #[coverage(off)]
514    fn process_next_input(&mut self) -> Result<(), ReasonForStopping<T>> {
515        let FuzzerState {
516            pool_storage,
517            sensor_and_pool,
518            input_idx,
519            mutator,
520            settings,
521            rng,
522            fuzzer_stats,
523            world,
524            ..
525        } = &mut self.state;
526
527        if let Some(idx) = sensor_and_pool.get_random_index() {
528            *input_idx = FuzzerInputIndex::Pool(idx);
529            let (input, subvalue_provider) =
530                Self::get_input_and_subvalue_provider(pool_storage, sensor_and_pool.as_mut(), rng, idx);
531            let generation = input.generation;
532            if let Some((unmutate_token, complexity)) =
533                input.mutate(mutator, subvalue_provider, settings.max_input_cplx)
534            {
535                //drop(subvalue_provider);
536                if complexity < self.state.settings.max_input_cplx {
537                    self.test_and_process_input(complexity)?;
538                }
539
540                // Retrieving the input may fail because the input may have been deleted
541                if let Some(input) = self.state.pool_storage.get_mut(idx.0).map(
542                    #[coverage(off)]
543                    |x| &mut x.input,
544                ) && input.generation == generation
545                {
546                    input.unmutate(&self.state.mutator, unmutate_token);
547                }
548
549                Ok(())
550            } else {
551                world.report_event(FuzzerEvent::End, Some((fuzzer_stats, sensor_and_pool.stats().as_ref())));
552                Err(ReasonForStopping::ExhaustedAllPossibleMutations)
553            }
554        } else if let Some((input, cplx)) = self.state.arbitrary_input() {
555            self.state.input_idx = FuzzerInputIndex::Temporary(input);
556
557            if cplx < self.state.settings.max_input_cplx {
558                self.test_and_process_input(cplx)?;
559            }
560
561            Ok(())
562        } else {
563            self.state.world.report_event(
564                FuzzerEvent::End,
565                Some((&self.state.fuzzer_stats, self.state.sensor_and_pool.stats().as_ref())),
566            );
567            Err(ReasonForStopping::ExhaustedAllPossibleMutations)
568        }
569    }
570
571    #[coverage(off)]
572    fn process_initial_inputs(&mut self) -> Result<(), ReasonForStopping<T>> {
573        let mut inputs: Vec<FuzzedInput<T, M>> = self
574            .state
575            .world
576            .read_input_corpus()
577            .expect(READ_INPUT_FILE_ERROR)
578            .into_iter()
579            .filter_map(
580                #[coverage(off)]
581                |value| {
582                    let value = self.state.serializer.from_data(&value)?;
583                    let cache = self.state.mutator.validate_value(&value)?;
584                    let mutation_step = self.state.mutator.default_mutation_step(&value, &cache);
585                    Some(FuzzedInput::new(value, cache, mutation_step, Generation(0)))
586                },
587            )
588            .collect();
589
590        for _ in 0..100 {
591            if let Some((input, _)) = self.state.arbitrary_input() {
592                inputs.push(input);
593            } else {
594                break;
595            }
596        }
597        inputs.retain(
598            #[coverage(off)]
599            |i| i.complexity(&self.state.mutator) <= self.state.settings.max_input_cplx,
600        );
601        // assert!(!inputs.is_empty());
602
603        self.state.world.set_checkpoint_instant();
604        for input in inputs {
605            let cplx = input.complexity(&self.state.mutator);
606            self.state.input_idx = FuzzerInputIndex::Temporary(input);
607            self.test_and_process_input(cplx)?;
608        }
609
610        Ok(())
611    }
612
613    #[coverage(off)]
614    fn main_loop(&mut self, minify: bool) -> Result<!, ReasonForStopping<T>> {
615        self.state.world.report_event(
616            FuzzerEvent::Start,
617            Some((&self.state.fuzzer_stats, self.state.sensor_and_pool.stats().as_ref())),
618        );
619        if !minify {
620            self.process_initial_inputs()?;
621            self.state.world.report_event(
622                FuzzerEvent::DidReadCorpus,
623                Some((&self.state.fuzzer_stats, self.state.sensor_and_pool.stats().as_ref())),
624            );
625        }
626
627        self.state.world.set_checkpoint_instant();
628        let mut next_milestone = (self.state.fuzzer_stats.total_number_of_runs + 10) * 2;
629        loop {
630            let duration_since_beginning = self.state.world.elapsed_time_since_start();
631            if duration_since_beginning > self.state.settings.maximum_duration {
632                return Err(ReasonForStopping::MaxDurationReached);
633            }
634            if self.state.fuzzer_stats.total_number_of_runs >= self.state.settings.maximum_iterations {
635                return Err(ReasonForStopping::MaxIterationsReached);
636            }
637            self.process_next_input()?;
638            if self.state.fuzzer_stats.total_number_of_runs >= next_milestone {
639                update_fuzzer_stats(&mut self.state.fuzzer_stats, &mut self.state.world);
640                self.state.world.report_event(
641                    FuzzerEvent::Pulse,
642                    Some((&self.state.fuzzer_stats, self.state.sensor_and_pool.stats().as_ref())),
643                );
644                next_milestone = self.state.fuzzer_stats.total_number_of_runs * 2;
645            }
646        }
647    }
648}
649
650pub enum TerminationStatus {
651    Success = 0,
652    Crash = 1,
653    TestFailure = 2,
654    Unknown = 3,
655}
656
657#[coverage(off)]
658pub fn launch<T, M>(
659    test: Box<dyn Fn(&T) -> bool>,
660    mutator: M,
661    serializer: Box<dyn Serializer<Value = T>>,
662    sensor_and_pool: Box<dyn SensorAndPool>,
663    mut args: Arguments,
664) -> FuzzingResult<T>
665where
666    T: Clone + 'static,
667    M: Mutator<T>,
668    Fuzzer<T, M>: 'static,
669{
670    let command = &args.command;
671    let reason_for_stopping = match command {
672        FuzzerCommand::Fuzz => {
673            if !args.stop_after_first_failure {
674                let test_failure_sensor = TestFailureSensor::default();
675                let test_failure_pool = TestFailurePool::new("test_failures");
676                let sensor_and_pool = AndSensorAndPool::new(
677                    sensor_and_pool,
678                    Box::new((test_failure_sensor, test_failure_pool)),
679                    10.0,
680                    1.0,
681                );
682                let mut fuzzer = Fuzzer::new(
683                    test,
684                    mutator,
685                    serializer,
686                    Box::new(sensor_and_pool),
687                    args.clone(),
688                    World::new(args.clone()).expect(WORLD_NEW_ERROR),
689                );
690
691                let mut stats_headers = vec![CSVField::String("time".to_string())];
692                stats_headers.extend(fuzzer.state.fuzzer_stats.csv_headers());
693                stats_headers.extend(fuzzer.state.sensor_and_pool.stats().csv_headers());
694                fuzzer
695                    .state
696                    .world
697                    .append_stats_file(&stats_headers)
698                    .expect(WRITE_STATS_ERROR);
699                unsafe { fuzzer.state.set_up_signal_handler() };
700
701                let reason_for_stopping = fuzzer.main_loop(false).unwrap_err();
702                fuzzer.state.write_stats().expect(WRITE_STATS_ERROR);
703
704                reason_for_stopping
705            } else {
706                let mut fuzzer = Fuzzer::new(
707                    test,
708                    mutator,
709                    serializer,
710                    sensor_and_pool,
711                    args.clone(),
712                    World::new(args.clone()).expect(WORLD_NEW_ERROR),
713                );
714                unsafe { fuzzer.state.set_up_signal_handler() };
715
716                let mut stats_headers = vec![CSVField::String("time".to_string())];
717                stats_headers.extend(fuzzer.state.fuzzer_stats.csv_headers());
718                stats_headers.extend(fuzzer.state.sensor_and_pool.stats().csv_headers());
719                fuzzer
720                    .state
721                    .world
722                    .append_stats_file(&stats_headers)
723                    .expect(WRITE_STATS_ERROR);
724                let reason_for_stopping = fuzzer.main_loop(false).unwrap_err();
725                fuzzer.state.write_stats().expect(WRITE_STATS_ERROR);
726
727                reason_for_stopping
728            }
729        }
730        FuzzerCommand::MinifyInput { input_file } => {
731            let world = World::new(args.clone()).expect(WORLD_NEW_ERROR);
732            let value = world.read_input_file(input_file).expect(READ_INPUT_FILE_ERROR);
733            let value = serializer.from_data(&value).expect(SERIALIZER_FROM_DATA_ERROR);
734            if let Some(cache) = mutator.validate_value(&value) {
735                let mutation_step = mutator.default_mutation_step(&value, &cache);
736                args.max_input_cplx = mutator.complexity(&value, &cache) - 0.01;
737
738                let noop_sensor = NoopSensor;
739                let unit_pool = UnitPool::new(PoolStorageIndex(0));
740                let sensor_and_pool =
741                    // 100:1 might seem like an excessive ratio, but the second pool will never make progress,
742                    // therefore its relative weight willl diminish over time
743                    // if after 100 iterations, the first pool makes progress, then the ratio will be 1:1
744                    // what the exact value should be and how the ratio should evolve is an open question to me
745                    AndSensorAndPool::new(sensor_and_pool, Box::new((noop_sensor, unit_pool)), 1.0, 100.0);
746                let mut fuzzer = Fuzzer::new(
747                    test,
748                    mutator,
749                    serializer,
750                    Box::new(sensor_and_pool),
751                    args.clone(),
752                    world,
753                );
754
755                let mut subvalues: HashMap<TypeId, Vec<(*const dyn Any, f64)>> = HashMap::default();
756
757                let mut act_on_subvalue = #[coverage(off)]
758                |subvalue: &dyn Any, complexity| {
759                    subvalues
760                        .entry(subvalue.type_id())
761                        .or_default()
762                        .push((subvalue as *const _, complexity));
763                };
764
765                fuzzer
766                    .state
767                    .mutator
768                    .visit_subvalues(&value, &cache, &mut act_on_subvalue);
769                let storage_idx_1 = fuzzer.state.pool_storage.next_slot();
770                let generation = Generation(0);
771                let subvalues = CrossoverSubValueProvider::new(
772                    SubValueProviderId {
773                        idx: storage_idx_1,
774                        generation,
775                    },
776                    &value,
777                    &cache,
778                    &fuzzer.state.mutator,
779                );
780                let stored_input = FuzzedInputAndSubValueProvider {
781                    input: FuzzedInput::new(value, cache, mutation_step, generation),
782                    subvalues,
783                };
784                let storage_idx_2 = fuzzer.state.pool_storage.insert(stored_input, 1);
785
786                assert_eq!(storage_idx_1, storage_idx_2);
787
788                unsafe { fuzzer.state.set_up_signal_handler() };
789
790                fuzzer.main_loop(true).unwrap_err()
791            } else {
792                // TODO: send a better error message saying some inputs in the corpus cannot be read
793                // TODO: there should be an option to ignore invalid values
794                panic!("A value in the input corpus is invalid.");
795            }
796        }
797        FuzzerCommand::Read { input_file } => {
798            // no signal handlers are installed, but that should be ok as the exit code won't be 0
799            let mut world = World::new(args.clone()).expect(WORLD_NEW_ERROR);
800            let value = world.read_input_file(input_file).expect(READ_INPUT_FILE_ERROR);
801            let value = serializer.from_data(&value).expect(SERIALIZER_FROM_DATA_ERROR);
802            if let Some(cache) = mutator.validate_value(&value) {
803                let mutation_step = mutator.default_mutation_step(&value, &cache);
804                let input = FuzzedInput::new(value, cache, mutation_step, Generation(0));
805                let cplx = input.complexity(&mutator);
806
807                if args.detect_infinite_loop {
808                    let _old_time_left = unsafe { libc::alarm(1) };
809                    // TODO: I think setitimer should be prefered, but libc
810                    // doesn't support it on linux, see:
811                    // https://github.com/rust-lang/libc/issues/1347#event-3879031340
812
813                    // let success = unsafe {
814                    // let t = itimerval {
815                    //     it_interval: libc::timeval { tv_sec: 0, tv_usec: 0 },
816                    //     it_value: libc::timeval { tv_sec: 1, tv_usec: 0 },
817                    // };
818                    // libc::setitimer(ITIMER_REAL, &t, std::ptr::null_mut())
819                    // };
820                    // assert!(success == 0);
821                }
822
823                let result = catch_unwind(AssertUnwindSafe(
824                    #[coverage(off)]
825                    || (test)(input.value.borrow()),
826                ));
827
828                if result.is_err() || !result.unwrap() {
829                    world.report_event(FuzzerEvent::TestFailure, None);
830                    let content = serializer.to_data(&input.value);
831                    world
832                        .save_artifact(content, cplx, serializer.extension())
833                        .expect(SAVE_ARTIFACTS_ERROR);
834                    // in this case we really want to exit with a non-zero termination status here
835                    // because the Read command is only used by the input minify command from cargo-fuzzcheck
836                    // which checks that a crash happens by looking at the exit code
837                    // so we don't want to handle any error
838                    exit(TerminationStatus::TestFailure as i32);
839                } else {
840                    exit(TerminationStatus::Success as i32);
841                }
842            } else {
843                // TODO: send a better error message saying some inputs in the corpus cannot be read
844                panic!("A value in the input corpus is invalid.");
845            }
846        }
847    };
848    let _ = std::panic::take_hook();
849
850    let found_test_failure =
851        unsafe { matches!(reason_for_stopping, ReasonForStopping::TestFailure(_)) || DID_FIND_ANY_TEST_FAILURE };
852
853    FuzzingResult {
854        found_test_failure,
855        reason_for_stopping,
856    }
857}