Skip to main content

foundry_evm_symbolic/executor/
create.rs

1use 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        // Isolation runs a CREATE, but not a CREATE2, as its own transaction.
108        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                    // Under an unmet `vm.expectRevert`, the caller sees a failed CREATE, but the
169                    // code stays deployed and `expectCreate` does not count it.
170                    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}