1use core::time::Duration;
2use std::fmt;
3use std::marker::PhantomData;
4
5use libafl::corpus::ondisk::OnDiskMetadataFormat;
6use libafl::prelude::*;
7use libafl_bolts::prelude::*;
8use log4rs::Handle;
9use serde::de::DeserializeOwned;
10use serde::Serialize;
11
12use super::harness;
13use crate::fuzzer::bit_mutations::{
14 bit_mutations_dy, havoc_mutations_dy, MakeMessage, ReadMessage,
15};
16pub(crate) use crate::fuzzer::config::FuzzerConfig;
17use crate::fuzzer::config::{FuzzingTarget, MIN_BIT_CORPUS, MIN_BIT_EXECS};
18use crate::fuzzer::feedback::{MinimizingFeedback, ObjectiveFeedback};
19use crate::fuzzer::mutations::{dy_mutations, MutationConfig};
20use crate::fuzzer::stages::FocusScheduledMutator;
21use crate::fuzzer::stats_monitor::StatsMonitor;
22use crate::fuzzer::stats_stage::{StatsStage, CORPUS_EXEC, CORPUS_EXEC_MINIMAL};
23use crate::log::{load_fuzzing_client, set_experiment_fuzzing_client};
24use crate::protocol::{build_corpus, ProtocolBehavior, ProtocolTypes};
25use crate::put::PutDescriptor;
26use crate::put_registry::PutRegistry;
27use crate::trace::{ConfigTrace, Spawner, Trace, TraceContext};
28
29pub const MAP_FEEDBACK_NAME: &str = "edges";
30const EDGES_OBSERVER_NAME: &str = "edges_observer";
31
32type ConcreteExecutor<'harness, EM, H, I, OT, S, Z> =
33 InProcessExecutor<'harness, EM, H, I, OT, S, Z>;
34
35type ConcreteState<C, R, SC, I> = StdState<C, I, R, SC>;
36
37struct RunClientBuilder<'harness, H, C, R, SC, EM, F, OF, OT, CS, PT>
38where
39 PT: ProtocolTypes + 'static,
40{
41 config: FuzzerConfig,
42
43 harness_fn: &'harness mut H,
44 existing_state: Option<ConcreteState<C, R, SC, Trace<PT>>>,
45 rand: Option<R>,
46 objective_corpus: Option<SC>,
47 corpus: Option<C>,
48 scheduler: Option<CS>,
49 event_manager: EM,
50 observers: Option<OT>,
51 feedback: Option<F>,
52 objective: Option<OF>,
53 initial_inputs: Option<Vec<(Trace<PT>, &'static str)>>,
54 phantom_data: PhantomData<PT>,
55}
56
57impl<'harness, H, C, R, SC, EM, F, OF, OT, CS, PT>
58 RunClientBuilder<'harness, H, C, R, SC, EM, F, OF, OT, CS, PT>
59where
60 C: Corpus<Trace<PT>> + Serialize + DeserializeOwned,
61 R: Rand,
62 SC: Corpus<Trace<PT>> + Serialize + DeserializeOwned,
63 H: FnMut(&Trace<PT>) -> ExitKind,
64 CS: Scheduler<Trace<PT>, ConcreteState<C, R, SC, Trace<PT>>>,
65 F: Feedback<EM, Trace<PT>, OT, ConcreteState<C, R, SC, Trace<PT>>>,
66 OF: Feedback<EM, Trace<PT>, OT, ConcreteState<C, R, SC, Trace<PT>>>,
67 OT: ObserversTuple<Trace<PT>, ConcreteState<C, R, SC, Trace<PT>>>
68 + Serialize
69 + DeserializeOwned,
70 EM: EventFirer<Trace<PT>, ConcreteState<C, R, SC, Trace<PT>>>
71 + EventRestarter<ConcreteState<C, R, SC, Trace<PT>>>
72 + SendExiting
73 + EventReceiver<Trace<PT>, ConcreteState<C, R, SC, Trace<PT>>>
74 + ProgressReporter<ConcreteState<C, R, SC, Trace<PT>>>,
75 ConcreteState<C, R, SC, Trace<PT>>: HasClientPerfMonitor + HasMetadata + HasExecutions,
76 PT: ProtocolTypes + 'static,
77{
78 fn new(
79 config: FuzzerConfig,
80 harness_fn: &'harness mut H,
81 existing_state: Option<ConcreteState<C, R, SC, Trace<PT>>>,
82 event_manager: EM,
83 ) -> Self {
84 Self {
85 config,
86 harness_fn,
87 existing_state,
88 rand: None,
89 objective_corpus: None,
90 corpus: None,
91 scheduler: None,
92 event_manager,
93 observers: None,
94 feedback: None,
95 objective: None,
96 initial_inputs: None,
97 phantom_data: Default::default(),
98 }
99 }
100
101 fn with_rand(mut self, rand: R) -> Self {
102 self.rand = Some(rand);
103 self
104 }
105
106 fn with_corpus(mut self, corpus: C) -> Self {
107 self.corpus = Some(corpus);
108 self
109 }
110
111 fn with_objective_corpus(mut self, objective_corpus: SC) -> Self {
112 self.objective_corpus = Some(objective_corpus);
113 self
114 }
115
116 fn with_scheduler(mut self, scheduler: CS) -> Self {
117 self.scheduler = Some(scheduler);
118 self
119 }
120
121 fn with_feedback(mut self, feedback: F) -> Self {
122 self.feedback = Some(feedback);
123 self
124 }
125
126 fn with_objective(mut self, objective: OF) -> Self {
127 self.objective = Some(objective);
128 self
129 }
130
131 fn with_observers(mut self, observers: OT) -> Self {
132 self.observers = Some(observers);
133 self
134 }
135
136 fn with_initial_inputs(mut self, initial_inputs: Vec<(Trace<PT>, &'static str)>) -> Self {
137 self.initial_inputs = Some(initial_inputs);
138 self
139 }
140
141 fn run_client<PB>(mut self, put_registry: &'harness PutRegistry<PB>) -> Result<(), Error>
142 where
143 PB: ProtocolBehavior<ProtocolTypes = PT> + 'static,
144 {
145 let mut feedback = self.feedback.unwrap();
146 let mut objective = self.objective.unwrap();
147
148 let mut state = self.existing_state.unwrap_or_else(|| {
150 StdState::new(
151 self.rand.unwrap(),
152 self.corpus.unwrap(),
153 self.objective_corpus.unwrap(),
154 &mut feedback,
155 &mut objective,
156 )
157 .unwrap()
158 });
159
160 let FuzzerConfig {
161 initial_corpus_dir,
162 initial_corpus,
163 max_iters,
164 mutation_config,
165 ..
166 } = self.config;
167
168 let mutation_config_dy = MutationConfig {
195 with_focus: false,
196 ..mutation_config
197 };
198 let mutator_dy = crate::fuzzer::stages::StepLockedStackMutator::new(
209 crate::fuzzer::stages::GeometricStackMutator::new(HavocScheduledMutator::new(
210 dy_mutations(mutation_config_dy, <PT>::signature(), put_registry),
211 )),
212 self.config.mutation_stage_config.step_locked_stacking,
213 );
214 let cb_dy = |_: &mut _, _: &mut _, _: &mut _, _: &mut _| -> Result<bool, Error> {
216 if mutation_config.with_dy {
217 log::debug!("[*] DY StdMutationalStage");
218 Ok(true)
219 } else {
220 Ok(false)
221 }
222 };
223 let stage_dy = IfStage::new(
224 cb_dy,
225 tuple_list!(StdMutationalStage::with_max_iterations(
226 mutator_dy,
227 self.config.mutation_stage_config.max_iterations_per_stage
228 )),
229 );
230
231 let mutation_config_bit = MutationConfig {
233 with_focus: false,
234 ..mutation_config
235 };
236 let mutator_bit = HavocScheduledMutator::new(bit_mutations_dy::<
237 StdState<C, Trace<PT>, R, SC>,
238 PT,
239 PB,
240 >(mutation_config_bit, put_registry));
241 let cb_bit_level = |_: &mut _,
244 _: &mut _,
245 state: &mut ConcreteState<C, R, SC, Trace<PT>>,
246 _: &mut _|
247 -> Result<bool, Error> {
248 if !mutation_config.with_bit_level {
249 return Ok(false);
250 }
251 if !mutation_config.with_dy
254 || (*state.executions() > MIN_BIT_EXECS && state.corpus().count() > MIN_BIT_CORPUS)
255 {
256 log::debug!("[*] BIT StdMutationalStage");
257 Ok(true)
258 } else {
259 Ok(false)
260 }
261 };
262 let mutation_config_focus = mutation_config; let mutator_bit_focus = FocusScheduledMutator::new(
264 tuple_list!(MakeMessage::new(mutation_config_focus, put_registry)),
265 havoc_mutations_dy::<StdState<C, Trace<PT>, R, SC>, PT>(mutation_config_focus),
266 tuple_list!(ReadMessage::new(mutation_config_focus, put_registry)),
267 );
268 let cb_focus_bit_level = |_: &mut _,
269 _: &mut _,
270 _: &mut ConcreteState<C, R, SC, Trace<PT>>,
271 _: &mut _|
272 -> Result<bool, Error> {
273 if !mutation_config.with_focus {
274 return Ok(false);
275 }
276 log::debug!("[*] BIT FocusScheduledMutator");
277 Ok(true)
278 };
279 let stage_bit = IfStage::new(
280 cb_bit_level,
281 tuple_list!(
282 StdMutationalStage::with_max_iterations(
283 mutator_bit,
284 self.config.mutation_stage_config.max_iterations_per_stage
285 ), IfStage::new(
287 cb_focus_bit_level,
288 tuple_list!(StdMutationalStage::with_max_iterations(
289 mutator_bit_focus,
290 self.config.mutation_stage_config.max_iterations_per_stage
291 ),)
292 ),
293 ), );
295 let stage_test_input = ClosureStage::new(
298 |_: &mut _,
299 _: &mut _,
300 cs: &mut ConcreteState<C, R, SC, Trace<PT>>,
301 _: &mut _|
302 -> Result<(), Error> {
303 if cfg!(feature = "introspection") {
304 CORPUS_EXEC.increment();
305 log::debug!("[*] Introspection stage");
306
307 let Some(current_idx) = cs.corpus().current() else {
310 return Ok(());
311 };
312
313 let mut current_testcase = cs.corpus().get(*current_idx)?.borrow_mut();
314 let current_input = current_testcase.load_input(cs.corpus()).unwrap();
316 let spawner = Spawner::new(put_registry.clone());
317 let mut ctx = TraceContext::new_config(
318 spawner,
319 ConfigTrace {
320 with_bit_level: self.config.mutation_config.with_bit_level,
321 ..Default::default()
322 },
323 );
324 let error_ok;
325 match current_input.execute_until_step_wrap(
326 &mut ctx,
327 current_input.len(),
328 &mut 0,
329 true,
330 ) {
331 Ok(()) => {
332 CORPUS_EXEC_MINIMAL.increment();
333 return Ok(());
334 }
335 Err(e) => match e {
336 crate::error::Error::Put(_) => {
337 error_ok = true;
338 }
339 crate::error::Error::SecurityClaim(_) => {
340 error_ok = true;
341 }
342 _ => {
343 return Ok(());
345 }
346 },
347 }
348 if error_ok {
349 if ctx.executed_until == current_input.len() - 1 {
352 CORPUS_EXEC_MINIMAL.increment();
353 return Ok(());
354 }
355 }
356 }
357 Ok(())
358 },
359 );
360
361 let mut stages = tuple_list!(stage_test_input, stage_dy, stage_bit, StatsStage::new());
363
364 let mut fuzzer: StdFuzzer<CS, F, _, _, OF> =
365 StdFuzzer::new(self.scheduler.unwrap(), feedback, objective);
366
367 let mut executor: ConcreteExecutor<
368 'harness,
369 EM,
370 H,
371 Trace<PT>,
372 OT,
373 ConcreteState<C, R, SC, Trace<PT>>,
374 StdFuzzer<CS, F, _, _, OF>,
375 > = InProcessExecutor::with_timeout(
376 self.harness_fn,
377 self.observers.unwrap(),
380 &mut fuzzer,
381 &mut state,
382 &mut self.event_manager,
383 Duration::new(5, 0),
384 )?;
385
386 if state.corpus().is_empty() {
388 if initial_corpus.is_none() && initial_corpus_dir.exists() {
389 state
390 .load_initial_inputs(
391 &mut fuzzer,
392 &mut executor,
393 &mut self.event_manager,
394 &[initial_corpus_dir.clone()],
395 )
396 .unwrap_or_else(|err| {
397 panic!(
398 "Failed to load initial corpus at {:?}: {}",
399 &initial_corpus_dir, err
400 )
401 });
402 log::info!("Imported {} inputs from disk.", state.corpus().count());
403 } else {
404 if initial_corpus.is_some() {
405 log::info!(
406 "--corpus given: using the requested seed corpus instead of {:?}.",
407 &initial_corpus_dir
408 );
409 } else {
410 log::info!("Initial seed corpus not found. Using embedded `default` seeds.");
411 }
412
413 for (seed, name) in self.initial_inputs.unwrap() {
414 log::info!("Using seed {}", name);
415 fuzzer
416 .add_input(&mut state, &mut executor, &mut self.event_manager, seed)
417 .expect("Failed to add input");
418 }
419 }
420 }
421
422 if let Some(max_iters) = max_iters {
423 fuzzer.fuzz_loop_for(
424 &mut stages,
425 &mut executor,
426 &mut state,
427 &mut self.event_manager,
428 max_iters,
429 )?;
430 } else {
431 fuzzer.fuzz_loop(
432 &mut stages,
433 &mut executor,
434 &mut state,
435 &mut self.event_manager,
436 )?;
437 }
438 Ok(())
439 }
440}
441
442type EdgesObserver = HitcountsMapObserver<StdMapObserver<'static, u8, false>>;
443type EdgesTracking = ExplicitTracking<EdgesObserver, true, false>;
444
445type ConcreteObservers<'a> = (EdgesTracking, (TimeObserver, ()));
446
447type ConcreteFeedback<'a> =
448 CombinedFeedback<MaxMapFeedback<EdgesTracking, EdgesObserver>, TimeFeedback, LogicEagerOr>;
449
450impl<'harness, 'a, H, SC, C, R, EM, OF, CS, PT>
451 RunClientBuilder<
452 'harness,
453 H,
454 C,
455 R,
456 SC,
457 EM,
458 CombinedFeedback<
459 MinimizingFeedback<
460 StdState<
461 CachedOnDiskCorpus<Trace<PT>>,
462 Trace<PT>,
463 RomuDuoJrRand,
464 CachedOnDiskCorpus<Trace<PT>>,
465 >,
466 PT,
467 >,
468 ConcreteFeedback<'a>,
469 LogicEagerOr,
470 >,
471 OF,
472 ConcreteObservers<'a>,
473 CS,
474 PT,
475 >
476where
477 Trace<PT>: Input,
478 C: Corpus<Trace<PT>> + fmt::Debug,
480 R: Rand,
481 SC: Corpus<Trace<PT>> + fmt::Debug,
482 H: FnMut(&Trace<PT>) -> ExitKind,
483 OF: Feedback<EM, Trace<PT>, ConcreteObservers<'a>, ConcreteState<C, R, SC, Trace<PT>>>,
484 CS: Scheduler<Trace<PT>, ConcreteState<C, R, SC, Trace<PT>>>,
485 EM: EventFirer<Trace<PT>, ConcreteState<C, R, SC, Trace<PT>>>
486 + EventRestarter<ConcreteState<C, R, SC, Trace<PT>>>
487 + HasEventManagerId
488 + ProgressReporter<ConcreteState<C, R, SC, Trace<PT>>>,
489 ConcreteState<C, R, SC, Trace<PT>>: HasClientPerfMonitor + HasMetadata + HasExecutions,
490 PT: ProtocolTypes + 'static,
491{
492 fn create_feedback_observers(&self) -> (ConcreteFeedback<'a>, ConcreteObservers<'a>) {
493 #[cfg(not(test))]
494 let map = unsafe {
495 pub use libafl_targets::{EDGES_MAP, MAX_EDGES_FOUND};
496 &mut EDGES_MAP[0..MAX_EDGES_FOUND]
497 };
498
499 #[cfg(test)]
500 let map = unsafe {
501 pub const EDGES_MAP_SIZE: usize = 65536;
503 pub static mut EDGES_MAP: [u8; EDGES_MAP_SIZE] = [0; EDGES_MAP_SIZE];
504 pub static mut MAX_EDGES_FOUND: usize = 0;
505 &mut EDGES_MAP[0..MAX_EDGES_FOUND]
506 };
507
508 {
509 let time_observer = TimeObserver::new("time");
510 let edges_observer =
511 HitcountsMapObserver::new(unsafe { StdMapObserver::new(EDGES_OBSERVER_NAME, map) });
512
513 let edges_observer = edges_observer.track_indices();
514
515 let map_feedback = MaxMapFeedback::with_name(MAP_FEEDBACK_NAME, &edges_observer);
516
517 let feedback = feedback_or!(
518 map_feedback,
522 TimeFeedback::new(&time_observer)
525 );
526 let observers = tuple_list!(edges_observer, time_observer);
527 (feedback, observers)
528 }
529 }
530}
531
532pub fn start<PB>(
534 put_registry: &PutRegistry<PB>,
535 config: FuzzerConfig,
536 log_handle: Handle,
537) -> Result<(), Error>
538where
539 PB: ProtocolBehavior + Clone + 'static,
540{
541 let FuzzerConfig {
542 core_definition,
543 corpus_dir,
544 objective_dir,
545 static_seed: _,
546 stats_file,
547 stats_interval,
548 broker_port,
549 tui,
550 no_launcher,
551 mutation_stage_config,
552 is_experiment,
553 log_folder,
554 verbosity,
555 target,
556 initial_corpus,
557 ..
558 } = &config;
559
560 log::info!("Running on cores: {}", &core_definition);
561 log::info!("Config: {:?}\n\nlog_handle: {:?}", &config, &log_handle);
562
563 let mut run_client = |state: Option<StdState<_, Trace<PB::ProtocolTypes>, _, _>>,
564 event_manager: LlmpRestartingEventManager<
565 _,
566 Trace<PB::ProtocolTypes>,
567 StdState<_, Trace<PB::ProtocolTypes>, _, _>,
568 _,
569 StdShMemProvider,
570 >,
571 _client_description: ClientDescription|
572 -> Result<(), Error> {
573 if *is_experiment {
574 log_handle
575 .clone()
576 .set_config(set_experiment_fuzzing_client(log_folder, *verbosity));
577 } else {
578 log_handle.clone().set_config(load_fuzzing_client());
579 }
580 log::info!("log_handle: {:?}", &log_handle);
581
582 let default_put = put_registry.default_put_descriptor();
583
584 let mut boxed_harness_fn: Box<dyn FnMut(&Trace<PB::ProtocolTypes>) -> ExitKind> =
588 match target {
589 FuzzingTarget::Single(put) => {
590 let put_desc = if let Some(put_desc) = put {
591 put_desc
592 } else {
593 &default_put
594 };
595 Box::new(|input: &_| harness::harness::<PB>(put_registry, put_desc, input))
596 }
597 FuzzingTarget::Differential(first, second) => Box::new(|input: &_| {
598 harness::differential_harness::<PB>(put_registry, first, second, input)
599 }),
600 };
601
602 let harness_fn = &mut (|input: &_| boxed_harness_fn(input));
603
604 let requested_puts: Vec<PutDescriptor> = match target {
608 FuzzingTarget::Single(put) => vec![put.clone().unwrap_or_else(|| default_put.clone())],
609 FuzzingTarget::Differential(first, second) => vec![first.clone(), second.clone()],
610 };
611
612 let default_corpus = vec!["default".to_string()];
614 let corpus_list: &[String] = initial_corpus.as_deref().unwrap_or(&default_corpus);
615
616 let mut builder = RunClientBuilder::new(config.clone(), harness_fn, state, event_manager);
617 builder = builder
618 .with_initial_inputs(build_corpus(put_registry, &requested_puts, corpus_list))
619 .with_rand(StdRand::new())
620 .with_corpus(
621 CachedOnDiskCorpus::with_meta_format(
623 corpus_dir.clone(),
624 4096, Some(OnDiskMetadataFormat::Json),
626 )
627 .unwrap(),
628 )
629 .with_objective_corpus(
630 CachedOnDiskCorpus::with_meta_format(
631 objective_dir.clone(),
632 4096, Some(OnDiskMetadataFormat::Json),
634 )
635 .unwrap(),
636 )
637 .with_objective(feedback_or_fast!(
638 ObjectiveFeedback::new(),
640 CrashFeedback::new(),
641 TimeoutFeedback::new()
642 ));
643
644 {
655 log::warn!("Running without minimizer is unsupported");
657 let (feedback, observer) = builder.create_feedback_observers();
658 let feedback_with_minimizer = feedback_or!(
659 MinimizingFeedback::new(mutation_stage_config.with_truncation),
660 feedback
661 );
662 #[cfg(feature = "with-min-scheduler")]
663 let scheduler = IndexesLenTimeMinimizerScheduler::new(QueueScheduler::new());
664 #[cfg(not(feature = "with-min-scheduler"))]
665 let scheduler = RandScheduler::new();
666
667 builder = builder
668 .with_feedback(feedback_with_minimizer)
669 .with_observers(observer)
670 .with_scheduler(scheduler);
671 } builder.run_client(put_registry)
675 };
676
677 if *no_launcher {
678 let stats_monitor = StatsMonitor::with_raw_output(stats_file.clone(), *stats_interval);
679
680 let (state, restarting_mgr) =
681 setup_restarting_mgr_std(stats_monitor, *broker_port, EventConfig::AlwaysUnique)?;
682
683 run_client(
684 state,
685 restarting_mgr,
686 ClientDescription::new(0, 0, CoreId(0)),
687 )
688 } else {
689 let cores = Cores::from_cmdline(config.core_definition.as_str()).unwrap();
690 let configuration: EventConfig = "launcher default".into();
691 let sh_mem_provider = StdShMemProvider::new().expect("Failed to init shared memory");
692
693 let out_path = log_folder.join("puffin_main_broker_stdout.log");
703 let out_file = out_path
704 .to_str()
705 .expect("failed to create path to redirect fuzzer clients' stdout");
706 let err_path = log_folder.join("puffin_main_broker_stderr.log");
707 let err_file = err_path
708 .to_str()
709 .expect("failed to create path to redirect fuzzer clients' stderr");
710
711 if *tui {
712 let stats_monitor = StatsMonitor::with_tui_output(stats_file.clone(), *stats_interval);
713
714 Launcher::builder()
715 .shmem_provider(sh_mem_provider)
716 .configuration(configuration)
717 .monitor(stats_monitor)
718 .run_client(&mut run_client)
719 .cores(&cores)
720 .broker_port(*broker_port)
721 .stdout_file(Some(out_file))
722 .stderr_file(Some(err_file))
723 .build()
724 .launch()
725 } else {
726 let stats_monitor = StatsMonitor::with_raw_output(stats_file.clone(), *stats_interval);
727
728 Launcher::builder()
729 .shmem_provider(sh_mem_provider)
730 .configuration(configuration)
731 .monitor(stats_monitor)
732 .run_client(&mut run_client)
733 .cores(&cores)
734 .broker_port(*broker_port)
735 .stdout_file(Some(out_file))
736 .stderr_file(Some(err_file))
737 .build()
738 .launch()
739 }
740 }
741}