From feb7834483c34b0fcb94bb00bf2bb04db35e363b Mon Sep 17 00:00:00 2001 From: Jon-Becker Date: Sat, 30 Dec 2023 10:17:00 -0600 Subject: [PATCH] feat(decompile): use `run_with_timeout` for symbolic execution --- common/src/ether/evm/ext/exec/mod.rs | 2 +- core/src/decompile/mod.rs | 39 ++++++++++++++++++++++++---- core/tests/test_decompile.rs | 10 +++++++ 3 files changed, 45 insertions(+), 6 deletions(-) diff --git a/common/src/ether/evm/ext/exec/mod.rs b/common/src/ether/evm/ext/exec/mod.rs index 779cb88c..af84c06c 100644 --- a/common/src/ether/evm/ext/exec/mod.rs +++ b/common/src/ether/evm/ext/exec/mod.rs @@ -24,7 +24,7 @@ use crate::{ }; use std::collections::HashMap; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default)] pub struct VMTrace { pub instruction: u128, pub gas_used: u128, diff --git a/core/src/decompile/mod.rs b/core/src/decompile/mod.rs index a598db33..06158803 100644 --- a/core/src/decompile/mod.rs +++ b/core/src/decompile/mod.rs @@ -5,7 +5,9 @@ pub mod precompile; pub mod resolve; pub mod util; use heimdall_common::{ - debug_max, ether::bytecode::get_bytecode_from_target, utils::strings::get_shortned_target, + debug_max, + ether::{bytecode::get_bytecode_from_target, evm::ext::exec::VMTrace}, + utils::{strings::get_shortned_target, threading::run_with_timeout}, }; use crate::{ @@ -80,6 +82,10 @@ pub struct DecompilerArgs { /// The name for the output file #[clap(long, short, default_value = "", hide_default_value = true)] pub name: String, + + /// The timeout for each function's symbolic execution in milliseconds. + #[clap(long, short, default_value = "10000", hide_default_value = true)] + pub timeout: u64, } impl DecompilerArgsBuilder { @@ -94,6 +100,7 @@ impl DecompilerArgsBuilder { include_yul: Some(false), output: Some(String::new()), name: Some(String::new()), + timeout: Some(10000), } } } @@ -252,8 +259,22 @@ pub async fn decompile( ); // get a map of possible jump destinations - let (map, jumpdest_count) = - &evm.clone().symbolic_exec_selector(&selector, function_entry_point); + let mut evm_clone = evm.clone(); + let selector_clone = selector.clone(); + let (map, jumpdest_count) = match run_with_timeout( + move || evm_clone.symbolic_exec_selector(&selector_clone, function_entry_point), + Duration::from_millis(args.timeout), + ) { + Some(map) => map, + None => { + trace.add_error( + func_analysis_trace, + line!(), + &format!("symbolic execution timed out!"), + ); + (VMTrace::default(), 0) + } + }; trace.add_debug( func_analysis_trace, @@ -276,7 +297,7 @@ pub async fn decompile( if args.include_yul { debug_max!("analyzing symbolic execution trace '0x{}' with yul analyzer", selector); analyzed_function = analyze_yul( - map, + &map, Function { selector: selector.clone(), entry_point: function_entry_point, @@ -301,7 +322,7 @@ pub async fn decompile( } else { debug_max!("analyzing symbolic execution trace '0x{}' with sol analyzer", selector); analyzed_function = analyze_sol( - map, + &map, Function { selector: selector.clone(), entry_point: function_entry_point, @@ -326,6 +347,14 @@ pub async fn decompile( ); } + // add notice to analyzed_function if jumpdest_count == 0, indicating that + // symbolic execution timed out + if jumpdest_count == 0 { + analyzed_function + .notices + .push("symbolic execution timed out. please report this!".to_string()); + } + let argument_count = analyzed_function.arguments.len(); if argument_count != 0 { diff --git a/core/tests/test_decompile.rs b/core/tests/test_decompile.rs index cef193e8..93f9f2ac 100644 --- a/core/tests/test_decompile.rs +++ b/core/tests/test_decompile.rs @@ -18,6 +18,7 @@ mod benchmark { include_yul: false, output: String::from(""), name: String::from(""), + timeout: 10000, }; let _ = heimdall_core::decompile::decompile(args).await; } @@ -38,6 +39,7 @@ mod benchmark { include_yul: false, output: String::from(""), name: String::from(""), + timeout: 10000, }; let _ = heimdall_core::decompile::decompile(args).await; } @@ -58,6 +60,7 @@ mod benchmark { include_yul: true, output: String::from(""), name: String::from(""), + timeout: 10000, }; let _ = heimdall_core::decompile::decompile(args).await; } @@ -78,6 +81,7 @@ mod benchmark { include_yul: true, output: String::from(""), name: String::from(""), + timeout: 10000, }; let _ = heimdall_core::decompile::decompile(args).await; } @@ -98,6 +102,7 @@ mod benchmark { include_yul: false, output: String::from(""), name: String::from(""), + timeout: 10000, }; let _ = heimdall_core::decompile::decompile(args).await; } @@ -118,6 +123,7 @@ mod benchmark { include_yul: false, output: String::from(""), name: String::from(""), + timeout: 10000, }; let _ = heimdall_core::decompile::decompile(args).await; } @@ -144,6 +150,7 @@ mod integration_tests { include_yul: false, output: String::from(""), name: String::from(""), + timeout: 10000, }) .await .unwrap(); @@ -173,6 +180,7 @@ mod integration_tests { include_yul: false, output: String::from(""), name: String::from(""), + timeout: 10000, }) .await .unwrap(); @@ -209,6 +217,7 @@ mod integration_tests { include_yul: false, output: String::from(""), name: String::from(""), + timeout: 10000, }) .await .unwrap(); @@ -322,6 +331,7 @@ mod integration_tests { include_yul: false, output: String::from(""), name: String::from(""), + timeout: 10000, }) .await .unwrap();