Merge pull request #9520 from ethereum/interpreterLiteral

Fix literal arguments in interpreter.
This commit is contained in:
Daniel Kirchner 2020-08-06 16:11:31 +02:00 committed by GitHub
commit 9ca48987dd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 110 additions and 37 deletions

View File

@ -0,0 +1,19 @@
object "t" {
code {
datacopy(not(datasize("object2.object3.object4.datablock")), 0, 0)
}
object "object2" {
code{}
object "object3" {
code{}
object "object4" {
code{}
data "datablock" ""
}
}
}
}
// ----
// Trace:
// Memory dump:
// Storage dump:

View File

@ -430,28 +430,46 @@ u256 EVMInstructionInterpreter::eval(
return 0;
}
u256 EVMInstructionInterpreter::evalBuiltin(BuiltinFunctionForEVM const& _fun, const std::vector<u256>& _arguments)
u256 EVMInstructionInterpreter::evalBuiltin(
BuiltinFunctionForEVM const& _fun,
vector<Expression> const& _arguments,
vector<u256> const& _evaluatedArguments
)
{
if (_fun.instruction)
return eval(*_fun.instruction, _arguments);
else if (_fun.name == "datasize"_yulstring)
return u256(keccak256(h256(_arguments.at(0)))) & 0xfff;
else if (_fun.name == "dataoffset"_yulstring)
return u256(keccak256(h256(_arguments.at(0) + 2))) & 0xfff;
else if (_fun.name == "datacopy"_yulstring)
return eval(*_fun.instruction, _evaluatedArguments);
string fun = _fun.name.str();
// Evaluate datasize/offset/copy instructions
if (fun == "datasize" || fun == "dataoffset")
{
string arg = std::get<Literal>(_arguments.at(0)).value.str();
if (arg.length() < 32)
arg.resize(32, 0);
if (fun == "datasize")
return u256(keccak256(arg)) & 0xfff;
else
{
// Force different value than for datasize
arg[31] += 2;
return u256(keccak256(arg)) & 0xfff;
}
}
else if (fun == "datacopy")
{
// This is identical to codecopy.
if (accessMemory(_arguments.at(0), _arguments.at(2)))
if (accessMemory(_evaluatedArguments.at(0), _evaluatedArguments.at(2)))
copyZeroExtended(
m_state.memory,
m_state.code,
size_t(_arguments.at(0)),
size_t(_arguments.at(1) & numeric_limits<size_t>::max()),
size_t(_arguments.at(2))
size_t(_evaluatedArguments.at(0)),
size_t(_evaluatedArguments.at(1) & numeric_limits<size_t>::max()),
size_t(_evaluatedArguments.at(2))
);
return 0;
}
else
yulAssert(false, "Unknown builtin: " + _fun.name.str());
yulAssert(false, "Unknown builtin: " + fun);
return 0;
}

View File

@ -71,7 +71,11 @@ public:
/// Evaluate instruction
u256 eval(evmasm::Instruction _instruction, std::vector<u256> const& _arguments);
/// Evaluate builtin function
u256 evalBuiltin(BuiltinFunctionForEVM const& _fun, std::vector<u256> const& _arguments);
u256 evalBuiltin(
BuiltinFunctionForEVM const& _fun,
std::vector<Expression> const& _arguments,
std::vector<u256> const& _evaluatedArguments
);
private:
/// Checks if the memory access is not too large for the interpreter and adjusts

View File

