Skip to content

Commit 718e6f7

Browse files
committed
Display reachable reverts.
1 parent 03946ad commit 718e6f7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+3803
-22
lines changed

Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,5 @@ num-bigint = "^0.4.3"
1111
tempfile = "3"
1212
clap = "^3.2"
1313
num-traits = "^0.2.15"
14+
colored = "^2"
15+
codespan-reporting = "^0.11.1"

src/encoder.rs

+51-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::evm_context;
2+
use crate::execution_position::ExecutionPositionManager;
23
use crate::smt::{self, SMTExpr, SMTFormat, SMTSort, SMTStatement, SMTVariable};
34
use crate::ssa_tracker::SSATracker;
45

@@ -17,6 +18,7 @@ pub trait Instructions: Default + Dialect {
1718
return_vars: &[SMTVariable],
1819
ssa: &mut SSATracker,
1920
path_conditions: &[SMTExpr],
21+
location: &Option<SourceLocation>,
2022
) -> Vec<SMTStatement>;
2123
}
2224

@@ -29,6 +31,7 @@ pub struct Encoder<InstructionsType> {
2931
interpreter: InstructionsType,
3032
loop_unroll: u64,
3133
path_conditions: Vec<SMTExpr>,
34+
execution_position: ExecutionPositionManager,
3235
}
3336

3437
pub fn encode<T: Instructions>(ast: &Block, loop_unroll: u64) -> String {
@@ -51,23 +54,43 @@ pub fn encode_revert_reachable<T: Instructions>(
5154
encoder.encode(ast, loop_unroll);
5255
encoder.encode_revert_reachable();
5356

54-
encode_with_counterexamples(encoder, counterexamples)
57+
encode_with_counterexamples(&mut encoder, counterexamples)
58+
}
59+
60+
pub struct PanicInfo {
61+
pub revert_start: String,
62+
pub revert_end: String,
63+
pub revert_position: String,
5564
}
5665

5766
pub fn encode_solc_panic_reachable<T: Instructions>(
5867
ast: &Block,
5968
loop_unroll: u64,
6069
counterexamples: &[Expression],
61-
) -> (String, Vec<String>) {
70+
) -> (String, Vec<String>, PanicInfo, ExecutionPositionManager) {
6271
let mut encoder = Encoder::<T>::default();
6372
encoder.encode(ast, loop_unroll);
6473
encoder.encode_solc_panic_reachable();
74+
let (revert_start, revert_end) = evm_context::revert_location(&mut encoder.ssa_tracker);
75+
let revert_position = ExecutionPositionManager::smt_variable()
76+
.smt_var(&mut encoder.ssa_tracker)
77+
.as_smt();
6578

66-
encode_with_counterexamples(encoder, counterexamples)
79+
let (enc, cex) = encode_with_counterexamples(&mut encoder, counterexamples);
80+
(
81+
enc,
82+
cex,
83+
PanicInfo {
84+
revert_start: revert_start.as_smt(),
85+
revert_end: revert_end.as_smt(),
86+
revert_position,
87+
},
88+
encoder.execution_position,
89+
)
6790
}
6891

6992
fn encode_with_counterexamples<T: Instructions>(
70-
mut encoder: Encoder<T>,
93+
encoder: &mut Encoder<T>,
7194
counterexamples: &[Expression],
7295
) -> (String, Vec<String>) {
7396
let encoded_counterexamples = counterexamples
@@ -110,7 +133,8 @@ impl<InstructionsType: Instructions> Encoder<InstructionsType> {
110133
}
111134

112135
fn encode_context_init(&mut self) {
113-
let output = evm_context::init(&mut self.ssa_tracker);
136+
let mut output = evm_context::init(&mut self.ssa_tracker);
137+
output.push(ExecutionPositionManager::init(&mut self.ssa_tracker));
114138
self.out_vec(output);
115139
}
116140

@@ -138,6 +162,7 @@ impl<InstructionsType: Instructions> Encoder<InstructionsType> {
138162
value: None,
139163
location: None,
140164
});
165+
141166
self.encode_block(&function.body);
142167
self.ssa_tracker.to_smt_variables(&function.returns)
143168
}
@@ -281,7 +306,26 @@ impl<InstructionsType: Instructions> Encoder<InstructionsType> {
281306
match expr {
282307
Expression::Literal(literal) => vec![self.encode_literal(literal)],
283308
Expression::Identifier(identifier) => vec![self.encode_identifier(identifier)],
284-
Expression::FunctionCall(function) => self.encode_function_call(function),
309+
Expression::FunctionCall(function) => {
310+
self.execution_position.function_called(function);
311+
let pos_pre = evm_context::assign_variable_if_executing_regularly(
312+
&mut self.ssa_tracker,
313+
&ExecutionPositionManager::smt_variable(),
314+
self.execution_position.position_id().into(),
315+
);
316+
self.out(pos_pre);
317+
318+
let fun_call = self.encode_function_call(function);
319+
320+
self.execution_position.function_returned();
321+
let pos_post = evm_context::assign_variable_if_executing_regularly(
322+
&mut self.ssa_tracker,
323+
&ExecutionPositionManager::smt_variable(),
324+
self.execution_position.position_id().into(),
325+
);
326+
self.out(pos_post);
327+
fun_call
328+
}
285329
}
286330
}
287331

