Fix zero size memory expansion semantics in the yul interpreter.

This commit is contained in:
Bhargava Shastry 2022-09-12 11:00:14 +02:00
parent 0b4b1045cf
commit 2c27405cbb
3 changed files with 37 additions and 7 deletions

View File

@ -0,0 +1,10 @@
{
calldatacopy(32, 0, 0)
// Used to store 32
sstore(0, msize())
}
// ----
// Trace:
// CALLDATACOPY(32, 0, 0)
// Memory dump:
// Storage dump:

View File

@ -499,13 +499,15 @@ u256 EVMInstructionInterpreter::evalBuiltin(
bool EVMInstructionInterpreter::accessMemory(u256 const& _offset, u256 const& _size) bool EVMInstructionInterpreter::accessMemory(u256 const& _offset, u256 const& _size)
{ {
if (((_offset + _size) >= _offset) && ((_offset + _size + 0x1f) >= (_offset + _size))) if (_size == 0)
return true;
else if (((_offset + _size) >= _offset) && ((_offset + _size + 0x1f) >= (_offset + _size)))
{ {
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);
// We only record accesses to contiguous memory chunks that are at most 0xffff bytes // We only record accesses to contiguous memory chunks that are at most s_maxRangeSize bytes
// in size and at an offset of at most numeric_limits<size_t>::max() - 0xffff // in size and at an offset of at most numeric_limits<size_t>::max() - s_maxRangeSize
return _size <= 0xffff && _offset <= u256(numeric_limits<size_t>::max() - 0xffff); return _size <= s_maxRangeSize && _offset <= u256(numeric_limits<size_t>::max() - s_maxRangeSize);
} }
else else
m_state.msize = u256(-1); m_state.msize = u256(-1);
@ -513,6 +515,15 @@ bool EVMInstructionInterpreter::accessMemory(u256 const& _offset, u256 const& _s
return false; return false;
} }
bytes EVMInstructionInterpreter::readMemory(u256 const& _offset, u256 const& _size)
{
yulAssert(_size <= s_maxRangeSize, "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) u256 EVMInstructionInterpreter::readMemoryWord(u256 const& _offset)
{ {
return u256(h256(m_state.readMemory(_offset, 32))); return u256(h256(m_state.readMemory(_offset, 32)));

View File

@ -88,9 +88,15 @@ public:
); );
private: private:
/// Checks if the memory access is not too large for the interpreter and adjusts /// Checks if the memory access is valid and adjusts msize accordingly.
/// msize accordingly. /// @returns true if memory access is valid, false otherwise
/// @returns false if the amount of bytes read is lager than 0xffff /// A valid memory access must satisfy all of the following pre-requisites:
/// - Sum of @param _offset and @param _size do not overflow modulo u256
/// - Sum of @param _offset, @param _size, and 31 do not overflow modulo u256 (see note below)
/// - @param _size is lesser than or equal to @a s_maxRangeSize
/// - @param _offset is lesser than or equal to the difference of numeric_limits<size_t>::max()
/// and @a s_maxRangeSize
/// Note: Memory expansion is carried out in multiples of 32 bytes.
bool accessMemory(u256 const& _offset, u256 const& _size = 32); bool accessMemory(u256 const& _offset, u256 const& _size = 32);
/// @returns the memory contents at the provided address. /// @returns the memory contents at the provided address.
/// Does not adjust msize, use @a accessMemory for that /// Does not adjust msize, use @a accessMemory for that
@ -125,6 +131,9 @@ private:
InterpreterState& m_state; InterpreterState& m_state;
/// Flag to disable trace of instructions that write to memory. /// Flag to disable trace of instructions that write to memory.
bool m_disableMemoryWriteInstructions; bool m_disableMemoryWriteInstructions;
public:
/// Maximum length for range-based memory access operations.
static constexpr unsigned s_maxRangeSize = 0xffff;
}; };
} // solidity::yul::test } // solidity::yul::test