@ -116,27 +116,40 @@ uint64_t popcnt(uint64_t _v)
using u512 = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<512, 256, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>>;
u256 EwasmBuiltinInterpreter::evalBuiltin(YulString _fun, vector<u256> const& _arguments)
u256 EwasmBuiltinInterpreter::evalBuiltin(
YulString _functionName,
vector<Expression> const& _arguments,
vector<u256> const& _evaluatedArguments
)
{
vector<uint64_t> arg;
for (u256 const& a: _arguments)
for (u256 const& a: _evaluatedArguments)
arg.emplace_back(uint64_t(a & uint64_t(-1)));
string fun = _fun.str();
if (fun == "datasize")
return u256(keccak256(h256(_arguments.at(0)))) & 0xfff;
else if (fun == "dataoffset")
return u256(keccak256(h256(_arguments.at(0) + 2))) & 0xfff;
string const fun = _functionName.str();
if (fun == "datasize" || fun == "dataoffset")
{
string arg = std::get<Literal>(_arguments.at(0)).value.str();
if (arg.length() < 32)
arg.resize(32, 0);
if (fun == "datasize")
return u256(util::keccak256(arg)) & 0xfff;
else if (fun == "dataoffset")
{
arg[31] += 2;
return u256(util::keccak256(arg)) & 0xfff;
}
}
else if (fun == "datacopy")
{
// This is identical to codecopy.
if (accessMemory(_arguments.at(0), _arguments.at(2)))
if (accessMemory(_evaluatedArguments.at(0), _evaluatedArguments.at(2)))
copyZeroExtended(
m_state.memory,
m_state.code,
static_cast<size_t>(_arguments.at(0)),
static_cast<size_t>(_arguments.at(1) & numeric_limits<size_t>::max()),
static_cast<size_t>(_arguments.at(2))
static_cast<size_t>(_evaluatedArguments.at(0)),
static_cast<size_t>(_evaluatedArguments.at(1) & numeric_limits<size_t>::max()),
static_cast<size_t>(_evaluatedArguments.at(2))
);
return 0;
}
@ -180,14 +193,14 @@ u256 EwasmBuiltinInterpreter::evalBuiltin(YulString _fun, vector<u256> const& _a
accessMemory(arg[0], 4);
return readMemoryHalfWord(arg[0]);
}
else if (_fun == "i32.clz"_yulstring)
else if (fun == "i32.clz")
// NOTE: the clz implementation assumes 64-bit inputs, hence the adjustment
return clz64(arg[0] & uint32_t(-1)) - 32;
else if (_fun == "i64.clz"_yulstring)
else if (fun == "i64.clz")
return clz64(arg[0]);
else if (_fun == "i32.ctz"_yulstring)
else if (fun == "i32.ctz")
return ctz32(uint32_t(arg[0] & uint32_t(-1)));
else if (_fun == "i64.ctz"_yulstring)
else if (fun == "i64.ctz")
return ctz64(arg[0]);
string prefix = fun;
@ -211,7 +224,7 @@ u256 EwasmBuiltinInterpreter::evalBuiltin(YulString _fun, vector<u256> const& _a
else if (prefix == "eth")
return evalEthBuiltin(suffix, arg);
yulAssert(false, "Unknown builtin: " + _fun.str() + " (or implementation did not return)");
yulAssert(false, "Unknown builtin: " + fun + " (or implementation did not return)");
return 0;
}

View File

@ -69,7 +69,11 @@ public:
m_state(_state)
{}
/// Evaluate builtin function
u256 evalBuiltin(YulString _fun, std::vector<u256> const& _arguments);
u256 evalBuiltin(
YulString _functionName,
std::vector<Expression> const& _arguments,
std::vector<u256> const& _evaluatedArguments
);
private:
template <typename Word>

View File

@ -261,14 +261,18 @@ void ExpressionEvaluator::operator()(Identifier const& _identifier)
void ExpressionEvaluator::operator()(FunctionCall const& _funCall)
{
evaluateArgs(_funCall.arguments);
vector<optional<LiteralKind>> const* literalArguments = nullptr;
if (BuiltinFunction const* builtin = m_dialect.builtin(_funCall.functionName.name))
if (!builtin->literalArguments.empty())
literalArguments = &builtin->literalArguments;
evaluateArgs(_funCall.arguments, literalArguments);
if (EVMDialect const* dialect = dynamic_cast<EVMDialect const*>(&m_dialect))
{
if (BuiltinFunctionForEVM const* fun = dialect->builtin(_funCall.functionName.name))
{
EVMInstructionInterpreter interpreter(m_state);
setValue(interpreter.evalBuiltin(*fun, values()));
setValue(interpreter.evalBuiltin(*fun, _funCall.arguments, values()));
return;
}
}
@ -276,7 +280,7 @@ void ExpressionEvaluator::operator()(FunctionCall const& _funCall)
if (dialect->builtin(_funCall.functionName.name))
{
EwasmBuiltinInterpreter interpreter(m_state);
setValue(interpreter.evalBuiltin(_funCall.functionName.name, values()));
setValue(interpreter.evalBuiltin(_funCall.functionName.name, _funCall.arguments, values()));
return;
}
@ -317,14 +321,22 @@ void ExpressionEvaluator::setValue(u256 _value)
m_values.emplace_back(std::move(_value));
}
void ExpressionEvaluator::evaluateArgs(vector<Expression> const& _expr)
void ExpressionEvaluator::evaluateArgs(
vector<Expression> const& _expr,
vector<optional<LiteralKind>> const* _literalArguments
)
{
vector<u256> values;
size_t i = 0;
/// Function arguments are evaluated in reverse.
for (auto const& expr: _expr | boost::adaptors::reversed)
{
visit(expr);
if (!_literalArguments || !_literalArguments->at(_expr.size() - i - 1))
visit(expr);
else
m_values = {0};
values.push_back(value());
++i;
}
m_values = std::move(values);
std::reverse(m_values.begin(), m_values.end());

View File

@ -21,7 +21,7 @@
#pragma once
#include <libyul/AsmDataForward.h>
#include <libyul/AsmData.h>
#include <libyul/optimiser/ASTWalker.h>
#include <libsolutil/FixedHash.h>
@ -197,7 +197,10 @@ private:
/// Evaluates the given expression from right to left and
/// stores it in m_value.
void evaluateArgs(std::vector<Expression> const& _expr);
void evaluateArgs(
std::vector<Expression> const& _expr,
std::vector<std::optional<LiteralKind>> const* _literalArguments
);
InterpreterState& m_state;
Dialect const& m_dialect;