Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ci/libafl.justfile
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
49 changes: 47 additions & 2 deletions doc/src/usage/reproducing.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 '<input base64>' | base64 --decode | ..." if you have the input as a base64 string
Expand All @@ -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 <path to the testcase> \
--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:
Expand Down
3 changes: 3 additions & 0 deletions fuzzamoto-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
33 changes: 29 additions & 4 deletions fuzzamoto-cli/src/utils/nyx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions fuzzamoto-scenarios/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 = []
Expand Down
46 changes: 45 additions & 1 deletion fuzzamoto-scenarios/bin/ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,45 @@ pub fn probe_recent_block_hashes<T: HasBlockChainInterface>(
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<TX, T> Scenario<'_, TestCase> for IrScenario<TX, T>
where
TX: Transport,
Expand Down Expand Up @@ -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
}
}

Expand Down
Loading