diff --git a/docs/src/config/common-options.md b/docs/src/config/common-options.md index 2a082c7e..22fd3efa 100644 --- a/docs/src/config/common-options.md +++ b/docs/src/config/common-options.md @@ -22,6 +22,7 @@ is desired. - [Enable Logging and Set Log path](#enable-logging-and-set-log-path) - [Keep All Corpus Entries](#keep-all-corpus-entries) - [Use Initial Buffer Contents As Corpus](#use-initial-buffer-contents-as-corpus) + - [Disable Auto-Continue in Repro Mode](#disable-auto-continue-in-repro-mode) ## Solution Configuration @@ -342,3 +343,13 @@ buffer can be used as a seed corpus entry. This can be enabled with: ```python @tsffs.use_initial_as_corpus = True ``` + +### Disable Auto-Continue in Repro Mode + +By default, when running in [repro mode](../fuzzing/analyzing-results.md), TSFFS +automatically resumes simulation after preparing the testcase. To instead pause +and let an external debugger (for example a GDB stub) control resume, set: + +```python +@tsffs.repro_auto_continue = False +``` diff --git a/docs/src/fuzzing/analyzing-results.md b/docs/src/fuzzing/analyzing-results.md index 0cfdf3b7..a9c769f6 100644 --- a/docs/src/fuzzing/analyzing-results.md +++ b/docs/src/fuzzing/analyzing-results.md @@ -11,3 +11,15 @@ Repro mode can be run after stopping execution, or before executing the fuzzing ```python @tsffs.iface.fuzz.repro("%simics%/solutions/TESTCASE") ``` + +By default, repro mode automatically resumes simulation after preparing the +testcase. If you need to attach an external debugger (for example a GDB stub) +before execution begins, you can disable auto-continue so that when the harness +is triggered, TSFFS prepares the testcase but waits for you to resume manually: + +```python +@tsffs.repro_auto_continue = False +``` + +See [Disable Auto-Continue in Repro Mode](../config/common-options.md#disable-auto-continue-in-repro-mode) +for more details. diff --git a/docs/src/tutorials/edk2-uefi/reproducing-runs.md b/docs/src/tutorials/edk2-uefi/reproducing-runs.md index a1eb6a78..8ae03e41 100644 --- a/docs/src/tutorials/edk2-uefi/reproducing-runs.md +++ b/docs/src/tutorials/edk2-uefi/reproducing-runs.md @@ -44,3 +44,9 @@ capabilities that SIMICS offers. When you're done exploring, run `c` to continue You can change the testcase you are examining by choosing a different one with `tsffs.iface.fuzz.repro`, but you cannot resume fuzzing after entering repro mode due to inconsistencies with the simulated system clock. + +> **Tip:** If you want to attach a debugger (such as a GDB stub) before the +> testcase executes, set `@tsffs.repro_auto_continue = False`. When the harness +> is triggered, TSFFS will prepare the testcase but wait for you to resume +> simulation manually. See [Disable Auto-Continue in Repro Mode](../../config/common-options.md#disable-auto-continue-in-repro-mode) +> for details. diff --git a/src/haps/mod.rs b/src/haps/mod.rs index 5732f6d8..e4603565 100644 --- a/src/haps/mod.rs +++ b/src/haps/mod.rs @@ -88,12 +88,19 @@ impl Tsffs { self.execution_trace.0.clear(); self.save_repro_bookmark_if_needed()?; - debug!(self.as_conf_object(), "Resuming simulation"); + if self.should_auto_continue_repro() { + debug!(self.as_conf_object(), "Resuming simulation"); - run_alone(|| { - continue_simulation(0)?; - Ok(()) - })?; + run_alone(|| { + continue_simulation(0)?; + Ok(()) + })?; + } else { + info!( + self.as_conf_object(), + "Repro testcase prepared; waiting for external resume." + ); + } Ok(()) } @@ -220,12 +227,19 @@ impl Tsffs { self.save_symbolic_coverage()?; } - debug!(self.as_conf_object(), "Resuming simulation"); + if self.should_auto_continue_repro() { + debug!(self.as_conf_object(), "Resuming simulation"); - run_alone(|| { - continue_simulation(0)?; - Ok(()) - })?; + run_alone(|| { + continue_simulation(0)?; + Ok(()) + })?; + } else { + info!( + self.as_conf_object(), + "Repro testcase prepared; waiting for external resume." + ); + } Ok(()) } @@ -290,12 +304,19 @@ impl Tsffs { self.execution_trace.0.clear(); self.save_repro_bookmark_if_needed()?; - debug!(self.as_conf_object(), "Resuming simulation"); + if self.should_auto_continue_repro() { + debug!(self.as_conf_object(), "Resuming simulation"); - run_alone(|| { - continue_simulation(0)?; - Ok(()) - })?; + run_alone(|| { + continue_simulation(0)?; + Ok(()) + })?; + } else { + info!( + self.as_conf_object(), + "Repro testcase prepared; waiting for external resume." + ); + } Ok(()) } diff --git a/src/interfaces/fuzz.rs b/src/interfaces/fuzz.rs index b75ba072..e8547c57 100644 --- a/src/interfaces/fuzz.rs +++ b/src/interfaces/fuzz.rs @@ -8,7 +8,7 @@ use crate::{ use anyhow::{anyhow, Result}; use libafl::inputs::HasBytesVec; use simics::{ - continue_simulation, debug, interface, lookup_file, run_alone, AsConfObject, AttrValue, + continue_simulation, debug, info, interface, lookup_file, run_alone, AsConfObject, AttrValue, ConfObject, GenericAddress, }; use std::{ @@ -50,10 +50,17 @@ impl Tsffs { self.get_and_write_testcase()?; self.post_timeout_event()?; - run_alone(|| { - continue_simulation(0)?; - Ok(()) - })?; + if self.should_auto_continue_repro() { + run_alone(|| { + continue_simulation(0)?; + Ok(()) + })?; + } else { + info!( + self.as_conf_object(), + "Repro testcase prepared; waiting for external resume." + ); + } } Ok(()) diff --git a/src/lib.rs b/src/lib.rs index 343a0ed7..7ca55d96 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -266,6 +266,13 @@ pub(crate) struct Tsffs { /// Whether the fuzzer should stop on compiled-in harnesses. If set to `True`, the fuzzer /// will start fuzzing when a harness macro is executed. pub stop_on_harness: bool, + #[class(attribute(optional, default = true))] + /// Whether TSFFS should automatically resume simulation after preparing a repro testcase. + /// + /// When set to `False`, TSFFS will prepare repro execution state (snapshot restore, + /// testcase write, timeout event, bookmark) but not call continue. This is useful when + /// an external debugger (for example a GDB stub) should control resume. + pub repro_auto_continue: bool, #[class(attribute(optional, default = 0))] /// The index number which is passed to the platform-specific magic instruction HAP /// by a compiled-in harness to signal that the fuzzer should start the fuzzing loop. @@ -858,6 +865,11 @@ impl Tsffs { Ok(()) } + + /// Return true if TSFFS should continue simulation after preparing repro state. + pub fn should_auto_continue_repro(&self) -> bool { + self.repro_testcase.is_none() || self.repro_auto_continue + } } impl Tsffs {