@@ -325,6 +369,7 @@ impl<InstructionsType: Instructions> Encoder<InstructionsType> {
325369
&return_vars,
326370
&mut self.ssa_tracker,
327371
&self.path_conditions,
372+
&call.location,
328373
);
329374
self.out_vec(result);
330375
return_vars

src/evm_builtins.rs

+5
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::evm_context::MemoryRange;
44
use crate::smt::{self, SMTExpr, SMTOp, SMTStatement, SMTVariable};
55
use crate::ssa_tracker::SSATracker;
66
use yultsur::dialect::{Builtin, Dialect, EVMDialect};
7+
use yultsur::yul::SourceLocation;
78

89
#[derive(Default)]
910
pub struct EVMInstructions;
@@ -22,6 +23,7 @@ impl Instructions for EVMInstructions {
2223
return_vars: &[SMTVariable],
2324
ssa: &mut SSATracker,
2425
_path_conditions: &[SMTExpr],
26+
location: &Option<SourceLocation>,
2527
) -> Vec<SMTStatement> {
2628
let single_return = |value: SMTExpr| {
2729
assert_eq!(return_vars.len(), 1);
@@ -192,6 +194,9 @@ impl Instructions for EVMInstructions {
192194
length: arg_1.unwrap().into(),
193195
},
194196
ssa,
197+
location
198+
.to_owned()
199+
.unwrap_or(SourceLocation { start: 0, end: 0 }),
195200
),
196201
"invalid" => panic!("Builtin {} not implemented", builtin.name), // TODO
197202
"selfdestruct" => panic!("Builtin {} not implemented", builtin.name), // TODO

src/evm_context.rs

+26-3
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,9 @@ context_variables! {
9090
stop_flag: Type::Default; Some(0.into()),
9191
revert_flag: Type::Default; Some(0.into()),
9292
revert_sig_4: Type::Constant(smt::bv(32)); None,
93-
revert_data_32: Type::Constant(smt::bv(256)); None
93+
revert_data_32: Type::Default; None,
94+
revert_source_location_start: Type::Default; None,
95+
revert_source_location_end: Type::Default; None
9496
}
9597

9698
// TODO can we make this a global variable?
@@ -112,7 +114,11 @@ impl MemoryRange {
112114
}
113115
}
114116

