1#![deny(clippy::all)]
26#![allow(clippy::not_unsafe_ptr_arg_deref)]
28#![deny(clippy::unwrap_used)]
29#![warn(missing_docs)]
30
31use crate::interfaces::{config::config, fuzz::fuzz};
32use crate::state::SolutionKind;
33#[cfg(simics_version = "6")]
34use crate::util::Utils;
35use anyhow::{anyhow, Result};
36use arch::{Architecture, ArchitectureHint, ArchitectureOperations};
37use fuzzer::{messages::FuzzerMessage, ShutdownMessage, Testcase};
38use indoc::indoc;
39use lcov2::Records;
40use libafl::{inputs::HasBytesVec, prelude::ExitKind};
41use libafl_bolts::prelude::OwnedMutSlice;
42use libafl_targets::AFLppCmpLogMap;
43use log::LogMessage;
44use magic::MagicNumber;
45use num_traits::FromPrimitive as _;
46use os::windows::WindowsOsInfo;
47use serde::{Deserialize, Serialize};
48use serde_json::to_writer;
49use simics::{
50 break_simulation, class, debug, error, free_attribute, get_class, get_interface,
51 get_processor_number, info, lookup_file, object_clock, run_command, run_python, simics_init,
52 sys::save_flags_t, trace, version_base, warn, write_configuration_to_file, AsConfObject,
53 BreakpointId, ClassCreate, ClassObjectsFinalize, ConfObject, CoreBreakpointMemopHap,
54 CoreControlRegisterWriteHap, CoreExceptionHap, CoreMagicInstructionHap,
55 CoreSimulationStoppedHap, CpuInstrumentationSubscribeInterface, Event, EventClassFlag,
56 FromConfObject, HapHandle, Interface,
57};
58#[cfg(simics_version = "6")]
59use simics::{
60 discard_future, restore_micro_checkpoint, save_micro_checkpoint, MicroCheckpointFlags,
61};
62#[cfg(simics_version = "7")]
63use simics::{restore_snapshot, save_snapshot};
67use source_cov::SourceCache;
68use state::StopReason;
69use std::{
70 alloc::{alloc_zeroed, Layout},
71 cell::OnceCell,
72 collections::{hash_map::Entry, BTreeSet, HashMap, HashSet},
73 fs::{create_dir_all, remove_dir_all, File},
74 hash::{DefaultHasher, Hash, Hasher},
75 path::PathBuf,
76 ptr::null_mut,
77 str::FromStr,
78 sync::mpsc::{Receiver, Sender},
79 thread::JoinHandle,
80 time::SystemTime,
81};
82use tracer::{
83 tsffs::{on_instruction_after, on_instruction_before},
84 ExecutionTrace,
85};
86use typed_builder::TypedBuilder;
87use versions::{Requirement, Versioning};
88
89pub(crate) mod arch;
90pub(crate) mod fuzzer;
91pub(crate) mod haps;
92pub(crate) mod interfaces;
93pub(crate) mod log;
94pub(crate) mod magic;
95pub(crate) mod os;
96pub(crate) mod source_cov;
97pub(crate) mod state;
98pub(crate) mod tracer;
99pub(crate) mod traits;
100pub(crate) mod util;
101
102pub const CLASS_NAME: &str = env!("CARGO_PKG_NAME");
104
105#[derive(Serialize, Deserialize, Clone, Debug)]
106pub(crate) enum StartPhysicalAddress {
109 WasVirtual(u64),
111 WasPhysical(u64),
113}
114
115impl StartPhysicalAddress {
116 pub fn physical_address(&self) -> u64 {
118 match self {
119 StartPhysicalAddress::WasVirtual(addr) => *addr,
120 StartPhysicalAddress::WasPhysical(addr) => *addr,
121 }
122 }
123}
124
125#[derive(Serialize, Deserialize, Clone, Debug)]
126pub(crate) enum ManualStartAddress {
127 Virtual(u64),
128 Physical(u64),
129}
130
131impl ManualStartAddress {
132 pub fn address(&self) -> u64 {
133 match self {
134 ManualStartAddress::Virtual(addr) => *addr,
135 ManualStartAddress::Physical(addr) => *addr,
136 }
137 }
138}
139
140#[derive(TypedBuilder, Serialize, Deserialize, Clone, Debug)]
141pub(crate) struct StartInfo {
142 pub address: StartPhysicalAddress,
145 pub contents: Vec<u8>,
147 pub size: StartSize,
153}
154
155#[derive(Serialize, Deserialize, Clone, Debug)]
156pub(crate) struct ManualStartInfo {
160 pub address: ManualStartAddress,
161 pub size: ManualStartSize,
162}
163
164#[derive(Serialize, Deserialize, Clone, Debug)]
165pub(crate) enum StartSize {
166 SizePtr {
167 address: StartPhysicalAddress,
168 maximum_size: usize,
169 },
170 MaxSize(usize),
171 SizePtrAndMaxSize {
172 address: StartPhysicalAddress,
173 maximum_size: usize,
174 },
175}
176
177impl StartSize {
178 pub fn maximum_size(&self) -> usize {
179 match self {
180 StartSize::SizePtr { maximum_size, .. } => *maximum_size,
181 StartSize::MaxSize(maximum_size) => *maximum_size,
182 StartSize::SizePtrAndMaxSize { maximum_size, .. } => *maximum_size,
183 }
184 }
185
186 pub fn physical_address(&self) -> Option<StartPhysicalAddress> {
187 match self {
188 StartSize::SizePtr { address, .. } => Some(address.clone()),
189 StartSize::MaxSize(_) => None,
190 StartSize::SizePtrAndMaxSize { address, .. } => Some(address.clone()),
191 }
192 }
193}
194
195#[derive(Serialize, Deserialize, Clone, Debug)]
196pub(crate) enum ManualStartSize {
197 SizePtr {
198 address: ManualStartAddress,
199 },
200 MaxSize(usize),
201 SizePtrAndMaxSize {
202 address: ManualStartAddress,
203 maximum_size: usize,
204 },
205}
206
207#[class(name = "tsffs", skip_objects_finalize)]
208#[derive(AsConfObject, FromConfObject, Default)]
209pub(crate) struct Tsffs {
211 #[class(attribute(optional, default = false))]
212 pub all_breakpoints_are_solutions: bool,
226 #[class(attribute(optional, default = false))]
227 pub all_exceptions_are_solutions: bool,
233 #[class(attribute(optional))]
234 pub exceptions: BTreeSet<i64>,
240 #[class(attribute(optional))]
241 pub breakpoints: BTreeSet<BreakpointId>,
249 #[class(attribute(optional, default = 5.0))]
250 pub timeout: f64,
254 #[class(attribute(optional, default = true))]
255 pub start_on_harness: bool,
258 #[class(attribute(optional, default = true))]
259 pub stop_on_harness: bool,
262 #[class(attribute(optional, default = 0))]
263 pub magic_start_index: u64,
272 #[class(attribute(optional, default = vec![0]))]
273 pub magic_stop_indices: Vec<u64>,
282 #[class(attribute(optional, default = vec![0]))]
283 pub magic_assert_indices: Vec<u64>,
289 #[class(attribute(optional))]
290 pub iteration_limit: usize,
294 #[class(attribute(optional, default = 8))]
295 pub initial_random_corpus_size: usize,
299 #[class(attribute(optional, default = lookup_file("%simics%")?.join("corpus")))]
300 pub corpus_directory: PathBuf,
307 #[class(attribute(optional, default = lookup_file("%simics%")?.join("solutions")))]
308 pub solutions_directory: PathBuf,
312 #[class(attribute(optional, default = false))]
313 pub generate_random_corpus: bool,
319 #[class(attribute(optional, default = true))]
320 pub cmplog: bool,
326 #[class(attribute(optional, default = true))]
327 pub coverage_reporting: bool,
330 #[class(attribute(optional))]
331 pub token_executables: Vec<PathBuf>,
334 #[class(attribute(optional))]
335 pub token_src_files: Vec<PathBuf>,
339 #[class(attribute(optional))]
340 pub token_files: Vec<PathBuf>,
347 #[class(attribute(optional))]
348 pub tokens: Vec<Vec<u8>>,
351 #[class(attribute(optional, default = lookup_file("%simics%")?.join("checkpoint.ckpt")))]
352 pub checkpoint_path: PathBuf,
354 #[class(attribute(optional, default = true))]
355 pub pre_snapshot_checkpoint: bool,
356 #[class(attribute(optional, default = lookup_file("%simics%")?.join("log.json")))]
357 pub log_path: PathBuf,
359 #[class(attribute(optional, default = true))]
360 pub log_to_file: bool,
361 #[class(attribute(optional, default = false))]
362 pub keep_all_corpus: bool,
363 #[class(attribute(optional, default = false))]
364 pub use_initial_as_corpus: bool,
366 #[class(attribute(optional, default = false))]
367 pub debug_log_libafl: bool,
369 #[class(attribute(optional, default = true))]
370 pub shutdown_on_stop_without_reason: bool,
372 #[class(attribute(optional, default = true))]
373 pub quit_on_iteration_limit: bool,
375 #[class(attribute(optional, default = false))]
376 pub save_timeout_execution_traces: bool,
378 #[class(attribute(optional, default = false))]
379 pub save_solution_execution_traces: bool,
381 #[class(attribute(optional, default = false))]
382 pub save_interesting_execution_traces: bool,
384 #[class(attribute(optional, default = false))]
385 pub save_all_execution_traces: bool,
388 #[class(attribute(optional, default = lookup_file("%simics%")?.join("execution-traces")))]
389 pub execution_trace_directory: PathBuf,
393 #[class(attribute(optional, default = false))]
394 pub execution_trace_pc_only: bool,
396 #[class(attribute(optional, default = true))]
397 pub heartbeat: bool,
399 #[class(attribute(optional, default = 60))]
400 pub heartbeat_interval: u64,
402
403 #[class(attribute(optional, default = false))]
404 pub symbolic_coverage: bool,
406 #[class(attribute(optional, default = false))]
407 pub windows: bool,
409 #[class(attribute(optional, default = lookup_file("%simics%")?.join("debuginfo-cache")))]
410 pub debuginfo_download_directory: PathBuf,
412 #[class(attribute(optional))]
413 pub debug_info: HashMap<String, Vec<PathBuf>>,
417 #[class(attribute(optional, default = lookup_file("%simics%")?.join("debuginfo-source")))]
418 pub debuginfo_source_directory: PathBuf,
421 #[class(attribute(optional, default = false))]
422 pub symbolic_coverage_system: bool,
425 #[class(attribute(optional, default = lookup_file("%simics%")?.join("symbolic-coverage")))]
426 pub symbolic_coverage_directory: PathBuf,
429
430 stop_hap_handle: HapHandle,
432 breakpoint_memop_hap_handle: HapHandle,
434 exception_hap_handle: HapHandle,
436 magic_hap_handle: HapHandle,
440 control_register_write_hap_handle: HapHandle,
442
443 pub architecture_hints: HashMap<i32, ArchitectureHint>,
449 fuzz_thread: OnceCell<JoinHandle<Result<()>>>,
452 fuzzer_tx: OnceCell<Sender<ExitKind>>,
455 fuzzer_rx: OnceCell<Receiver<Testcase>>,
458 fuzzer_shutdown: OnceCell<Sender<ShutdownMessage>>,
460 fuzzer_messages: OnceCell<Receiver<FuzzerMessage>>,
463
464 coverage_map: OnceCell<OwnedMutSlice<'static, u8>>,
467 aflpp_cmp_map_ptr: OnceCell<*mut AFLppCmpLogMap>,
469 aflpp_cmp_map: OnceCell<&'static mut AFLppCmpLogMap>,
471 coverage_prev_loc: u64,
473 timeout_event: OnceCell<Event>,
476 edges_seen: HashSet<u64>,
478 edges_seen_since_last: HashMap<u64, u64>,
481 execution_trace: ExecutionTrace,
483 coverage: Records,
486
487 snapshot_name: OnceCell<String>,
489 micro_checkpoint_index: OnceCell<i32>,
492
493 stop_reason: Option<StopReason>,
495 start_info: OnceCell<StartInfo>,
497
498 start_time: OnceCell<SystemTime>,
501 last_heartbeat_time: Option<SystemTime>,
504
505 log: OnceCell<File>,
506
507 coverage_enabled: bool,
509 cmplog_enabled: bool,
511 start_processor_number: OnceCell<i32>,
513 processors: HashMap<i32, Architecture>,
516 repro_testcase: Option<Vec<u8>>,
518 repro_bookmark_set: bool,
520 stopped_for_repro: bool,
522 iterations: usize,
524 use_snapshots: bool,
526 timeouts: usize,
528 solutions: usize,
530
531 windows_os_info: WindowsOsInfo,
532 cr3_cache: HashMap<i32, i64>,
533 source_file_cache: SourceCache,
534}
535
536impl ClassObjectsFinalize for Tsffs {
537 unsafe fn objects_finalized(instance: *mut ConfObject) -> simics::Result<()> {
538 let tsffs: &'static mut Tsffs = instance.into();
539 tsffs.stop_hap_handle = CoreSimulationStoppedHap::add_callback(
540 move |_, _, _| {
545 let tsffs: &'static mut Tsffs = instance.into();
550 tsffs
551 .on_simulation_stopped()
552 .expect("Error calling simulation stopped callback");
553 },
554 )?;
555 tsffs.breakpoint_memop_hap_handle =
556 CoreBreakpointMemopHap::add_callback(move |trigger_obj, breakpoint_number, memop| {
557 let tsffs: &'static mut Tsffs = instance.into();
558 tsffs
559 .on_breakpoint_memop(trigger_obj, breakpoint_number, memop)
560 .expect("Error calling breakpoint memop callback");
561 })?;
562 tsffs.exception_hap_handle =
563 CoreExceptionHap::add_callback(move |trigger_obj, exception_number| {
564 let tsffs: &'static mut Tsffs = instance.into();
565 tsffs
566 .on_exception(trigger_obj, exception_number)
567 .expect("Error calling breakpoint memop callback");
568 })?;
569 tsffs.magic_hap_handle =
570 CoreMagicInstructionHap::add_callback(move |trigger_obj, magic_number| {
571 let tsffs: &'static mut Tsffs = instance.into();
572
573 if let Some(magic_number) = MagicNumber::from_i64(magic_number) {
578 tsffs
579 .on_magic_instruction(trigger_obj, magic_number)
580 .expect("Failed to execute on_magic_instruction callback")
581 }
582 })?;
583 tsffs.control_register_write_hap_handle =
584 CoreControlRegisterWriteHap::add_callback(move |trigger_obj, register_nr, value| {
585 let tsffs: &'static mut Tsffs = instance.into();
586 tsffs
587 .on_control_register_write(trigger_obj, register_nr, value)
588 .expect("Failed to execute on_control_register_write callback")
589 })?;
590 tsffs
591 .coverage_map
592 .set(OwnedMutSlice::from(vec![0; Tsffs::COVERAGE_MAP_SIZE]))
593 .map_err(|_e| anyhow!("Value already set"))?;
594
595 tsffs
596 .aflpp_cmp_map_ptr
597 .set(unsafe { alloc_zeroed(Layout::new::<AFLppCmpLogMap>()) as *mut _ })
598 .map_err(|_e| anyhow!("Value already set"))?;
599
600 tsffs
601 .aflpp_cmp_map
602 .set(unsafe {
603 &mut **tsffs
604 .aflpp_cmp_map_ptr
605 .get()
606 .expect("Value just set and known to be valid")
607 })
608 .map_err(|_e| anyhow!("Value already set"))?;
609
610 tsffs
611 .timeout_event
612 .set(
613 Event::builder()
614 .name(Tsffs::TIMEOUT_EVENT_NAME)
615 .cls(get_class(CLASS_NAME).expect("Error getting class"))
616 .flags(EventClassFlag::Sim_EC_No_Flags)
617 .build(),
618 )
619 .map_err(|_e| anyhow!("Value already set"))?;
620
621 let version = version_base()
624 .map_err(|e| anyhow!("Error getting version string: {}", e))
625 .and_then(|v| {
626 v.split(' ')
627 .next_back()
628 .ok_or_else(|| anyhow!("Error parsing version string '{}'", v))
629 .map(|s| s.to_string())
630 })
631 .and_then(|v| {
632 Versioning::from_str(&v).map_err(|e| anyhow!("Error parsing version string: {e}"))
633 })?;
634
635 tsffs.use_snapshots = Requirement::from_str(">=7.0.0")
636 .map_err(|e| anyhow!("Error parsing requirement: {}", e))?
637 .matches(&version);
638
639 Ok(())
640 }
641}
642
643impl Tsffs {
644 pub const COVERAGE_MAP_SIZE: usize = 128 * 1024;
646 pub const TIMEOUT_EVENT_NAME: &'static str = "detector_timeout_event";
648 pub const SNAPSHOT_NAME: &'static str = "tsffs-origin-snapshot";
650}
651
652impl Tsffs {
654 pub fn stop_simulation(&mut self, reason: StopReason) -> Result<()> {
656 let break_string = reason.to_string();
657
658 self.stop_reason = Some(reason);
659
660 break_simulation(break_string)?;
661
662 Ok(())
663 }
664}
665
666impl Tsffs {
668 pub fn add_processor(&mut self, cpu: *mut ConfObject, is_start: bool) -> Result<()> {
671 let cpu_number = get_processor_number(cpu)?;
672 debug!(
673 self.as_conf_object(),
674 "Adding {}processor {} to fuzzer",
675 if is_start { "start " } else { "" },
676 cpu_number
677 );
678
679 if let Entry::Vacant(e) = self.processors.entry(cpu_number) {
680 let architecture = if let Some(hint) = self.architecture_hints.get(&cpu_number) {
681 hint.architecture(cpu)?
682 } else {
683 Architecture::new(cpu)?
684 };
685 e.insert(architecture);
686 let mut cpu_interface: CpuInstrumentationSubscribeInterface = get_interface(cpu)?;
687 cpu_interface.register_instruction_after_cb(
688 null_mut(),
689 Some(on_instruction_after),
690 self as *mut Self as *mut _,
691 )?;
692 cpu_interface.register_instruction_before_cb(
693 null_mut(),
694 Some(on_instruction_before),
695 self as *mut Self as *mut _,
696 )?;
697 }
698
699 if is_start {
700 self.start_processor_number
701 .set(cpu_number)
702 .map_err(|_| anyhow!("Start processor number already set"))?;
703 }
704
705 Ok(())
706 }
707
708 pub fn start_processor(&mut self) -> Option<&mut Architecture> {
711 self.start_processor_number
712 .get()
713 .and_then(|n| self.processors.get_mut(n))
714 }
715}
716
717impl Tsffs {
718 pub fn save_initial_snapshot(&mut self) -> Result<()> {
721 if self.have_initial_snapshot() {
722 return Ok(());
723 }
724
725 info!(self.as_conf_object(), "Disabling VMP");
727 if let Err(e) = run_command("disable-vmp") {
728 warn!(self.as_conf_object(), "Failed to disable VMP: {}", e);
729 }
730
731 info!(self.as_conf_object(), "Initializing source cache");
733 self.source_file_cache = SourceCache::new(&self.debuginfo_source_directory)?;
734
735 self.log(LogMessage::startup())?;
736
737 #[cfg(simics_version = "7")]
738 {
739 if self.pre_snapshot_checkpoint {
740 debug!(
741 self.as_conf_object(),
742 "Saving checkpoint to {}",
743 self.checkpoint_path.display()
744 );
745
746 if self.checkpoint_path.exists() {
747 remove_dir_all(&self.checkpoint_path)?;
748 }
749
750 write_configuration_to_file(&self.checkpoint_path, save_flags_t(0))?;
751 }
752
753 debug!(self.as_conf_object(), "Saving initial snapshot");
754
755 save_snapshot(Self::SNAPSHOT_NAME)?;
756 self.snapshot_name
757 .set(Self::SNAPSHOT_NAME.to_string())
758 .map_err(|_| anyhow!("Snapshot name already set"))?;
759 }
760
761 #[cfg(simics_version = "6")]
762 {
763 if self.pre_snapshot_checkpoint {
764 debug!(
765 self.as_conf_object(),
766 "Saving checkpoint to {}",
767 self.checkpoint_path.display()
768 );
769
770 if self.checkpoint_path.exists() {
771 remove_dir_all(&self.checkpoint_path)?;
772 }
773
774 write_configuration_to_file(&self.checkpoint_path, save_flags_t(0))?;
775 }
776
777 debug!(self.as_conf_object(), "Saving initial micro checkpoint");
778
779 save_micro_checkpoint(
780 Self::SNAPSHOT_NAME,
781 MicroCheckpointFlags::Sim_MC_ID_User | MicroCheckpointFlags::Sim_MC_Persistent,
782 )?;
783
784 self.snapshot_name
785 .set(Self::SNAPSHOT_NAME.to_string())
786 .map_err(|_| anyhow!("Snapshot name already set"))?;
787
788 self.micro_checkpoint_index
789 .set(
790 Utils::get_micro_checkpoints()?
791 .iter()
792 .enumerate()
793 .find_map(|(i, c)| (c.name == Self::SNAPSHOT_NAME).then_some(i as i32))
794 .ok_or_else(|| {
795 anyhow!("No micro checkpoint with just-registered name found")
796 })?,
797 )
798 .map_err(|_| anyhow!("Micro checkpoint index already set"))?;
799 }
800
801 Ok(())
802 }
803
804 pub fn restore_initial_snapshot(&mut self) -> Result<()> {
807 #[cfg(simics_version = "7")]
808 restore_snapshot(Self::SNAPSHOT_NAME)?;
809 #[cfg(simics_version = "6")]
810 {
811 restore_micro_checkpoint(*self.micro_checkpoint_index.get().ok_or_else(|| {
812 anyhow!("Not using snapshots and no micro checkpoint index present")
813 })?)?;
814
815 discard_future()?;
816 }
817
818 Ok(())
819 }
820
821 pub fn have_initial_snapshot(&self) -> bool {
823 let have = if cfg!(simics_version = "7") {
824 self.snapshot_name.get().is_some()
825 } else if cfg!(simics_version = "6") {
826 self.snapshot_name.get().is_some() && self.micro_checkpoint_index.get().is_some()
827 } else {
828 error!(self.as_conf_object(), "Unsupported SIMICS version");
829 false
830 };
831 have
832 }
833
834 pub fn save_repro_bookmark_if_needed(&mut self) -> Result<()> {
836 if self.repro_testcase.is_some() && !self.repro_bookmark_set {
837 free_attribute(run_command("set-bookmark start")?)?;
838 self.repro_bookmark_set = true;
839 }
840
841 Ok(())
842 }
843}
844
845impl Tsffs {
846 pub fn get_and_write_testcase(&mut self) -> Result<()> {
848 let testcase = self.get_testcase()?;
849
850 let start_info = self
852 .start_info
853 .get()
854 .ok_or_else(|| anyhow!("No start info"))?
855 .clone();
856
857 let start_processor = self
858 .start_processor()
859 .ok_or_else(|| anyhow!("No start processor"))?;
860
861 start_processor.write_start(testcase.testcase.bytes(), &start_info)?;
862
863 Ok(())
864 }
865
866 pub fn post_timeout_event(&mut self) -> Result<()> {
869 let tsffs_ptr = self.as_conf_object_mut();
870 let start_processor = self
871 .start_processor()
872 .ok_or_else(|| anyhow!("No start processor"))?;
873 let start_processor_time = start_processor.cycle().get_time()?;
874 let start_processor_cpu = start_processor.cpu();
875 let start_processor_clock = object_clock(start_processor_cpu)?;
876 let timeout_time = self.timeout + start_processor_time;
877 trace!(
878 self.as_conf_object(),
879 "Posting event on processor at time {} for {}s (time {})",
880 start_processor_time,
881 self.timeout,
882 timeout_time
883 );
884 self.timeout_event
885 .get_mut()
886 .ok_or_else(|| anyhow!("No timeout event set"))?
887 .post_time(
888 start_processor_cpu,
889 start_processor_clock,
890 self.timeout,
891 move |_obj| {
892 let tsffs: &'static mut Tsffs = tsffs_ptr.into();
893 tsffs
894 .stop_simulation(StopReason::Solution {
895 kind: SolutionKind::Timeout,
896 })
897 .expect("Error calling timeout callback");
898 },
899 )?;
900
901 Ok(())
902 }
903
904 pub fn cancel_timeout_event(&mut self) -> Result<()> {
907 if let Some(start_processor) = self.start_processor() {
908 let start_processor_time = start_processor.cycle().get_time()?;
909 let start_processor_cpu = start_processor.cpu();
910 let start_processor_clock = object_clock(start_processor_cpu)?;
911 match self
912 .timeout_event
913 .get()
914 .ok_or_else(|| anyhow!("No timeout event set"))?
915 .find_next_time(start_processor_clock, start_processor_cpu)
916 {
917 Ok(next_time) => trace!(
918 self.as_conf_object(),
919 "Cancelling event with next time {} (current time {})",
920 next_time,
921 start_processor_time
922 ),
923 Err(e) => trace!(
926 self.as_conf_object(),
927 "Not cancelling event with next time due to error: {e}"
928 ),
929 }
930 self.timeout_event
931 .get()
932 .ok_or_else(|| anyhow!("No timeout event set"))?
933 .cancel_time(start_processor_cpu, start_processor_clock)?;
934 }
935 Ok(())
936 }
937
938 pub fn save_symbolic_coverage(&mut self) -> Result<()> {
939 if self.symbolic_coverage_directory.is_dir() {
940 create_dir_all(&self.symbolic_coverage_directory)?;
941 }
942
943 debug!(
944 self.as_conf_object(),
945 "Saving symbolic coverage to {}",
946 self.symbolic_coverage_directory.display()
947 );
948
949 self.coverage.to_html(&self.symbolic_coverage_directory)?;
950
951 debug!(
952 self.as_conf_object(),
953 "Symbolic coverage saved to {}",
954 self.symbolic_coverage_directory.display()
955 );
956
957 Ok(())
958 }
959
960 pub fn save_execution_trace(&mut self) -> Result<()> {
962 let mut hasher = DefaultHasher::new();
963 self.execution_trace.hash(&mut hasher);
964 let hash = hasher.finish();
965
966 if !self.execution_trace_directory.is_dir() {
967 create_dir_all(&self.execution_trace_directory)?;
968 }
969
970 let trace_path = self
971 .execution_trace_directory
972 .join(format!("{:x}.json", hash));
973
974 if !trace_path.exists() {
975 let trace_file = File::create(&trace_path)?;
976 to_writer(trace_file, &self.execution_trace)?;
977 }
978 Ok(())
979 }
980}
981
982#[simics_init(name = "tsffs", class = "tsffs")]
983fn init() {
985 let tsffs = Tsffs::create().expect("Failed to create class tsffs");
986 config::register(tsffs).expect("Failed to register config interface for tsffs");
987 fuzz::register(tsffs).expect("Failed to register fuzz interface for tsffs");
988 run_python(indoc! {r#"
989 def init_tsffs_cmd():
990 try:
991 global tsffs
992 tsffs = SIM_create_object(SIM_get_class("tsffs"), "tsffs", [])
993 except Exception as e:
994 raise CliError(f"Failed to create tsffs: {e}")
995
996 print("TSFFS initialized. Configure and use it as @tsffs.")
997 "#})
998 .expect("Failed to run python");
999 run_python(indoc! {r#"
1000 new_command(
1001 "init-tsffs",
1002 init_tsffs_cmd,
1003 [],
1004 type = ["Fuzzing"],
1005 see_also = [],
1006 short = "Initialize the TSFFS fuzzer",
1007 doc = "Initialize the TSFFS fuzzer"
1008 )
1009 "#})
1010 .map_err(|e| {
1011 error!(tsffs, "{e}");
1012 e
1013 })
1014 .expect("Failed to run python");
1015}