Respect memory model for revert.

This commit is contained in:
chriseth 2021-03-15 16:45:00 +01:00
parent 850c25bf8e
commit 4c40c7b3b7
10 changed files with 92 additions and 82 deletions

View File

@ -205,7 +205,7 @@ string ABIFunctions::tupleDecoder(TypePointers const& _types, bool _fromMemory)
Whiskers templ(R"(
function <functionName>(headStart, dataEnd) <arrow> <valueReturnParams> {
if slt(sub(dataEnd, headStart), <minimumSize>) { <revertString> }
if slt(sub(dataEnd, headStart), <minimumSize>) { <revertString>() }
<decodeElements>
}
)");
@ -235,7 +235,7 @@ string ABIFunctions::tupleDecoder(TypePointers const& _types, bool _fromMemory)
{
<?dynamic>
let offset := <load>(add(headStart, <pos>))
if gt(offset, 0xffffffffffffffff) { <revertString> }
if gt(offset, 0xffffffffffffffff) { <revertString>() }
<!dynamic>
let offset := <pos>
</dynamic>
@ -487,7 +487,7 @@ string ABIFunctions::abiEncodingFunctionCalldataArrayWithoutCleanup(
else
templ("scaleLengthByStride",
Whiskers(R"(
if gt(length, <maxLength>) { <revertString> }
if gt(length, <maxLength>) { <revertString>() }
length := mul(length, <stride>)
)")
("stride", toCompactHexWithPrefix(fromArrayType.calldataStride()))
@ -1148,7 +1148,7 @@ string ABIFunctions::abiDecodingFunctionArray(ArrayType const& _type, bool _from
R"(
// <readableTypeName>
function <functionName>(offset, end) -> array {
if iszero(slt(add(offset, 0x1f), end)) { <revertString> }
if iszero(slt(add(offset, 0x1f), end)) { <revertString>() }
let length := <retrieveLength>
array := <abiDecodeAvailableLen>(<offset>, length, end)
}
@ -1188,7 +1188,7 @@ string ABIFunctions::abiDecodingFunctionArrayAvailableLength(ArrayType const& _t
</dynamic>
let src := offset
if gt(add(src, mul(length, <stride>)), end) {
<revertInvalidStride>
<revertInvalidStride>()
}
for { let i := 0 } lt(i, length) { i := add(i, 1) }
{
@ -1241,11 +1241,11 @@ string ABIFunctions::abiDecodingFunctionCalldataArray(ArrayType const& _type)
w = Whiskers(R"(
// <readableTypeName>
function <functionName>(offset, end) -> arrayPos, length {
if iszero(slt(add(offset, 0x1f), end)) { <revertStringOffset> }
if iszero(slt(add(offset, 0x1f), end)) { <revertStringOffset>() }
length := calldataload(offset)
if gt(length, 0xffffffffffffffff) { <revertStringLength> }
if gt(length, 0xffffffffffffffff) { <revertStringLength>() }
arrayPos := add(offset, 0x20)
if gt(add(arrayPos, mul(length, <stride>)), end) { <revertStringPos> }
if gt(add(arrayPos, mul(length, <stride>)), end) { <revertStringPos>() }
}
)");
w("revertStringOffset", revertReasonIfDebug("ABI decoding: invalid calldata array offset"));
@ -1257,7 +1257,7 @@ string ABIFunctions::abiDecodingFunctionCalldataArray(ArrayType const& _type)
// <readableTypeName>
function <functionName>(offset, end) -> arrayPos {
arrayPos := offset
if gt(add(arrayPos, mul(<length>, <stride>)), end) { <revertStringPos> }
if gt(add(arrayPos, mul(<length>, <stride>)), end) { <revertStringPos>() }
}
)");
w("length", toCompactHexWithPrefix(_type.length()));
@ -1288,7 +1288,7 @@ string ABIFunctions::abiDecodingFunctionByteArrayAvailableLength(ArrayType const
array := <allocate>(<allocationSize>(length))
mstore(array, length)
let dst := add(array, 0x20)
if gt(add(src, length), end) { <revertStringLength> }
if gt(add(src, length), end) { <revertStringLength>() }
<copyToMemFun>(src, dst, length)
}
)");
@ -1312,7 +1312,7 @@ string ABIFunctions::abiDecodingFunctionCalldataStruct(StructType const& _type)
Whiskers w{R"(
// <readableTypeName>
function <functionName>(offset, end) -> value {
if slt(sub(end, offset), <minimumSize>) { <revertString> }
if slt(sub(end, offset), <minimumSize>) { <revertString>() }
value := offset
}
)"};
@ -1337,7 +1337,7 @@ string ABIFunctions::abiDecodingFunctionStruct(StructType const& _type, bool _fr
Whiskers templ(R"(
// <readableTypeName>
function <functionName>(headStart, end) -> value {
if slt(sub(end, headStart), <minimumSize>) { <revertString> }
if slt(sub(end, headStart), <minimumSize>) { <revertString>() }
value := <allocate>(<memorySize>)
<#members>
{
@ -1365,7 +1365,7 @@ string ABIFunctions::abiDecodingFunctionStruct(StructType const& _type, bool _fr
Whiskers memberTempl(R"(
<?dynamic>
let offset := <load>(add(headStart, <pos>))
if gt(offset, 0xffffffffffffffff) { <revertString> }
if gt(offset, 0xffffffffffffffff) { <revertString>() }
<!dynamic>
let offset := <pos>
</dynamic>
@ -1441,7 +1441,7 @@ string ABIFunctions::calldataAccessFunction(Type const& _type)
Whiskers w(R"(
function <functionName>(base_ref, ptr) -> <return> {
let rel_offset_of_tail := calldataload(ptr)
if iszero(slt(rel_offset_of_tail, sub(sub(calldatasize(), base_ref), sub(<neededLength>, 1)))) { <revertStringOffset> }
if iszero(slt(rel_offset_of_tail, sub(sub(calldatasize(), base_ref), sub(<neededLength>, 1)))) { <revertStringOffset>() }
value := add(rel_offset_of_tail, base_ref)
<handleLength>
}
@ -1453,8 +1453,8 @@ string ABIFunctions::calldataAccessFunction(Type const& _type)
w("handleLength", Whiskers(R"(
length := calldataload(value)
value := add(value, 0x20)
if gt(length, 0xffffffffffffffff) { <revertStringLength> }
if sgt(base_ref, sub(calldatasize(), mul(length, <calldataStride>))) { <revertStringStride> }
if gt(length, 0xffffffffffffffff) { <revertStringLength>() }
if sgt(base_ref, sub(calldatasize(), mul(length, <calldataStride>))) { <revertStringStride>() }
)")
("calldataStride", toCompactHexWithPrefix(arrayType->calldataStride()))
// TODO add test
@ -1557,5 +1557,5 @@ size_t ABIFunctions::numVariablesForType(Type const& _type, EncodingOptions cons
std::string ABIFunctions::revertReasonIfDebug(std::string const& _message)
{
return YulUtilFunctions::revertReasonIfDebug(m_revertStrings, _message);
return m_utils.revertReasonIfDebug(_message);
}

View File

@ -273,7 +273,7 @@ private:
/// is true), for which it is two.
static size_t numVariablesForType(Type const& _type, EncodingOptions const& _options);
/// @returns code that stores @param _message for revert reason
/// @returns the name of a function that uses @param _message for revert reason
/// if m_revertStrings is debug.
std::string revertReasonIfDebug(std::string const& _message = "");

View File

@ -560,7 +560,11 @@ void CompilerContext::optimizeYul(yul::Object& _object, yul::EVMDialect const& _
string CompilerContext::revertReasonIfDebug(string const& _message)
{
return YulUtilFunctions::revertReasonIfDebug(m_revertStrings, _message);
return YulUtilFunctions::revertReasonIfDebugBody(
m_revertStrings,
_message,
"mload(" + to_string(CompilerUtils::freeMemoryPointer) + ")"
);
}
void CompilerContext::updateSourceLocation()

View File

@ -268,7 +268,7 @@ public:
);
/// If m_revertStrings is debug, @returns inline assembly code that
/// stores @param _message in memory position 0 and reverts.
/// stores @param _message at the free memory pointer and reverts.
/// Otherwise returns "revert(0, 0)".
std::string revertReasonIfDebug(std::string const& _message = "");

View File

@ -2220,8 +2220,8 @@ string YulUtilFunctions::calldataArrayIndexRangeAccess(ArrayType const& _type)
return m_functionCollector.createFunction(functionName, [&]() {
return Whiskers(R"(
function <functionName>(offset, length, startIndex, endIndex) -> offsetOut, lengthOut {
if gt(startIndex, endIndex) { <revertSliceStartAfterEnd> }
if gt(endIndex, length) { <revertSliceGreaterThanLength> }
if gt(startIndex, endIndex) { <revertSliceStartAfterEnd>() }
if gt(endIndex, length) { <revertSliceGreaterThanLength>() }
offsetOut := add(offset, mul(startIndex, <stride>))
lengthOut := sub(endIndex, startIndex)
}
@ -2243,13 +2243,13 @@ string YulUtilFunctions::accessCalldataTailFunction(Type const& _type)
return Whiskers(R"(
function <functionName>(base_ref, ptr_to_tail) -> addr<?dynamicallySized>, length</dynamicallySized> {
let rel_offset_of_tail := calldataload(ptr_to_tail)
if iszero(slt(rel_offset_of_tail, sub(sub(calldatasize(), base_ref), sub(<neededLength>, 1)))) { <invalidCalldataTailOffset> }
if iszero(slt(rel_offset_of_tail, sub(sub(calldatasize(), base_ref), sub(<neededLength>, 1)))) { <invalidCalldataTailOffset>() }
addr := add(base_ref, rel_offset_of_tail)
<?dynamicallySized>
length := calldataload(addr)
if gt(length, 0xffffffffffffffff) { <invalidCalldataTailLength> }
if gt(length, 0xffffffffffffffff) { <invalidCalldataTailLength>() }
addr := add(addr, 32)
if sgt(addr, sub(calldatasize(), mul(length, <calldataStride>))) { <shortCalldataTail> }
if sgt(addr, sub(calldatasize(), mul(length, <calldataStride>))) { <shortCalldataTail>() }
</dynamicallySized>
}
)")
@ -4210,42 +4210,52 @@ string YulUtilFunctions::readFromMemoryOrCalldata(Type const& _type, bool _fromC
});
}
string YulUtilFunctions::revertReasonIfDebug(RevertStrings revertStrings, string const& _message)
{
if (revertStrings >= RevertStrings::Debug && !_message.empty())
{
Whiskers templ(R"({
mstore(0, <sig>)
mstore(4, 0x20)
mstore(add(4, 0x20), <length>)
let reasonPos := add(4, 0x40)
<#word>
mstore(add(reasonPos, <offset>), <wordValue>)
</word>
revert(0, add(reasonPos, <end>))
})");
templ("sig", util::selectorFromSignature("Error(string)").str());
templ("length", to_string(_message.length()));
size_t words = (_message.length() + 31) / 32;
vector<map<string, string>> wordParams(words);
for (size_t i = 0; i < words; ++i)
{
wordParams[i]["offset"] = to_string(i * 32);
wordParams[i]["wordValue"] = formatAsStringOrNumber(_message.substr(32 * i, 32));
}
templ("word", wordParams);
templ("end", to_string(words * 32));
return templ.render();
}
else
return "revert(0, 0)";
}
string YulUtilFunctions::revertReasonIfDebug(string const& _message)
{
return revertReasonIfDebug(m_revertStrings, _message);
string functionName = "revert_error_" + util::toHex(util::keccak256(_message).asBytes());
return m_functionCollector.createFunction(functionName, [&](auto&, auto&) -> string {
return revertReasonIfDebugBody(m_revertStrings, allocateUnboundedFunction() + "()", _message);
});
}
string YulUtilFunctions::revertReasonIfDebugBody(
RevertStrings _revertStrings,
string const& _allocation,
string const& _message
)
{
if (_revertStrings < RevertStrings::Debug || _message.empty())
return "revert(0, 0)";
Whiskers templ(R"({
let start := <allocate>
let pos := start
mstore(pos, <sig>)
pos := add(pos, 4)
mstore(pos, 0x20)
pos := add(pos, 0x20)
mstore(pos, <length>)
pos := add(pos, 0x20)
<#word>
mstore(add(pos, <offset>), <wordValue>)
</word>
revert(start, <overallLength>)
})");
templ("allocate", _allocation);
templ("sig", util::selectorFromSignature("Error(string)").str());
templ("length", to_string(_message.length()));
size_t words = (_message.length() + 31) / 32;
vector<map<string, string>> wordParams(words);
for (size_t i = 0; i < words; ++i)
{
wordParams[i]["offset"] = to_string(i * 32);
wordParams[i]["wordValue"] = formatAsStringOrNumber(_message.substr(32 * i, 32));
}
templ("word", wordParams);
templ("overallLength", to_string(4 + 0x20 + 0x20 + words * 32));
return templ.render();
}
string YulUtilFunctions::panicFunction(util::PanicCode _code)

View File

@ -453,13 +453,19 @@ public:
/// signature: (slot, offset) ->
std::string storageSetToZeroFunction(Type const& _type);
/// If revertStrings is debug, @returns inline assembly code that
/// If revertStrings is debug, @returns the name of a function that
/// stores @param _message in memory position 0 and reverts.
/// Otherwise returns "revert(0, 0)".
static std::string revertReasonIfDebug(RevertStrings revertStrings, std::string const& _message = "");
/// Otherwise returns the name of a function that uses "revert(0, 0)".
std::string revertReasonIfDebug(std::string const& _message = "");
/// @returns the function body of ``revertReasonIfDebug``.
/// Should only be used internally and by the old code generator.
static std::string revertReasonIfDebugBody(
RevertStrings _revertStrings,
std::string const& _allocation,
std::string const& _message
);
/// Reverts with ``Panic(uint256)`` and the given code.
std::string panicFunction(util::PanicCode _code);

View File

@ -177,8 +177,3 @@ ABIFunctions IRGenerationContext::abiFunctions()
{
return ABIFunctions(m_evmVersion, m_revertStrings, m_functions);
}
std::string IRGenerationContext::revertReasonIfDebug(std::string const& _message)
{
return YulUtilFunctions::revertReasonIfDebug(m_revertStrings, _message);
}

View File

@ -143,10 +143,6 @@ public:
ABIFunctions abiFunctions();
/// @returns code that stores @param _message for revert reason
/// if m_revertStrings is debug.
std::string revertReasonIfDebug(std::string const& _message = "");
RevertStrings revertStrings() const { return m_revertStrings; }
std::set<ContractDefinition const*, ASTNode::CompareByID>& subObjectsCreated() { return m_subObjects; }

View File

@ -852,7 +852,7 @@ string IRGenerator::deployCode(ContractDefinition const& _contract)
string IRGenerator::callValueCheck()
{
return "if callvalue() { " + m_context.revertReasonIfDebug("Ether sent to non-payable function") + " }";
return "if callvalue() { " + m_utils.revertReasonIfDebug("Ether sent to non-payable function") + "() }";
}
string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
@ -898,8 +898,8 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
// we revert.
delegatecallCheck =
"if iszero(called_via_delegatecall) { " +
m_context.revertReasonIfDebug("Non-view function of library called without DELEGATECALL") +
" }";
m_utils.revertReasonIfDebug("Non-view function of library called without DELEGATECALL") +
"() }";
}
templ["delegatecallCheck"] = delegatecallCheck;
templ["callValueCheck"] = (type->isPayable() || _contract.isLibrary()) ? "" : callValueCheck();
@ -950,12 +950,11 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
t("fallback", fallbackCode);
}
else
t(
"fallback",
t("fallback", (
etherReceiver ?
m_context.revertReasonIfDebug("Unknown signature and no fallback defined") :
m_context.revertReasonIfDebug("Contract does not have fallback nor receive functions")
);
m_utils.revertReasonIfDebug("Unknown signature and no fallback defined") :
m_utils.revertReasonIfDebug("Contract does not have fallback nor receive functions")
) + "()");
return t.render();
}

View File

@ -2433,7 +2433,7 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
}
Whiskers templ(R"(
if iszero(extcodesize(<address>)) { <revertNoCode> }
if iszero(extcodesize(<address>)) { <revertNoCode>() }
// storage for arguments and returned data
let <pos> := <allocateUnbounded>()
@ -2458,7 +2458,7 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
<?+retVars> <retVars> := </+retVars> <abiDecode>(<pos>, add(<pos>, <returnSize>))
}
)");
templ("revertNoCode", m_context.revertReasonIfDebug("Target contract does not contain code"));
templ("revertNoCode", m_utils.revertReasonIfDebug("Target contract does not contain code"));
templ("pos", m_context.newYulVariable());
templ("end", m_context.newYulVariable());
if (_functionCall.annotation().tryCall)