Skip to content

Commit 4b69b5f

Browse files
committed
YulRunner: Add support for external calls to the same contract
1 parent 53b6733 commit 4b69b5f

File tree

9 files changed

+187
-27
lines changed

9 files changed

+187
-27
lines changed

libevmasm/Instruction.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,21 @@ enum class Instruction: uint8_t
188188
SELFDESTRUCT = 0xff ///< halt execution and register account for later deletion
189189
};
190190

191+
/// @returns true if the instruction is of the CALL opcode family
192+
constexpr bool isCallInstruction(Instruction _inst) noexcept
193+
{
194+
switch (_inst)
195+
{
196+
case Instruction::CALL:
197+
case Instruction::CALLCODE:
198+
case Instruction::DELEGATECALL:
199+
case Instruction::STATICCALL:
200+
return true;
201+
default:
202+
return false;
203+
}
204+
}
205+
191206
/// @returns true if the instruction is a PUSH
192207
inline bool isPushInstruction(Instruction _inst)
193208
{

test/libyul/EwasmTranslationTest.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ string EwasmTranslationTest::interpret()
112112
state,
113113
WasmDialect{},
114114
*m_object->code,
115+
/*disableExternalCalls=*/true,
115116
/*disableMemoryTracing=*/false
116117
);
117118
}

test/libyul/YulInterpreterTest.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ string YulInterpreterTest::interpret()
9898
state,
9999
EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion{}),
100100
*m_ast,
101+
/*disableExternalCalls=*/true,
101102
/*disableMemoryTracing=*/false
102103
);
103104
}

test/tools/ossfuzz/yulFuzzerCommon.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ yulFuzzerUtil::TerminationReason yulFuzzerUtil::interpret(
5353
TerminationReason reason = TerminationReason::None;
5454
try
5555
{
56-
Interpreter::run(state, _dialect, *_ast, _disableMemoryTracing);
56+
Interpreter::run(state, _dialect, *_ast, true, _disableMemoryTracing);
5757
}
5858
catch (StepLimitReached const&)
5959
{

test/tools/yulInterpreter/EVMInstructionInterpreter.cpp

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ u256 readZeroExtended(bytes const& _data, u256 const& _offset)
7070
}
7171
}
7272