115-
pub fn revert(input: MemoryRange, ssa: &mut SSATracker) -> Vec<SMTStatement> {
117+
pub fn revert(
118+
input: MemoryRange,
119+
ssa: &mut SSATracker,
120+
location: SourceLocation,
121+
) -> Vec<SMTStatement> {
116122
let sig = smt::ite(
117123
smt::bvuge(input.length.clone(), 4),
118124
mload4(input.offset.clone(), ssa),
@@ -126,12 +132,29 @@ pub fn revert(input: MemoryRange, ssa: &mut SSATracker) -> Vec<SMTStatement> {
126132
vec![
127133
assign_variable_if_executing_regularly(ssa, &context().revert_sig_4, sig),
128134
assign_variable_if_executing_regularly(ssa, &context().revert_data_32, data),
135+
assign_variable_if_executing_regularly(
136+
ssa,
137+
&context().revert_source_location_start,
138+
(location.start as u64).into(),
139+
),
140+
assign_variable_if_executing_regularly(
141+
ssa,
142+
&context().revert_source_location_end,
143+
(location.end as u64).into(),
144+
),
129145
// The order here is important: revert_flag is used to build the two expressions above,
130146
// so we can only change it after.
131147
assign_variable_if_executing_regularly(ssa, &context().revert_flag, 1.into()),
132148
]
133149
}
134150

151+
pub fn revert_location(ssa: &mut SSATracker) -> (SMTVariable, SMTVariable) {
152+
(
153+
context().revert_source_location_start.smt_var(ssa),
154+
context().revert_source_location_end.smt_var(ssa),
155+
)
156+
}
157+
135158
pub fn set_stopped(ssa: &mut SSATracker) -> SMTStatement {
136159
assign_variable_if_executing_regularly(ssa, &context().stop_flag, 1.into())
137160
}
@@ -422,7 +445,7 @@ pub fn encode_solc_panic_reachable(ssa: &mut SSATracker) -> SMTStatement {
422445

423446
/// Assigns to the variable if we neither stopped nor reverted. Otherwise, the variable keeps
424447
/// its value. Increases the SSA index in any case.
425-
fn assign_variable_if_executing_regularly(
448+
pub fn assign_variable_if_executing_regularly(
426449
ssa: &mut SSATracker,
427450
variable: &SystemVariable,
428451
new_value: SMTExpr,

src/execution_position.rs

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
use yultsur::yul::{FunctionCall, Identifier, IdentifierID, SourceLocation};
2+
3+
use crate::smt::{self, SMTExpr};
4+
use crate::ssa_tracker::SSATracker;
5+
use crate::variables::SystemVariable;
6+
7+
/**
8+
* Stores the current call stack encoded in a single number that can be used
9+
* during the SMT encoding.
10+
*
11+
* Can be extended in the future with other information like loop iterations.
12+
*/
13+
#[derive(Default)]
14+
pub struct ExecutionPositionManager {
15+
positions: Vec<ExecutionPosition>,
16+
}
17+
18+
pub struct PositionID(pub usize);
19+
20+
impl From<PositionID> for SMTExpr {
21+
fn from(pos: PositionID) -> Self {
22+
(pos.0 as u64).into()
23+
}
24+
}
25+
26+
struct ExecutionPosition {
27+
call_stack: Vec<Option<SourceLocation>>,
28+
}
29+
30+
impl ExecutionPositionManager {
31+
pub fn init(ssa_tracker: &mut SSATracker) -> smt::SMTStatement {
32+
Self::smt_variable().generate_definition(ssa_tracker)
33+
}
34+
35+
/// @returns the SystemVariable that encodes the position.
36+
pub fn smt_variable() -> SystemVariable {
37+
SystemVariable {
38+
identifier: Identifier {
39+
id: IdentifierID::Reference(8128),
40+
name: "_exe_pos".to_string(),
41+
location: None,
42+
},
43+
domain: vec![],
44+
codomain: smt::bv(256),
45+
default_value: Some(0.into()),
46+
}
47+
}
48+
49+
pub fn function_called(&mut self, fun: &FunctionCall) {
50+
self.append_position(ExecutionPosition {
51+
call_stack: [self.current_call_stack(), vec![fun.location.clone()]].concat(),
52+
})
53+
}
54+
pub fn function_returned(&mut self) {
55+
let mut call_stack = self.current_call_stack();
56+
call_stack.pop().unwrap();
57+
self.append_position(ExecutionPosition { call_stack })
58+
}
59+
60+
/// Returns the current position ID that can later
61+
/// be translated into e.g. a call stack.
62+
pub fn position_id(&self) -> PositionID {
63+
PositionID(self.positions.len())
64+
}
65+
66+
pub fn call_stack_at_position(&self, pos: PositionID) -> &Vec<Option<SourceLocation>> {
67+
&self.positions[pos.0].call_stack
68+
}
69+
70+
fn append_position(&mut self, pos: ExecutionPosition) {
71+
self.positions.push(pos)
72+
}
73+
fn current_call_stack(&self) -> Vec<Option<SourceLocation>> {
74+
match self.positions.iter().last() {
75+
Some(p) => p.call_stack.clone(),
76+
None => vec![],
77+
}
78+
}
79+
}

src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ pub mod cfg;
22
pub mod encoder;
33
pub mod evm_builtins;
44
pub mod evm_context;
5+
pub mod execution_position;
56
pub mod sexpr;
67
pub mod smt;
78
pub mod solver;

0 commit comments

Comments
 (0)