mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #7325 from ethereum/arbitraryMemoryForInterpreter
Change interpreter memory to be non-contiguous.
This commit is contained in:
commit
09b03fa07d
@ -121,7 +121,6 @@ string YulInterpreterTest::interpret()
|
|||||||
InterpreterState state;
|
InterpreterState state;
|
||||||
state.maxTraceSize = 10000;
|
state.maxTraceSize = 10000;
|
||||||
state.maxSteps = 10000;
|
state.maxSteps = 10000;
|
||||||
state.maxMemSize = 0x20000000;
|
|
||||||
Interpreter interpreter(state, EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion{}));
|
Interpreter interpreter(state, EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion{}));
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -25,14 +25,12 @@ void yulFuzzerUtil::interpret(
|
|||||||
shared_ptr<yul::Block> _ast,
|
shared_ptr<yul::Block> _ast,
|
||||||
Dialect const& _dialect,
|
Dialect const& _dialect,
|
||||||
size_t _maxSteps,
|
size_t _maxSteps,
|
||||||
size_t _maxTraceSize,
|
size_t _maxTraceSize
|
||||||
size_t _maxMemory
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
InterpreterState state;
|
InterpreterState state;
|
||||||
state.maxTraceSize = _maxTraceSize;
|
state.maxTraceSize = _maxTraceSize;
|
||||||
state.maxSteps = _maxSteps;
|
state.maxSteps = _maxSteps;
|
||||||
state.maxMemSize = _maxMemory;
|
|
||||||
Interpreter interpreter(state, _dialect);
|
Interpreter interpreter(state, _dialect);
|
||||||
interpreter(*_ast);
|
interpreter(*_ast);
|
||||||
state.dumpTraceAndState(_os);
|
state.dumpTraceAndState(_os);
|
||||||
|
@ -30,12 +30,10 @@ struct yulFuzzerUtil
|
|||||||
std::shared_ptr<yul::Block> _ast,
|
std::shared_ptr<yul::Block> _ast,
|
||||||
Dialect const& _dialect,
|
Dialect const& _dialect,
|
||||||
size_t _maxSteps = maxSteps,
|
size_t _maxSteps = maxSteps,
|
||||||
size_t _maxTraceSize = maxTraceSize,
|
size_t _maxTraceSize = maxTraceSize
|
||||||
size_t _maxMemory = maxMemory
|
|
||||||
);
|
);
|
||||||
static size_t constexpr maxSteps = 100;
|
static size_t constexpr maxSteps = 100;
|
||||||
static size_t constexpr maxTraceSize = 75;
|
static size_t constexpr maxTraceSize = 75;
|
||||||
static size_t constexpr maxMemory = 0x200;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,13 +63,11 @@ u256 readZeroExtended(bytes const& _data, u256 const& _offset)
|
|||||||
/// Copy @a _size bytes of @a _source at offset @a _sourceOffset to
|
/// Copy @a _size bytes of @a _source at offset @a _sourceOffset to
|
||||||
/// @a _target at offset @a _targetOffset. Behaves as if @a _source would
|
/// @a _target at offset @a _targetOffset. Behaves as if @a _source would
|
||||||
/// continue with an infinite sequence of zero bytes beyond its end.
|
/// continue with an infinite sequence of zero bytes beyond its end.
|
||||||
/// Asserts the target is large enough to hold the copied segment.
|
|
||||||
void copyZeroExtended(
|
void copyZeroExtended(
|
||||||
bytes& _target, bytes const& _source,
|
map<u256, uint8_t>& _target, bytes const& _source,
|
||||||
size_t _targetOffset, size_t _sourceOffset, size_t _size
|
size_t _targetOffset, size_t _sourceOffset, size_t _size
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
yulAssert(_targetOffset + _size <= _target.size(), "");
|
|
||||||
for (size_t i = 0; i < _size; ++i)
|
for (size_t i = 0; i < _size; ++i)
|
||||||
_target[_targetOffset + i] = _sourceOffset + i < _source.size() ? _source[_sourceOffset + i] : 0;
|
_target[_targetOffset + i] = _sourceOffset + i < _source.size() ? _source[_sourceOffset + i] : 0;
|
||||||
}
|
}
|
||||||
@ -176,7 +174,7 @@ u256 EVMInstructionInterpreter::eval(
|
|||||||
return u256("0x1234cafe1234cafe1234cafe") + arg[0];
|
return u256("0x1234cafe1234cafe1234cafe") + arg[0];
|
||||||
uint64_t offset = uint64_t(arg[0] & uint64_t(-1));
|
uint64_t offset = uint64_t(arg[0] & uint64_t(-1));
|
||||||
uint64_t size = uint64_t(arg[1] & uint64_t(-1));
|
uint64_t size = uint64_t(arg[1] & uint64_t(-1));
|
||||||
return u256(keccak256(bytesConstRef(m_state.memory.data() + offset, size)));
|
return u256(keccak256(readMemory(offset, size)));
|
||||||
}
|
}
|
||||||
case Instruction::ADDRESS:
|
case Instruction::ADDRESS:
|
||||||
return m_state.address;
|
return m_state.address;
|
||||||
@ -251,16 +249,16 @@ u256 EVMInstructionInterpreter::eval(
|
|||||||
// --------------- memory / storage / logs ---------------
|
// --------------- memory / storage / logs ---------------
|
||||||
case Instruction::MLOAD:
|
case Instruction::MLOAD:
|
||||||
if (accessMemory(arg[0], 0x20))
|
if (accessMemory(arg[0], 0x20))
|
||||||
return u256(*reinterpret_cast<h256 const*>(m_state.memory.data() + size_t(arg[0])));
|
return readMemoryWord(arg[0]);
|
||||||
else
|
else
|
||||||
return 0x1234 + arg[0];
|
return 0x1234 + arg[0];
|
||||||
case Instruction::MSTORE:
|
case Instruction::MSTORE:
|
||||||
if (accessMemory(arg[0], 0x20))
|
if (accessMemory(arg[0], 0x20))
|
||||||
*reinterpret_cast<h256*>(m_state.memory.data() + size_t(arg[0])) = h256(arg[1]);
|
writeMemoryWord(arg[0], arg[1]);
|
||||||
return 0;
|
return 0;
|
||||||
case Instruction::MSTORE8:
|
case Instruction::MSTORE8:
|
||||||
if (accessMemory(arg[0], 1))
|
if (accessMemory(arg[0], 1))
|
||||||
m_state.memory[size_t(arg[0])] = uint8_t(arg[1] & 0xff);
|
m_state.memory[arg[0]] = uint8_t(arg[1] & 0xff);
|
||||||
return 0;
|
return 0;
|
||||||
case Instruction::SLOAD:
|
case Instruction::SLOAD:
|
||||||
return m_state.storage[h256(arg[0])];
|
return m_state.storage[h256(arg[0])];
|
||||||
@ -319,7 +317,7 @@ u256 EVMInstructionInterpreter::eval(
|
|||||||
{
|
{
|
||||||
bytes data;
|
bytes data;
|
||||||
if (accessMemory(arg[0], arg[1]))
|
if (accessMemory(arg[0], arg[1]))
|
||||||
data = bytesConstRef(m_state.memory.data() + size_t(arg[0]), size_t(arg[1])).toBytes();
|
data = readMemory(arg[0], arg[1]);
|
||||||
logTrace(_instruction, arg, data);
|
logTrace(_instruction, arg, data);
|
||||||
throw ExplicitlyTerminated();
|
throw ExplicitlyTerminated();
|
||||||
}
|
}
|
||||||
@ -455,12 +453,7 @@ bool EVMInstructionInterpreter::accessMemory(u256 const& _offset, u256 const& _s
|
|||||||
{
|
{
|
||||||
u256 newSize = (_offset + _size + 0x1f) & ~u256(0x1f);
|
u256 newSize = (_offset + _size + 0x1f) & ~u256(0x1f);
|
||||||
m_state.msize = max(m_state.msize, newSize);
|
m_state.msize = max(m_state.msize, newSize);
|
||||||
if (newSize < m_state.maxMemSize)
|
return _size <= 0xffff;
|
||||||
{
|
|
||||||
if (m_state.memory.size() < newSize)
|
|
||||||
m_state.memory.resize(size_t(newSize));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
m_state.msize = u256(-1);
|
m_state.msize = u256(-1);
|
||||||
@ -468,6 +461,27 @@ bool EVMInstructionInterpreter::accessMemory(u256 const& _offset, u256 const& _s
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bytes EVMInstructionInterpreter::readMemory(u256 const& _offset, u256 const& _size)
|
||||||
|
{
|
||||||
|
yulAssert(_size <= 0xffff, "Too large read.");
|
||||||
|
bytes data(size_t(_size), uint8_t(0));
|
||||||
|
for (size_t i = 0; i < data.size(); ++i)
|
||||||
|
data[i] = m_state.memory[_offset + i];
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
u256 EVMInstructionInterpreter::readMemoryWord(u256 const& _offset)
|
||||||
|
{
|
||||||
|
return u256(h256(readMemory(_offset, 32)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void EVMInstructionInterpreter::writeMemoryWord(u256 const& _offset, u256 const& _value)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < 32; i++)
|
||||||
|
m_state.memory[_offset + i] = uint8_t((_value >> (8 * (31 - i))) & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void EVMInstructionInterpreter::logTrace(dev::eth::Instruction _instruction, std::vector<u256> const& _arguments, bytes const& _data)
|
void EVMInstructionInterpreter::logTrace(dev::eth::Instruction _instruction, std::vector<u256> const& _arguments, bytes const& _data)
|
||||||
{
|
{
|
||||||
logTrace(dev::eth::instructionInfo(_instruction).name, _arguments, _data);
|
logTrace(dev::eth::instructionInfo(_instruction).name, _arguments, _data);
|
||||||
|
@ -75,9 +75,19 @@ public:
|
|||||||
dev::u256 evalBuiltin(BuiltinFunctionForEVM const& _fun, std::vector<dev::u256> const& _arguments);
|
dev::u256 evalBuiltin(BuiltinFunctionForEVM const& _fun, std::vector<dev::u256> const& _arguments);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Resizes the memory to accommodate the memory access.
|
/// Checks if the memory access is not too large for the interpreter and adjusts
|
||||||
/// @returns false if memory would have to be expanded beyond m_state.maxMemSize.
|
/// msize accordingly.
|
||||||
|
/// @returns false if the amount of bytes read is lager than 0xffff
|
||||||
bool accessMemory(dev::u256 const& _offset, dev::u256 const& _size = 32);
|
bool accessMemory(dev::u256 const& _offset, dev::u256 const& _size = 32);
|
||||||
|
/// @returns the memory contents at the provided address.
|
||||||
|
/// Does not adjust msize, use @a accessMemory for that
|
||||||
|
dev::bytes readMemory(dev::u256 const& _offset, dev::u256 const& _size = 32);
|
||||||
|
/// @returns the memory contents at the provided address.
|
||||||
|
/// Does not adjust msize, use @a accessMemory for that
|
||||||
|
dev::u256 readMemoryWord(dev::u256 const& _offset);
|
||||||
|
/// @returns writes a word to memory
|
||||||
|
/// Does not adjust msize, use @a accessMemory for that
|
||||||
|
void writeMemoryWord(dev::u256 const& _offset, dev::u256 const& _value);
|
||||||
|
|
||||||
void logTrace(dev::eth::Instruction _instruction, std::vector<dev::u256> const& _arguments = {}, dev::bytes const& _data = {});
|
void logTrace(dev::eth::Instruction _instruction, std::vector<dev::u256> const& _arguments = {}, dev::bytes const& _data = {});
|
||||||
/// Appends a log to the trace representing an instruction or similar operation by string,
|
/// Appends a log to the trace representing an instruction or similar operation by string,
|
||||||
|
@ -47,13 +47,12 @@ void InterpreterState::dumpTraceAndState(ostream& _out) const
|
|||||||
for (auto const& line: trace)
|
for (auto const& line: trace)
|
||||||
_out << " " << line << endl;
|
_out << " " << line << endl;
|
||||||
_out << "Memory dump:\n";
|
_out << "Memory dump:\n";
|
||||||
for (size_t i = 0; i < memory.size(); i += 0x20)
|
map<u256, u256> words;
|
||||||
{
|
for (auto const& [offset, value]: memory)
|
||||||
bytesConstRef data(memory.data() + i, 0x20);
|
words[(offset / 0x20) * 0x20] |= u256(uint32_t(value)) << (256 - 8 - 8 * size_t(offset % 0x20));
|
||||||
if (boost::algorithm::all_of_equal(data, 0))
|
for (auto const& [offset, value]: words)
|
||||||
continue;
|
if (value != 0)
|
||||||
_out << " " << std::hex << std::setw(4) << i << ": " << toHex(data.toBytes()) << endl;
|
_out << " " << std::hex << std::setw(4) << offset << ": " << h256(value).hex() << endl;
|
||||||
}
|
|
||||||
_out << "Storage dump:" << endl;
|
_out << "Storage dump:" << endl;
|
||||||
for (auto const& slot: storage)
|
for (auto const& slot: storage)
|
||||||
if (slot.second != h256(0))
|
if (slot.second != h256(0))
|
||||||
|
@ -64,8 +64,7 @@ struct InterpreterState
|
|||||||
{
|
{
|
||||||
dev::bytes calldata;
|
dev::bytes calldata;
|
||||||
dev::bytes returndata;
|
dev::bytes returndata;
|
||||||
/// TODO turn this into "vector with holes" for the randomized testing
|
std::map<dev::u256, uint8_t> memory;
|
||||||
dev::bytes memory;
|
|
||||||
/// This is different than memory.size() because we ignore gas.
|
/// This is different than memory.size() because we ignore gas.
|
||||||
dev::u256 msize;
|
dev::u256 msize;
|
||||||
std::map<dev::h256, dev::h256> storage;
|
std::map<dev::h256, dev::h256> storage;
|
||||||
@ -86,9 +85,6 @@ struct InterpreterState
|
|||||||
std::vector<std::string> trace;
|
std::vector<std::string> trace;
|
||||||
/// This is actually an input parameter that more or less limits the runtime.
|
/// This is actually an input parameter that more or less limits the runtime.
|
||||||
size_t maxTraceSize = 0;
|
size_t maxTraceSize = 0;
|
||||||
/// Memory size limit. Anything beyond this will still work, but it has
|
|
||||||
/// deterministic yet not necessarily consistent behaviour.
|
|
||||||
size_t maxMemSize = 0x200;
|
|
||||||
size_t maxSteps = 0;
|
size_t maxSteps = 0;
|
||||||
size_t numSteps = 0;
|
size_t numSteps = 0;
|
||||||
LoopState loopState = LoopState::Default;
|
LoopState loopState = LoopState::Default;
|
||||||
|
@ -87,7 +87,6 @@ void interpret(string const& _source)
|
|||||||
|
|
||||||
InterpreterState state;
|
InterpreterState state;
|
||||||
state.maxTraceSize = 10000;
|
state.maxTraceSize = 10000;
|
||||||
state.maxMemSize = 0x20000000;
|
|
||||||
Dialect const& dialect(EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion{}));
|
Dialect const& dialect(EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion{}));
|
||||||
Interpreter interpreter(state, dialect);
|
Interpreter interpreter(state, dialect);
|
||||||
try
|
try
|
||||||
|
Loading…
Reference in New Issue
Block a user