tsffs/haps/
mod.rs

1// Copyright (C) 2024 Intel Corporation
2// SPDX-License-Identifier: Apache-2.0
3
4//! Handlers for HAPs in the simulator
5
6use std::time::SystemTime;
7
8use crate::{
9    arch::ArchitectureOperations,
10    magic::MagicNumber,
11    os::DebugInfoConfig,
12    state::{SolutionKind, StopReason},
13    ManualStartInfo, Tsffs,
14};
15use anyhow::{anyhow, bail, Result};
16use libafl::prelude::ExitKind;
17use simics::{
18    api::{
19        continue_simulation, log_level, object_is_processor, quit, run_alone, set_log_level,
20        AsConfObject, ConfObject, GenericTransaction, LogLevel,
21    },
22    debug, get_processor_number, info, trace, warn,
23};
24
25impl Tsffs {
26    fn on_simulation_stopped_magic_start(&mut self, magic_number: MagicNumber) -> Result<()> {
27        if !self.have_initial_snapshot() {
28            self.start_fuzzer_thread()?;
29
30            let start_processor = self
31                .start_processor()
32                .ok_or_else(|| anyhow!("No start processor"))?;
33            let start_processor_raw = start_processor.cpu();
34
35            let start_info = match magic_number {
36                MagicNumber::StartBufferPtrSizePtr => {
37                    start_processor.get_magic_start_buffer_ptr_size_ptr()?
38                }
39                MagicNumber::StartBufferPtrSizeVal => {
40                    start_processor.get_magic_start_buffer_ptr_size_val()?
41                }
42                MagicNumber::StartBufferPtrSizePtrVal => {
43                    start_processor.get_magic_start_buffer_ptr_size_ptr_val()?
44                }
45                MagicNumber::StopNormal => unreachable!("StopNormal is not handled here"),
46                MagicNumber::StopAssert => unreachable!("StopAssert is not handled here"),
47            };
48
49            debug!(self.as_conf_object(), "Start info: {start_info:?}");
50
51            self.start_info
52                .set(start_info)
53                .map_err(|_| anyhow!("Failed to set start size"))?;
54            self.start_time
55                .set(SystemTime::now())
56                .map_err(|_| anyhow!("Failed to set start time"))?;
57            self.coverage_enabled = true;
58            self.save_initial_snapshot()?;
59            // Collect windows coverage info if enabled
60            if self.windows && self.symbolic_coverage {
61                info!(self.as_conf_object(), "Collecting initial coverage info");
62                self.windows_os_info.collect(
63                    start_processor_raw,
64                    &self.debuginfo_download_directory,
65                    &mut DebugInfoConfig {
66                        system: self.symbolic_coverage_system,
67                        user_debug_info: &self.debug_info,
68                        coverage: &mut self.coverage,
69                    },
70                    &self.source_file_cache,
71                )?;
72            }
73            self.get_and_write_testcase()?;
74            self.post_timeout_event()?;
75        }
76
77        self.execution_trace.0.clear();
78        self.save_repro_bookmark_if_needed()?;
79
80        debug!(self.as_conf_object(), "Resuming simulation");
81
82        run_alone(|| {
83            continue_simulation(0)?;
84            Ok(())
85        })?;
86
87        Ok(())
88    }
89
90    fn on_simulation_stopped_magic_assert(&mut self) -> Result<()> {
91        self.on_simulation_stopped_solution(SolutionKind::Manual)
92    }
93
94    fn on_simulation_stopped_magic_stop(&mut self) -> Result<()> {
95        if !self.have_initial_snapshot() {
96            warn!(
97                self.as_conf_object(),
98                "Stopped normally before start was reached (no snapshot). Resuming without restoring non-existent snapshot."
99            );
100        } else {
101            self.cancel_timeout_event()?;
102
103            if self.repro_bookmark_set {
104                self.stopped_for_repro = true;
105                let current_log_level = log_level(self.as_conf_object_mut())?;
106
107                if current_log_level < LogLevel::Info as u32 {
108                    set_log_level(self.as_conf_object_mut(), LogLevel::Info)?;
109                }
110
111                info!(
112                    self.as_conf_object(),
113                    "Stopped for repro. Restore to start bookmark with 'reverse-to start'"
114                );
115
116                // Skip the shutdown and continue, we are finished here
117                return Ok(());
118            }
119
120            self.iterations += 1;
121
122            if self.iteration_limit != 0 && self.iterations >= self.iteration_limit {
123                let duration = SystemTime::now().duration_since(
124                    *self
125                        .start_time
126                        .get()
127                        .ok_or_else(|| anyhow!("Start time was not set"))?,
128                )?;
129
130                // Set the log level so this message always prints
131                set_log_level(self.as_conf_object_mut(), LogLevel::Info)?;
132
133                info!(
134                    self.as_conf_object(),
135                    "Configured iteration count {} reached. Stopping after {} seconds ({} exec/s).",
136                    self.iterations,
137                    duration.as_secs_f32(),
138                    self.iterations as f32 / duration.as_secs_f32()
139                );
140
141                self.send_shutdown()?;
142
143                if self.quit_on_iteration_limit {
144                    quit(0)?;
145                } else {
146                    return Ok(());
147                }
148            }
149
150            let fuzzer_tx = self
151                .fuzzer_tx
152                .get()
153                .ok_or_else(|| anyhow!("No fuzzer tx channel"))?;
154
155            fuzzer_tx.send(ExitKind::Ok)?;
156
157            self.restore_initial_snapshot()?;
158            self.coverage_prev_loc = 0;
159
160            if self.start_info.get().is_some() {
161                self.get_and_write_testcase()?;
162            } else {
163                debug!(
164                    self.as_conf_object(),
165                    "Missing start buffer or size, not writing testcase."
166                );
167            }
168
169            self.post_timeout_event()?;
170        }
171
172        if self.save_all_execution_traces {
173            self.save_execution_trace()?;
174        }
175
176        if self.symbolic_coverage {
177            self.save_symbolic_coverage()?;
178        }
179
180        debug!(self.as_conf_object(), "Resuming simulation");
181
182        run_alone(|| {
183            continue_simulation(0)?;
184            Ok(())
185        })?;
186
187        Ok(())
188    }
189
190    fn on_simulation_stopped_with_magic(&mut self, magic_number: MagicNumber) -> Result<()> {
191        match magic_number {
192            MagicNumber::StartBufferPtrSizePtr
193            | MagicNumber::StartBufferPtrSizeVal
194            | MagicNumber::StartBufferPtrSizePtrVal => {
195                self.on_simulation_stopped_magic_start(magic_number)?
196            }
197            MagicNumber::StopNormal => self.on_simulation_stopped_magic_stop()?,
198            MagicNumber::StopAssert => self.on_simulation_stopped_magic_assert()?,
199        }
200
201        Ok(())
202    }
203
204    fn on_simulation_stopped_with_manual_start(
205        &mut self,
206        processor: *mut ConfObject,
207        info: ManualStartInfo,
208    ) -> Result<()> {
209        if !self.have_initial_snapshot() {
210            self.start_fuzzer_thread()?;
211            self.add_processor(processor, true)?;
212
213            let start_info = self
214                .start_processor()
215                .ok_or_else(|| anyhow!("No start processor"))?
216                .get_manual_start_info(&info)?;
217
218            self.start_info
219                .set(start_info)
220                .map_err(|_| anyhow!("Failed to set start info"))?;
221            self.start_time
222                .set(SystemTime::now())
223                .map_err(|_| anyhow!("Failed to set start time"))?;
224            self.coverage_enabled = true;
225            self.save_initial_snapshot()?;
226
227            // Collect windows coverage info if enabled
228            if self.windows && self.symbolic_coverage {
229                info!(self.as_conf_object(), "Collecting initial coverage info");
230                self.windows_os_info.collect(
231                    processor,
232                    &self.debuginfo_download_directory,
233                    &mut DebugInfoConfig {
234                        system: self.symbolic_coverage_system,
235                        user_debug_info: &self.debug_info,
236                        coverage: &mut self.coverage,
237                    },
238                    &self.source_file_cache,
239                )?;
240            }
241
242            self.get_and_write_testcase()?;
243
244            self.post_timeout_event()?;
245        }
246
247        self.execution_trace.0.clear();
248        self.save_repro_bookmark_if_needed()?;
249
250        debug!(self.as_conf_object(), "Resuming simulation");
251
252        run_alone(|| {
253            continue_simulation(0)?;
254            Ok(())
255        })?;
256
257        Ok(())
258    }
259
260    fn on_simulation_stopped_manual_start_without_buffer(
261        &mut self,
262        processor: *mut ConfObject,
263    ) -> Result<()> {
264        if !self.have_initial_snapshot() {
265            self.start_fuzzer_thread()?;
266            self.add_processor(processor, true)?;
267
268            self.start_time
269                .set(SystemTime::now())
270                .map_err(|_| anyhow!("Failed to set start time"))?;
271            self.coverage_enabled = true;
272            self.save_initial_snapshot()?;
273
274            // Collect windows coverage info if enabled
275            if self.windows && self.symbolic_coverage {
276                info!(self.as_conf_object(), "Collecting initial coverage info");
277                self.windows_os_info.collect(
278                    processor,
279                    &self.debuginfo_download_directory,
280                    &mut DebugInfoConfig {
281                        system: self.symbolic_coverage_system,
282                        user_debug_info: &self.debug_info,
283                        coverage: &mut self.coverage,
284                    },
285                    &self.source_file_cache,
286                )?;
287            }
288
289            self.post_timeout_event()?;
290        }
291
292        self.execution_trace.0.clear();
293        self.save_repro_bookmark_if_needed()?;
294
295        debug!(self.as_conf_object(), "Resuming simulation");
296
297        run_alone(|| {
298            continue_simulation(0)?;
299            Ok(())
300        })?;
301
302        Ok(())
303    }
304
305    fn on_simulation_stopped_manual_stop(&mut self) -> Result<()> {
306        if !self.have_initial_snapshot() {
307            warn!(
308                self.as_conf_object(),
309                "Stopped for manual stop before start was reached (no snapshot). Resuming without restoring non-existent snapshot."
310            );
311        } else {
312            self.cancel_timeout_event()?;
313
314            if self.repro_bookmark_set {
315                self.stopped_for_repro = true;
316                let current_log_level = log_level(self.as_conf_object_mut())?;
317
318                if current_log_level < LogLevel::Info as u32 {
319                    set_log_level(self.as_conf_object_mut(), LogLevel::Info)?;
320                }
321
322                info!(
323                    self.as_conf_object(),
324                    "Stopped for repro. Restore to start bookmark with 'reverse-to start'"
325                );
326
327                // Skip the shutdown and continue, we are finished here
328                return Ok(());
329            }
330
331            self.iterations += 1;
332
333            if self.iteration_limit != 0 && self.iterations >= self.iteration_limit {
334                let duration = SystemTime::now().duration_since(
335                    *self
336                        .start_time
337                        .get()
338                        .ok_or_else(|| anyhow!("Start time was not set"))?,
339                )?;
340
341                // Set the log level so this message always prints
342                set_log_level(self.as_conf_object_mut(), LogLevel::Info)?;
343
344                info!(
345                    self.as_conf_object(),
346                    "Configured iteration count {} reached. Stopping after {} seconds ({} exec/s).",
347                    self.iterations,
348                    duration.as_secs_f32(),
349                    self.iterations as f32 / duration.as_secs_f32()
350                );
351
352                self.send_shutdown()?;
353
354                if self.quit_on_iteration_limit {
355                    quit(0)?;
356                } else {
357                    return Ok(());
358                }
359            }
360
361            let fuzzer_tx = self
362                .fuzzer_tx
363                .get()
364                .ok_or_else(|| anyhow!("No fuzzer tx channel"))?;
365
366            fuzzer_tx.send(ExitKind::Ok)?;
367
368            self.restore_initial_snapshot()?;
369            self.coverage_prev_loc = 0;
370
371            if self.start_info.get().is_some() {
372                self.get_and_write_testcase()?;
373            } else {
374                debug!(
375                    self.as_conf_object(),
376                    "Missing start buffer or size, not writing testcase. This may be due to using manual no-buffer harnessing."
377                );
378            }
379
380            self.post_timeout_event()?;
381        }
382
383        if self.save_all_execution_traces {
384            self.save_execution_trace()?;
385        }
386
387        if self.symbolic_coverage {
388            self.save_symbolic_coverage()?;
389        }
390
391        debug!(self.as_conf_object(), "Resuming simulation");
392
393        run_alone(|| {
394            continue_simulation(0)?;
395            Ok(())
396        })?;
397
398        Ok(())
399    }
400
401    fn on_simulation_stopped_solution(&mut self, kind: SolutionKind) -> Result<()> {
402        if !self.have_initial_snapshot() {
403            warn!(
404                self.as_conf_object(),
405                "Solution {kind:?} before start was reached (no snapshot). Resuming without restoring non-existent snapshot."
406            );
407        } else {
408            self.cancel_timeout_event()?;
409
410            if self.repro_bookmark_set {
411                self.stopped_for_repro = true;
412                let current_log_level = log_level(self.as_conf_object_mut())?;
413
414                if current_log_level < LogLevel::Info as u32 {
415                    set_log_level(self.as_conf_object_mut(), LogLevel::Info)?;
416                }
417
418                info!(
419                    self.as_conf_object(),
420                    "Stopped for repro. Restore to start bookmark with 'reverse-to start'"
421                );
422
423                // Skip the shutdown and continue, we are finished here
424                return Ok(());
425            }
426
427            self.iterations += 1;
428
429            if self.iteration_limit != 0 && self.iterations >= self.iteration_limit {
430                let duration = SystemTime::now().duration_since(
431                    *self
432                        .start_time
433                        .get()
434                        .ok_or_else(|| anyhow!("Start time was not set"))?,
435                )?;
436
437                // Set the log level so this message always prints
438                set_log_level(self.as_conf_object_mut(), LogLevel::Info)?;
439
440                info!(
441                    self.as_conf_object(),
442                    "Configured iteration count {} reached. Stopping after {} seconds ({} exec/s).",
443                    self.iterations,
444                    duration.as_secs_f32(),
445                    self.iterations as f32 / duration.as_secs_f32()
446                );
447
448                self.send_shutdown()?;
449
450                if self.quit_on_iteration_limit {
451                    quit(0)?;
452                } else {
453                    return Ok(());
454                }
455            }
456
457            let fuzzer_tx = self
458                .fuzzer_tx
459                .get()
460                .ok_or_else(|| anyhow!("No fuzzer tx channel"))?;
461
462            match kind {
463                SolutionKind::Timeout => {
464                    self.timeouts += 1;
465                    fuzzer_tx.send(ExitKind::Timeout)?
466                }
467                SolutionKind::Exception | SolutionKind::Breakpoint | SolutionKind::Manual => {
468                    self.solutions += 1;
469                    fuzzer_tx.send(ExitKind::Crash)?
470                }
471            }
472
473            self.restore_initial_snapshot()?;
474            self.coverage_prev_loc = 0;
475
476            if self.start_info.get().is_some() {
477                self.get_and_write_testcase()?;
478            } else {
479                debug!(
480                    self.as_conf_object(),
481                    "Missing start buffer or size, not writing testcase."
482                );
483            }
484
485            self.post_timeout_event()?;
486        }
487
488        if self.save_all_execution_traces {
489            self.save_execution_trace()?;
490        }
491
492        if self.symbolic_coverage {
493            self.save_symbolic_coverage()?;
494        }
495
496        debug!(self.as_conf_object(), "Resuming simulation");
497
498        run_alone(|| {
499            continue_simulation(0)?;
500            Ok(())
501        })?;
502
503        Ok(())
504    }
505
506    fn on_simulation_stopped_with_reason(&mut self, reason: StopReason) -> Result<()> {
507        debug!(
508            self.as_conf_object(),
509            "Simulation stopped with reason {reason:?}"
510        );
511
512        match reason {
513            StopReason::Magic { magic_number } => {
514                self.on_simulation_stopped_with_magic(magic_number)
515            }
516            StopReason::ManualStart { processor, info } => {
517                self.on_simulation_stopped_with_manual_start(processor, info)
518            }
519            StopReason::ManualStartWithoutBuffer { processor } => {
520                self.on_simulation_stopped_manual_start_without_buffer(processor)
521            }
522            StopReason::ManualStop => self.on_simulation_stopped_manual_stop(),
523            StopReason::Solution { kind } => self.on_simulation_stopped_solution(kind),
524        }
525    }
526
527    fn on_simulation_stopped_without_reason(&mut self) -> Result<()> {
528        if self.have_initial_snapshot() {
529            // We only do anything here if we have run, otherwise the simulation was just
530            // stopped for a reason unrelated to fuzzing (like the user using the CLI)
531            self.cancel_timeout_event()?;
532
533            let fuzzer_tx = self
534                .fuzzer_tx
535                .get()
536                .ok_or_else(|| anyhow!("No fuzzer tx channel"))?;
537
538            fuzzer_tx.send(ExitKind::Ok)?;
539
540            info!(
541                self.as_conf_object(),
542                "Simulation stopped without reason, not resuming."
543            );
544
545            let duration = SystemTime::now().duration_since(
546                *self
547                    .start_time
548                    .get()
549                    .ok_or_else(|| anyhow!("Start time was not set"))?,
550            )?;
551
552            // Set the log level so this message always prints
553            set_log_level(self.as_conf_object_mut(), LogLevel::Info)?;
554
555            info!(
556                self.as_conf_object(),
557                "Stopped after {} iterations in {} seconds ({} exec/s).",
558                self.iterations,
559                duration.as_secs_f32(),
560                self.iterations as f32 / duration.as_secs_f32()
561            );
562
563            if self.shutdown_on_stop_without_reason {
564                self.send_shutdown()?;
565            }
566        }
567
568        Ok(())
569    }
570
571    /// Called on core simulation stopped HAP
572    pub fn on_simulation_stopped(&mut self) -> Result<()> {
573        if self.stopped_for_repro {
574            // If we are stopped for repro, we do nothing on this HAP!
575            return Ok(());
576        }
577
578        //  Log information from the fuzzer
579        self.log_messages()?;
580
581        if let Some(reason) = self.stop_reason.take() {
582            self.on_simulation_stopped_with_reason(reason)
583        } else {
584            self.on_simulation_stopped_without_reason()
585        }
586    }
587
588    /// Called on core exception HAP. Check to see if this exception is configured as a solution
589    /// or all exceptions are solutions and trigger a stop if so
590    pub fn on_exception(&mut self, _obj: *mut ConfObject, exception: i64) -> Result<()> {
591        if self.all_exceptions_are_solutions || self.exceptions.contains(&exception) {
592            self.stop_simulation(StopReason::Solution {
593                kind: SolutionKind::Exception,
594            })?;
595        }
596        Ok(())
597    }
598
599    /// Called on breakpoint memory operation HAP. Check to see if this breakpoint is configured
600    /// as a solution or if all breakpoints are solutions and trigger a stop if so
601    pub fn on_breakpoint_memop(
602        &mut self,
603        obj: *mut ConfObject,
604        breakpoint: i64,
605        transaction: *mut GenericTransaction,
606    ) -> Result<()> {
607        if self.all_breakpoints_are_solutions || self.breakpoints.contains(&(breakpoint as i32)) {
608            info!(
609                self.as_conf_object(),
610                "on_breakpoint_memop({:#x}, {}, {:#x})",
611                obj as usize,
612                breakpoint,
613                transaction as usize
614            );
615
616            self.stop_simulation(StopReason::Solution {
617                kind: SolutionKind::Breakpoint,
618            })?;
619        }
620        Ok(())
621    }
622
623    /// Check if magic instructions are set to trigger start and stop conditions, and trigger
624    /// them if needed
625    pub fn on_magic_instruction(
626        &mut self,
627        trigger_obj: *mut ConfObject,
628        magic_number: MagicNumber,
629    ) -> Result<()> {
630        trace!(
631            self.as_conf_object(),
632            "Got magic instruction with magic #{magic_number})"
633        );
634
635        if object_is_processor(trigger_obj)? {
636            let processor_number = get_processor_number(trigger_obj)?;
637
638            if !self.processors.contains_key(&processor_number) {
639                self.add_processor(trigger_obj, false)?;
640            }
641
642            let processor = self
643                .processors
644                .get_mut(&processor_number)
645                .ok_or_else(|| anyhow!("Processor not found"))?;
646
647            let index_selector = processor.get_magic_index_selector()?;
648
649            if match magic_number {
650                MagicNumber::StartBufferPtrSizePtr
651                | MagicNumber::StartBufferPtrSizeVal
652                | MagicNumber::StartBufferPtrSizePtrVal => {
653                    self.start_on_harness
654                        && (if self.magic_start_index == index_selector {
655                            // Set this processor as the start processor now that we know it is
656                            // enabled, but only set if it is not already set
657                            let _ = self.start_processor_number.get_or_init(|| processor_number);
658                            true
659                        } else {
660                            debug!(
661                                "Not setting processor {} as start processor",
662                                processor_number
663                            );
664                            false
665                        })
666                }
667                MagicNumber::StopNormal => {
668                    self.stop_on_harness && self.magic_stop_indices.contains(&index_selector)
669                }
670                MagicNumber::StopAssert => {
671                    self.stop_on_harness && self.magic_assert_indices.contains(&index_selector)
672                }
673            } {
674                self.stop_simulation(StopReason::Magic { magic_number })?;
675            } else {
676                debug!(
677                    self.as_conf_object(),
678                    "Magic instruction {magic_number} was triggered by processor {trigger_obj:?} with index {index_selector} but the index is not configured for this magic number or start/stop on harness was disabled. Configured indices are: start: {}, stop: {:?}, assert: {:?}",
679                    self.magic_start_index,
680                    self.magic_stop_indices,
681                    self.magic_assert_indices
682                );
683            }
684        } else {
685            bail!("Magic instruction was triggered by a non-processor object");
686        }
687
688        Ok(())
689    }
690
691    pub fn on_control_register_write(
692        &mut self,
693        trigger_obj: *mut ConfObject,
694        register_nr: i64,
695        value: i64,
696    ) -> Result<()> {
697        self.on_control_register_write_windows_symcov(trigger_obj, register_nr, value)?;
698
699        Ok(())
700    }
701}