diff --git a/README.md b/README.md
index 4c6744d..c809ff7 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,14 @@
# π Phink
+
+
![Build Status](https://github.com/srlabs/phink/actions/workflows/rust.yml/badge.svg)
[![License](https://img.shields.io/github/license/srlabs/phink)](https://github.com/srlabs/phink/blob/main/LICENSE)
[![dependency status](https://deps.rs/repo/github/srlabs/phink/status.svg)](https://deps.rs/repo/github/srlabs/phink)
[![Discord](https://img.shields.io/discord/1276519988349374587.svg?label=&logo=discord&logoColor=ffffff&color=7289DA&labelColor=2C2F33)](https://discord.gg/gAahQMGE)
+
+
**Phink** is a blazing-fastβ‘, property-based, coverage-guided fuzzer for ink! smart contracts. It enables developers to
embed inviolable properties into their smart contract testing workflows, equipping them with automatic tools to detect
vulnerabilities and ensure contract reliability before deployment.
diff --git a/output_test_assert_output_created_when_fuzzing/phink/afl/mainaflfuzzer/.cur_input b/output_test_assert_output_created_when_fuzzing/phink/afl/mainaflfuzzer/.cur_input
new file mode 100644
index 0000000..e69de29
diff --git a/output_test_assert_output_created_when_fuzzing/phink/afl/mainaflfuzzer/cmdline b/output_test_assert_output_created_when_fuzzing/phink/afl/mainaflfuzzer/cmdline
new file mode 100644
index 0000000..6d287f4
--- /dev/null
+++ b/output_test_assert_output_created_when_fuzzing/phink/afl/mainaflfuzzer/cmdline
@@ -0,0 +1 @@
+./target/afl/debug/phink
diff --git a/output_test_assert_output_created_when_fuzzing/phink/afl/mainaflfuzzer/fuzzer_setup b/output_test_assert_output_created_when_fuzzing/phink/afl/mainaflfuzzer/fuzzer_setup
new file mode 100644
index 0000000..7cdb02d
--- /dev/null
+++ b/output_test_assert_output_created_when_fuzzing/phink/afl/mainaflfuzzer/fuzzer_setup
@@ -0,0 +1,20 @@
+# environment variables:
+AFL_AUTORESUME=1
+AFL_CMPLOG_ONLY_NEW=1
+AFL_CUSTOM_INFO_PROGRAM=./target/afl/debug/phink
+AFL_CUSTOM_INFO_PROGRAM_ARGV=
+AFL_CUSTOM_INFO_OUT=output_test_assert_output_created_when_fuzzing/phink/afl/mainaflfuzzer
+AFL_DEBUG=1
+AFL_DISABLE_TRIM=1
+AFL_FAST_CAL=1
+AFL_FINAL_SYNC=1
+AFL_FORCE_UI=1
+AFL_FUZZER_STATS_UPDATE_INTERVAL=10
+AFL_FORKSRV_INIT_TMOUT=10000000
+AFL_IGNORE_SEED_PROBLEMS=1
+AFL_IGNORE_UNKNOWN_ENVS=1
+AFL_IMPORT_FIRST=1
+AFL_NO_WARN_INSTABILITY=1
+AFL_TESTCACHE_SIZE=100
+# command line:
+'/Users/kevinvalerio/.local/share/afl.rs/rustc-1.82.0-nightly-91376f4/afl.rs-0.15.10/afl/bin/afl-fuzz' '-c0' '-Mmainaflfuzzer' '-ioutput_test_assert_output_created_when_fuzzing/phink/corpus/' '-pexplore' '-ooutput_test_assert_output_created_when_fuzzing/phink/afl' '-g4' '-G1048576' '-c-' '-P600' '-x./output/phink/selectors.dict' './target/afl/debug/phink'
diff --git a/output_test_assert_output_created_when_fuzzing/phink/afl/mainaflfuzzer/is_main_node b/output_test_assert_output_created_when_fuzzing/phink/afl/mainaflfuzzer/is_main_node
new file mode 100644
index 0000000..e69de29
diff --git a/output_test_assert_output_created_when_fuzzing/phink/afl/mainaflfuzzer/plot_data b/output_test_assert_output_created_when_fuzzing/phink/afl/mainaflfuzzer/plot_data
new file mode 100644
index 0000000..58e8358
--- /dev/null
+++ b/output_test_assert_output_created_when_fuzzing/phink/afl/mainaflfuzzer/plot_data
@@ -0,0 +1 @@
+# relative_time, cycles_done, cur_item, corpus_count, pending_total, pending_favs, map_size, saved_crashes, saved_hangs, max_depth, execs_per_sec, total_execs, edges_found
diff --git a/output_test_assert_output_created_when_fuzzing/phink/afl/mainaflfuzzer/queue/id:000000,time:0,execs:0,orig:init b/output_test_assert_output_created_when_fuzzing/phink/afl/mainaflfuzzer/queue/id:000000,time:0,execs:0,orig:init
new file mode 100644
index 0000000..5b290d0
--- /dev/null
+++ b/output_test_assert_output_created_when_fuzzing/phink/afl/mainaflfuzzer/queue/id:000000,time:0,execs:0,orig:init
@@ -0,0 +1 @@
+00000000
diff --git a/output_test_assert_output_created_when_fuzzing/phink/corpus/init b/output_test_assert_output_created_when_fuzzing/phink/corpus/init
new file mode 100644
index 0000000..5b290d0
--- /dev/null
+++ b/output_test_assert_output_created_when_fuzzing/phink/corpus/init
@@ -0,0 +1 @@
+00000000
diff --git a/output_test_assert_output_created_when_fuzzing/phink/logs/afl.log b/output_test_assert_output_created_when_fuzzing/phink/logs/afl.log
new file mode 100644
index 0000000..c627654
--- /dev/null
+++ b/output_test_assert_output_created_when_fuzzing/phink/logs/afl.log
@@ -0,0 +1,42 @@
+
+If you see an error message like `shmget() failed` above, try running the following command:
+
+ cargo-afl afl system-config
+
+Note: You might be prompted to enter your password as root privileges are required and hence sudo is run within this command.
+ironment variable AFL_DEBUG with value 1[0m
+[1;92m[+] [0mEnabled environment variable AFL_FAST_CAL with value 1[0m
+[1;92m[+] [0mEnabled environment variable AFL_FINAL_SYNC with value 1[0m
+[1;92m[+] [0mEnabled environment variable AFL_FORCE_UI with value 1[0m
+[1;92m[+] [0mEnabled environment variable AFL_FORKSRV_INIT_TMOUT with value 10000000[0m
+[1;92m[+] [0mEnabled environment variable AFL_FUZZER_STATS_UPDATE_INTERVAL with value 10[0m
+[1;92m[+] [0mEnabled environment variable AFL_IGNORE_SEED_PROBLEMS with value 1[0m
+[1;92m[+] [0mEnabled environment variable AFL_IMPORT_FIRST with value 1[0m
+[1;92m[+] [0mEnabled environment variable AFL_NO_WARN_INSTABILITY with value 1[0m
+[1;92m[+] [0mEnabled environment variable AFL_TESTCACHE_SIZE with value 100[0m
+[0;36mafl-fuzz++4.21c[0m based on afl by Michal Zalewski and a large online community
+[1;94m[*] [0mDisabling cmplog again because of '-c -'.[0m
+[1;92m[+] [0mAFL++ is maintained by Marc "van Hauser" Heuse, Dominik Maier, Andrea Fioraldi and Heiko "hexcoder" EiΓfeldt[0m
+[1;92m[+] [0mAFL++ is open source, get it at https://github.com/AFLplusplus/AFLplusplus[0m
+[1;92m[+] [0mNOTE: AFL++ >= v3 has changed defaults and behaviours - see README.md[0m
+[1;92m[+] [0mEnabled environment variable AFL_DISABLE_TRIM with value 1[0m
+[1;94m[*] [0mGetting to work...[0m
+[1;92m[+] [0mUsing exploration-based constant power schedule (EXPLORE)[0m
+[1;92m[+] [0mEnabled testcache with 100 MB[0m
+[1;92m[+] [0mGenerating fuzz data with a length of min=4 max=1048576[0m
+[1;94m[*] [0mChecking CPU scaling governor...[0m
+[1;93m[!] [1;97mWARNING: [0mCould not check CPU min frequency[0m
+[1;92m[+] [0mLooks like we're not running on a tty, so I'll be a bit less verbose.[0m
+[1;92m[+] [0mYou have 8 CPU cores and 13 runnable tasks (utilization: 162%).[0m
+[1;93m[!] [1;97mWARNING: [0mSystem under apparent load, performance may be spotty.[0m
+[1;94m[*] [0mSetting up output directories...[0m
+[1;94m[*] [0mScanning 'output_test_assert_output_created_when_fuzzing/phink/corpus/'...[0m
+[1;92m[+] [0mLoaded a total of 1 seeds.[0m
+[1;94m[*] [0mCreating hard links for all input files...[0m
+[1;94m[*] [0mValidating target binary...[0m
+[1;92m[+] [0m[1;95mPersistent mode binary detected.[0m
+[1;92m[+] [0m[1;95mDeferred forkserver binary detected.[0m
+[?25h[0m[1;91m
+[-] SYSTEM ERROR : [0mshmget() failed, try running afl-system-config[1;91m
+ Stop location : [0mafl_shm_init(), src/afl-sharedmem.c:284
+[1;91m OS message : [0mInvalid argument
diff --git a/src/cli/ziggy.rs b/src/cli/ziggy.rs
index fbce202..d0d292d 100644
--- a/src/cli/ziggy.rs
+++ b/src/cli/ziggy.rs
@@ -119,8 +119,7 @@ impl ZiggyConfig {
}
pub fn parse(config_str: String) -> Self {
- let config: Self =
- serde_json::from_str(&config_str).expect("β Failed to parse config");
+ let config: Self = serde_json::from_str(&config_str).expect("β Failed to parse config");
if config.config.verbose {
println!("π¨οΈ Using PHINK_START_FUZZING_WITH_CONFIG = {}", config_str);
}
diff --git a/src/contract/payload.rs b/src/contract/payload.rs
index 8ab00bf..ed3c69a 100644
--- a/src/contract/payload.rs
+++ b/src/contract/payload.rs
@@ -40,9 +40,7 @@ impl PayloadCrafter {
if entry.path().extension().map_or(false, |ext| ext == "json") {
if let Ok(contents) = fs::read_to_string(entry.path()) {
if let Ok(v) = serde_json::from_str::(&contents) {
- if let Ok(spec) =
- serde_json::from_value::(v["spec"].clone())
- {
+ if let Ok(spec) = serde_json::from_value::(v["spec"].clone()) {
let selectors = Self::parse_selectors(&spec);
all_selectors.extend(selectors);
}
@@ -73,8 +71,7 @@ impl PayloadCrafter {
/// # Arguments
/// * `json_data`: The JSON specs of the smart-contract
pub fn extract_invariants(json_data: &str) -> Option> {
- let data: Value =
- serde_json::from_str(json_data).expect("JSON was not well-formatted");
+ let data: Value = serde_json::from_str(json_data).expect("JSON was not well-formatted");
Some(
data["spec"]["messages"]
@@ -245,8 +242,8 @@ mod test {
}
let hash_two: [u8; 32] = [
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 2,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 2,
];
println!("{:?}", hex::encode(hash_two.encode()));
@@ -257,10 +254,9 @@ mod test {
let metadata_path = Path::new("sample/dns/target/ink/dns.json");
let transcoder = ContractMessageTranscoder::load(metadata_path).unwrap();
- let encoded_bytes = hex::decode(
- "229b553f9400000000000000000027272727272727272700002727272727272727272727",
- )
- .unwrap();
+ let encoded_bytes =
+ hex::decode("229b553f9400000000000000000027272727272727272700002727272727272727272727")
+ .unwrap();
let hex = transcoder.decode_contract_message(&mut &encoded_bytes[..]);
assert_eq!(
hex.unwrap().to_string(),
diff --git a/src/contract/remote.rs b/src/contract/remote.rs
index 0cf9c4c..b913925 100644
--- a/src/contract/remote.rs
+++ b/src/contract/remote.rs
@@ -71,8 +71,7 @@ pub struct ContractBridge {
}
impl ContractBridge {
- pub const DEFAULT_GAS_LIMIT: Weight =
- Weight::from_parts(100_000_000_000, 3 * 1024 * 1024);
+ pub const DEFAULT_GAS_LIMIT: Weight = Weight::from_parts(100_000_000_000, 3 * 1024 * 1024);
pub const DEFAULT_DEPLOYER: AccountId32 = AccountId32::new([1u8; 32]);
/// Create a proper genesis storage, deploy and instantiate a given ink!
diff --git a/src/cover/report.rs b/src/cover/report.rs
index e6ee30e..8b36b0e 100644
--- a/src/cover/report.rs
+++ b/src/cover/report.rs
@@ -45,9 +45,7 @@ impl CoverageTracker {
for (i, line) in lines.iter().enumerate() {
let trimmed = line.trim();
- if let Some(cov_num) =
- trimmed.strip_prefix("ink::env::debug_println!(\"COV={}\", ")
- {
+ if let Some(cov_num) = trimmed.strip_prefix("ink::env::debug_println!(\"COV={}\", ") {
if let Some(cov_num) = cov_num.strip_suffix(");") {
if let Ok(num) = cov_num.parse::() {
if self.hit_lines.contains(&num) {
@@ -202,9 +200,7 @@ impl CoverageTracker {
let filtered_lines: Vec<&str> = lines
.into_iter()
- .filter(|line| {
- !(line.contains("ink::env::debug_println!") && line.contains("COV="))
- })
+ .filter(|line| !(line.contains("ink::env::debug_println!") && line.contains("COV=")))
.collect();
*html = filtered_lines.join("\n");
diff --git a/src/fuzzer/bug.rs b/src/fuzzer/bug.rs
index 1cbc2a9..7d9e3ec 100644
--- a/src/fuzzer/bug.rs
+++ b/src/fuzzer/bug.rs
@@ -130,9 +130,7 @@ impl BugManager {
}
pub fn is_contract_trapped(&self, contract_response: &FullContractResponse) -> bool {
- if let Err(DispatchError::Module(ModuleError { message, .. })) =
- contract_response.result
- {
+ if let Err(DispatchError::Module(ModuleError { message, .. })) = contract_response.result {
if message == Some("ContractTrapped") {
return true;
}
diff --git a/src/fuzzer/fuzz.rs b/src/fuzzer/fuzz.rs
index ece0bc5..8667c3e 100644
--- a/src/fuzzer/fuzz.rs
+++ b/src/fuzzer/fuzz.rs
@@ -247,8 +247,7 @@ fn write_dict_entry(dict_file: &mut fs::File, selector: &Selector) {
write!(&mut acc, "\\x{:02X}", b).unwrap();
acc
});
- writeln!(dict_file, "\"{}\"", selector_string)
- .expect("π
Failed to write to dict_file");
+ writeln!(dict_file, "\"{}\"", selector_string).expect("π
Failed to write to dict_file");
}
fn execute_messages(
@@ -295,8 +294,7 @@ fn check_invariants(
bug_manager.display_trap(decoded_msgs.messages[0].clone(), response.clone());
});
- if let Err(invariant_tested) = bug_manager.are_invariants_passing(decoded_msgs.origin)
- {
+ if let Err(invariant_tested) = bug_manager.are_invariants_passing(decoded_msgs.origin) {
bug_manager.display_invariant(
all_msg_responses.to_vec(),
decoded_msgs.clone(),
@@ -320,10 +318,9 @@ mod tests {
.expect("Failed to load ContractMessageTranscoder"),
);
- let encoded_bytes = hex::decode(
- "229b553f9400000000000000000027272727272727272700002727272727272727272727",
- )
- .expect("Failed to decode hex string");
+ let encoded_bytes =
+ hex::decode("229b553f9400000000000000000027272727272727272700002727272727272727272727")
+ .expect("Failed to decode hex string");
let hex = transcoder
.lock()
diff --git a/src/fuzzer/parser.rs b/src/fuzzer/parser.rs
index 0650a86..9a7ddc8 100644
--- a/src/fuzzer/parser.rs
+++ b/src/fuzzer/parser.rs
@@ -159,8 +159,7 @@ pub fn parse_input(
{
let is_payable: bool = is_message_payable(
&Selector::from(
- <&[u8] as TryInto<[u8; 4]>>::try_into(&encoded_message[0..4])
- .unwrap(),
+ <&[u8] as TryInto<[u8; 4]>>::try_into(&encoded_message[0..4]).unwrap(),
),
transcoder.get_mut().unwrap().metadata(),
);
diff --git a/src/instrumenter/instrumentation.rs b/src/instrumenter/instrumentation.rs
index ce4f0f2..ed022ed 100644
--- a/src/instrumenter/instrumentation.rs
+++ b/src/instrumenter/instrumentation.rs
@@ -83,9 +83,7 @@ impl Instrumenter {
})?
.filter_map(|entry| {
let path = entry.ok()?.path();
- if path.is_file()
- && path.extension().and_then(OsStr::to_str) == Some("wasm")
- {
+ if path.is_file() && path.extension().and_then(OsStr::to_str) == Some("wasm") {
Some(path)
} else {
None
@@ -94,8 +92,7 @@ impl Instrumenter {
.next()
.ok_or("π
No .wasm file found in target directory")?;
- let specs_path =
- PathBuf::from(wasm_path.to_str().unwrap().replace(".wasm", ".json"));
+ let specs_path = PathBuf::from(wasm_path.to_str().unwrap().replace(".wasm", ".json"));
Ok(InkFilesPath {
wasm_path,
@@ -221,10 +218,8 @@ impl ContractInstrumenter for Instrumenter {
contract_cov_manager
);
- let modified_code =
- Self::parse_and_visit(&code, contract_cov_manager).map_err(|_| {
- format!("π
Failed to parse and visit code in {}", path.display())
- })?;
+ let modified_code = Self::parse_and_visit(&code, contract_cov_manager)
+ .map_err(|_| format!("π
Failed to parse and visit code in {}", path.display()))?;
Self::save_and_format(modified_code, PathBuf::from(path)).map_err(|e| {
format!(
@@ -294,8 +289,7 @@ mod instrument {
// borrowing issues
let mut stmts = std::mem::take(&mut block.stmts);
for mut stmt in stmts.drain(..) {
- let line_lit =
- LitInt::new(self.line_id.to_string().as_str(), Span::call_site());
+ let line_lit = LitInt::new(self.line_id.to_string().as_str(), Span::call_site());
self.line_id = self.line_id + 1;
@@ -303,8 +297,7 @@ mod instrument {
ink::env::debug_println!("COV={}", #line_lit)
};
// Convert this expression into a statement
- let pre_stmt: Stmt =
- Stmt::Expr(insert_expr, Some(Token![;](Span::call_site())));
+ let pre_stmt: Stmt = Stmt::Expr(insert_expr, Some(Token![;](Span::call_site())));
new_stmts.push(pre_stmt);
// Use recursive visitation to handle nested blocks and other
// statement types
diff --git a/src/instrumenter/instrumented_path.rs b/src/instrumenter/instrumented_path.rs
index 7f835bb..e356dd7 100644
--- a/src/instrumenter/instrumented_path.rs
+++ b/src/instrumenter/instrumented_path.rs
@@ -43,10 +43,8 @@ impl InstrumentedPath {
}
pub fn clean() -> Result<(), io::Error> {
- let dirs_to_remove = Self::get_dirs_to_remove(
- Path::new("/tmp"),
- DEFAULT_PATH_PATTERN_INSTRUMENTEDPATH,
- )?;
+ let dirs_to_remove =
+ Self::get_dirs_to_remove(Path::new("/tmp"), DEFAULT_PATH_PATTERN_INSTRUMENTEDPATH)?;
if dirs_to_remove.is_empty() {
println!("β No directories found matching the pattern '{}'. There's nothing to be cleaned :)", DEFAULT_PATH_PATTERN_INSTRUMENTEDPATH);
@@ -67,17 +65,12 @@ impl InstrumentedPath {
Ok(())
}
- fn get_dirs_to_remove(
- tmp_dir: &Path,
- pattern: &str,
- ) -> Result, io::Error> {
+ fn get_dirs_to_remove(tmp_dir: &Path, pattern: &str) -> Result, io::Error> {
Ok(fs::read_dir(tmp_dir)?
.filter_map(|entry| {
let entry = entry.ok()?;
let path = entry.path();
- if path.is_dir()
- && path.file_name()?.to_string_lossy().starts_with(pattern)
- {
+ if path.is_dir() && path.file_name()?.to_string_lossy().starts_with(pattern) {
Some(path)
} else {
None
diff --git a/src/lib.rs b/src/lib.rs
index d9b8e57..9b92a5c 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -89,8 +89,7 @@ pub fn main() {
// We execute `handle_cli()` first, then re-enter into `main()`
if let Ok(config_str) = var("PHINK_START_FUZZING_WITH_CONFIG") {
if var("PHINK_FROM_ZIGGY").is_ok() {
- Fuzzer::execute_harness(Fuzz, ZiggyConfig::parse(config_str.clone()))
- .unwrap();
+ Fuzzer::execute_harness(Fuzz, ZiggyConfig::parse(config_str.clone())).unwrap();
}
} else {
handle_cli();
@@ -140,10 +139,7 @@ fn handle_cli() {
.unwrap();
}
Commands::Coverage(contract_path) => {
- CoverageTracker::generate(ZiggyConfig::new(
- config,
- contract_path.contract_path,
- ));
+ CoverageTracker::generate(ZiggyConfig::new(config, contract_path.contract_path));
}
Commands::Clean => {
InstrumentedPath::clean().unwrap();
diff --git a/tests/cli_fuzz_integration_test.rs b/tests/cli_fuzz_integration_test.rs
index c283ffd..172c29f 100644
--- a/tests/cli_fuzz_integration_test.rs
+++ b/tests/cli_fuzz_integration_test.rs
@@ -11,6 +11,7 @@ mod tests {
get_corpus_files,
instrument,
samples::Sample,
+ try_cleanup_fuzzoutput,
with_modified_phink_config,
};
use anyhow::{
@@ -28,46 +29,67 @@ mod tests {
};
#[test]
- fn test_assert_output_created_when_fuzzing() {
- let fuzz_output = PathBuf::from("output_for_integration_test");
+ fn test_fuzz_assert_output_created_when_fuzzing() {
+ let fuzz_output = PathBuf::from("output_test_assert_output_created_when_fuzzing");
let config = Configuration {
instrumented_contract_path: Some(InstrumentedPath::new(PathBuf::from(
- "just_a_random_path",
+ "test_assert_output_created_when_fuzzing_instrumented",
))),
fuzz_output: Some(fuzz_output.clone()),
cores: Some(1),
..Default::default()
};
- let corpus_path = &fuzz_output.join("phink").join("corpus");
+ let corpus_path = &fuzz_output.join("phink").join("corpus"); // That's the default Ziggy corpus path
with_modified_phink_config(&config, || {
instrument(Sample::Dummy);
- // Defaut Ziggy corpus location
let mut initial_corpus_files = 0_usize;
- // While fuzzing
- let test_passed =
- ensure_while_fuzzing(&config, Duration::from_secs(30), || {
+ // While fuzzing, let's perform the tests
+ ensure!(
+ ensure_while_fuzzing(&config, Duration::from_secs(120), || {
// We check that the output is properly created
let fuzz_created = fs::metadata(fuzz_output.clone()).is_ok();
- ensure!(false); // todo why the fuck is that passing
+ println!("Fuzz output created yet : {:?}", fuzz_created);
+ ensure!(fuzz_created, "Fuzz output directory wasn't created");
+ // // ensure!(false); // todo why the fuck is that passing
if fuzz_created {
initial_corpus_files = get_corpus_files(corpus_path).len();
+ println!("initial_corpus_files: {:?}", initial_corpus_files);
+
+ ensure!(
+ !initial_corpus_files != 0,
+ "After created, the corpus files wasn't empty{:?}",
+ initial_corpus_files
+ );
+
+ ensure!(
+ fs::metadata(&fuzz_output.join("phink").join("selectors.dict")).is_ok(),
+ "Selectors.dict doesn't exist"
+ );
+
+ ensure!(
+ fs::metadata(&fuzz_output.join("phink").join("allowlist.txt")).is_ok(),
+ "ALLOWLIST for AFL doesn't exist"
+ );
}
- // We check that the corpus isn't empty
- ensure!(!initial_corpus_files != 0);
Ok(())
- });
+ })
+ .is_ok(),
+ "ensure_while_fuzzing failed to pass"
+ );
- ensure!(test_passed.is_ok());
// Check that new files have been added to the corpus during fuzzing
ensure!(
- get_corpus_files(&fuzz_output).len() > initial_corpus_files,
+ get_corpus_files(&corpus_path).len() > initial_corpus_files,
"There was no new corpus while fuzzing"
);
+
+ try_cleanup_fuzzoutput(&config);
+
Ok(())
})
.unwrap();
diff --git a/tests/shared/mod.rs b/tests/shared/mod.rs
index 587020e..5a67e3c 100644
--- a/tests/shared/mod.rs
+++ b/tests/shared/mod.rs
@@ -43,8 +43,8 @@ pub const DEFAULT_TEST_PHINK_TOML: &str = "phink_temp_test.toml";
/// # Arguments
///
/// * `config`: A `Configuration` struct, the same one used for CLI
-/// * `executed_test`: The function being executed that effectively performs the tests,
-/// i.e functions containing `ensure!`
+/// * `executed_test`: The function being executed that effectively performs the tests, i.e
+/// functions containing `ensure!`
/// # Examples
///
/// ```
@@ -53,20 +53,11 @@ pub const DEFAULT_TEST_PHINK_TOML: &str = "phink_temp_test.toml";
/// Ok(())
/// });
/// ```
-pub fn with_modified_phink_config(
- config: &Configuration,
- executed_test: F,
-) -> Result<()>
+pub fn with_modified_phink_config(config: &Configuration, executed_test: F) -> Result<()>
where
F: FnOnce() -> Result<()>,
{
- let _ = fs::remove_dir_all(
- &config
- .instrumented_contract_path
- .clone()
- .unwrap_or_default()
- .path,
- );
+ try_cleanup_instrumented(config);
let _ = fs::remove_dir_all(&config.fuzz_output.clone().unwrap_or_default());
config.save_as_toml(DEFAULT_TEST_PHINK_TOML);
@@ -77,13 +68,7 @@ where
// We remove the temp config file
let _ = fs::remove_file(DEFAULT_TEST_PHINK_TOML);
// We clean the instrumented path
- let _ = fs::remove_dir_all(
- &config
- .instrumented_contract_path
- .clone()
- .unwrap_or_default()
- .path,
- );
+ try_cleanup_instrumented(config);
let _ = fs::remove_dir_all(config.fuzz_output.clone().unwrap_or_default());
test_result
@@ -97,10 +82,10 @@ where
/// # Arguments
///
/// * `config`: A `Configuration` struct, the same one used for CLI
-/// * `timeout`: A timeout where the test would be considered as failed if the conditions
-/// inside `executed_test` couldn't be met (i.e, it couldn't `Ok(())`)
-/// * `executed_test`: The function being executed that effectively performs the tests,
-/// i.e functions containing `ensure`
+/// * `timeout`: A timeout where the test would be considered as failed if the conditions inside
+/// `executed_test` couldn't be met (i.e, it couldn't `Ok(())`)
+/// * `executed_test`: The function being executed that effectively performs the tests, i.e
+/// functions containing `ensure`
///
/// returns: Result<(), Error>
///
@@ -121,6 +106,8 @@ pub fn ensure_while_fuzzing(
where
F: FnMut() -> Result<()>,
{
+ try_cleanup_instrumented(config);
+
// We start the fuzzer
let mut child = fuzz(
config
@@ -136,20 +123,46 @@ where
loop {
if let Ok(_) = executed_test() {
child.kill().context("Failed to kill Ziggy")?;
+ try_cleanup_instrumented(config);
return Ok(());
}
if start_time.elapsed() > timeout {
child.kill().context("Failed to kill Ziggy")?;
+ try_cleanup_instrumented(config);
// If we haven't return `Ok(())` early on, we `Err()` because we timeout.
return Err(anyhow!(
"Couldn't check the assert within the given timeout"
));
}
+ // We perform the tests every `1` second
thread::sleep(Duration::from_secs(1));
}
}
+
+/// Try to clean up the path where the instrumented contract is. If it fails, it doesn't matter
+pub fn try_cleanup_instrumented(config: &Configuration) {
+ let _ = fs::remove_dir_all(config.clone().instrumented_contract_path.unwrap().path);
+}
+
+/// Try to clean up the path where the output of the fuzzing campaign is. If it fails, it doesn't
+/// matter
+pub fn try_cleanup_fuzzoutput(config: &Configuration) {
+ let output = config.clone().fuzz_output.unwrap_or_default();
+ match fs::remove_dir_all(&output) {
+ Ok(_) => {
+ println!("Removed {}", output.display())
+ }
+ Err(_) => {
+ println!("**DIDN'T** removed {}", output.display())
+ }
+ };
+}
+
+/// Simple `phink` bin pop from cargo to instrument `contract_path`
+/// ** Important **
+/// This should only be used in test !
pub fn instrument(contract_path: Sample) {
let mut cmd = Command::cargo_bin("phink").unwrap();
let _binding = cmd
@@ -160,6 +173,9 @@ pub fn instrument(contract_path: Sample) {
.success();
}
+/// Simple `phink` bin pop from cargo to fuzz `path_instrumented_contract`
+/// ** Important **
+/// This should only be used in test !
pub fn fuzz(path_instrumented_contract: InstrumentedPath) -> Child {
let mut child = NativeCommand::new("cargo")
.arg("run")
@@ -175,6 +191,7 @@ pub fn fuzz(path_instrumented_contract: InstrumentedPath) -> Child {
child
}
+/// Return `true` if `target` is found in any `*.rs` file of `dir`, otherwise `false`
pub fn find_string_in_rs_files(dir: &Path, target: &str) -> bool {
fn file_contains_string(file_path: &Path, target: &str) -> bool {
let mut file = fs::File::open(file_path).expect("Unable to open file");
@@ -202,6 +219,7 @@ pub fn find_string_in_rs_files(dir: &Path, target: &str) -> bool {
false
}
+/// A function to get all entries from the corpus directory
pub fn get_corpus_files(corpus_path: &PathBuf) -> HashSet {
println!("Got corpus files in: {:?}", corpus_path);
fs::read_dir(corpus_path)