1use crate::{
7 fuzzer::{
8 executors::inprocess::InProcessExecutor, feedbacks::ReportingMapFeedback,
9 messages::FuzzerMessage,
10 },
11 Tsffs,
12};
13use anyhow::{anyhow, Result};
14use libafl::{
15 feedback_or, feedback_or_fast,
16 inputs::{HasBytesVec, Input},
17 prelude::{
18 havoc_mutations, ondisk::OnDiskMetadataFormat, tokens_mutations, AFLppRedQueen, BytesInput,
19 CachedOnDiskCorpus, Corpus, CrashFeedback, ExitKind, HasCurrentCorpusIdx, HasTargetBytes,
20 HitcountsMapObserver, I2SRandReplace, MaxMapFeedback, OnDiskCorpus, RandBytesGenerator,
21 SimpleEventManager, SimpleMonitor, StdCmpValuesObserver, StdMOptMutator, StdMapObserver,
22 StdScheduledMutator, TimeFeedback, TimeObserver, Tokens,
23 },
24 schedulers::{
25 powersched::PowerSchedule, IndexesLenTimeMinimizerScheduler, StdWeightedScheduler,
26 },
27 stages::{
28 mutational::MultiMutationalStage, CalibrationStage, ColorizationStage, DumpToDiskStage,
29 GeneralizationStage, IfStage, StdMutationalStage, StdPowerMutationalStage,
30 SyncFromDiskStage, TracingStage,
31 },
32 state::{HasCorpus, HasMetadata, StdState},
33 Fuzzer, StdFuzzer,
34};
35use libafl_bolts::{
36 current_nanos,
37 prelude::{OwnedMutSlice, OwnedRefMut},
38 rands::StdRand,
39 tuples::{tuple_list, Merge},
40 AsMutSlice, AsSlice,
41};
42use libafl_targets::{AFLppCmpLogObserver, AFLppCmplogTracingStage};
43use simics::{api::AsConfObject, debug, trace, warn};
44use std::{
45 cell::RefCell, fmt::Debug, fs::write, io::stderr, slice::from_raw_parts_mut,
46 sync::mpsc::channel, thread::spawn,
47};
48use tokenize::{tokenize_executable_file, tokenize_src_file};
49use tracing::{level_filters::LevelFilter, Level};
50use tracing_subscriber::{
51 filter::filter_fn, fmt, layer::SubscriberExt, registry, util::SubscriberInitExt, Layer,
52};
53
54pub mod executors;
55pub mod feedbacks;
56pub mod messages;
57pub mod tokenize;
58
59#[derive(Clone, PartialEq, Eq)]
60pub(crate) struct Testcase {
61 pub testcase: BytesInput,
62 pub cmplog: bool,
63}
64
65impl Debug for Testcase {
66 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67 f.debug_struct("Testcase")
68 .field(
69 "testcase",
70 &format!(
71 "{:?}{} ({} bytes)",
72 &self.testcase.bytes()[..(if self.testcase.bytes().len() < 32 {
73 self.testcase.bytes().len()
74 } else {
75 32
76 })],
77 if self.testcase.bytes().len() >= 32 {
78 "..."
79 } else {
80 ""
81 },
82 self.testcase.bytes().len()
83 ),
84 )
85 .field("cmplog", &self.cmplog)
86 .finish()
87 }
88}
89
90#[derive(Debug, Clone, Default, PartialEq, Eq)]
91pub(crate) struct ShutdownMessage {}
92
93impl Tsffs {
94 const EDGES_OBSERVER_NAME: &'static str = "coverage";
95 const AFLPP_CMP_OBSERVER_NAME: &'static str = "aflpp_cmplog";
96 const CMPLOG_OBSERVER_NAME: &'static str = "cmplog";
97 const TIME_OBSERVER_NAME: &'static str = "time";
98 const TIMEOUT_FEEDBACK_NAME: &'static str = "time";
99 const CORPUS_CACHE_SIZE: usize = 4096;
100
101 pub fn start_fuzzer_thread(&mut self) -> Result<()> {
103 if self.fuzz_thread.get().is_some() {
104 warn!(self.as_conf_object(), "Fuzz thread already started but start_fuzzer_thread called. Returning without error.");
105 return Ok(());
107 }
108
109 debug!(self.as_conf_object_mut(), "Starting fuzzer thread");
110
111 let (tx, orx) = channel::<ExitKind>();
112 let (otx, rx) = channel::<Testcase>();
113 let (stx, srx) = channel::<ShutdownMessage>();
114 let (mtx, mrx) = channel::<FuzzerMessage>();
115
116 self.fuzzer_tx
117 .set(tx)
118 .map_err(|_| anyhow!("Fuzzer sender already set"))?;
119 self.fuzzer_rx
120 .set(rx)
121 .map_err(|_| anyhow!("Fuzzer receiver already set"))?;
122 self.fuzzer_shutdown
123 .set(stx)
124 .map_err(|_| anyhow!("Fuzzer shutdown sender already set"))?;
125 self.fuzzer_messages
126 .set(mrx)
127 .map_err(|_| anyhow!("Fuzzer messages receiver already set"))?;
128
129 let client = RefCell::new((otx, orx));
130
131 let coverage_map = unsafe {
132 from_raw_parts_mut(
133 self.coverage_map
134 .get_mut()
135 .ok_or_else(|| anyhow!("Coverage map not set"))?
136 .as_mut_slice()
137 .as_mut_ptr(),
138 Self::COVERAGE_MAP_SIZE,
139 )
140 };
141
142 let aflpp_cmp_map = Box::leak(unsafe {
143 Box::from_raw(
144 *self
145 .aflpp_cmp_map_ptr
146 .get()
147 .ok_or_else(|| anyhow!("Comparison map pointer not set"))?,
148 )
149 });
150
151 let aflpp_cmp_map_dup = Box::leak(unsafe {
152 Box::from_raw(
153 *self
154 .aflpp_cmp_map_ptr
155 .get()
156 .ok_or_else(|| anyhow!("Comparison map pointer not set"))?,
157 )
158 });
159
160 let cmplog_enabled = self.cmplog;
161 let corpus_directory = self.corpus_directory.clone();
162 let solutions_directory = self.solutions_directory.clone();
163 let executable_tokens = self
164 .token_executables
165 .iter()
166 .map(tokenize_executable_file)
167 .collect::<Result<Vec<_>>>()?
168 .into_iter()
169 .flatten()
170 .collect::<Vec<_>>();
171 let src_file_tokens = self
172 .token_src_files
173 .iter()
174 .map(|f| {
175 tokenize_src_file(f)
176 .map(|t| t.iter().map(|s| s.as_bytes().to_vec()).collect::<Vec<_>>())
177 })
178 .collect::<Result<Vec<_>>>()?
179 .into_iter()
180 .flatten()
181 .collect::<Vec<_>>();
182 let token_files = self.token_files.clone();
183 let input_tokens = self.tokens.clone();
184 let generate_random_corpus = self.generate_random_corpus;
185 let initial_random_corpus_size = self.initial_random_corpus_size;
186 let debug_log_libafl = self.debug_log_libafl;
187 let initial_contents = self
188 .use_initial_as_corpus
189 .then(|| {
190 self.start_info
191 .get()
192 .map(|si| BytesInput::new(si.contents.clone()))
193 })
194 .flatten();
195
196 self.fuzz_thread
199 .set(spawn(move || -> Result<()> {
200 if debug_log_libafl {
201 let reg = registry().with({
202 fmt::layer()
203 .compact()
204 .with_thread_ids(true)
205 .with_thread_names(true)
206 .with_writer(stderr)
207 .with_filter(LevelFilter::TRACE)
208 .with_filter(filter_fn(|metadata| {
209 !(metadata.target() == "libafl_bolts::llmp"
211 && matches!(metadata.level(), &Level::TRACE))
212 }))
213 });
214
215 reg.try_init()
216 .map_err(|e| {
217 eprintln!("Could not install tracing subscriber: {}", e);
218 e
219 })
220 .ok();
221 }
222
223 let mut harness = |input: &BytesInput| {
224 let testcase = BytesInput::new(input.target_bytes().as_slice().to_vec());
225 client
226 .borrow_mut()
227 .0
228 .send(Testcase {
229 testcase,
230 cmplog: false,
231 })
232 .expect("Failed to send testcase message");
233
234 let status = match client.borrow_mut().1.recv() {
235 Err(e) => panic!("Error receiving status: {e}"),
236 Ok(m) => m,
237 };
238
239 status
240 };
241
242 let mut aflpp_cmp_harness = |input: &BytesInput| {
243 let testcase = BytesInput::new(input.target_bytes().as_slice().to_vec());
244 client
245 .borrow_mut()
246 .0
247 .send(Testcase {
248 testcase,
249 cmplog: true,
250 })
251 .expect("Failed to send testcase message");
252
253 let status = match client.borrow_mut().1.recv() {
254 Err(e) => panic!("Error receiving status: {e}"),
255 Ok(m) => m,
256 };
257
258 status
259 };
260
261 let mut tracing_harness = aflpp_cmp_harness;
262
263 let edges_observer = HitcountsMapObserver::new(StdMapObserver::from_mut_slice(
264 Self::EDGES_OBSERVER_NAME,
265 OwnedMutSlice::from(coverage_map),
266 ));
267
268 let aflpp_cmp_observer = AFLppCmpLogObserver::new(
269 Self::AFLPP_CMP_OBSERVER_NAME,
270 OwnedRefMut::Ref(aflpp_cmp_map),
271 true,
272 );
273
274 let cmplog_observer = StdCmpValuesObserver::new(
275 Self::CMPLOG_OBSERVER_NAME,
276 OwnedRefMut::Ref(aflpp_cmp_map_dup),
277 true,
278 );
279 let time_observer = TimeObserver::new(Self::TIME_OBSERVER_NAME);
280
281 let map_feedback = ReportingMapFeedback::new(
282 MaxMapFeedback::tracking(&edges_observer, true, true),
283 mtx.clone(),
284 );
285 let time_feedback = TimeFeedback::with_observer(&time_observer);
286
287 let crash_feedback = CrashFeedback::new();
288 let timeout_feedback = TimeFeedback::new(Self::TIMEOUT_FEEDBACK_NAME);
289
290 let solutions = OnDiskCorpus::with_meta_format(
291 solutions_directory.clone(),
292 OnDiskMetadataFormat::JsonPretty,
293 )
294 .map_err(|e| {
295 eprintln!("Failed to initialize solutions corpus: {e}");
296 anyhow!("Failed to initialize solutions corpus: {e}")
297 })?;
298
299 let corpus = CachedOnDiskCorpus::with_meta_format(
300 corpus_directory.clone(),
301 Self::CORPUS_CACHE_SIZE,
302 Some(OnDiskMetadataFormat::Json),
303 )
304 .map_err(|e| {
305 eprintln!("Failed to initialize corpus: {e}");
306 anyhow!("Failed to initialize corpus: {e}")
307 })?;
308
309 let calibration_stage = CalibrationStage::new(&map_feedback);
311 let colorization_stage = ColorizationStage::new(&edges_observer);
312 let generalization_stage = GeneralizationStage::new(&edges_observer);
313
314 let mut feedback = feedback_or!(map_feedback, time_feedback);
315 let mut objective = feedback_or_fast!(crash_feedback, timeout_feedback);
316
317 let mut state = StdState::new(
318 StdRand::with_seed(current_nanos()),
319 corpus,
320 solutions,
321 &mut feedback,
322 &mut objective,
323 )
324 .map_err(|e| {
325 eprintln!("Couldn't initialize fuzzer state: {e}");
326 anyhow!("Couldn't initialize state: {e}")
327 })?;
328
329 let mut tokens = Tokens::default().add_from_files(token_files)?;
330
331 tokens.add_tokens(executable_tokens);
332 tokens.add_tokens(src_file_tokens);
333 tokens.add_tokens(input_tokens);
334
335 state.add_metadata(tokens);
336
337 let scheduler =
338 IndexesLenTimeMinimizerScheduler::new(StdWeightedScheduler::with_schedule(
339 &mut state,
340 &edges_observer,
341 Some(PowerSchedule::EXPLORE),
342 ));
343
344 let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
345
346 let monitor = {
347 let mtx = mtx.clone();
348 SimpleMonitor::new(move |s| {
349 mtx.send(FuzzerMessage::String(s.to_string()))
350 .expect("Failed to send monitor message");
351 })
352 };
353
354 let mut manager = SimpleEventManager::new(monitor);
355
356 let mut executor = InProcessExecutor::new(
357 &mut harness,
358 tuple_list!(edges_observer, time_observer),
359 &mut fuzzer,
360 &mut manager,
361 )
362 .map_err(|e| {
363 eprintln!("Couldn't initialize fuzzer executor: {e}");
364 anyhow!("Couldn't initialize fuzzer executor: {e}")
365 })?;
366
367 let aflpp_cmp_executor = InProcessExecutor::new(
368 &mut aflpp_cmp_harness,
369 tuple_list!(aflpp_cmp_observer),
370 &mut fuzzer,
371 &mut manager,
372 )
373 .map_err(|e| {
374 eprintln!("Couldn't initialize fuzzer AFL++ cmplog executor: {e}");
375 anyhow!("Couldn't initialize fuzzer AFL++ cmplog executor: {e}")
376 })?;
377
378 let tracing_executor = InProcessExecutor::new(
379 &mut tracing_harness,
380 tuple_list!(cmplog_observer),
381 &mut fuzzer,
382 &mut manager,
383 )
384 .map_err(|e| {
385 eprintln!("Couldn't initialize fuzzer AFL++ cmplog executor: {e}");
386 anyhow!("Couldn't initialize fuzzer AFL++ cmplog executor: {e}")
387 })?;
388
389 let input_to_state_stage = StdMutationalStage::new(StdScheduledMutator::new(
390 tuple_list!(I2SRandReplace::new()),
391 ));
392 let havoc_mutational_stage = StdPowerMutationalStage::new(
393 StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations())),
394 );
395 let mopt_mutational_stage = StdPowerMutationalStage::new(
396 StdMOptMutator::new(
397 &mut state,
398 havoc_mutations().merge(tokens_mutations()),
399 7,
400 5,
401 )
402 .map_err(|e| {
403 eprintln!("Couldn't initialize fuzzer MOpt mutator: {e}");
404 anyhow!("Couldn't initialize fuzzer MOpt mutator: {e}")
405 })?,
406 );
407 let redqueen_mutational_stage =
408 MultiMutationalStage::new(AFLppRedQueen::with_cmplog_options(true, true));
409 let aflpp_tracing_stage = AFLppCmplogTracingStage::with_cmplog_observer_name(
410 aflpp_cmp_executor,
411 Self::AFLPP_CMP_OBSERVER_NAME,
412 );
413 let tracing_stage = TracingStage::new(tracing_executor);
414 let synchronize_corpus_stage =
415 SyncFromDiskStage::with_from_file(corpus_directory.clone());
416 let dump_corpus_stage = DumpToDiskStage::new(
417 |input: &BytesInput, _state: &_| input.target_bytes().as_slice().to_vec(),
418 corpus_directory.clone(),
419 solutions_directory.clone(),
420 )
421 .map_err(|e| {
422 eprintln!("Couldn't initialize fuzzer dump to disk stage: {e}");
423 anyhow!("Couldn't initialize fuzzer dump to disk stage: {e}")
424 })?;
425
426 if let Some(contents) = initial_contents {
427 write(
428 corpus_directory.join(contents.generate_name(0)),
429 contents.bytes(),
430 )?;
431 }
432
433 if state.must_load_initial_inputs() {
434 state
435 .load_initial_inputs(
436 &mut fuzzer,
437 &mut executor,
438 &mut manager,
439 std::slice::from_ref(&corpus_directory),
440 )
441 .map_err(|e| {
442 eprintln!(
443 "Error loading initial inputs from {corpus_directory:?}: {e}"
444 );
445 anyhow!("Error loading initial inputs from {corpus_directory:?}: {e}")
446 })?;
447
448 if state.corpus().count() < 1 && generate_random_corpus {
449 let mut generator = RandBytesGenerator::new(64);
450 state
451 .generate_initial_inputs(
452 &mut fuzzer,
453 &mut executor,
454 &mut generator,
455 &mut manager,
456 initial_random_corpus_size,
457 )
458 .map_err(|e| {
459 eprintln!("Error generating random inputs: {e}");
460 anyhow!("Error generating random inputs: {e}")
461 })?;
462 }
463 }
464
465 if state.corpus().count() < 1 {
466 panic!(
467 "No interesting cases found from inputs! This may mean \
468 your harness is incorrect (check your arguments), your inputs \
469 are not triggering new code paths, or all inputs are causing \
470 crashes.",
471 );
472 }
473
474 let mut stages = tuple_list!(
475 calibration_stage,
476 generalization_stage,
477 IfStage::new(
478 |_fuzzer: &mut _,
479 _executor: &mut _,
480 state: &mut StdState<_, CachedOnDiskCorpus<_>, _, _>,
481 _event_manager: &mut _|
482 -> Result<bool, libafl::Error> {
483 Ok(cmplog_enabled
484 && state
485 .corpus()
486 .get(
487 state
488 .current_corpus_idx()
489 .map_err(|e| {
490 eprintln!(
491 "Error getting current corpus index: {e}"
492 );
493 e
497 })?
498 .ok_or_else(|| {
499 eprintln!("No current corpus index");
500
501 libafl::Error::unknown("No current corpus index")
502 })?,
503 )
504 .map_err(|e| {
505 eprintln!("Error getting current corpus entry: {e}");
506 e
507 })?
508 .borrow()
509 .scheduled_count()
510 == 1)
511 },
512 tuple_list!(
513 colorization_stage,
514 aflpp_tracing_stage,
515 redqueen_mutational_stage
516 )
517 ),
518 IfStage::new(
519 |_fuzzer: &mut _,
520 _executor: &mut _,
521 _state: &mut StdState<_, CachedOnDiskCorpus<_>, _, _>,
522 _event_manager: &mut _|
523 -> Result<bool, libafl::Error> {
524 Ok(cmplog_enabled)
525 },
526 tuple_list!(tracing_stage, input_to_state_stage)
527 ),
528 havoc_mutational_stage,
529 mopt_mutational_stage,
530 dump_corpus_stage,
531 synchronize_corpus_stage,
532 );
533
534 loop {
535 if let Ok(_msg) = srx.try_recv() {
537 break;
538 }
539
540 fuzzer
541 .fuzz_one(&mut stages, &mut executor, &mut state, &mut manager)
542 .map_err(|e| {
543 eprintln!("Error running iteration of fuzzing loop: {e}");
544 anyhow!("Error running iteration of fuzzing loop: {e}")
545 })?;
546 }
547
548 println!("Fuzzing loop exited.");
549
550 Ok(())
551 }))
552 .map_err(|_| anyhow!("Fuzzer thread already set"))?;
553
554 Ok(())
555 }
556
557 pub fn send_shutdown(&mut self) -> Result<()> {
558 if let Some(stx) = self.fuzzer_shutdown.get_mut() {
559 stx.send(ShutdownMessage::default())?;
560 }
561
562 Ok(())
563 }
564
565 pub fn get_testcase(&mut self) -> Result<Testcase> {
566 let testcase = if let Some(testcase) = self.repro_testcase.as_ref() {
567 debug!(self.as_conf_object(), "Using repro testcase");
568 Testcase {
569 testcase: BytesInput::new(testcase.clone()),
570 cmplog: false,
571 }
572 } else {
573 self.fuzzer_rx
574 .get_mut()
575 .ok_or_else(|| anyhow!("Fuzzer receiver not set"))?
576 .recv()
577 .map_err(|e| anyhow!("Error receiving from fuzzer: {e}"))?
578 };
579
580 if self.keep_all_corpus {
581 let testcase_name = testcase.testcase.generate_name(0);
582 trace!(
583 self.as_conf_object(),
584 "Writing testcase {}.testcase to corpus directory: {}",
585 &testcase_name,
586 self.corpus_directory.display()
587 );
588
589 write(
590 self.corpus_directory
591 .join(format!("{}.testcase", &testcase_name)),
592 testcase.testcase.bytes(),
593 )?;
594 }
595
596 self.cmplog_enabled = testcase.cmplog;
597
598 debug!(self.as_conf_object(), "Testcase: {testcase:?}");
599
600 Ok(testcase)
601 }
602}