73+
}
74+
75+
namespace solidity::yul::test
76+
{
7377
/// Copy @a _size bytes of @a _source at offset @a _sourceOffset to
7478
/// @a _target at offset @a _targetOffset. Behaves as if @a _source would
7579
/// continue with an infinite sequence of zero bytes beyond its end.
@@ -185,7 +189,7 @@ u256 EVMInstructionInterpreter::eval(
185189
return u256("0x1234cafe1234cafe1234cafe") + arg[0];
186190
uint64_t offset = uint64_t(arg[0] & uint64_t(-1));
187191
uint64_t size = uint64_t(arg[1] & uint64_t(-1));
188-
return u256(keccak256(readMemory(offset, size)));
192+
return u256(keccak256(m_state.readMemory(offset, size)));
189193
}
190194
case Instruction::ADDRESS:
191195
return h256(m_state.address, h256::AlignRight);
@@ -322,7 +326,6 @@ u256 EVMInstructionInterpreter::eval(
322326
return (0xdddddd + arg[1]) & u256("0xffffffffffffffffffffffffffffffffffffffff");
323327
case Instruction::CALL:
324328
case Instruction::CALLCODE:
325-
// TODO assign returndata
326329
accessMemory(arg[3], arg[4]);
327330
accessMemory(arg[5], arg[6]);
328331
logTrace(_instruction, arg);
@@ -335,11 +338,11 @@ u256 EVMInstructionInterpreter::eval(
335338
return 0;
336339
case Instruction::RETURN:
337340
{
338-
bytes data;
341+
m_state.returndata = {};
339342
if (accessMemory(arg[0], arg[1]))
340-
data = readMemory(arg[0], arg[1]);
341-
logTrace(_instruction, arg, data);
342-
BOOST_THROW_EXCEPTION(ExplicitlyTerminated());
343+
m_state.returndata = m_state.readMemory(arg[0], arg[1]);
344+
logTrace(_instruction, arg, m_state.returndata);
345+
BOOST_THROW_EXCEPTION(ExplicitlyTerminatedWithReturn());
343346
}
344347
case Instruction::REVERT:
345348
accessMemory(arg[0], arg[1]);
@@ -499,18 +502,9 @@ bool EVMInstructionInterpreter::accessMemory(u256 const& _offset, u256 const& _s
499502
return false;
500503
}
501504

502-
bytes EVMInstructionInterpreter::readMemory(u256 const& _offset, u256 const& _size)
503-
{
504-
yulAssert(_size <= 0xffff, "Too large read.");
505-
bytes data(size_t(_size), uint8_t(0));
506-
for (size_t i = 0; i < data.size(); ++i)
507-
data[i] = m_state.memory[_offset + i];
508-
return data;
509-
}
510-
511505
u256 EVMInstructionInterpreter::readMemoryWord(u256 const& _offset)
512506
{
513-
return u256(h256(readMemory(_offset, 32)));
507+
return u256(h256(m_state.readMemory(_offset, 32)));
514508
}
515509

516510
void EVMInstructionInterpreter::writeMemoryWord(u256 const& _offset, u256 const& _value)

test/tools/yulInterpreter/EVMInstructionInterpreter.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,14 @@ struct BuiltinFunctionForEVM;
4242
namespace solidity::yul::test
4343
{
4444

45+
/// Copy @a _size bytes of @a _source at offset @a _sourceOffset to
46+
/// @a _target at offset @a _targetOffset. Behaves as if @a _source would
47+
/// continue with an infinite sequence of zero bytes beyond its end.
48+
void copyZeroExtended(
49+
std::map<u256, uint8_t>& _target, bytes const& _source,
50+
size_t _targetOffset, size_t _sourceOffset, size_t _size
51+
);
52+
4553
struct InterpreterState;
4654

4755
/**

test/tools/yulInterpreter/Interpreter.cpp

Lines changed: 101 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,12 @@ void Interpreter::run(
7878
InterpreterState& _state,
7979
Dialect const& _dialect,
8080
Block const& _ast,
81+
bool _disableExternalCalls,
8182
bool _disableMemoryTrace
8283
)
8384
{
8485
Scope scope;
85-
Interpreter{_state, _dialect, scope, _disableMemoryTrace}(_ast);
86+
Interpreter{_state, _dialect, scope, _disableExternalCalls, _disableMemoryTrace}(_ast);
8687
}
8788

8889
void Interpreter::operator()(ExpressionStatement const& _expressionStatement)
@@ -217,14 +218,14 @@ void Interpreter::operator()(Block const& _block)
217218

218219
u256 Interpreter::evaluate(Expression const& _expression)
219220
{
220-
ExpressionEvaluator ev(m_state, m_dialect, *m_scope, m_variables, m_disableMemoryTrace);
221+
ExpressionEvaluator ev(m_state, m_dialect, *m_scope, m_variables, m_disableExternalCalls, m_disableMemoryTrace);
221222
ev.visit(_expression);
222223
return ev.value();
223224
}
224225

225226
vector<u256> Interpreter::evaluateMulti(Expression const& _expression)
226227
{
227-
ExpressionEvaluator ev(m_state, m_dialect, *m_scope, m_variables, m_disableMemoryTrace);
228+
ExpressionEvaluator ev(m_state, m_dialect, *m_scope, m_variables, m_disableExternalCalls, m_disableMemoryTrace);
228229
ev.visit(_expression);
229230
return ev.values();
230231
}
@@ -288,7 +289,17 @@ void ExpressionEvaluator::operator()(FunctionCall const& _funCall)
288289
if (BuiltinFunctionForEVM const* fun = dialect->builtin(_funCall.functionName.name))
289290
{
290291
EVMInstructionInterpreter interpreter(m_state, m_disableMemoryTrace);
291-
setValue(interpreter.evalBuiltin(*fun, _funCall.arguments, values()));
292+
293+
u256 const value = interpreter.evalBuiltin(*fun, _funCall.arguments, values());
294+
295+
if (
296+
!m_disableExternalCalls &&
297+
fun->instruction &&
298+
evmasm::isCallInstruction(*fun->instruction)
299+
)
300+
runExternalCall(*fun->instruction);
301+
302+
setValue(value);
292303
return;
293304
}
294305
}
@@ -316,13 +327,13 @@ void ExpressionEvaluator::operator()(FunctionCall const& _funCall)
316327
variables[fun->returnVariables.at(i).name] = 0;
317328

318329
m_state.controlFlowState = ControlFlowState::Default;
319-
Interpreter interpreter(m_state, m_dialect, *scope, m_disableMemoryTrace, std::move(variables));
320-
interpreter(fun->body);
330+
unique_ptr<Interpreter> interpreter = makeInterpreterCopy(std::move(variables));
331+
(*interpreter)(fun->body);
321332
m_state.controlFlowState = ControlFlowState::Default;
322333

323334
m_values.clear();
324335
for (auto const& retVar: fun->returnVariables)
325-
m_values.emplace_back(interpreter.valueOfVariable(retVar.name));
336+
m_values.emplace_back(interpreter->valueOfVariable(retVar.name));
326337
}
327338

328339
u256 ExpressionEvaluator::value() const
@@ -380,3 +391,86 @@ void ExpressionEvaluator::incrementStep()
380391
BOOST_THROW_EXCEPTION(ExpressionNestingLimitReached());
381392
}
382393
}
394+
395+
void ExpressionEvaluator::runExternalCall(evmasm::Instruction _instruction)
396+
{
397+
u256 memOutOffset = 0;
398+
u256 memOutSize = 0;
399+
u256 callvalue = 0;
400+
u256 memInOffset = 0;
401+
u256 memInSize = 0;
402+
403+
// Setup memOut* values
404+
if (
405+
_instruction == evmasm::Instruction::CALL ||
406+
_instruction == evmasm::Instruction::CALLCODE
407+
)
408+
{
409+
memOutOffset = values()[5];
410+
memOutSize = values()[6];
411+
callvalue = values()[2];
412+
memInOffset = values()[3];
413+
memInSize = values()[4];
414+
}
415+
else if (
416+
_instruction == evmasm::Instruction::DELEGATECALL ||
417+
_instruction == evmasm::Instruction::STATICCALL
418+
)
419+
{
420+
memOutOffset = values()[4];
421+
memOutSize = values()[5];
422+
memInOffset = values()[2];
423+
memInSize = values()[3];
424+
}
425+
else
426+
yulAssert(false);
427+
428+
// Don't execute external call if it isn't our own address
429+
if (values()[1] != util::h160::Arith(m_state.address))
430+
return;
431+
432+
Scope tmpScope;
433+
InterpreterState tmpState;
434+
tmpState.calldata = m_state.readMemory(memInOffset, memInSize);
435+
tmpState.callvalue = callvalue;
436+
437+
// Create new interpreter for the called contract
438+
unique_ptr<Interpreter> newInterpreter = makeInterpreterNew(tmpState, tmpScope);
439+
440+
Scope* abstractRootScope = &m_scope;
441+
Scope* fileScope = nullptr;
442+
Block const* ast = nullptr;
443+
444+
// Find file scope
445+
while (abstractRootScope->parent)
446+
{
447+
fileScope = abstractRootScope;
448+
abstractRootScope = abstractRootScope->parent;
449+
}
450+
451+
// Get AST for file scope
452+
for (auto&& [block, scope]: abstractRootScope->subScopes)
453+
if (scope.get() == fileScope)
454+
{
455+
ast = block;
456+
break;
457+
}
458+
459+
yulAssert(ast);
460+
461+
try
462+
{
463+
(*newInterpreter)(*ast);
464+
}
465+
catch (ExplicitlyTerminatedWithReturn const&)
466+
{
467+
// Copy return data to our memory
468+
copyZeroExtended(
469+
m_state.memory,
470+
newInterpreter->returnData(),
471+
memOutOffset.convert_to<size_t>(),
472+
0,
473+
memOutSize.convert_to<size_t>()
474+
);
475+
}
476+
}

0 commit comments

Comments
 (0)