foundry_evm_symbolic/
runtime.rs1use super::{abi::*, *};
2
3mod address;
4mod bytes;
5mod calldata;
6mod cheatcodes;
7mod control;
8mod evm;
9mod expr;
10mod memory;
11mod precompiles;
12mod solver;
13mod state;
14mod symbols;
15
16pub(crate) use address::*;
17pub(crate) use bytes::*;
18pub(crate) use calldata::*;
19pub(crate) use cheatcodes::*;
20pub(crate) use control::*;
21pub(crate) use evm::*;
22pub(crate) use expr::*;
23pub(crate) use memory::*;
24pub(crate) use precompiles::*;
25pub use solver::PortfolioDiagnostics;
26pub(crate) use solver::{
27 BranchFeasibility, SmtLibSubprocessSolver, solver_portfolio_availability_warning,
28};
29pub(crate) use state::*;
30pub(crate) use symbols::*;
31
32#[cfg(test)]
33pub(crate) use solver::{
34 SolverCommand, SolverConfigError, SolverOutcome, SolverRunSummary, fallback_single_var_model,
35 hard_arith_fallback_model, named_solver_command, normalize_bool_for_solver,
36 normalize_constraints_for_solver, normalize_expr_for_solver, parse_model,
37 product_monotonic_unsat, solver_commands_for_config, split_solver_command,
38 validate_solver_model_output,
39};
40
41#[derive(Clone, Copy, Debug, PartialEq, Eq)]
43pub struct SymbolicBranchTarget {
44 address: Address,
46 pc: usize,
48 opcode: u8,
50 result: bool,
52}
53
54impl SymbolicBranchTarget {
55 pub const fn new(address: Address, pc: usize, opcode: u8, result: bool) -> Self {
56 Self { address, pc, opcode, result }
57 }
58
59 pub(crate) const fn result(self) -> bool {
60 self.result
61 }
62
63 pub(crate) fn matches(self, address: Address, pc: usize, opcode: u8) -> bool {
64 self.address == address && self.pc == pc && self.opcode == opcode
65 }
66}
67
68pub struct SymbolicRunInput<'a, FEN: FoundryEvmNetwork> {
69 pub executor: &'a Executor<FEN>,
71 pub target: Address,
73 pub sender: Address,
75 pub function: &'a Function,
77 pub value: U256,
79 pub ffi_enabled: bool,
81 pub collect_success_input: bool,
83 pub corpus_seeds: Vec<SymbolicConcreteInput>,
85 pub branch_target: Option<SymbolicBranchTarget>,
87}
88
89#[derive(Debug, Error)]
95pub enum SymbolicError {
96 #[error("missing account {0}")]
98 MissingAccount(Address),
99 #[error("missing code for account {0}")]
101 MissingCode(Address),
102 #[error("backend error: {0}")]
104 Backend(String),
105 #[error("unsupported ABI type for symbolic execution: {0}")]
107 UnsupportedAbi(String),
108 #[error("symbolic calldata variant limit exceeded ({0})")]
110 CalldataVariantLimit(usize),
111 #[error("unsupported symbolic execution feature: {0}")]
113 Unsupported(&'static str),
114 #[error("unsupported opcode 0x{0:02x}")]
116 UnsupportedOpcode(u8),
117 #[error("invalid bytecode: {0}")]
119 InvalidBytecode(&'static str),
120 #[error("invalid jump destination {0}")]
122 InvalidJump(usize),
123 #[error("stack underflow")]
125 StackUnderflow,
126 #[error("stack overflow")]
128 StackOverflow,
129 #[error("solver error: {0}")]
131 Solver(String),
132 #[error("solver returned unknown")]
134 SolverUnknown,
135 #[error("symbolic execution timeout exceeded ({0}s)")]
137 Timeout(u32),
138 #[error("symbolic solver query limit exceeded ({0})")]
140 SolverQueryLimit(usize),
141 #[error(transparent)]
143 Abi(#[from] alloy_dyn_abi::Error),
144}
145
146impl SymbolicError {
147 pub(super) const fn stop_reason(&self) -> SymbolicStopReason {
148 match self {
149 Self::Unsupported(_)
150 | Self::CalldataVariantLimit(_)
151 | Self::UnsupportedOpcode(_)
152 | Self::SolverQueryLimit(_) => SymbolicStopReason::Stuck,
153 Self::SolverUnknown | Self::Timeout(_) => SymbolicStopReason::Timeout,
154 Self::Solver(_)
155 | Self::MissingAccount(_)
156 | Self::MissingCode(_)
157 | Self::Backend(_)
158 | Self::UnsupportedAbi(_)
159 | Self::InvalidBytecode(_)
160 | Self::InvalidJump(_)
161 | Self::StackUnderflow
162 | Self::StackOverflow
163 | Self::Abi(_) => SymbolicStopReason::Error,
164 }
165 }
166}
167
168impl fmt::Display for SymbolicRunResult {
169 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
170 match self {
171 Self::Safe { stats, .. } => write!(f, "safe after {} paths", stats.paths),
172 Self::Counterexample { stats, .. } => {
173 write!(f, "counterexample after {} paths", stats.paths)
174 }
175 Self::Incomplete { kind, reason, .. } => {
176 write!(f, "incomplete symbolic execution ({kind:?}): {reason}")
177 }
178 }
179 }
180}