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::{SnapshotRestorePolicy, SolutionKind, StopReason};
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::continue_simulation;
50use simics::{
51 break_simulation, class, debug, error, get_class, get_interface, get_processor_number, info,
52 lookup_file, object_clock, run_alone, run_command, run_python, simics_init, sys::save_flags_t,
53 trace, version_base, warn, write_configuration_to_file, AsConfObject, BreakpointId,
54 ClassCreate, ClassObjectsFinalize, ConfObject, CoreBreakpointMemopHap,
55 CoreControlRegisterWriteHap, CoreExceptionHap, CoreMagicInstructionHap,
56 CoreSimulationStoppedHap, CpuInstrumentationSubscribeInterface, Event, EventClassFlag,
57 FromConfObject, HapHandle, Interface,
58};
59#[cfg(simics_version = "6")]
60use simics::free_attribute;
61#[cfg(simics_version = "6")]
62use simics::{
63 discard_future, restore_micro_checkpoint, save_micro_checkpoint, MicroCheckpointFlags,
64};
65#[cfg(simics_version = "7")]
66use simics::{restore_snapshot, save_snapshot};
70use source_cov::SourceCache;
71use std::{
72 alloc::{alloc_zeroed, Layout},
73 cell::OnceCell,
74 collections::{hash_map::Entry, BTreeSet, HashMap, HashSet},
75 fs::{create_dir_all, remove_dir_all, File},
76 hash::{DefaultHasher, Hash, Hasher},
77 path::PathBuf,
78 ptr::null_mut,
79 str::FromStr,
80 sync::mpsc::{Receiver, Sender},
81 thread::JoinHandle,
82 time::SystemTime,
83};
84use tracer::{
85 tsffs::{on_instruction_after, on_instruction_before},
86 ExecutionTrace,
87};
88use typed_builder::TypedBuilder;
89use versions::{Requirement, Versioning};
90
91pub(crate) mod arch;
92pub(crate) mod fuzzer;
93pub(crate) mod haps;
94pub(crate) mod interfaces;
95pub(crate) mod log;
96pub(crate) mod magic;
97pub(crate) mod os;
98pub(crate) mod source_cov;
99pub(crate) mod state;
100pub(crate) mod tracer;
101pub(crate) mod traits;
102pub(crate) mod util;
103
104pub const CLASS_NAME: &str = env!("CARGO_PKG_NAME");
106
107#[derive(Serialize, Deserialize, Clone, Debug)]
108pub(crate) enum StartPhysicalAddress {
111 WasVirtual(u64),
113 WasPhysical(u64),
115}
116
117impl StartPhysicalAddress {
118 pub fn physical_address(&self) -> u64 {
120 match self {
121 StartPhysicalAddress::WasVirtual(addr) => *addr,
122 StartPhysicalAddress::WasPhysical(addr) => *addr,
123 }
124 }
125}
126
127#[derive(Serialize, Deserialize, Clone, Debug)]
128pub(crate) enum ManualStartAddress {
129 Virtual(u64),
130 Physical(u64),
131}
132
133impl ManualStartAddress {
134 pub fn address(&self) -> u64 {
135 match self {
136 ManualStartAddress::Virtual(addr) => *addr,
137 ManualStartAddress::Physical(addr) => *addr,
138 }
139 }
140}
141
142#[derive(TypedBuilder, Serialize, Deserialize, Clone, Debug)]
143pub(crate) struct StartInfo {
144 pub address: StartPhysicalAddress,
147 pub contents: Vec<u8>,
149 pub size: StartSize,
155}
156
157#[derive(Serialize, Deserialize, Clone, Debug)]
158pub(crate) struct ManualStartInfo {
162 pub address: ManualStartAddress,
163 pub size: ManualStartSize,
164}
165
166#[derive(Serialize, Deserialize, Clone, Debug)]
167pub(crate) enum StartSize {
168 SizePtr {
169 address: StartPhysicalAddress,
170 maximum_size: usize,
171 },
172 MaxSize(usize),
173 SizePtrAndMaxSize {
174 address: StartPhysicalAddress,
175 maximum_size: usize,
176 },
177}
178
179impl StartSize {
180 pub fn maximum_size(&self) -> usize {
181 match self {
182 StartSize::SizePtr { maximum_size, .. } => *maximum_size,
183 StartSize::MaxSize(maximum_size) => *maximum_size,
184 StartSize::SizePtrAndMaxSize { maximum_size, .. } => *maximum_size,
185 }
186 }
187
188 pub fn physical_address(&self) -> Option<StartPhysicalAddress> {
189 match self {
190 StartSize::SizePtr { address, .. } => Some(address.clone()),
191 StartSize::MaxSize(_) => None,
192 StartSize::SizePtrAndMaxSize { address, .. } => Some(address.clone()),
193 }
194 }
195}
196
197#[derive(Serialize, Deserialize, Clone, Debug)]
198pub(crate) enum ManualStartSize {
199 SizePtr {
200 address: ManualStartAddress,
201 },
202 MaxSize(usize),
203 SizePtrAndMaxSize {
204 address: ManualStartAddress,
205 maximum_size: usize,
206 },
207}
208
209#[class(name = "tsffs", skip_objects_finalize)]
210#[derive(AsConfObject, FromConfObject, Default)]
211pub(crate) struct Tsffs {
213 #[class(attribute(optional, default = false))]
214 pub all_breakpoints_are_solutions: bool,
228 #[class(attribute(optional, default = false))]
229 pub all_exceptions_are_solutions: bool,
235 #[class(attribute(optional))]
236 pub exceptions: BTreeSet<i64>,
242 #[class(attribute(optional))]
243 pub breakpoints: BTreeSet<BreakpointId>,
251 #[class(attribute(optional, default = 5.0))]
252 pub timeout: f64,
256 #[class(attribute(optional, default = SnapshotRestorePolicy::Always))]
257 pub snapshot_restore_interval: SnapshotRestorePolicy,
266 #[class(attribute(optional, default = true))]
267 pub start_on_harness: bool,
270 #[class(attribute(optional, default = true))]
271 pub stop_on_harness: bool,
274 #[class(attribute(optional, default = true))]
275 pub repro_auto_continue: bool,
281 #[class(attribute(optional, default = 0))]
282 pub magic_start_index: u64,
291 #[class(attribute(optional, default = vec![0]))]
292 pub magic_stop_indices: Vec<u64>,
301 #[class(attribute(optional, default = vec![0]))]
302 pub magic_assert_indices: Vec<u64>,
308 #[class(attribute(optional))]
309 pub iteration_limit: usize,
313 #[class(attribute(optional, default = 8))]
314 pub initial_random_corpus_size: usize,
318 #[class(attribute(optional, default = lookup_file("%simics%")?.join("corpus")))]
319 pub corpus_directory: PathBuf,
326 #[class(attribute(optional, default = lookup_file("%simics%")?.join("solutions")))]
327 pub solutions_directory: PathBuf,
331 #[class(attribute(optional, default = false))]
332 pub generate_random_corpus: bool,
338 #[class(attribute(optional, default = true))]
339 pub cmplog: bool,
345 #[class(attribute(optional, default = true))]
346 pub coverage_reporting: bool,
349 #[class(attribute(optional))]
350 pub token_executables: Vec<PathBuf>,
353 #[class(attribute(optional))]
354 pub token_src_files: Vec<PathBuf>,
358 #[class(attribute(optional))]
359 pub token_files: Vec<PathBuf>,
366 #[class(attribute(optional))]
367 pub tokens: Vec<Vec<u8>>,
370 #[class(attribute(optional, default = lookup_file("%simics%")?.join("checkpoint.ckpt")))]
371 pub checkpoint_path: PathBuf,
373 #[class(attribute(optional, default = true))]
374 pub pre_snapshot_checkpoint: bool,
375 #[class(attribute(optional, default = lookup_file("%simics%")?.join("log.json")))]
376 pub log_path: PathBuf,
378 #[class(attribute(optional, default = true))]
379 pub log_to_file: bool,
380 #[class(attribute(optional, default = false))]
381 pub keep_all_corpus: bool,
382 #[class(attribute(optional, default = false))]
383 pub use_initial_as_corpus: bool,
385 #[class(attribute(optional, default = false))]
386 pub debug_log_libafl: bool,
388 #[class(attribute(optional, default = true))]
389 pub shutdown_on_stop_without_reason: bool,
391 #[class(attribute(optional, default = true))]
392 pub quit_on_iteration_limit: bool,
394 #[class(attribute(optional, default = false))]
395 pub save_timeout_execution_traces: bool,
397 #[class(attribute(optional, default = false))]
398 pub save_solution_execution_traces: bool,
400 #[class(attribute(optional, default = false))]
401 pub save_interesting_execution_traces: bool,
403 #[class(attribute(optional, default = false))]
404 pub save_all_execution_traces: bool,
407 #[class(attribute(optional, default = lookup_file("%simics%")?.join("execution-traces")))]
408 pub execution_trace_directory: PathBuf,
412 #[class(attribute(optional, default = false))]
413 pub execution_trace_pc_only: bool,
415 #[class(attribute(optional, default = true))]
416 pub heartbeat: bool,
418 #[class(attribute(optional, default = 60))]
419 pub heartbeat_interval: u64,
421
422 #[class(attribute(optional, default = false))]
423 pub symbolic_coverage: bool,
425 #[class(attribute(optional, default = false))]
426 pub windows: bool,
428 #[class(attribute(optional, default = lookup_file("%simics%")?.join("debuginfo-cache")))]
429 pub debuginfo_download_directory: PathBuf,
431 #[class(attribute(optional))]
432 pub debug_info: HashMap<String, Vec<PathBuf>>,
436 #[class(attribute(optional, default = lookup_file("%simics%")?.join("debuginfo-source")))]
437 pub debuginfo_source_directory: PathBuf,
440 #[class(attribute(optional, default = false))]
441 pub symbolic_coverage_system: bool,
444 #[class(attribute(optional, default = lookup_file("%simics%")?.join("symbolic-coverage")))]
445 pub symbolic_coverage_directory: PathBuf,
448
449 stop_hap_handle: HapHandle,
451 breakpoint_memop_hap_handle: HapHandle,
453 exception_hap_handle: HapHandle,
455 magic_hap_handle: HapHandle,
459 control_register_write_hap_handle: HapHandle,
461
462 pub architecture_hints: HashMap<i32, ArchitectureHint>,
468 fuzz_thread: OnceCell<JoinHandle<Result<()>>>,
471 fuzzer_tx: OnceCell<Sender<ExitKind>>,
474 fuzzer_rx: OnceCell<Receiver<Testcase>>,
477 fuzzer_shutdown: OnceCell<Sender<ShutdownMessage>>,
479 fuzzer_messages: OnceCell<Receiver<FuzzerMessage>>,
482
483 coverage_map: OnceCell<OwnedMutSlice<'static, u8>>,
486 aflpp_cmp_map_ptr: OnceCell<*mut AFLppCmpLogMap>,
488 aflpp_cmp_map: OnceCell<&'static mut AFLppCmpLogMap>,
490 coverage_prev_loc: u64,
492 timeout_event: OnceCell<Event>,
495 edges_seen: HashSet<u64>,
497 edges_seen_since_last: HashMap<u64, u64>,
500 execution_trace: ExecutionTrace,
502 coverage: Records,
505
506 snapshot_name: OnceCell<String>,
508 micro_checkpoint_index: OnceCell<i32>,
511
512 stop_reason: Option<StopReason>,
514 start_info: OnceCell<StartInfo>,
516
517 start_time: OnceCell<SystemTime>,
520 last_heartbeat_time: Option<SystemTime>,
523
524 log: OnceCell<File>,
525
526 coverage_enabled: bool,
528 cmplog_enabled: bool,
530 start_processor_number: OnceCell<i32>,
532 processors: HashMap<i32, Architecture>,
535 repro_testcase: Option<Vec<u8>>,
537 repro_bookmark_set: bool,
539 stopped_for_repro: bool,
541 iterations: usize,
543 use_snapshots: bool,
545 timeouts: usize,
547 solutions: usize,
549
550 windows_os_info: WindowsOsInfo,
551 cr3_cache: HashMap<i32, i64>,
552 source_file_cache: SourceCache,
553}
554
555impl ClassObjectsFinalize for Tsffs {
556 unsafe fn objects_finalized(instance: *mut ConfObject) -> simics::Result<()> {
557 let tsffs: &'static mut Tsffs = instance.into();
558 tsffs.stop_hap_handle = CoreSimulationStoppedHap::add_callback(
559 move |_, _, _| {
564 let tsffs: &'static mut Tsffs = instance.into();
569 tsffs
570 .on_simulation_stopped()
571 .expect("Error calling simulation stopped callback");
572 },
573 )?;
574 tsffs.breakpoint_memop_hap_handle =
575 CoreBreakpointMemopHap::add_callback(move |trigger_obj, breakpoint_number, memop| {
576 let tsffs: &'static mut Tsffs = instance.into();
577 tsffs
578 .on_breakpoint_memop(trigger_obj, breakpoint_number, memop)
579 .expect("Error calling breakpoint memop callback");
580 })?;
581 tsffs.exception_hap_handle =
582 CoreExceptionHap::add_callback(move |trigger_obj, exception_number| {
583 let tsffs: &'static mut Tsffs = instance.into();
584 tsffs
585 .on_exception(trigger_obj, exception_number)
586 .expect("Error calling breakpoint memop callback");
587 })?;
588 tsffs.magic_hap_handle =
589 CoreMagicInstructionHap::add_callback(move |trigger_obj, magic_number| {
590 let tsffs: &'static mut Tsffs = instance.into();
591
592 if let Some(magic_number) = MagicNumber::from_i64(magic_number) {
597 tsffs
598 .on_magic_instruction(trigger_obj, magic_number)
599 .expect("Failed to execute on_magic_instruction callback")
600 }
601 })?;
602 tsffs.control_register_write_hap_handle =
603 CoreControlRegisterWriteHap::add_callback(move |trigger_obj, register_nr, value| {
604 let tsffs: &'static mut Tsffs = instance.into();
605 tsffs
606 .on_control_register_write(trigger_obj, register_nr, value)
607 .expect("Failed to execute on_control_register_write callback")
608 })?;
609 tsffs
610 .coverage_map
611 .set(OwnedMutSlice::from(vec![0; Tsffs::COVERAGE_MAP_SIZE]))
612 .map_err(|_e| anyhow!("Value already set"))?;
613
614 tsffs
615 .aflpp_cmp_map_ptr
616 .set(unsafe { alloc_zeroed(Layout::new::<AFLppCmpLogMap>()) as *mut _ })
617 .map_err(|_e| anyhow!("Value already set"))?;
618
619 tsffs
620 .aflpp_cmp_map
621 .set(unsafe {
622 &mut **tsffs
623 .aflpp_cmp_map_ptr
624 .get()
625 .expect("Value just set and known to be valid")
626 })
627 .map_err(|_e| anyhow!("Value already set"))?;
628
629 tsffs
630 .timeout_event
631 .set(
632 Event::builder()
633 .name(Tsffs::TIMEOUT_EVENT_NAME)
634 .cls(get_class(CLASS_NAME).expect("Error getting class"))
635 .flags(EventClassFlag::Sim_EC_No_Flags)
636 .build(),
637 )
638 .map_err(|_e| anyhow!("Value already set"))?;
639
640 let version = version_base()
643 .map_err(|e| anyhow!("Error getting version string: {}", e))
644 .and_then(|v| {
645 v.split(' ')
646 .next_back()
647 .ok_or_else(|| anyhow!("Error parsing version string '{}'", v))
648 .map(|s| s.to_string())
649 })
650 .and_then(|v| {
651 Versioning::from_str(&v).map_err(|e| anyhow!("Error parsing version string: {e}"))
652 })?;
653
654 tsffs.use_snapshots = Requirement::from_str(">=7.0.0")
655 .map_err(|e| anyhow!("Error parsing requirement: {}", e))?
656 .matches(&version);
657
658 Ok(())
659 }
660}
661
662impl Tsffs {
663 pub const COVERAGE_MAP_SIZE: usize = 128 * 1024;
665 pub const TIMEOUT_EVENT_NAME: &'static str = "detector_timeout_event";
667 pub const SNAPSHOT_NAME: &'static str = "tsffs-origin-snapshot";
669}
670
671impl Tsffs {
672 pub fn repro_restore_command() -> String {
675 #[cfg(simics_version = "6")]
676 return "reverse-to start".to_string();
677 #[cfg(simics_version = "7")]
678 return format!("restore-snapshot {}", Self::SNAPSHOT_NAME);
679 }
680}
681
682impl Tsffs {
684 pub fn stop_simulation(&mut self, reason: StopReason) -> Result<()> {
686 let break_string = reason.to_string();
687
688 self.stop_reason = Some(reason);
689
690 break_simulation(break_string)?;
691
692 Ok(())
693 }
694}
695
696impl Tsffs {
698 pub fn add_processor(&mut self, cpu: *mut ConfObject, is_start: bool) -> Result<()> {
701 let cpu_number = get_processor_number(cpu)?;
702 debug!(
703 self.as_conf_object(),
704 "Adding {}processor {} to fuzzer",
705 if is_start { "start " } else { "" },
706 cpu_number
707 );
708
709 if let Entry::Vacant(e) = self.processors.entry(cpu_number) {
710 let architecture = if let Some(hint) = self.architecture_hints.get(&cpu_number) {
711 hint.architecture(cpu)?
712 } else {
713 Architecture::new(cpu)?
714 };
715 e.insert(architecture);
716 let mut cpu_interface: CpuInstrumentationSubscribeInterface = get_interface(cpu)?;
717 cpu_interface.register_instruction_after_cb(
718 null_mut(),
719 Some(on_instruction_after),
720 self as *mut Self as *mut _,
721 )?;
722 cpu_interface.register_instruction_before_cb(
723 null_mut(),
724 Some(on_instruction_before),
725 self as *mut Self as *mut _,
726 )?;
727 }
728
729 if is_start {
730 self.start_processor_number
731 .set(cpu_number)
732 .map_err(|_| anyhow!("Start processor number already set"))?;
733 }
734
735 Ok(())
736 }
737
738 pub fn start_processor(&mut self) -> Option<&mut Architecture> {
741 self.start_processor_number
742 .get()
743 .and_then(|n| self.processors.get_mut(n))
744 }
745}
746
747impl Tsffs {
748 pub fn save_initial_snapshot(&mut self) -> Result<()> {
751 if self.have_initial_snapshot() {
752 return Ok(());
753 }
754
755 info!(self.as_conf_object(), "Disabling VMP");
757 if let Err(e) = run_command("disable-vmp") {
758 warn!(self.as_conf_object(), "Failed to disable VMP: {}", e);
759 }
760
761 info!(self.as_conf_object(), "Initializing source cache");
763 self.source_file_cache = SourceCache::new(&self.debuginfo_source_directory)?;
764
765 self.log(LogMessage::startup())?;
766
767 #[cfg(simics_version = "7")]
768 {
769 if self.pre_snapshot_checkpoint {
770 debug!(
771 self.as_conf_object(),
772 "Saving checkpoint to {}",
773 self.checkpoint_path.display()
774 );
775
776 if self.checkpoint_path.exists() {
777 remove_dir_all(&self.checkpoint_path)?;
778 }
779
780 write_configuration_to_file(&self.checkpoint_path, save_flags_t(0))?;
781 }
782
783 debug!(self.as_conf_object(), "Saving initial snapshot");
784
785 save_snapshot(Self::SNAPSHOT_NAME)?;
786 self.snapshot_name
787 .set(Self::SNAPSHOT_NAME.to_string())
788 .map_err(|_| anyhow!("Snapshot name already set"))?;
789 }
790
791 #[cfg(simics_version = "6")]
792 {
793 if self.pre_snapshot_checkpoint {
794 debug!(
795 self.as_conf_object(),
796 "Saving checkpoint to {}",
797 self.checkpoint_path.display()
798 );
799
800 if self.checkpoint_path.exists() {
801 remove_dir_all(&self.checkpoint_path)?;
802 }
803
804 write_configuration_to_file(&self.checkpoint_path, save_flags_t(0))?;
805 }
806
807 debug!(self.as_conf_object(), "Saving initial micro checkpoint");
808
809 save_micro_checkpoint(
810 Self::SNAPSHOT_NAME,
811 MicroCheckpointFlags::Sim_MC_ID_User | MicroCheckpointFlags::Sim_MC_Persistent,
812 )?;
813
814 self.snapshot_name
815 .set(Self::SNAPSHOT_NAME.to_string())
816 .map_err(|_| anyhow!("Snapshot name already set"))?;
817
818 self.micro_checkpoint_index
819 .set(
820 Utils::get_micro_checkpoints()?
821 .iter()
822 .enumerate()
823 .find_map(|(i, c)| (c.name == Self::SNAPSHOT_NAME).then_some(i as i32))
824 .ok_or_else(|| {
825 anyhow!("No micro checkpoint with just-registered name found")
826 })?,
827 )
828 .map_err(|_| anyhow!("Micro checkpoint index already set"))?;
829 }
830
831 Ok(())
832 }
833
834 pub fn restore_initial_snapshot(&mut self) -> Result<()> {
837 #[cfg(simics_version = "7")]
838 restore_snapshot(Self::SNAPSHOT_NAME)?;
839 #[cfg(simics_version = "6")]
840 {
841 restore_micro_checkpoint(*self.micro_checkpoint_index.get().ok_or_else(|| {
842 anyhow!("Not using snapshots and no micro checkpoint index present")
843 })?)?;
844
845 discard_future()?;
846 }
847
848 Ok(())
849 }
850
851 pub fn should_restore_snapshot_this_iteration(&self) -> bool {
855 match self.snapshot_restore_interval {
856 SnapshotRestorePolicy::Never => false,
857 SnapshotRestorePolicy::Always => true,
858 SnapshotRestorePolicy::Every(n) => self.iterations.is_multiple_of(n),
859 }
860 }
861
862 pub fn have_initial_snapshot(&self) -> bool {
864 let have = if cfg!(simics_version = "7") {
865 self.snapshot_name.get().is_some()
866 } else if cfg!(simics_version = "6") {
867 self.snapshot_name.get().is_some() && self.micro_checkpoint_index.get().is_some()
868 } else {
869 error!(self.as_conf_object(), "Unsupported SIMICS version");
870 false
871 };
872 have
873 }
874
875 pub fn save_repro_bookmark_if_needed(&mut self) -> Result<()> {
877 if self.repro_testcase.is_some() && !self.repro_bookmark_set {
878 #[cfg(simics_version = "6")]
881 free_attribute(run_command("set-bookmark start")?)?;
882 self.repro_bookmark_set = true;
883 }
884
885 Ok(())
886 }
887
888 pub fn should_auto_continue_repro(&self) -> bool {
890 self.repro_testcase.is_none() || self.repro_auto_continue
891 }
892
893 pub fn continue_after_repro_prepared(&self) -> Result<()> {
895 if self.should_auto_continue_repro() {
896 debug!(self.as_conf_object(), "Resuming simulation");
897
898 run_alone(|| {
899 continue_simulation(0)?;
900 Ok(())
901 })?;
902 } else {
903 info!(
904 self.as_conf_object(),
905 "Repro testcase prepared; waiting for external resume."
906 );
907 }
908
909 Ok(())
910 }
911}
912
913impl Tsffs {
914 pub fn get_and_write_testcase(&mut self) -> Result<()> {
916 let testcase = self.get_testcase()?;
917
918 let start_info = self
920 .start_info
921 .get()
922 .ok_or_else(|| anyhow!("No start info"))?
923 .clone();
924
925 let start_processor = self
926 .start_processor()
927 .ok_or_else(|| anyhow!("No start processor"))?;
928
929 start_processor.write_start(testcase.testcase.bytes(), &start_info)?;
930
931 Ok(())
932 }
933
934 pub fn post_timeout_event(&mut self) -> Result<()> {
937 let tsffs_ptr = self.as_conf_object_mut();
938 let start_processor = self
939 .start_processor()
940 .ok_or_else(|| anyhow!("No start processor"))?;
941 let start_processor_time = start_processor.cycle().get_time()?;
942 let start_processor_cpu = start_processor.cpu();
943 let start_processor_clock = object_clock(start_processor_cpu)?;
944 let timeout_time = self.timeout + start_processor_time;
945 trace!(
946 self.as_conf_object(),
947 "Posting event on processor at time {} for {}s (time {})",
948 start_processor_time,
949 self.timeout,
950 timeout_time
951 );
952 self.timeout_event
953 .get_mut()
954 .ok_or_else(|| anyhow!("No timeout event set"))?
955 .post_time(
956 start_processor_cpu,
957 start_processor_clock,
958 self.timeout,
959 move |_obj| {
960 let tsffs: &'static mut Tsffs = tsffs_ptr.into();
961 tsffs
962 .stop_simulation(StopReason::Solution {
963 kind: SolutionKind::Timeout,
964 })
965 .expect("Error calling timeout callback");
966 },
967 )?;
968
969 Ok(())
970 }
971
972 pub fn cancel_timeout_event(&mut self) -> Result<()> {
975 if let Some(start_processor) = self.start_processor() {
976 let start_processor_time = start_processor.cycle().get_time()?;
977 let start_processor_cpu = start_processor.cpu();
978 let start_processor_clock = object_clock(start_processor_cpu)?;
979 match self
980 .timeout_event
981 .get()
982 .ok_or_else(|| anyhow!("No timeout event set"))?
983 .find_next_time(start_processor_clock, start_processor_cpu)
984 {
985 Ok(next_time) => trace!(
986 self.as_conf_object(),
987 "Cancelling event with next time {} (current time {})",
988 next_time,
989 start_processor_time
990 ),
991 Err(e) => trace!(
994 self.as_conf_object(),
995 "Not cancelling event with next time due to error: {e}"
996 ),
997 }
998 self.timeout_event
999 .get()
1000 .ok_or_else(|| anyhow!("No timeout event set"))?
1001 .cancel_time(start_processor_cpu, start_processor_clock)?;
1002 }
1003 Ok(())
1004 }
1005
1006 pub fn save_symbolic_coverage(&mut self) -> Result<()> {
1007 if self.symbolic_coverage_directory.is_dir() {
1008 create_dir_all(&self.symbolic_coverage_directory)?;
1009 }
1010
1011 debug!(
1012 self.as_conf_object(),
1013 "Saving symbolic coverage to {}",
1014 self.symbolic_coverage_directory.display()
1015 );
1016
1017 self.coverage.to_html(&self.symbolic_coverage_directory)?;
1018
1019 debug!(
1020 self.as_conf_object(),
1021 "Symbolic coverage saved to {}",
1022 self.symbolic_coverage_directory.display()
1023 );
1024
1025 Ok(())
1026 }
1027
1028 pub fn save_execution_trace(&mut self) -> Result<()> {
1030 let mut hasher = DefaultHasher::new();
1031 self.execution_trace.hash(&mut hasher);
1032 let hash = hasher.finish();
1033
1034 if !self.execution_trace_directory.is_dir() {
1035 create_dir_all(&self.execution_trace_directory)?;
1036 }
1037
1038 let trace_path = self
1039 .execution_trace_directory
1040 .join(format!("{:x}.json", hash));
1041
1042 if !trace_path.exists() {
1043 let trace_file = File::create(&trace_path)?;
1044 to_writer(trace_file, &self.execution_trace)?;
1045 }
1046 Ok(())
1047 }
1048}
1049
1050#[simics_init(name = "tsffs", class = "tsffs")]
1051fn init() {
1053 let tsffs = Tsffs::create().expect("Failed to create class tsffs");
1054 config::register(tsffs).expect("Failed to register config interface for tsffs");
1055 fuzz::register(tsffs).expect("Failed to register fuzz interface for tsffs");
1056 run_python(indoc! {r#"
1057 def init_tsffs_cmd():
1058 try:
1059 global tsffs
1060 tsffs = SIM_create_object(SIM_get_class("tsffs"), "tsffs", [])
1061 except Exception as e:
1062 raise CliError(f"Failed to create tsffs: {e}")
1063
1064 print("TSFFS initialized. Configure and use it as @tsffs.")
1065 "#})
1066 .expect("Failed to run python");
1067 run_python(indoc! {r#"
1068 new_command(
1069 "init-tsffs",
1070 init_tsffs_cmd,
1071 [],
1072 type = ["Fuzzing"],
1073 see_also = [],
1074 short = "Initialize the TSFFS fuzzer",
1075 doc = "Initialize the TSFFS fuzzer"
1076 )
1077 "#})
1078 .map_err(|e| {
1079 error!(tsffs, "{e}");
1080 e
1081 })
1082 .expect("Failed to run python");
1083}