Coverage Report

Created: 2026-08-14 07:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}