/src/solidity/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp
Line | Count | Source |
1 | | /* |
2 | | This file is part of solidity. |
3 | | |
4 | | solidity is free software: you can redistribute it and/or modify |
5 | | it under the terms of the GNU General Public License as published by |
6 | | the Free Software Foundation, either version 3 of the License, or |
7 | | (at your option) any later version. |
8 | | |
9 | | solidity is distributed in the hope that it will be useful, |
10 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | | GNU General Public License for more details. |
13 | | |
14 | | You should have received a copy of the GNU General Public License |
15 | | along with solidity. If not, see <http://www.gnu.org/licenses/>. |
16 | | */ |
17 | | // SPDX-License-Identifier: GPL-3.0 |
18 | | /** |
19 | | * Yul interpreter module that evaluates EVM instructions. |
20 | | */ |
21 | | |
22 | | #include <test/tools/yulInterpreter/EVMInstructionInterpreter.h> |
23 | | |
24 | | #include <test/tools/yulInterpreter/Interpreter.h> |
25 | | |
26 | | #include <libyul/backends/evm/EVMDialect.h> |
27 | | #include <libyul/AST.h> |
28 | | #include <libyul/Utilities.h> |
29 | | |
30 | | #include <libevmasm/Instruction.h> |
31 | | #include <libevmasm/SemanticInformation.h> |
32 | | |
33 | | #include <liblangutil/Exceptions.h> |
34 | | #include <libsolutil/Keccak256.h> |
35 | | #include <libsolutil/Numeric.h> |
36 | | #include <libsolutil/picosha2.h> |
37 | | |
38 | | #include <limits> |
39 | | |
40 | | using namespace solidity; |
41 | | using namespace solidity::evmasm; |
42 | | using namespace solidity::yul; |
43 | | using namespace solidity::yul::test; |
44 | | |
45 | | using solidity::util::h160; |
46 | | using solidity::util::h256; |
47 | | using solidity::util::keccak256; |
48 | | |
49 | | namespace |
50 | | { |
51 | | |
52 | | /// Reads 32 bytes from @a _data at position @a _offset bytes while |
53 | | /// interpreting @a _data to be padded with an infinite number of zero |
54 | | /// bytes beyond its end. |
55 | | u256 readZeroExtended(bytes const& _data, u256 const& _offset) |
56 | 102k | { |
57 | 102k | if (_offset >= _data.size()) |
58 | 36.3k | return 0; |
59 | 65.9k | else if (_offset + 32 <= _data.size()) |
60 | 64.9k | return *reinterpret_cast<h256 const*>(_data.data() + static_cast<size_t>(_offset)); |
61 | 961 | else |
62 | 961 | { |
63 | 961 | size_t off = static_cast<size_t>(_offset); |
64 | 961 | u256 val; |
65 | 31.7k | for (size_t i = 0; i < 32; ++i) |
66 | 30.7k | { |
67 | 30.7k | val <<= 8; |
68 | 30.7k | if (off + i < _data.size()) |
69 | 20.0k | val += _data[off + i]; |
70 | 30.7k | } |
71 | 961 | return val; |
72 | 961 | } |
73 | 102k | } |
74 | | |
75 | | } |
76 | | |
77 | | namespace solidity::yul::test |
78 | | { |
79 | | |
80 | | void copyZeroExtended( |
81 | | std::map<u256, uint8_t>& _target, |
82 | | bytes const& _source, |
83 | | size_t _targetOffset, |
84 | | size_t _sourceOffset, |
85 | | size_t _size |
86 | | ) |
87 | 39.5k | { |
88 | 389M | for (size_t i = 0; i < _size; ++i) |
89 | 389M | _target[_targetOffset + i] = (_sourceOffset + i < _source.size() ? _source[_sourceOffset + i] : 0); |
90 | 39.5k | } |
91 | | |
92 | | void copyZeroExtendedWithOverlap( |
93 | | std::map<u256, uint8_t>& _target, |
94 | | std::map<u256, uint8_t> const& _source, |
95 | | size_t _targetOffset, |
96 | | size_t _sourceOffset, |
97 | | size_t _size |
98 | | ) |
99 | 7.25k | { |
100 | 7.25k | if (_targetOffset >= _sourceOffset) |
101 | 40.7M | for (size_t i = _size; i > 0; --i) |
102 | 40.7M | _target[_targetOffset + i - 1] = (_source.count(_sourceOffset + i - 1) != 0 ? _source.at(_sourceOffset + i - 1) : 0); |
103 | 1.89k | else |
104 | 18.8M | for (size_t i = 0; i < _size; ++i) |
105 | 18.8M | _target[_targetOffset + i] = (_source.count(_sourceOffset + i) != 0 ? _source.at(_sourceOffset + i) : 0); |
106 | 7.25k | } |
107 | | |
108 | | } |
109 | | |
110 | | u256 EVMInstructionInterpreter::eval( |
111 | | evmasm::Instruction _instruction, |
112 | | std::vector<u256> const& _arguments |
113 | | ) |
114 | 3.64M | { |
115 | 3.64M | using namespace solidity::evmasm; |
116 | 3.64M | using evmasm::Instruction; |
117 | | |
118 | 3.64M | auto info = instructionInfo(_instruction, m_evmVersion); |
119 | 3.64M | yulAssert(static_cast<size_t>(info.args) == _arguments.size(), ""); |
120 | | |
121 | 3.64M | auto const& arg = _arguments; |
122 | 3.64M | switch (_instruction) |
123 | 3.64M | { |
124 | 769 | case Instruction::STOP: |
125 | 769 | logTrace(_instruction); |
126 | 769 | BOOST_THROW_EXCEPTION(ExplicitlyTerminated()); |
127 | | // --------------- arithmetic --------------- |
128 | 172k | case Instruction::ADD: |
129 | 172k | return arg[0] + arg[1]; |
130 | 92.4k | case Instruction::MUL: |
131 | 92.4k | return arg[0] * arg[1]; |
132 | 79.4k | case Instruction::SUB: |
133 | 79.4k | return arg[0] - arg[1]; |
134 | 124k | case Instruction::DIV: |
135 | 124k | return arg[1] == 0 ? 0 : arg[0] / arg[1]; |
136 | 103k | case Instruction::SDIV: |
137 | 103k | return arg[1] == 0 ? 0 : s2u(u2s(arg[0]) / u2s(arg[1])); |
138 | 554k | case Instruction::MOD: |
139 | 554k | return arg[1] == 0 ? 0 : arg[0] % arg[1]; |
140 | 34.3k | case Instruction::SMOD: |
141 | 34.3k | return arg[1] == 0 ? 0 : s2u(u2s(arg[0]) % u2s(arg[1])); |
142 | 49.5k | case Instruction::EXP: |
143 | 49.5k | return exp256(arg[0], arg[1]); |
144 | 204k | case Instruction::NOT: |
145 | 204k | return ~arg[0]; |
146 | 172k | case Instruction::LT: |
147 | 172k | return arg[0] < arg[1] ? 1 : 0; |
148 | 10.0k | case Instruction::GT: |
149 | 10.0k | return arg[0] > arg[1] ? 1 : 0; |
150 | 10.1k | case Instruction::SLT: |
151 | 10.1k | return u2s(arg[0]) < u2s(arg[1]) ? 1 : 0; |
152 | 9.87k | case Instruction::SGT: |
153 | 9.87k | return u2s(arg[0]) > u2s(arg[1]) ? 1 : 0; |
154 | 16.7k | case Instruction::EQ: |
155 | 16.7k | return arg[0] == arg[1] ? 1 : 0; |
156 | 15.2k | case Instruction::ISZERO: |
157 | 15.2k | return arg[0] == 0 ? 1 : 0; |
158 | 67.1k | case Instruction::AND: |
159 | 67.1k | return arg[0] & arg[1]; |
160 | 116k | case Instruction::OR: |
161 | 116k | return arg[0] | arg[1]; |
162 | 24.7k | case Instruction::XOR: |
163 | 24.7k | return arg[0] ^ arg[1]; |
164 | 8.49k | case Instruction::BYTE: |
165 | 8.49k | return arg[0] >= 32 ? 0 : (arg[1] >> unsigned(8 * (31 - arg[0]))) & 0xff; |
166 | 145k | case Instruction::SHL: |
167 | 145k | return arg[0] > 255 ? 0 : (arg[1] << unsigned(arg[0])); |
168 | 28.8k | case Instruction::SHR: |
169 | 28.8k | return arg[0] > 255 ? 0 : (arg[1] >> unsigned(arg[0])); |
170 | 32.4k | case Instruction::SAR: |
171 | 32.4k | { |
172 | 32.4k | static u256 const hibit = u256(1) << 255; |
173 | 32.4k | if (arg[0] >= 256) |
174 | 5.25k | return arg[1] & hibit ? u256(-1) : 0; |
175 | 27.2k | else |
176 | 27.2k | { |
177 | 27.2k | unsigned amount = unsigned(arg[0]); |
178 | 27.2k | u256 v = arg[1] >> amount; |
179 | 27.2k | if (arg[1] & hibit) |
180 | 3.05k | v |= u256(-1) << (256 - amount); |
181 | 27.2k | return v; |
182 | 27.2k | } |
183 | 32.4k | } |
184 | 26 | case Instruction::CLZ: |
185 | 26 | return arg[0] == 0 ? 256 : 255 - msb(arg[0]); |
186 | 80.3k | case Instruction::ADDMOD: |
187 | 80.3k | return arg[2] == 0 ? 0 : u256((u512(arg[0]) + u512(arg[1])) % arg[2]); |
188 | 72.1k | case Instruction::MULMOD: |
189 | 72.1k | return arg[2] == 0 ? 0 : u256((u512(arg[0]) * u512(arg[1])) % arg[2]); |
190 | 27.6k | case Instruction::SIGNEXTEND: |
191 | 27.6k | if (arg[0] >= 31) |
192 | 10.4k | return arg[1]; |
193 | 17.2k | else |
194 | 17.2k | { |
195 | 17.2k | unsigned testBit = unsigned(arg[0]) * 8 + 7; |
196 | 17.2k | u256 ret = arg[1]; |
197 | 17.2k | u256 mask = ((u256(1) << testBit) - 1); |
198 | 17.2k | if (boost::multiprecision::bit_test(ret, testBit)) |
199 | 6.09k | ret |= ~mask; |
200 | 11.1k | else |
201 | 11.1k | ret &= mask; |
202 | 17.2k | return ret; |
203 | 17.2k | } |
204 | | // --------------- blockchain stuff --------------- |
205 | 30.0k | case Instruction::KECCAK256: |
206 | 30.0k | { |
207 | 30.0k | if (!accessMemory(arg[0], arg[1])) |
208 | 2.61k | return u256("0x1234cafe1234cafe1234cafe") + arg[0]; |
209 | 27.4k | uint64_t offset = uint64_t(arg[0] & uint64_t(-1)); |
210 | 27.4k | uint64_t size = uint64_t(arg[1] & uint64_t(-1)); |
211 | 27.4k | return u256(keccak256(m_state.readMemory(offset, size))); |
212 | 30.0k | } |
213 | 16.9k | case Instruction::ADDRESS: |
214 | 16.9k | return h256(m_state.address, h256::AlignRight); |
215 | 44.8k | case Instruction::BALANCE: |
216 | 44.8k | if (arg[0] == h256(m_state.address, h256::AlignRight)) |
217 | 512 | return m_state.selfbalance; |
218 | 44.3k | else |
219 | 44.3k | return m_state.balance; |
220 | 6.62k | case Instruction::SELFBALANCE: |
221 | 6.62k | return m_state.selfbalance; |
222 | 10.1k | case Instruction::ORIGIN: |
223 | 10.1k | return h256(m_state.origin, h256::AlignRight); |
224 | 4.48k | case Instruction::CALLER: |
225 | 4.48k | return h256(m_state.caller, h256::AlignRight); |
226 | 3.49k | case Instruction::CALLVALUE: |
227 | 3.49k | return m_state.callvalue; |
228 | 102k | case Instruction::CALLDATALOAD: |
229 | 102k | return readZeroExtended(m_state.calldata, arg[0]); |
230 | 9.63k | case Instruction::CALLDATASIZE: |
231 | 9.63k | return m_state.calldata.size(); |
232 | 7.33k | case Instruction::CALLDATACOPY: |
233 | 7.33k | if (accessMemory(arg[0], arg[2])) |
234 | 7.20k | copyZeroExtended( |
235 | 7.20k | m_state.memory, m_state.calldata, |
236 | 7.20k | size_t(arg[0]), size_t(arg[1]), size_t(arg[2]) |
237 | 7.20k | ); |
238 | 7.33k | logTrace(_instruction, arg); |
239 | 7.33k | return 0; |
240 | 12.0k | case Instruction::CODESIZE: |
241 | 12.0k | return m_state.code.size(); |
242 | 6.11k | case Instruction::CODECOPY: |
243 | 6.11k | if (accessMemory(arg[0], arg[2])) |
244 | 5.80k | copyZeroExtended( |
245 | 5.80k | m_state.memory, m_state.code, |
246 | 5.80k | size_t(arg[0]), size_t(arg[1]), size_t(arg[2]) |
247 | 5.80k | ); |
248 | 6.11k | logTrace(_instruction, arg); |
249 | 6.11k | return 0; |
250 | 9.09k | case Instruction::GASPRICE: |
251 | 9.09k | return m_state.gasprice; |
252 | 5.98k | case Instruction::CHAINID: |
253 | 5.98k | return m_state.chainid; |
254 | 3.43k | case Instruction::BASEFEE: |
255 | 3.43k | return m_state.basefee; |
256 | 16.6k | case Instruction::BLOBHASH: |
257 | 16.6k | return blobHash(arg[0]); |
258 | 1.18k | case Instruction::BLOBBASEFEE: |
259 | 1.18k | return m_state.blobbasefee; |
260 | 75 | case Instruction::SLOTNUM: |
261 | 75 | return m_state.slotnum; |
262 | 4.78k | case Instruction::EXTCODESIZE: |
263 | 4.78k | return u256(keccak256(h256(arg[0]))) & 0xffffff; |
264 | 8.87k | case Instruction::EXTCODEHASH: |
265 | 8.87k | return u256(keccak256(h256(arg[0] + 1))); |
266 | 21.1k | case Instruction::EXTCODECOPY: |
267 | 21.1k | if (accessMemory(arg[1], arg[3])) |
268 | | // TODO this way extcodecopy and codecopy do the same thing. |
269 | 20.8k | copyZeroExtended( |
270 | 20.8k | m_state.memory, m_state.code, |
271 | 20.8k | size_t(arg[1]), size_t(arg[2]), size_t(arg[3]) |
272 | 20.8k | ); |
273 | 21.1k | logTrace(_instruction, arg); |
274 | 21.1k | return 0; |
275 | 12.6k | case Instruction::RETURNDATASIZE: |
276 | 12.6k | return m_state.returndata.size(); |
277 | 4.27k | case Instruction::RETURNDATACOPY: |
278 | 4.27k | if (accessMemory(arg[0], arg[2])) |
279 | 4.11k | copyZeroExtended( |
280 | 4.11k | m_state.memory, m_state.returndata, |
281 | 4.11k | size_t(arg[0]), size_t(arg[1]), size_t(arg[2]) |
282 | 4.11k | ); |
283 | 4.27k | logTrace(_instruction, arg); |
284 | 4.27k | return 0; |
285 | 7.42k | case Instruction::MCOPY: |
286 | 7.42k | if (accessMemory(arg[1], arg[2]) && accessMemory(arg[0], arg[2])) |
287 | 7.25k | copyZeroExtendedWithOverlap( |
288 | 7.25k | m_state.memory, |
289 | 7.25k | m_state.memory, |
290 | 7.25k | static_cast<size_t>(arg[0]), |
291 | 7.25k | static_cast<size_t>(arg[1]), |
292 | 7.25k | static_cast<size_t>(arg[2]) |
293 | 7.25k | ); |
294 | 7.42k | logTrace(_instruction, arg); |
295 | 7.42k | return 0; |
296 | 37.1k | case Instruction::BLOCKHASH: |
297 | 37.1k | if (arg[0] >= m_state.blockNumber || arg[0] + 256 < m_state.blockNumber) |
298 | 36.4k | return 0; |
299 | 727 | else |
300 | 727 | return 0xaaaaaaaa + (arg[0] - m_state.blockNumber - 256); |
301 | 5.35k | case Instruction::COINBASE: |
302 | 5.35k | return h256(m_state.coinbase, h256::AlignRight); |
303 | 6.89k | case Instruction::TIMESTAMP: |
304 | 6.89k | return m_state.timestamp; |
305 | 6.02k | case Instruction::NUMBER: |
306 | 6.02k | return m_state.blockNumber; |
307 | 4.83k | case Instruction::PREVRANDAO: |
308 | 4.83k | return (m_evmVersion < langutil::EVMVersion::paris()) ? m_state.difficulty : m_state.prevrandao; |
309 | 4.82k | case Instruction::GASLIMIT: |
310 | 4.82k | return m_state.gaslimit; |
311 | | // --------------- memory / storage / logs --------------- |
312 | 164k | case Instruction::MLOAD: |
313 | 164k | accessMemory(arg[0], 0x20); |
314 | 164k | return readMemoryWord(arg[0]); |
315 | 195k | case Instruction::MSTORE: |
316 | 195k | accessMemory(arg[0], 0x20); |
317 | 195k | writeMemoryWord(arg[0], arg[1]); |
318 | 195k | return 0; |
319 | 21.2k | case Instruction::MSTORE8: |
320 | 21.2k | accessMemory(arg[0], 1); |
321 | 21.2k | m_state.memory[arg[0]] = uint8_t(arg[1] & 0xff); |
322 | 21.2k | return 0; |
323 | 83.7k | case Instruction::SLOAD: |
324 | 83.7k | return m_state.storage[h256(arg[0])]; |
325 | 230k | case Instruction::SSTORE: |
326 | 230k | m_state.storage[h256(arg[0])] = h256(arg[1]); |
327 | 230k | return 0; |
328 | 0 | case Instruction::PC: |
329 | 0 | return 0x77; |
330 | 25.1k | case Instruction::MSIZE: |
331 | 25.1k | return m_state.msize; |
332 | 20.6k | case Instruction::GAS: |
333 | 20.6k | return 0x99; |
334 | 11.6k | case Instruction::LOG0: |
335 | 11.6k | accessMemory(arg[0], arg[1]); |
336 | 11.6k | logTrace(_instruction, arg); |
337 | 11.6k | return 0; |
338 | 4.20k | case Instruction::LOG1: |
339 | 4.20k | accessMemory(arg[0], arg[1]); |
340 | 4.20k | logTrace(_instruction, arg); |
341 | 4.20k | return 0; |
342 | 2.22k | case Instruction::LOG2: |
343 | 2.22k | accessMemory(arg[0], arg[1]); |
344 | 2.22k | logTrace(_instruction, arg); |
345 | 2.22k | return 0; |
346 | 2.67k | case Instruction::LOG3: |
347 | 2.67k | accessMemory(arg[0], arg[1]); |
348 | 2.67k | logTrace(_instruction, arg); |
349 | 2.67k | return 0; |
350 | 9.00k | case Instruction::LOG4: |
351 | 9.00k | accessMemory(arg[0], arg[1]); |
352 | 9.00k | logTrace(_instruction, arg); |
353 | 9.00k | return 0; |
354 | 14.4k | case Instruction::TLOAD: |
355 | 14.4k | return m_state.transientStorage[h256(arg[0])]; |
356 | 17.3k | case Instruction::TSTORE: |
357 | 17.3k | m_state.transientStorage[h256(arg[0])] = h256(arg[1]); |
358 | 17.3k | return 0; |
359 | | // --------------- calls --------------- |
360 | 28.7k | case Instruction::CREATE: |
361 | 28.7k | accessMemory(arg[1], arg[2]); |
362 | 28.7k | logTrace(_instruction, arg); |
363 | 28.7k | if (arg[2] != 0) |
364 | 17.9k | return (0xcccccc + arg[1]) & u256("0xffffffffffffffffffffffffffffffffffffffff"); |
365 | 10.8k | else |
366 | 10.8k | return 0xcccccc; |
367 | 10.1k | case Instruction::CREATE2: |
368 | 10.1k | accessMemory(arg[1], arg[2]); |
369 | 10.1k | logTrace(_instruction, arg); |
370 | 10.1k | if (arg[2] != 0) |
371 | 6.86k | return (0xdddddd + arg[1]) & u256("0xffffffffffffffffffffffffffffffffffffffff"); |
372 | 3.25k | else |
373 | 3.25k | return 0xdddddd; |
374 | 76.9k | case Instruction::CALL: |
375 | 89.1k | case Instruction::CALLCODE: |
376 | 89.1k | accessMemory(arg[3], arg[4]); |
377 | 89.1k | accessMemory(arg[5], arg[6]); |
378 | 89.1k | logTrace(_instruction, arg); |
379 | | // Randomly fail based on the called address if it isn't a call to self. |
380 | | // Used for fuzzing. |
381 | 89.1k | return ( |
382 | 89.1k | (arg[0] > 0) && |
383 | 77.7k | (arg[1] == util::h160::Arith(m_state.address) || (arg[1] & 1)) |
384 | 89.1k | ) ? 1 : 0; |
385 | 5.43k | case Instruction::DELEGATECALL: |
386 | 8.89k | case Instruction::STATICCALL: |
387 | 8.89k | accessMemory(arg[2], arg[3]); |
388 | 8.89k | accessMemory(arg[4], arg[5]); |
389 | 8.89k | logTrace(_instruction, arg); |
390 | | // Randomly fail based on the called address if it isn't a call to self. |
391 | | // Used for fuzzing. |
392 | 8.89k | return ( |
393 | 8.89k | (arg[0] > 0) && |
394 | 7.57k | (arg[1] == util::h160::Arith(m_state.address) || (arg[1] & 1)) |
395 | 8.89k | ) ? 1 : 0; |
396 | 2.68k | case Instruction::RETURN: |
397 | 2.68k | { |
398 | 2.68k | m_state.returndata = {}; |
399 | 2.68k | if (accessMemory(arg[0], arg[1])) |
400 | 2.13k | m_state.returndata = m_state.readMemory(arg[0], arg[1]); |
401 | 2.68k | logTrace(_instruction, arg, m_state.returndata); |
402 | 2.68k | BOOST_THROW_EXCEPTION(ExplicitlyTerminatedWithReturn()); |
403 | 5.43k | } |
404 | 669 | case Instruction::REVERT: |
405 | 669 | accessMemory(arg[0], arg[1]); |
406 | 669 | logTrace(_instruction, arg); |
407 | 669 | m_state.storage.clear(); |
408 | 669 | m_state.transientStorage.clear(); |
409 | 669 | BOOST_THROW_EXCEPTION(ExplicitlyTerminated()); |
410 | 935 | case Instruction::INVALID: |
411 | 935 | logTrace(_instruction); |
412 | 935 | m_state.storage.clear(); |
413 | 935 | m_state.transientStorage.clear(); |
414 | 935 | m_state.trace.clear(); |
415 | 935 | BOOST_THROW_EXCEPTION(ExplicitlyTerminated()); |
416 | 1.90k | case Instruction::SELFDESTRUCT: |
417 | 1.90k | logTrace(_instruction, arg); |
418 | 1.90k | m_state.storage.clear(); |
419 | 1.90k | m_state.transientStorage.clear(); |
420 | 1.90k | m_state.trace.clear(); |
421 | 1.90k | BOOST_THROW_EXCEPTION(ExplicitlyTerminated()); |
422 | 31.9k | case Instruction::POP: |
423 | 31.9k | return 0; |
424 | | // --------------- invalid in strict assembly --------------- |
425 | 0 | case Instruction::JUMP: |
426 | 0 | case Instruction::JUMPI: |
427 | 0 | case Instruction::JUMPDEST: |
428 | 0 | case Instruction::PUSH0: |
429 | 0 | case Instruction::PUSH1: |
430 | 0 | case Instruction::PUSH2: |
431 | 0 | case Instruction::PUSH3: |
432 | 0 | case Instruction::PUSH4: |
433 | 0 | case Instruction::PUSH5: |
434 | 0 | case Instruction::PUSH6: |
435 | 0 | case Instruction::PUSH7: |
436 | 0 | case Instruction::PUSH8: |
437 | 0 | case Instruction::PUSH9: |
438 | 0 | case Instruction::PUSH10: |
439 | 0 | case Instruction::PUSH11: |
440 | 0 | case Instruction::PUSH12: |
441 | 0 | case Instruction::PUSH13: |
442 | 0 | case Instruction::PUSH14: |
443 | 0 | case Instruction::PUSH15: |
444 | 0 | case Instruction::PUSH16: |
445 | 0 | case Instruction::PUSH17: |
446 | 0 | case Instruction::PUSH18: |
447 | 0 | case Instruction::PUSH19: |
448 | 0 | case Instruction::PUSH20: |
449 | 0 | case Instruction::PUSH21: |
450 | 0 | case Instruction::PUSH22: |
451 | 0 | case Instruction::PUSH23: |
452 | 0 | case Instruction::PUSH24: |
453 | 0 | case Instruction::PUSH25: |
454 | 0 | case Instruction::PUSH26: |
455 | 0 | case Instruction::PUSH27: |
456 | 0 | case Instruction::PUSH28: |
457 | 0 | case Instruction::PUSH29: |
458 | 0 | case Instruction::PUSH30: |
459 | 0 | case Instruction::PUSH31: |
460 | 0 | case Instruction::PUSH32: |
461 | 0 | case Instruction::DUP1: |
462 | 0 | case Instruction::DUP2: |
463 | 0 | case Instruction::DUP3: |
464 | 0 | case Instruction::DUP4: |
465 | 0 | case Instruction::DUP5: |
466 | 0 | case Instruction::DUP6: |
467 | 0 | case Instruction::DUP7: |
468 | 0 | case Instruction::DUP8: |
469 | 0 | case Instruction::DUP9: |
470 | 0 | case Instruction::DUP10: |
471 | 0 | case Instruction::DUP11: |
472 | 0 | case Instruction::DUP12: |
473 | 0 | case Instruction::DUP13: |
474 | 0 | case Instruction::DUP14: |
475 | 0 | case Instruction::DUP15: |
476 | 0 | case Instruction::DUP16: |
477 | 0 | case Instruction::SWAP1: |
478 | 0 | case Instruction::SWAP2: |
479 | 0 | case Instruction::SWAP3: |
480 | 0 | case Instruction::SWAP4: |
481 | 0 | case Instruction::SWAP5: |
482 | 0 | case Instruction::SWAP6: |
483 | 0 | case Instruction::SWAP7: |
484 | 0 | case Instruction::SWAP8: |
485 | 0 | case Instruction::SWAP9: |
486 | 0 | case Instruction::SWAP10: |
487 | 0 | case Instruction::SWAP11: |
488 | 0 | case Instruction::SWAP12: |
489 | 0 | case Instruction::SWAP13: |
490 | 0 | case Instruction::SWAP14: |
491 | 0 | case Instruction::SWAP15: |
492 | 0 | case Instruction::SWAP16: |
493 | 0 | yulAssert(false, "Impossible in strict assembly."); |
494 | 3.64M | } |
495 | | |
496 | 0 | util::unreachable(); |
497 | 0 | } |
498 | | |
499 | | u256 EVMInstructionInterpreter::evalBuiltin( |
500 | | BuiltinFunctionForEVM const& _fun, |
501 | | std::vector<Expression> const& _arguments, |
502 | | std::vector<u256> const& _evaluatedArguments |
503 | | ) |
504 | 3.71M | { |
505 | 3.71M | if (_fun.instruction) |
506 | 3.64M | return eval(*_fun.instruction, _evaluatedArguments); |
507 | | |
508 | 70.5k | std::string const& fun = _fun.name; |
509 | | // Evaluate datasize/offset/copy instructions |
510 | 70.5k | if (fun == "datasize" || fun == "dataoffset") |
511 | 15.1k | { |
512 | 15.1k | std::string arg = formatLiteral(std::get<Literal>(_arguments.at(0))); |
513 | 15.1k | if (arg.length() < 32) |
514 | 13.9k | arg.resize(32, 0); |
515 | 15.1k | if (fun == "datasize") |
516 | 8.72k | return u256(keccak256(arg)) & 0xfff; |
517 | 6.43k | else |
518 | 6.43k | { |
519 | | // Force different value than for datasize |
520 | 6.43k | arg[31]++; |
521 | 6.43k | arg[31]++; |
522 | 6.43k | return u256(keccak256(arg)) & 0xfff; |
523 | 6.43k | } |
524 | 15.1k | } |
525 | | |
526 | 55.4k | if (fun == "datacopy") |
527 | 2.50k | { |
528 | | // This is identical to codecopy. |
529 | 2.50k | if ( |
530 | 2.50k | _evaluatedArguments.at(2) != 0 && |
531 | 1.66k | accessMemory(_evaluatedArguments.at(0), _evaluatedArguments.at(2)) |
532 | 2.50k | ) |
533 | 1.52k | copyZeroExtended( |
534 | 1.52k | m_state.memory, |
535 | 1.52k | m_state.code, |
536 | 1.52k | size_t(_evaluatedArguments.at(0)), |
537 | 1.52k | size_t(_evaluatedArguments.at(1) & std::numeric_limits<size_t>::max()), |
538 | 1.52k | size_t(_evaluatedArguments.at(2)) |
539 | 1.52k | ); |
540 | 2.50k | return 0; |
541 | 2.50k | } |
542 | | |
543 | 52.9k | if (fun == "memoryguard") |
544 | 52.8k | return _evaluatedArguments.at(0); |
545 | | |
546 | 96 | if (fun == "linkersymbol") |
547 | 6 | { |
548 | 6 | yulAssert(_arguments.size() == 1); |
549 | 6 | yulAssert(std::holds_alternative<Literal>(_arguments[0])); |
550 | 6 | std::string const placeholder = formatLiteral(std::get<Literal>(_arguments[0])); |
551 | 6 | h256 const identifier(keccak256(placeholder)); |
552 | 6 | m_linkerSymbols.emplace(identifier, placeholder); |
553 | 6 | return u256(identifier); |
554 | 6 | } |
555 | | |
556 | 90 | if (fun == "loadimmutable") |
557 | 2 | { |
558 | 2 | yulAssert(_arguments.size() == 1); |
559 | 2 | yulAssert(std::holds_alternative<Literal>(_arguments[0])); |
560 | 2 | std::string const identifier = formatLiteral(std::get<Literal>(_arguments[0])); |
561 | | // Return a deterministic value based on the identifier. |
562 | | // This is sufficient for differential fuzzing since the same identifier |
563 | | // will always return the same value, maintaining trace equivalence. |
564 | 2 | return u256(h256(keccak256(identifier))); |
565 | 2 | } |
566 | | |
567 | 88 | if (fun == "setimmutable") |
568 | 88 | { |
569 | 88 | yulAssert(_arguments.size() == 3); |
570 | | // No-op: The real implementation patches placeholder bytes in memory-loaded runtime code. |
571 | | // For differential fuzzing, this ensures correct code never fails (no false positives), though some |
572 | | // bugs in setimmutable handling may not be detected (potential false negatives). |
573 | 88 | return 0; |
574 | 88 | } |
575 | | |
576 | 0 | yulAssert(false, "Unknown builtin: " + fun); |
577 | 0 | } |
578 | | |
579 | | |
580 | | bool EVMInstructionInterpreter::accessMemory(u256 const& _offset, u256 const& _size) |
581 | 734k | { |
582 | 734k | if (_size == 0) |
583 | 114k | return true; |
584 | | |
585 | 620k | if (_offset <= (_offset + _size) && (_offset + _size) <= (_offset + _size + 0x1f)) |
586 | 618k | { |
587 | 618k | u256 newMSize = (_offset + _size + 0x1f) & ~u256(0x1f); |
588 | 618k | m_state.msize = std::max(m_state.msize, newMSize); |
589 | | // We only record accesses to contiguous memory chunks that are at most s_maxRangeSize bytes |
590 | | // in size and at an offset of at most numeric_limits<size_t>::max() - s_maxRangeSize |
591 | 618k | return _size <= s_maxRangeSize && _offset <= u256(std::numeric_limits<size_t>::max() - s_maxRangeSize); |
592 | 618k | } |
593 | | |
594 | 1.48k | m_state.msize = u256(-1); |
595 | 1.48k | return false; |
596 | 620k | } |
597 | | |
598 | | bytes EVMInstructionInterpreter::readMemory(u256 const& _offset, u256 const& _size) |
599 | 0 | { |
600 | 0 | yulAssert(_size <= s_maxRangeSize, "Too large read."); |
601 | 0 | bytes data(size_t(_size), uint8_t(0)); |
602 | 0 | for (size_t i = 0; i < data.size(); ++i) |
603 | 0 | data[i] = m_state.memory[_offset + i]; |
604 | 0 | return data; |
605 | 0 | } |
606 | | |
607 | | u256 EVMInstructionInterpreter::readMemoryWord(u256 const& _offset) |
608 | 164k | { |
609 | 164k | return u256(h256(m_state.readMemory(_offset, 32))); |
610 | 164k | } |
611 | | |
612 | | void EVMInstructionInterpreter::writeMemoryWord(u256 const& _offset, u256 const& _value) |
613 | 195k | { |
614 | 6.45M | for (size_t i = 0; i < 32; i++) |
615 | 6.26M | m_state.memory[_offset + i] = uint8_t((_value >> (8 * (31 - i))) & 0xff); |
616 | 195k | } |
617 | | |
618 | | |
619 | | void EVMInstructionInterpreter::logTrace( |
620 | | evmasm::Instruction _instruction, |
621 | | std::vector<u256> const& _arguments, |
622 | | bytes const& _data |
623 | | ) |
624 | 220k | { |
625 | 220k | logTrace( |
626 | 220k | evmasm::instructionInfo(_instruction, m_evmVersion).name, |
627 | 220k | SemanticInformation::memory(_instruction) == SemanticInformation::Effect::Write, |
628 | 220k | _arguments, |
629 | 220k | _data |
630 | 220k | ); |
631 | 220k | } |
632 | | |
633 | | void EVMInstructionInterpreter::logTrace( |
634 | | std::string const& _pseudoInstruction, |
635 | | bool _writesToMemory, |
636 | | std::vector<u256> const& _arguments, |
637 | | bytes const& _data |
638 | | ) |
639 | 220k | { |
640 | 220k | if (!(_writesToMemory && memWriteTracingDisabled())) |
641 | 75.6k | { |
642 | 75.6k | std::string message = _pseudoInstruction + "("; |
643 | 75.6k | std::pair<bool, size_t> inputMemoryPtrModified = isInputMemoryPtrModified(_pseudoInstruction, _arguments); |
644 | 323k | for (size_t i = 0; i < _arguments.size(); ++i) |
645 | 247k | { |
646 | 247k | bool printZero = inputMemoryPtrModified.first && inputMemoryPtrModified.second == i; |
647 | 247k | u256 arg = printZero ? 0 : _arguments[i]; |
648 | 247k | message += (i > 0 ? ", " : "") + formatNumber(arg); |
649 | 247k | } |
650 | 75.6k | message += ")"; |
651 | 75.6k | if (!_data.empty()) |
652 | 1.49k | message += " [" + util::toHex(_data) + "]"; |
653 | 75.6k | m_state.trace.emplace_back(std::move(message)); |
654 | 75.6k | if (m_state.maxTraceSize > 0 && m_state.trace.size() >= m_state.maxTraceSize) |
655 | 186 | { |
656 | 186 | m_state.trace.emplace_back("Trace size limit reached."); |
657 | 186 | BOOST_THROW_EXCEPTION(TraceLimitReached()); |
658 | 186 | } |
659 | 75.6k | } |
660 | 220k | } |
661 | | |
662 | | std::pair<bool, size_t> EVMInstructionInterpreter::isInputMemoryPtrModified( |
663 | | std::string const& _pseudoInstruction, |
664 | | std::vector<u256> const& _arguments |
665 | | ) |
666 | 75.6k | { |
667 | 75.6k | if (_pseudoInstruction == "RETURN" || _pseudoInstruction == "REVERT") |
668 | 3.35k | { |
669 | 3.35k | if (_arguments[1] == 0) |
670 | 865 | return {true, 0}; |
671 | 2.49k | else |
672 | 2.49k | return {false, 0}; |
673 | 3.35k | } |
674 | 72.2k | else if ( |
675 | 72.2k | _pseudoInstruction == "RETURNDATACOPY" || _pseudoInstruction == "CALLDATACOPY" |
676 | 72.2k | || _pseudoInstruction == "CODECOPY") |
677 | 0 | { |
678 | 0 | if (_arguments[2] == 0) |
679 | 0 | return {true, 0}; |
680 | 0 | else |
681 | 0 | return {false, 0}; |
682 | 0 | } |
683 | 72.2k | else if (_pseudoInstruction == "EXTCODECOPY") |
684 | 0 | { |
685 | 0 | if (_arguments[3] == 0) |
686 | 0 | return {true, 1}; |
687 | 0 | else |
688 | 0 | return {false, 0}; |
689 | 0 | } |
690 | 72.2k | else if ( |
691 | 72.2k | _pseudoInstruction == "LOG0" || _pseudoInstruction == "LOG1" || _pseudoInstruction == "LOG2" |
692 | 54.1k | || _pseudoInstruction == "LOG3" || _pseudoInstruction == "LOG4") |
693 | 29.7k | { |
694 | 29.7k | if (_arguments[1] == 0) |
695 | 12.0k | return {true, 0}; |
696 | 17.7k | else |
697 | 17.7k | return {false, 0}; |
698 | 29.7k | } |
699 | 42.5k | if (_pseudoInstruction == "CREATE" || _pseudoInstruction == "CREATE2") |
700 | 38.9k | { |
701 | 38.9k | if (_arguments[2] == 0) |
702 | 14.0k | return {true, 1}; |
703 | 24.8k | else |
704 | 24.8k | return {false, 0}; |
705 | 38.9k | } |
706 | 3.60k | if (_pseudoInstruction == "CALL" || _pseudoInstruction == "CALLCODE") |
707 | 0 | { |
708 | 0 | if (_arguments[4] == 0) |
709 | 0 | return {true, 3}; |
710 | 0 | else |
711 | 0 | return {false, 0}; |
712 | 0 | } |
713 | 3.60k | else if (_pseudoInstruction == "DELEGATECALL" || _pseudoInstruction == "STATICCALL") |
714 | 0 | { |
715 | 0 | if (_arguments[3] == 0) |
716 | 0 | return {true, 2}; |
717 | 0 | else |
718 | 0 | return {false, 0}; |
719 | 0 | } |
720 | 3.60k | else |
721 | 3.60k | return {false, 0}; |
722 | 3.60k | } |
723 | | |
724 | | h256 EVMInstructionInterpreter::blobHash(u256 const& _index) |
725 | 16.6k | { |
726 | 16.6k | yulAssert(m_evmVersion.hasBlobHash()); |
727 | 16.6k | if (_index >= m_state.blobCommitments.size()) |
728 | 14.9k | return util::FixedHash<32>{}; |
729 | | |
730 | 1.74k | h256 hashedCommitment = h256(picosha2::hash256(toBigEndian(m_state.blobCommitments[static_cast<size_t>(_index)]))); |
731 | 1.74k | yulAssert(m_state.blobHashVersion.size == 1); |
732 | 1.74k | hashedCommitment[0] = *m_state.blobHashVersion.data(); |
733 | | yulAssert(hashedCommitment.size == 32); |
734 | 1.74k | return hashedCommitment; |
735 | 16.6k | } |