Skip to content

Commit 55ee688

Browse files
chen4903Jon-Becker
andauthored
fix(cfg): graph starts from offset 0x01 instead of 0x00, prune redundant nodes (#559)
* simple fix * fix: when a node has already appeared, we will not add it to the graph * move tokio to dev-dependencies * chore: fix nightly fmt issues, make tests pass --------- Co-authored-by: Jon-Becker <[email protected]>
1 parent 68736c8 commit 55ee688

File tree

8 files changed

+49
-21
lines changed

8 files changed

+49
-21
lines changed

Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/cfg/Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,6 @@ alloy = { version = "0.3.3", features = ["full", "rpc-types-debug", "rpc-types-t
2929

3030
heimdall-disassembler.workspace = true
3131
heimdall-vm.workspace = true
32+
33+
[dev-dependencies]
34+
tokio = { version = "1", features = ["full"] }

crates/cfg/src/core/graph.rs

+32-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use heimdall_vm::{
66
ext::exec::VMTrace,
77
};
88
use petgraph::{matrix_graph::NodeIndex, Graph};
9+
use std::collections::HashSet;
910

1011
/// convert a symbolic execution [`VMTrace`] into a [`Graph`] of blocks, illustrating the
1112
/// control-flow graph found by the symbolic execution engine.
@@ -15,6 +16,7 @@ pub fn build_cfg(
1516
contract_cfg: &mut Graph<String, String>,
1617
parent_node: Option<NodeIndex<u32>>,
1718
jump_taken: bool,
19+
seen_nodes: &mut HashSet<String>,
1820
) -> Result<()> {
1921
let mut cfg_node: String = String::new();
2022
let mut parent_node = parent_node;
@@ -23,9 +25,11 @@ pub fn build_cfg(
2325
for operation in &vm_trace.operations {
2426
let opcode_name = opcode_name(operation.last_instruction.opcode);
2527

28+
let opcode_offset = operation.last_instruction.instruction - 1; // start from 0x00
29+
2630
let assembly = format!(
2731
"{} {} {}",
28-
encode_hex_reduced(U256::from(operation.last_instruction.instruction)),
32+
encode_hex_reduced(U256::from(opcode_offset)),
2933
opcode_name,
3034
if opcode_name.contains("PUSH") {
3135
encode_hex_reduced(
@@ -43,6 +47,12 @@ pub fn build_cfg(
4347
cfg_node.push_str(&format!("{}\n", &assembly));
4448
}
4549

50+
// check if this node has been seen before
51+
if seen_nodes.contains(&cfg_node) {
52+
return Ok(());
53+
}
54+
seen_nodes.insert(cfg_node.clone());
55+
4656
// add the node to the graph
4757
let node_index = contract_cfg.add_node(cfg_node);
4858
if let Some(parent_node) = parent_node {
@@ -63,8 +73,29 @@ pub fn build_cfg(
6373
.last_instruction
6474
.opcode ==
6575
JUMPDEST,
76+
seen_nodes,
6677
)?;
6778
}
6879

6980
Ok(())
7081
}
82+
83+
#[cfg(test)]
84+
mod tests {
85+
use super::*;
86+
use crate::{cfg, CfgArgsBuilder};
87+
use tokio::test;
88+
89+
#[test]
90+
async fn test_build_cfg() -> Result<(), Box<dyn std::error::Error>> {
91+
let args = CfgArgsBuilder::new()
92+
.target("0x6080604052348015600e575f80fd5b50600436106030575f3560e01c80632125b65b146034578063b69ef8a8146044575b5f80fd5b6044603f3660046046565b505050565b005b5f805f606084860312156057575f80fd5b833563ffffffff811681146069575f80fd5b925060208401356001600160a01b03811681146083575f80fd5b915060408401356001600160e01b0381168114609d575f80fd5b80915050925092509256".to_string())
93+
.build()?;
94+
95+
let result = cfg(args).await?;
96+
97+
println!("Contract Cfg: {:#?}", result);
98+
99+
Ok(())
100+
}
101+
}

crates/cfg/src/core/mod.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use alloy::primitives::Address;
44
use eyre::eyre;
55
use heimdall_common::{ether::compiler::detect_compiler, utils::strings::StringExt};
66
use heimdall_vm::core::vm::VM;
7+
use std::collections::HashSet;
78

89
use petgraph::{dot::Dot, Graph};
910
use std::time::{Duration, Instant};
@@ -93,7 +94,8 @@ pub async fn cfg(args: CfgArgs) -> Result<CfgResult, Error> {
9394
let start_cfg_time = Instant::now();
9495
info!("building cfg for '{}' from symbolic execution trace", args.target.truncate(64));
9596
let mut contract_cfg = Graph::new();
96-
build_cfg(&map, &mut contract_cfg, None, false)?;
97+
let mut seen_nodes: HashSet<String> = HashSet::new();
98+
build_cfg(&map, &mut contract_cfg, None, false, &mut seen_nodes)?;
9799
debug!("building cfg took {:?}", start_cfg_time.elapsed());
98100

99101
debug!("cfg generated in {:?}", start_time.elapsed());

crates/common/src/ether/types.rs

+2-8
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,7 @@ fn extract_types_from_string(string: &str) -> Result<Vec<DynSolType>> {
6969
let array_range = find_balanced_encapsulator(split, ('[', ']'))?;
7070

7171
let size = split[array_range].to_string();
72-
array_size = match size.parse::<usize>() {
73-
Ok(size) => Some(size),
74-
Err(_) => None,
75-
};
72+
array_size = size.parse::<usize>().ok();
7673
}
7774
}
7875

@@ -174,10 +171,7 @@ pub fn to_type(string: &str) -> DynSolType {
174171

175172
let size = string[array_range].to_string();
176173

177-
array_size.push_back(match size.parse::<usize>() {
178-
Ok(size) => Some(size),
179-
Err(_) => None,
180-
});
174+
array_size.push_back(size.parse::<usize>().ok());
181175

182176
string = string.replacen(&format!("[{}]", &size), "", 1);
183177
}

crates/core/tests/test_cfg.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ mod integration_tests {
5757

5858
let output = format!("{}", Dot::with_config(&result.graph, &[]));
5959

60-
for line in &[String::from("\"0x03a0 JUMPDEST \\l0x03a1 STOP \\l\"")] {
60+
for line in &[String::from("\"0x039f JUMPDEST \\l0x03a0 STOP \\l\"")] {
6161
assert!(output.contains(line))
6262
}
6363
}

crates/vm/src/core/types.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,7 @@ pub fn to_type(string: &str) -> DynSolType {
3535

3636
let size = string[array_range].to_string();
3737

38-
array_size.push_back(match size.parse::<usize>() {
39-
Ok(size) => Some(size),
40-
Err(_) => None,
41-
});
38+
array_size.push_back(size.parse::<usize>().ok());
4239

4340
string = string.replacen(&format!("[{}]", &size), "", 1);
4441
}

crates/vm/src/core/vm.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -744,7 +744,7 @@ impl VM {
744744
let result = keccak256(data);
745745

746746
// consume dynamic gas
747-
let minimum_word_size = ((size + 31) / 32) as u128;
747+
let minimum_word_size = size.div_ceil(32) as u128;
748748
let gas_cost = 6 * minimum_word_size + self.memory.expansion_cost(offset, size);
749749
self.consume_gas(gas_cost);
750750

@@ -850,7 +850,7 @@ impl VM {
850850
}
851851

852852
// consume dynamic gas
853-
let minimum_word_size = ((size + 31) / 32) as u128;
853+
let minimum_word_size = size.div_ceil(32) as u128;
854854
let gas_cost = 3 * minimum_word_size + self.memory.expansion_cost(offset, size);
855855
self.consume_gas(gas_cost);
856856

@@ -892,7 +892,7 @@ impl VM {
892892
}
893893

894894
// consume dynamic gas
895-
let minimum_word_size = ((size + 31) / 32) as u128;
895+
let minimum_word_size = size.div_ceil(32) as u128;
896896
let gas_cost = 3 * minimum_word_size + self.memory.expansion_cost(offset, size);
897897
self.consume_gas(gas_cost);
898898

@@ -940,7 +940,7 @@ impl VM {
940940
value.fill(0xff);
941941

942942
// consume dynamic gas
943-
let minimum_word_size = ((size + 31) / 32) as u128;
943+
let minimum_word_size = size.div_ceil(32) as u128;
944944
let gas_cost =
945945
3 * minimum_word_size + self.memory.expansion_cost(dest_offset, size);
946946
self.consume_gas(gas_cost);
@@ -979,7 +979,7 @@ impl VM {
979979
value.fill(0xff);
980980

981981
// consume dynamic gas
982-
let minimum_word_size = ((size + 31) / 32) as u128;
982+
let minimum_word_size = size.div_ceil(32) as u128;
983983
let gas_cost =
984984
3 * minimum_word_size + self.memory.expansion_cost(dest_offset, size);
985985
self.consume_gas(gas_cost);
@@ -1211,7 +1211,7 @@ impl VM {
12111211
}
12121212

12131213
// consume dynamic gas
1214-
let minimum_word_size = ((size + 31) / 32) as u128;
1214+
let minimum_word_size = size.div_ceil(32) as u128;
12151215
let gas_cost = 3 * minimum_word_size + self.memory.expansion_cost(offset, size);
12161216
self.consume_gas(gas_cost);
12171217

0 commit comments

Comments
 (0)