foundry_evm_symbolic/executor/
create.rs1use super::*;
2
3impl SymbolicExecutor {
4 pub(super) fn create(
5 &mut self,
6 executor: &ExecutorView<'_>,
7 state: &mut PathState,
8 worklist: &mut VecDeque<PathState>,
9 completed_paths: &mut usize,
10 kind: CreateKind,
11 ) -> Result<StepOutcome, SymbolicError> {
12 if state.is_static {
13 state.return_data = SymReturnData::empty(&mut self.cx);
14 return Ok(StepOutcome::ExceptionalHalt);
15 }
16
17 let offset = state.stack.peek(1)?.clone();
18 let size = state.stack.peek(2)?.clone();
19 if let Some(outcome) = self.guard_memory_range(executor, state, worklist, &offset, &size)? {
20 return Ok(outcome);
21 }
22
23 let value = state.stack.pop()?;
24 let offset = state.stack.pop()?;
25 let size = state.stack.pop()?;
26 let size = match state.constrained_usize_checked(&mut self.cx, &size) {
27 Some(Ok(size)) => BoundedCopySize::Concrete(size),
28 Some(Err(_)) => {
29 state.return_data = SymReturnData::empty(&mut self.cx);
30 return Ok(StepOutcome::Revert);
31 }
32 None => {
33 let max_limit = self.config.max_calldata_bytes as usize;
34 let max_size = self.solver_upper_bound_usize(
35 state,
36 &size,
37 max_limit,
38 "symbolic CREATE initcode size",
39 )?;
40 BoundedCopySize::Symbolic { size, max_size }
41 }
42 };
43 let salt =
44 if matches!(kind, CreateKind::Create2) { Some(state.stack.pop()?) } else { None };
45
46 size.expand_memory(&mut self.cx, &mut state.memory, offset.clone());
47
48 let initcode = match &size {
49 BoundedCopySize::Concrete(size) => {
50 if let Some(offset) = state.constrained_usize(&mut self.cx, &offset) {
51 let bytes = state.memory.read_bytes(&mut self.cx, offset, *size);
52 SymCode::from_bytes(&mut self.cx, bytes)
53 } else {
54 SymCode::from_memory_offset(&mut self.cx, &state.memory, offset, *size)
55 }
56 }
57 BoundedCopySize::Symbolic { size, max_size } => SymCode::from_memory_symbolic_size(
58 &mut self.cx,
59 &state.memory,
60 offset,
61 size.clone(),
62 *max_size,
63 ),
64 };
65 let (created_word, created) = match kind {
66 CreateKind::Create => {
67 let nonce = state.world.nonce(executor, state.address)?;
68 let address = state.address.create(nonce);
69 (SymExpr::constant(&mut self.cx, address_word(address)), address)
70 }
71 CreateKind::Create2 => create2_address_word(
72 &mut self.cx,
73 state,
74 state.address,
75 salt.expect("CREATE2 salt exists"),
76 &initcode,
77 )?,
78 };
79
80 if !self.prepare_create_value_transfer(executor, state, worklist, value.clone())? {
81 return Ok(StepOutcome::Continue);
82 }
83
84 let mut failure_world = state.world.clone();
85 failure_world.increment_nonce(executor, state.address)?;
86
87 if failure_world.has_code_or_nonce(&mut self.cx, executor, created)? {
88 state.world = failure_world;
89 state.return_data = SymReturnData::empty(&mut self.cx);
90 state.stack.push(SymExpr::zero(&mut self.cx))?;
91 return Ok(StepOutcome::Continue);
92 }
93
94 let calldata = SymBytes::empty(&mut self.cx);
95 let calldata = SymCalldata::from_bytes(&mut self.cx, calldata);
96 let mut frame = CallFrame::new(
97 &mut self.cx,
98 created,
99 created,
100 state.address,
101 value.clone(),
102 false,
103 calldata,
104 );
105 frame.address_word = created_word.clone();
106 frame.caller_word = state.address_word.clone();
107 let isolated =
109 matches!(kind, CreateKind::Create) && state.starts_isolated_transaction(executor);
110 let mut child = state.child(frame);
111 let pending_expected_creates = std::mem::take(&mut child.expected_creates);
112 child.world = failure_world.clone();
113 if isolated {
114 child.world.clear_transient_storage();
115 }
116 child.world.mark_current_transaction_created(created);
117 child.world.set_nonce(created, 1);
118 child.world.transfer(&mut self.cx, executor, state.address, created, value);
119
120 let outcomes = self.execute_external_call(executor, child, &initcode, completed_paths)?;
121 if outcomes.is_empty() {
122 return Ok(StepOutcome::AssumeRejected);
123 }
124
125 let mut parents = VecDeque::with_capacity(outcomes.len());
126 for mut outcome in outcomes {
127 let runtime = &outcome.state.frame.return_data;
128 let spec_id = executor.spec_id;
129 let mut rejected_runtime = false;
130 if matches!(outcome.status, CallStatus::Success) {
131 if runtime_exceeds_code_size_limit(executor.max_code_size, spec_id, runtime) {
132 outcome.status = CallStatus::Revert;
133 outcome.state.frame.return_data = SymReturnData::empty(&mut self.cx);
134 } else if runtime_has_rejected_prefix(&mut self.cx, spec_id, runtime)? {
135 outcome.status = CallStatus::Revert;
136 rejected_runtime = true;
137 }
138 }
139 match self.join_call_outcome(executor, state, outcome, created)? {
140 JoinedCallOutcome::Rejected => {}
141 JoinedCallOutcome::Failure(mut parent) => {
142 parent.return_data = SymReturnData::empty(&mut self.cx);
143 *state = parent;
144 return Ok(StepOutcome::Failure);
145 }
146 JoinedCallOutcome::ExceptionalHalt(mut parent) => {
147 parent.return_data = SymReturnData::empty(&mut self.cx);
148 parent.world = failure_world.clone();
149 parent.stack.push(SymExpr::zero(&mut self.cx))?;
150 parents.push_back(parent);
151 }
152 JoinedCallOutcome::ExpectedRevert { mut parent, .. } => {
153 parent.return_data = SymReturnData::empty(&mut self.cx);
154 parent.expected_creates = pending_expected_creates.clone();
155 parent.world = failure_world.clone();
156 parent.stack.push(created_word.clone())?;
157 parents.push_back(parent);
158 }
159 JoinedCallOutcome::Success { mut parent, child, unmet_expected_revert } => {
160 parent.return_data = SymReturnData::empty(&mut self.cx);
161 let runtime = &child.frame.return_data;
162 parent.world = child.world;
163 if isolated {
164 parent.world.restore_transient_storage(&failure_world);
165 }
166 parent.expected_emit = child.expected_emit;
167 parent.expected_creates = pending_expected_creates.clone();
168 if !unmet_expected_revert {
171 self.observe_expected_create(&mut parent, state.address, kind, runtime)?;
172 }
173 if !parent.world.is_destroyed(created) {
174 parent.world.install_code(created, runtime.to_code(&mut self.cx)?);
175 parent.world.set_nonce(created, 1);
176 }
177 if unmet_expected_revert {
178 parent.return_data = error_string_return_data(
179 &mut self.cx,
180 "next call did not revert as expected",
181 );
182 parent.stack.push(SymExpr::zero(&mut self.cx))?;
183 } else {
184 parent.stack.push(created_word.clone())?;
185 }
186 parents.push_back(parent);
187 }
188 JoinedCallOutcome::Revert { mut parent, child } => {
189 parent.return_data = SymReturnData::empty(&mut self.cx);
190 parent.world = failure_world.clone();
191 if !rejected_runtime {
192 parent.return_data = child.frame.return_data;
193 }
194 parent.stack.push(SymExpr::zero(&mut self.cx))?;
195 parents.push_back(parent);
196 }
197 }
198 }
199
200 Ok(self.resume_parent_paths(state, worklist, parents))
201 }
202
203 pub(super) fn execute_external_call(
204 &mut self,
205 executor: &ExecutorView<'_>,
206 initial: PathState,
207 code: &SymCode,
208 completed_paths: &mut usize,
209 ) -> Result<Vec<CallOutcome>, SymbolicError> {
210 let mut worklist = VecDeque::from([initial]);
211 let mut deferred_worklist = VecDeque::new();
212 self.execute_call_path_batch(
213 executor,
214 code,
215 &mut worklist,
216 &mut deferred_worklist,
217 completed_paths,
218 CallPathKind::External,
219 )
220 }
221}
222
223fn runtime_exceeds_code_size_limit(
224 max_code_size: usize,
225 spec_id: SpecId,
226 runtime: &SymReturnData,
227) -> bool {
228 spec_id >= SpecId::SPURIOUS_DRAGON
229 && runtime.len_word.as_const().is_some()
230 && runtime.len() > max_code_size
231}
232
233fn runtime_has_rejected_prefix(
234 cx: &mut SymCx,
235 spec_id: SpecId,
236 runtime: &SymReturnData,
237) -> Result<bool, SymbolicError> {
238 if spec_id < SpecId::LONDON || runtime.len() == 0 {
239 return Ok(false);
240 }
241 let first_byte = runtime.byte(cx, 0);
242 let Some(first_byte) = first_byte.as_const() else {
243 return Err(SymbolicError::Unsupported("CREATE with symbolic runtime prefix not modeled"));
244 };
245 Ok(first_byte == U256::from(0xef))
246}