diff --git a/ci/libafl.justfile b/ci/libafl.justfile
index d74ac940..75045903 100644
--- a/ci/libafl.justfile
+++ b/ci/libafl.justfile
@@ -23,7 +23,7 @@ clean:
test: compile compile_nyx corpus
#!/bin/bash
timeout 16s sh -c './target/release/fuzzamoto-libafl --input /tmp/in/ --output /tmp/out/ --share /tmp/fuzzamoto_scenario-ir/ --cores 0 --verbose > stdout.log'
- if grep -qa "corpus: 30" stdout.log; then
+ if grep -qa "corpus: 15" stdout.log; then
echo "Fuzzer is working"
else
echo "Fuzzer does not generate enough testcases"
diff --git a/doc/src/usage/reproducing.md b/doc/src/usage/reproducing.md
index 9d8c4c0b..43a74cd3 100644
--- a/doc/src/usage/reproducing.md
+++ b/doc/src/usage/reproducing.md
@@ -15,11 +15,18 @@ inherit stdout from the target application, such that any logs, stack traces,
etc. are printed to the terminal.
-## `http-server` example
+## `http-server` example (Out-of-VM execution)
-Run the scneario with the input supplied through `stdin` and pass the right
+You can just run the scenario directly to debug the out-of-vm execution of the scenario
`bitcoind` binary:
+First, compile the scenario with `reproduce` features
+```
+cargo build --release -p fuzzamoto-scenarios --features "fuzzamoto/reproduce","force_send_and_ping"
+```
+
+Then, run the scneario with the input supplied through `stdin` and pass the right
+
```
cat ./testcase.dat | RUST_LOG=info ./target/release/scenario-http-server ./bitcoind
# Use "echo '' | base64 --decode | ..." if you have the input as a base64 string
@@ -31,6 +38,44 @@ Or alternatively using `FUZZAMOTO_INPUT`:
FUZZAMOTO_INPUT=$PWD/testcase.dat RUST_LOG=info ./target/release/scenario-http-server ./bitcoind
```
+## `ir` example (In-VM execution)
+You can also use `-r` option to debug the in-vm execution of the scenario.
+
+First, build all the packages with both `fuzz` and `nyx_log` feature.
+```
+cd /fuzzamoto
+BITCOIND_PATH=/bitcoin/build_fuzz/bin/bitcoind cargo build --workspace --release --features fuzz,nyx_log,inherit_stdout
+```
+
+`nyx_log` feature is necessary as it allows us to retrieve the log from bitcoind later.
+You can also add `inherit_stdout` feature if you want more verbose logs from bitcoind.
+
+Then, build the crash handler and initialize the nyx share dir as usual:
+
+```
+# Build the crash handler
+clang-19 -fPIC -DENABLE_NYX -D_GNU_SOURCE -DNO_PT_NYX \
+ ./fuzzamoto-nyx-sys/src/nyx-crash-handler.c -ldl -I. -shared -o libnyx_crash_handler.so
+# Initialize the nyx share dir
+./target/release/fuzzamoto-cli init --sharedir /tmp/fuzzamoto_scenario-ir \
+ --crash-handler /fuzzamoto/libnyx_crash_handler.so \
+ --bitcoind /bitcoin/build_fuzz/bin/bitcoind \
+ --scenario ./target/release/scenario-ir \
+ --nyx-dir ./target/release/
+```
+
+Now, run the fuzzer with `-r` option
+```
+RUST_LOG=info ./target/release/fuzzamoto-libafl \
+ --input /tmp/in --output /tmp/out/ \
+ --share /tmp/fuzzamoto_scenario-ir/ \
+ -r \
+ --cores 0 --verbose
+```
+This will execute the given testcase for only once in "reproduce" mode
+
+After this you will find the log from the bitcoind in `/tmp/out/workdir/dump/primary.log`
+
## Troubleshooting
* Make sure to not use the `nyx` feature or else you'll see:
diff --git a/fuzzamoto-cli/Cargo.toml b/fuzzamoto-cli/Cargo.toml
index c381884c..7a93594e 100644
--- a/fuzzamoto-cli/Cargo.toml
+++ b/fuzzamoto-cli/Cargo.toml
@@ -9,6 +9,9 @@ repository.workspace = true
[lints]
workspace = true
+[features]
+nyx_log = []
+
[dependencies]
clap = { version = "4.4", features = ["derive", "string"] }
env_logger = "0.11.6"
diff --git a/fuzzamoto-cli/src/utils/nyx.rs b/fuzzamoto-cli/src/utils/nyx.rs
index f73b59dc..f8c49525 100644
--- a/fuzzamoto-cli/src/utils/nyx.rs
+++ b/fuzzamoto-cli/src/utils/nyx.rs
@@ -99,23 +99,48 @@ pub fn create_nyx_script(
]
.join(":");
+ #[cfg(feature = "nyx_log")]
+ let primary_log = " > /tmp/primary.log 2>&1";
+ #[cfg(not(feature = "nyx_log"))]
+ let primary_log = "";
+
+ #[cfg(feature = "nyx_log")]
+ let secondary_log = " > /tmp/secondary.log 2>&1";
+ #[cfg(not(feature = "nyx_log"))]
+ let secondary_log = "";
+
let asan_options = format!("ASAN_OPTIONS={}", asan_options);
let crash_handler_preload = format!("LD_PRELOAD=./{}", crash_handler_name);
let proxy_script = format!(
- "{} LD_LIBRARY_PATH=/tmp LD_BIND_NOW=1 {} ./bitcoind \\$@",
- asan_options, crash_handler_preload,
+ "{} LD_LIBRARY_PATH=/tmp LD_BIND_NOW=1 {} ./bitcoind \\$@{}",
+ asan_options, crash_handler_preload, primary_log
);
script.push("echo \"#!/bin/sh\" > ./bitcoind_proxy".to_string());
script.push(format!("echo \"{}\" >> ./bitcoind_proxy", proxy_script));
script.push("chmod +x ./bitcoind_proxy".to_string());
+ if secondary_bitcoind.is_some() {
+ let secondary_proxy_script = format!(
+ "{} LD_LIBRARY_PATH=/tmp LD_BIND_NOW=1 {} ./{} \\$@{}",
+ asan_options,
+ crash_handler_preload,
+ secondary_bitcoind.unwrap(),
+ secondary_log
+ );
+ script.push("echo \"#!/bin/sh\" > ./bitcoind2_proxy".to_string());
+ script.push(format!(
+ "echo \"{}\" >> ./bitcoind2_proxy",
+ secondary_proxy_script
+ ));
+ script.push("chmod +x ./bitcoind2_proxy".to_string());
+ }
+
// Run the scenario
script.push(format!(
- "RUST_LOG=debug LD_LIBRARY_PATH=/tmp LD_BIND_NOW=1 ./{} ./bitcoind_proxy {} ./{} > log.txt 2>&1",
+ "RUST_LOG=debug LD_LIBRARY_PATH=/tmp LD_BIND_NOW=1 ./{} ./bitcoind_proxy {} ./bitcoind2_proxy > log.txt 2>&1",
scenario_name,
rpc_path.unwrap_or(""),
- secondary_bitcoind.unwrap_or("")
));
// Debug info
diff --git a/fuzzamoto-scenarios/Cargo.toml b/fuzzamoto-scenarios/Cargo.toml
index 3a0f87fd..95eddf4a 100644
--- a/fuzzamoto-scenarios/Cargo.toml
+++ b/fuzzamoto-scenarios/Cargo.toml
@@ -9,6 +9,7 @@ repository.workspace = true
[features]
fuzz = ["compile_in_vm", "force_send_and_ping", "nyx"]
reproduce = ["compile_in_vm", "force_send_and_ping", "fuzzamoto/reproduce"]
+nyx_log = ["nyx"]
nyx = ["dep:fuzzamoto-nyx-sys"]
compile_in_vm = []
diff --git a/fuzzamoto-scenarios/bin/ir.rs b/fuzzamoto-scenarios/bin/ir.rs
index 7b9bb7cc..05b70d1a 100644
--- a/fuzzamoto-scenarios/bin/ir.rs
+++ b/fuzzamoto-scenarios/bin/ir.rs
@@ -428,6 +428,45 @@ pub fn probe_recent_block_hashes(
return Some(ProbeResult::RecentBlockes { result: result });
}
+#[cfg(feature = "nyx_log")]
+const PRIMARY_LOG: &str = "/tmp/primary.log";
+#[cfg(all(
+ feature = "nyx_log",
+ any(feature = "oracle_netsplit", feature = "oracle_consensus")
+))]
+const SECONDARY_LOG: &str = "/tmp/secondary.log";
+
+#[cfg(feature = "nyx_log")]
+fn dump_log_to_host() {
+ {
+ let log = std::fs::read(PRIMARY_LOG);
+ if let Ok(data) = log {
+ unsafe {
+ nyx_dump_file_to_host(
+ PRIMARY_LOG.as_ptr() as *const i8,
+ PRIMARY_LOG.len(),
+ data.as_ptr(),
+ data.len(),
+ );
+ }
+ }
+ #[cfg(any(feature = "oracle_netsplit", feature = "oracle_consensus"))]
+ {
+ let log = std::fs::read(SECONDARY_LOG);
+ if let Ok(data) = log {
+ unsafe {
+ nyx_dump_file_to_host(
+ SECONDARY_LOG.as_ptr() as *const i8,
+ SECONDARY_LOG.len(),
+ data.as_ptr(),
+ data.len(),
+ );
+ }
+ }
+ }
+ }
+}
+
impl Scenario<'_, TestCase> for IrScenario
where
TX: Transport,
@@ -472,7 +511,12 @@ where
}
self.print_received();
- self.evaluate_oracles()
+ let res = self.evaluate_oracles();
+
+ #[cfg(feature = "nyx_log")]
+ dump_log_to_host();
+
+ res
}
}