Panic codes.

This commit is contained in:
chriseth 2020-10-12 16:01:45 +02:00
parent b559de11ee
commit bfd267459c
14 changed files with 156 additions and 79 deletions

View File

@ -29,6 +29,9 @@
#include <libsolidity/codegen/CompilerUtils.h> #include <libsolidity/codegen/CompilerUtils.h>
#include <libsolidity/codegen/LValue.h> #include <libsolidity/codegen/LValue.h>
#include <libsolutil/FunctionSelector.h>
#include <libsolutil/Whiskers.h>
#include <libevmasm/Instruction.h> #include <libevmasm/Instruction.h>
#include <liblangutil/Exceptions.h> #include <liblangutil/Exceptions.h>
@ -857,13 +860,17 @@ void ArrayUtils::popStorageArrayElement(ArrayType const& _type) const
if (_type.isByteArray()) if (_type.isByteArray())
{ {
m_context.appendInlineAssembly(R"({ util::Whiskers code(R"({
let slot_value := sload(ref) let slot_value := sload(ref)
switch and(slot_value, 1) switch and(slot_value, 1)
case 0 { case 0 {
// short byte array // short byte array
let length := and(div(slot_value, 2), 0x1f) let length := and(div(slot_value, 2), 0x1f)
if iszero(length) { invalid() } if iszero(length) {
mstore(0, <panicSelector>)
mstore(4, <emptyArrayPop>)
revert(0, 0x24)
}
// Zero-out the suffix including the least significant byte. // Zero-out the suffix including the least significant byte.
let mask := sub(exp(0x100, sub(33, length)), 1) let mask := sub(exp(0x100, sub(33, length)), 1)
@ -901,7 +908,10 @@ void ArrayUtils::popStorageArrayElement(ArrayType const& _type) const
sstore(ref, slot_value) sstore(ref, slot_value)
} }
} }
})", {"ref"}); })");
code("panicSelector", util::selectorFromSignature("Panic(uint256)").str());
code("emptyArrayPop", to_string(unsigned(util::PanicCode::EmptyArrayPop)));
m_context.appendInlineAssembly(code.render(), {"ref"});
m_context << Instruction::POP; m_context << Instruction::POP;
} }
else else
@ -912,7 +922,7 @@ void ArrayUtils::popStorageArrayElement(ArrayType const& _type) const
m_context << Instruction::DUP1; m_context << Instruction::DUP1;
// stack: ArrayReference oldLength oldLength // stack: ArrayReference oldLength oldLength
m_context << Instruction::ISZERO; m_context << Instruction::ISZERO;
m_context.appendConditionalInvalid(); m_context.appendConditionalPanic(util::PanicCode::EmptyArrayPop);
// Stack: ArrayReference oldLength // Stack: ArrayReference oldLength
m_context << u256(1) << Instruction::SWAP1 << Instruction::SUB; m_context << u256(1) << Instruction::SWAP1 << Instruction::SUB;
@ -1058,7 +1068,7 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType, bool _doBoundsCheck, b
// check out-of-bounds access // check out-of-bounds access
m_context << Instruction::DUP2 << Instruction::LT << Instruction::ISZERO; m_context << Instruction::DUP2 << Instruction::LT << Instruction::ISZERO;
// out-of-bounds access throws exception // out-of-bounds access throws exception
m_context.appendConditionalInvalid(); m_context.appendConditionalPanic(util::PanicCode::ArrayOutOfBounds);
} }
if (location == DataLocation::CallData && _arrayType.isDynamicallySized()) if (location == DataLocation::CallData && _arrayType.isDynamicallySized())
// remove length if present // remove length if present

View File

@ -72,7 +72,7 @@ public:
/// Stack pre: reference (excludes byte offset) /// Stack pre: reference (excludes byte offset)
/// Stack post: new_length /// Stack post: new_length
void incrementDynamicArraySize(ArrayType const& _type) const; void incrementDynamicArraySize(ArrayType const& _type) const;
/// Decrements the size of a dynamic array by one if length is nonzero. Causes an invalid instruction otherwise. /// Decrements the size of a dynamic array by one if length is nonzero. Causes a Panic otherwise.
/// Clears the removed data element. In case of a byte array, this might move the data. /// Clears the removed data element. In case of a byte array, this might move the data.
/// Stack pre: reference /// Stack pre: reference
/// Stack post: /// Stack post:

View File

@ -41,6 +41,7 @@
#include <libyul/Utilities.h> #include <libyul/Utilities.h>
#include <libsolutil/Whiskers.h> #include <libsolutil/Whiskers.h>
#include <libsolutil/FunctionSelector.h>
#include <liblangutil/ErrorReporter.h> #include <liblangutil/ErrorReporter.h>
#include <liblangutil/Scanner.h> #include <liblangutil/Scanner.h>
@ -330,16 +331,24 @@ CompilerContext& CompilerContext::appendJump(evmasm::AssemblyItem::JumpType _jum
return *this << item; return *this << item;
} }
CompilerContext& CompilerContext::appendInvalid() CompilerContext& CompilerContext::appendPanic(util::PanicCode _code)
{ {
return *this << Instruction::INVALID; Whiskers templ(R"({
mstore(0, <selector>)
mstore(4, <code>)
revert(0, 0x24)
})");
templ("selector", util::selectorFromSignature("Panic(uint256)").str());
templ("code", u256(_code).str());
appendInlineAssembly(templ.render());
return *this;
} }
CompilerContext& CompilerContext::appendConditionalInvalid() CompilerContext& CompilerContext::appendConditionalPanic(util::PanicCode _code)
{ {
*this << Instruction::ISZERO; *this << Instruction::ISZERO;
evmasm::AssemblyItem afterTag = appendConditionalJump(); evmasm::AssemblyItem afterTag = appendConditionalJump();
*this << Instruction::INVALID; appendPanic(_code);
*this << afterTag; *this << afterTag;
return *this; return *this;
} }

View File

@ -36,6 +36,7 @@
#include <liblangutil/ErrorReporter.h> #include <liblangutil/ErrorReporter.h>
#include <liblangutil/EVMVersion.h> #include <liblangutil/EVMVersion.h>
#include <libsolutil/Common.h> #include <libsolutil/Common.h>
#include <libsolutil/ErrorCodes.h>
#include <libyul/AsmAnalysisInfo.h> #include <libyul/AsmAnalysisInfo.h>
#include <libyul/backends/evm/EVMDialect.h> #include <libyul/backends/evm/EVMDialect.h>
@ -195,10 +196,10 @@ public:
evmasm::AssemblyItem appendJumpToNew() { return m_asm->appendJump().tag(); } evmasm::AssemblyItem appendJumpToNew() { return m_asm->appendJump().tag(); }
/// Appends a JUMP to a tag already on the stack /// Appends a JUMP to a tag already on the stack
CompilerContext& appendJump(evmasm::AssemblyItem::JumpType _jumpType = evmasm::AssemblyItem::JumpType::Ordinary); CompilerContext& appendJump(evmasm::AssemblyItem::JumpType _jumpType = evmasm::AssemblyItem::JumpType::Ordinary);
/// Appends an INVALID instruction /// Appends code to revert with a Panic(uint256) error.
CompilerContext& appendInvalid(); CompilerContext& appendPanic(util::PanicCode _code);
/// Appends a conditional INVALID instruction /// Appends code to revert with a Panic(uint256) error if the topmost stack element is nonzero.
CompilerContext& appendConditionalInvalid(); CompilerContext& appendConditionalPanic(util::PanicCode _code);
/// Appends a REVERT(0, 0) call /// Appends a REVERT(0, 0) call
/// @param _message is an optional revert message used in debug mode /// @param _message is an optional revert message used in debug mode
CompilerContext& appendRevert(std::string const& _message = ""); CompilerContext& appendRevert(std::string const& _message = "");

View File

@ -810,7 +810,7 @@ void CompilerUtils::convertType(
if (_asPartOfArgumentDecoding) if (_asPartOfArgumentDecoding)
m_context.appendConditionalRevert(false, "Enum out of range"); m_context.appendConditionalRevert(false, "Enum out of range");
else else
m_context.appendConditionalInvalid(); m_context.appendConditionalPanic(util::PanicCode::EnumConversionError);
enumOverflowCheckPending = false; enumOverflowCheckPending = false;
} }
break; break;
@ -849,7 +849,7 @@ void CompilerUtils::convertType(
EnumType const& enumType = dynamic_cast<decltype(enumType)>(_targetType); EnumType const& enumType = dynamic_cast<decltype(enumType)>(_targetType);
solAssert(enumType.numberOfMembers() > 0, "empty enum should have caused a parser error."); solAssert(enumType.numberOfMembers() > 0, "empty enum should have caused a parser error.");
m_context << u256(enumType.numberOfMembers() - 1) << Instruction::DUP2 << Instruction::GT; m_context << u256(enumType.numberOfMembers() - 1) << Instruction::DUP2 << Instruction::GT;
m_context.appendConditionalInvalid(); m_context.appendConditionalPanic(util::PanicCode::EnumConversionError);
enumOverflowCheckPending = false; enumOverflowCheckPending = false;
} }
else if (targetTypeCategory == Type::Category::FixedPoint) else if (targetTypeCategory == Type::Category::FixedPoint)
@ -1213,13 +1213,13 @@ void CompilerUtils::pushZeroValue(Type const& _type)
if (funType->kind() == FunctionType::Kind::Internal) if (funType->kind() == FunctionType::Kind::Internal)
{ {
m_context << m_context.lowLevelFunctionTag("$invalidFunction", 0, 0, [](CompilerContext& _context) { m_context << m_context.lowLevelFunctionTag("$invalidFunction", 0, 0, [](CompilerContext& _context) {
_context.appendInvalid(); _context.appendPanic(util::PanicCode::InvalidInternalFunction);
}); });
if (CompilerContext* runCon = m_context.runtimeContext()) if (CompilerContext* runCon = m_context.runtimeContext())
{ {
leftShiftNumberOnStack(32); leftShiftNumberOnStack(32);
m_context << runCon->lowLevelFunctionTag("$invalidFunction", 0, 0, [](CompilerContext& _context) { m_context << runCon->lowLevelFunctionTag("$invalidFunction", 0, 0, [](CompilerContext& _context) {
_context.appendInvalid(); _context.appendPanic(util::PanicCode::InvalidInternalFunction);
}).toSubAssemblyTag(m_context.runtimeSub()); }).toSubAssemblyTag(m_context.runtimeSub());
m_context << Instruction::OR; m_context << Instruction::OR;
} }

View File

@ -47,6 +47,7 @@
#include <liblangutil/ErrorReporter.h> #include <liblangutil/ErrorReporter.h>
#include <libsolutil/Whiskers.h> #include <libsolutil/Whiskers.h>
#include <libsolutil/FunctionSelector.h>
#include <boost/range/adaptor/reversed.hpp> #include <boost/range/adaptor/reversed.hpp>
@ -229,19 +230,26 @@ size_t ContractCompiler::deployLibrary(ContractDefinition const& _contract)
m_context.pushSubroutineSize(m_context.runtimeSub()); m_context.pushSubroutineSize(m_context.runtimeSub());
m_context.pushSubroutineOffset(m_context.runtimeSub()); m_context.pushSubroutineOffset(m_context.runtimeSub());
// This code replaces the address added by appendDeployTimeAddress(). // This code replaces the address added by appendDeployTimeAddress().
m_context.appendInlineAssembly(R"( m_context.appendInlineAssembly(
{ Whiskers(R"(
// If code starts at 11, an mstore(0) writes to the full PUSH20 plus data {
// without the need for a shift. // If code starts at 11, an mstore(0) writes to the full PUSH20 plus data
let codepos := 11 // without the need for a shift.
codecopy(codepos, subOffset, subSize) let codepos := 11
// Check that the first opcode is a PUSH20 codecopy(codepos, subOffset, subSize)
if iszero(eq(0x73, byte(0, mload(codepos)))) { invalid() } // Check that the first opcode is a PUSH20
mstore(0, address()) if iszero(eq(0x73, byte(0, mload(codepos)))) {
mstore8(codepos, 0x73) mstore(0, <panicSig>)
return(codepos, subSize) mstore(4, 0)
} revert(0, 0x24)
)", {"subSize", "subOffset"}); }
mstore(0, address())
mstore8(codepos, 0x73)
return(codepos, subSize)
}
)")("panicSig", util::selectorFromSignature("Panic(uint256)").str()).render(),
{"subSize", "subOffset"}
);
return m_context.runtimeSub(); return m_context.runtimeSub();
} }

View File

@ -933,7 +933,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
{ {
acceptAndConvert(*arguments[2], *TypeProvider::uint256()); acceptAndConvert(*arguments[2], *TypeProvider::uint256());
m_context << Instruction::DUP1 << Instruction::ISZERO; m_context << Instruction::DUP1 << Instruction::ISZERO;
m_context.appendConditionalInvalid(); m_context.appendConditionalPanic(util::PanicCode::DivisionByZero);
for (unsigned i = 1; i < 3; i ++) for (unsigned i = 1; i < 3; i ++)
acceptAndConvert(*arguments[2 - i], *TypeProvider::uint256()); acceptAndConvert(*arguments[2 - i], *TypeProvider::uint256());
if (function.kind() == FunctionType::Kind::AddMod) if (function.kind() == FunctionType::Kind::AddMod)
@ -1051,7 +1051,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
m_context << u256(0xffffffffffffffff); m_context << u256(0xffffffffffffffff);
m_context << Instruction::DUP2; m_context << Instruction::DUP2;
m_context << Instruction::GT; m_context << Instruction::GT;
m_context.appendConditionalRevert(); m_context.appendConditionalPanic(PanicCode::ResourceError);
// Stack: requested_length // Stack: requested_length
utils().fetchFreeMemoryPointer(); utils().fetchFreeMemoryPointer();
@ -1121,7 +1121,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
auto success = m_context.appendConditionalJump(); auto success = m_context.appendConditionalJump();
if (function.kind() == FunctionType::Kind::Assert) if (function.kind() == FunctionType::Kind::Assert)
// condition was not met, flag an error // condition was not met, flag an error
m_context.appendInvalid(); m_context.appendPanic(util::PanicCode::Assert);
else if (haveReasonString) else if (haveReasonString)
{ {
utils().revertWithStringData(*arguments.at(1)->annotation().type); utils().revertWithStringData(*arguments.at(1)->annotation().type);
@ -1886,7 +1886,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
m_context << u256(fixedBytesType.numBytes()); m_context << u256(fixedBytesType.numBytes());
m_context << Instruction::DUP2 << Instruction::LT << Instruction::ISZERO; m_context << Instruction::DUP2 << Instruction::LT << Instruction::ISZERO;
// out-of-bounds access throws exception // out-of-bounds access throws exception
m_context.appendConditionalInvalid(); m_context.appendConditionalPanic(util::PanicCode::ArrayOutOfBounds);
m_context << Instruction::BYTE; m_context << Instruction::BYTE;
utils().leftShiftNumberOnStack(256 - 8); utils().leftShiftNumberOnStack(256 - 8);
@ -2154,7 +2154,7 @@ void ExpressionCompiler::appendArithmeticOperatorCode(Token _operator, Type cons
{ {
// Test for division by zero // Test for division by zero
m_context << Instruction::DUP2 << Instruction::ISZERO; m_context << Instruction::DUP2 << Instruction::ISZERO;
m_context.appendConditionalInvalid(); m_context.appendConditionalPanic(util::PanicCode::DivisionByZero);
if (_operator == Token::Div) if (_operator == Token::Div)
m_context << (c_isSigned ? Instruction::SDIV : Instruction::DIV); m_context << (c_isSigned ? Instruction::SDIV : Instruction::DIV);

View File

@ -122,7 +122,7 @@ string YulUtilFunctions::requireOrAssertFunction(bool _assert, Type const* _mess
if iszero(condition) { <error> } if iszero(condition) { <error> }
} }
)") )")
("error", _assert ? panicFunction() + "()" : "revert(0, 0)") ("error", _assert ? panicFunction(PanicCode::Assert) + "()" : "revert(0, 0)")
("functionName", functionName) ("functionName", functionName)
.render(); .render();
@ -478,7 +478,7 @@ string YulUtilFunctions::overflowCheckedIntAddFunction(IntegerType const& _type)
("maxValue", toCompactHexWithPrefix(u256(_type.maxValue()))) ("maxValue", toCompactHexWithPrefix(u256(_type.maxValue())))
("minValue", toCompactHexWithPrefix(u256(_type.minValue()))) ("minValue", toCompactHexWithPrefix(u256(_type.minValue())))
("cleanupFunction", cleanupFunction(_type)) ("cleanupFunction", cleanupFunction(_type))
("panic", panicFunction()) ("panic", panicFunction(PanicCode::UnderOverflow))
.render(); .render();
}); });
} }
@ -530,7 +530,7 @@ string YulUtilFunctions::overflowCheckedIntMulFunction(IntegerType const& _type)
("maxValue", toCompactHexWithPrefix(u256(_type.maxValue()))) ("maxValue", toCompactHexWithPrefix(u256(_type.maxValue())))
("minValue", toCompactHexWithPrefix(u256(_type.minValue()))) ("minValue", toCompactHexWithPrefix(u256(_type.minValue())))
("cleanupFunction", cleanupFunction(_type)) ("cleanupFunction", cleanupFunction(_type))
("panic", panicFunction()) ("panic", panicFunction(PanicCode::UnderOverflow))
.render(); .render();
}); });
} }
@ -560,13 +560,13 @@ string YulUtilFunctions::overflowCheckedIntDivFunction(IntegerType const& _type)
function <functionName>(x, y) -> r { function <functionName>(x, y) -> r {
x := <cleanupFunction>(x) x := <cleanupFunction>(x)
y := <cleanupFunction>(y) y := <cleanupFunction>(y)
if iszero(y) { <panic>() } if iszero(y) { <panicDivZero>() }
<?signed> <?signed>
// overflow for minVal / -1 // overflow for minVal / -1
if and( if and(
eq(x, <minVal>), eq(x, <minVal>),
eq(y, sub(0, 1)) eq(y, sub(0, 1))
) { <panic>() } ) { <panicOverflow>() }
</signed> </signed>
r := <?signed>s</signed>div(x, y) r := <?signed>s</signed>div(x, y)
} }
@ -575,7 +575,8 @@ string YulUtilFunctions::overflowCheckedIntDivFunction(IntegerType const& _type)
("signed", _type.isSigned()) ("signed", _type.isSigned())
("minVal", toCompactHexWithPrefix(u256(_type.minValue()))) ("minVal", toCompactHexWithPrefix(u256(_type.minValue())))
("cleanupFunction", cleanupFunction(_type)) ("cleanupFunction", cleanupFunction(_type))
("panic", panicFunction()) ("panicDivZero", panicFunction(PanicCode::DivisionByZero))
("panicOverflow", panicFunction(PanicCode::UnderOverflow))
.render(); .render();
}); });
} }
@ -596,7 +597,7 @@ string YulUtilFunctions::wrappingIntDivFunction(IntegerType const& _type)
("functionName", functionName) ("functionName", functionName)
("cleanupFunction", cleanupFunction(_type)) ("cleanupFunction", cleanupFunction(_type))
("signed", _type.isSigned()) ("signed", _type.isSigned())
("error", panicFunction()) ("error", panicFunction(PanicCode::DivisionByZero))
.render(); .render();
}); });
} }
@ -617,7 +618,7 @@ string YulUtilFunctions::intModFunction(IntegerType const& _type)
("functionName", functionName) ("functionName", functionName)
("signed", _type.isSigned()) ("signed", _type.isSigned())
("cleanupFunction", cleanupFunction(_type)) ("cleanupFunction", cleanupFunction(_type))
("panic", panicFunction()) ("panic", panicFunction(PanicCode::DivisionByZero))
.render(); .render();
}); });
} }
@ -647,7 +648,7 @@ string YulUtilFunctions::overflowCheckedIntSubFunction(IntegerType const& _type)
("maxValue", toCompactHexWithPrefix(u256(_type.maxValue()))) ("maxValue", toCompactHexWithPrefix(u256(_type.maxValue())))
("minValue", toCompactHexWithPrefix(u256(_type.minValue()))) ("minValue", toCompactHexWithPrefix(u256(_type.minValue())))
("cleanupFunction", cleanupFunction(_type)) ("cleanupFunction", cleanupFunction(_type))
("panic", panicFunction()) ("panic", panicFunction(PanicCode::UnderOverflow))
.render(); .render();
}); });
} }
@ -808,7 +809,7 @@ string YulUtilFunctions::overflowCheckedIntLiteralExpFunction(
("exponentCleanupFunction", cleanupFunction(_exponentType)) ("exponentCleanupFunction", cleanupFunction(_exponentType))
("needsOverflowCheck", needsOverflowCheck) ("needsOverflowCheck", needsOverflowCheck)
("exponentUpperbound", to_string(exponentUpperbound)) ("exponentUpperbound", to_string(exponentUpperbound))
("panic", panicFunction()) ("panic", panicFunction(PanicCode::UnderOverflow))
("base", bigint2u(baseValue).str()) ("base", bigint2u(baseValue).str())
.render(); .render();
}); });
@ -866,7 +867,7 @@ string YulUtilFunctions::overflowCheckedUnsignedExpFunction()
} }
)") )")
("functionName", functionName) ("functionName", functionName)
("panic", panicFunction()) ("panic", panicFunction(PanicCode::UnderOverflow))
("expLoop", overflowCheckedExpLoopFunction()) ("expLoop", overflowCheckedExpLoopFunction())
("shr_1", shiftRightFunction(1)) ("shr_1", shiftRightFunction(1))
.render(); .render();
@ -916,7 +917,7 @@ string YulUtilFunctions::overflowCheckedSignedExpFunction()
} }
)") )")
("functionName", functionName) ("functionName", functionName)
("panic", panicFunction()) ("panic", panicFunction(PanicCode::UnderOverflow))
("expLoop", overflowCheckedExpLoopFunction()) ("expLoop", overflowCheckedExpLoopFunction())
("shr_1", shiftRightFunction(1)) ("shr_1", shiftRightFunction(1))
.render(); .render();
@ -957,7 +958,7 @@ string YulUtilFunctions::overflowCheckedExpLoopFunction()
} }
)") )")
("functionName", functionName) ("functionName", functionName)
("panic", panicFunction()) ("panic", panicFunction(PanicCode::UnderOverflow))
("shr_1", shiftRightFunction(1)) ("shr_1", shiftRightFunction(1))
.render(); .render();
}); });
@ -1087,7 +1088,7 @@ std::string YulUtilFunctions::resizeDynamicArrayFunction(ArrayType const& _type)
} }
})") })")
("functionName", functionName) ("functionName", functionName)
("panic", panicFunction()) ("panic", panicFunction(PanicCode::ResourceError))
("fetchLength", arrayLengthFunction(_type)) ("fetchLength", arrayLengthFunction(_type))
("convertToSize", arrayConvertLengthToSize(_type)) ("convertToSize", arrayConvertLengthToSize(_type))
("dataPosition", arrayDataAreaFunction(_type)) ("dataPosition", arrayDataAreaFunction(_type))
@ -1123,7 +1124,7 @@ string YulUtilFunctions::resizeDynamicByteArrayFunction(ArrayType const& _type)
} }
})") })")
("functionName", functionName) ("functionName", functionName)
("panic", panicFunction()) ("panic", panicFunction(PanicCode::ResourceError))
("extractLength", extractByteArrayLengthFunction()) ("extractLength", extractByteArrayLengthFunction())
("maxArrayLength", (u256(1) << 64).str()) ("maxArrayLength", (u256(1) << 64).str())
("decreaseSize", decreaseByteArraySizeFunction(_type)) ("decreaseSize", decreaseByteArraySizeFunction(_type))
@ -1266,7 +1267,7 @@ string YulUtilFunctions::storageArrayPopFunction(ArrayType const& _type)
sstore(array, newLen) sstore(array, newLen)
})") })")
("functionName", functionName) ("functionName", functionName)
("panic", panicFunction()) ("panic", panicFunction(PanicCode::EmptyArrayPop))
("fetchLength", arrayLengthFunction(_type)) ("fetchLength", arrayLengthFunction(_type))
("indexAccess", storageArrayIndexAccessFunction(_type)) ("indexAccess", storageArrayIndexAccessFunction(_type))
("setToZero", storageSetToZeroFunction(*_type.baseType())) ("setToZero", storageSetToZeroFunction(*_type.baseType()))
@ -1308,7 +1309,7 @@ string YulUtilFunctions::storageByteArrayPopFunction(ArrayType const& _type)
} }
})") })")
("functionName", functionName) ("functionName", functionName)
("panic", panicFunction()) ("panic", panicFunction(PanicCode::EmptyArrayPop))
("extractByteArrayLength", extractByteArrayLengthFunction()) ("extractByteArrayLength", extractByteArrayLengthFunction())
("transitLongToShort", byteArrayTransitLongToShortFunction(_type)) ("transitLongToShort", byteArrayTransitLongToShortFunction(_type))
("encodeUsedSetLen", shortByteArrayEncodeUsedAreaSetLengthFunction()) ("encodeUsedSetLen", shortByteArrayEncodeUsedAreaSetLengthFunction())
@ -1370,7 +1371,7 @@ string YulUtilFunctions::storageArrayPushFunction(ArrayType const& _type)
</isByteArray> </isByteArray>
})") })")
("functionName", functionName) ("functionName", functionName)
("panic", panicFunction()) ("panic", panicFunction(PanicCode::ResourceError))
("extractByteArrayLength", _type.isByteArray() ? extractByteArrayLengthFunction() : "") ("extractByteArrayLength", _type.isByteArray() ? extractByteArrayLengthFunction() : "")
("dataAreaFunction", arrayDataAreaFunction(_type)) ("dataAreaFunction", arrayDataAreaFunction(_type))
("isByteArray", _type.isByteArray()) ("isByteArray", _type.isByteArray())
@ -1400,7 +1401,7 @@ string YulUtilFunctions::storageArrayPushZeroFunction(ArrayType const& _type)
slot, offset := <indexAccess>(array, oldLen) slot, offset := <indexAccess>(array, oldLen)
})") })")
("functionName", functionName) ("functionName", functionName)
("panic", panicFunction()) ("panic", panicFunction(PanicCode::ResourceError))
("fetchLength", arrayLengthFunction(_type)) ("fetchLength", arrayLengthFunction(_type))
("indexAccess", storageArrayIndexAccessFunction(_type)) ("indexAccess", storageArrayIndexAccessFunction(_type))
("maxArrayLength", (u256(1) << 64).str()) ("maxArrayLength", (u256(1) << 64).str())
@ -1715,7 +1716,7 @@ string YulUtilFunctions::arrayAllocationSizeFunction(ArrayType const& _type)
} }
)"); )");
w("functionName", functionName); w("functionName", functionName);
w("panic", panicFunction()); w("panic", panicFunction(PanicCode::ResourceError));
w("byteArray", _type.isByteArray()); w("byteArray", _type.isByteArray());
w("dynamic", _type.isDynamicallySized()); w("dynamic", _type.isDynamicallySized());
return w.render(); return w.render();
@ -1786,7 +1787,7 @@ string YulUtilFunctions::storageArrayIndexAccessFunction(ArrayType const& _type)
} }
)") )")
("functionName", functionName) ("functionName", functionName)
("panic", panicFunction()) ("panic", panicFunction(PanicCode::ArrayOutOfBounds))
("arrayLen", arrayLengthFunction(_type)) ("arrayLen", arrayLengthFunction(_type))
("dataAreaFunc", arrayDataAreaFunction(_type)) ("dataAreaFunc", arrayDataAreaFunction(_type))
("multipleItemsPerSlot", _type.baseType()->storageBytes() <= 16) ("multipleItemsPerSlot", _type.baseType()->storageBytes() <= 16)
@ -1816,7 +1817,7 @@ string YulUtilFunctions::memoryArrayIndexAccessFunction(ArrayType const& _type)
} }
)") )")
("functionName", functionName) ("functionName", functionName)
("panic", panicFunction()) ("panic", panicFunction(PanicCode::ArrayOutOfBounds))
("arrayLen", arrayLengthFunction(_type)) ("arrayLen", arrayLengthFunction(_type))
("stride", to_string(_type.memoryStride())) ("stride", to_string(_type.memoryStride()))
("dynamicallySized", _type.isDynamicallySized()) ("dynamicallySized", _type.isDynamicallySized())
@ -1839,7 +1840,7 @@ string YulUtilFunctions::calldataArrayIndexAccessFunction(ArrayType const& _type
} }
)") )")
("functionName", functionName) ("functionName", functionName)
("panic", panicFunction()) ("panic", panicFunction(PanicCode::ArrayOutOfBounds))
("stride", to_string(_type.calldataStride())) ("stride", to_string(_type.calldataStride()))
("dynamicallySized", _type.isDynamicallySized()) ("dynamicallySized", _type.isDynamicallySized())
("dynamicallyEncodedBase", _type.baseType()->isDynamicallyEncoded()) ("dynamicallyEncodedBase", _type.baseType()->isDynamicallyEncoded())
@ -2509,7 +2510,7 @@ string YulUtilFunctions::allocationFunction()
)") )")
("functionName", functionName) ("functionName", functionName)
("freeMemoryPointer", to_string(CompilerUtils::freeMemoryPointer)) ("freeMemoryPointer", to_string(CompilerUtils::freeMemoryPointer))
("panic", panicFunction()) ("panic", panicFunction(PanicCode::ResourceError))
.render(); .render();
}); });
} }
@ -3085,10 +3086,7 @@ string YulUtilFunctions::validatorFunction(Type const& _type, bool _revertOnFail
} }
)"); )");
templ("functionName", functionName); templ("functionName", functionName);
if (_revertOnFailure) PanicCode panicCode = PanicCode::Generic;
templ("failure", "revert(0, 0)");
else
templ("failure", panicFunction() + "()");
switch (_type.category()) switch (_type.category())
{ {
@ -3111,6 +3109,7 @@ string YulUtilFunctions::validatorFunction(Type const& _type, bool _revertOnFail
{ {
size_t members = dynamic_cast<EnumType const&>(_type).numberOfMembers(); size_t members = dynamic_cast<EnumType const&>(_type).numberOfMembers();
solAssert(members > 0, "empty enum should have caused a parser error."); solAssert(members > 0, "empty enum should have caused a parser error.");
panicCode = PanicCode::EnumConversionError;
templ("condition", "lt(value, " + to_string(members) + ")"); templ("condition", "lt(value, " + to_string(members) + ")");
break; break;
} }
@ -3121,6 +3120,11 @@ string YulUtilFunctions::validatorFunction(Type const& _type, bool _revertOnFail
solAssert(false, "Validation of type " + _type.identifier() + " requested."); solAssert(false, "Validation of type " + _type.identifier() + " requested.");
} }
if (_revertOnFailure)
templ("failure", "revert(0, 0)");
else
templ("failure", panicFunction(panicCode) + "()");
return templ.render(); return templ.render();
}); });
} }
@ -3196,7 +3200,7 @@ std::string YulUtilFunctions::decrementCheckedFunction(Type const& _type)
} }
)") )")
("functionName", functionName) ("functionName", functionName)
("panic", panicFunction()) ("panic", panicFunction(PanicCode::UnderOverflow))
("minval", toCompactHexWithPrefix(type.min())) ("minval", toCompactHexWithPrefix(type.min()))
("cleanupFunction", cleanupFunction(_type)) ("cleanupFunction", cleanupFunction(_type))
.render(); .render();
@ -3237,7 +3241,7 @@ std::string YulUtilFunctions::incrementCheckedFunction(Type const& _type)
)") )")
("functionName", functionName) ("functionName", functionName)
("maxval", toCompactHexWithPrefix(type.max())) ("maxval", toCompactHexWithPrefix(type.max()))
("panic", panicFunction()) ("panic", panicFunction(PanicCode::UnderOverflow))
("cleanupFunction", cleanupFunction(_type)) ("cleanupFunction", cleanupFunction(_type))
.render(); .render();
}); });
@ -3278,7 +3282,7 @@ string YulUtilFunctions::negateNumberCheckedFunction(Type const& _type)
("functionName", functionName) ("functionName", functionName)
("minval", toCompactHexWithPrefix(type.min())) ("minval", toCompactHexWithPrefix(type.min()))
("cleanupFunction", cleanupFunction(_type)) ("cleanupFunction", cleanupFunction(_type))
("panic", panicFunction()) ("panic", panicFunction(PanicCode::UnderOverflow))
.render(); .render();
}); });
} }
@ -3400,7 +3404,7 @@ string YulUtilFunctions::storageSetToZeroFunction(Type const& _type)
)") )")
("functionName", functionName) ("functionName", functionName)
("clearArray", clearStorageArrayFunction(dynamic_cast<ArrayType const&>(_type))) ("clearArray", clearStorageArrayFunction(dynamic_cast<ArrayType const&>(_type)))
("panic", panicFunction()) ("panic", panicFunction(PanicCode::Generic))
.render(); .render();
else if (_type.category() == Type::Category::Struct) else if (_type.category() == Type::Category::Struct)
return Whiskers(R"( return Whiskers(R"(
@ -3411,7 +3415,7 @@ string YulUtilFunctions::storageSetToZeroFunction(Type const& _type)
)") )")
("functionName", functionName) ("functionName", functionName)
("clearStruct", clearStorageStructFunction(dynamic_cast<StructType const&>(_type))) ("clearStruct", clearStorageStructFunction(dynamic_cast<StructType const&>(_type)))
("panic", panicFunction()) ("panic", panicFunction(PanicCode::Generic))
.render(); .render();
else else
solUnimplemented("setToZero for type " + _type.identifier() + " not yet implemented!"); solUnimplemented("setToZero for type " + _type.identifier() + " not yet implemented!");
@ -3645,16 +3649,20 @@ string YulUtilFunctions::revertReasonIfDebug(string const& _message)
return revertReasonIfDebug(m_revertStrings, _message); return revertReasonIfDebug(m_revertStrings, _message);
} }
string YulUtilFunctions::panicFunction() string YulUtilFunctions::panicFunction(util::PanicCode _code)
{ {
string functionName = "panic_error"; string functionName = "panic_error_" + toCompactHexWithPrefix(uint64_t(_code));
return m_functionCollector.createFunction(functionName, [&]() { return m_functionCollector.createFunction(functionName, [&]() {
return Whiskers(R"( return Whiskers(R"(
function <functionName>() { function <functionName>() {
invalid() mstore(0, <selector>)
mstore(4, <code>)
revert(0, 0x24)
} }
)") )")
("functionName", functionName) ("functionName", functionName)
("selector", util::selectorFromSignature("Panic(uint256)").str())
("code", toCompactHexWithPrefix(_code))
.render(); .render();
}); });
} }

View File

@ -28,6 +28,8 @@
#include <libsolidity/interface/DebugSettings.h> #include <libsolidity/interface/DebugSettings.h>
#include <libsolutil/ErrorCodes.h>
#include <memory> #include <memory>
#include <string> #include <string>
#include <vector> #include <vector>
@ -406,9 +408,8 @@ public:
std::string revertReasonIfDebug(std::string const& _message = ""); std::string revertReasonIfDebug(std::string const& _message = "");
/// Executes the invalid opcode. /// Reverts with ``Panic(uint256)`` and the given code.
/// Might use revert with special error code in the future. std::string panicFunction(util::PanicCode _code);
std::string panicFunction();
/// Returns the name of a function that decodes an error message. /// Returns the name of a function that decodes an error message.
/// signature: () -> arrayPtr /// signature: () -> arrayPtr

View File

@ -208,7 +208,7 @@ InternalDispatchMap IRGenerator::generateInternalDispatchFunctions()
} }
)"); )");
templ("functionName", funName); templ("functionName", funName);
templ("panic", m_utils.panicFunction()); templ("panic", m_utils.panicFunction(PanicCode::InvalidInternalFunction));
templ("in", suffixedVariableNameList("in_", 0, arity.in)); templ("in", suffixedVariableNameList("in_", 0, arity.in));
templ("out", suffixedVariableNameList("out_", 0, arity.out)); templ("out", suffixedVariableNameList("out_", 0, arity.out));

View File

@ -1302,7 +1302,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
define(modulus, *arguments[2]); define(modulus, *arguments[2]);
Whiskers templ("if iszero(<modulus>) { <panic>() }\n"); Whiskers templ("if iszero(<modulus>) { <panic>() }\n");
templ("modulus", modulus.name()); templ("modulus", modulus.name());
templ("panic", m_utils.panicFunction()); templ("panic", m_utils.panicFunction(PanicCode::DivisionByZero));
m_code << templ.render(); m_code << templ.render();
string args; string args;
@ -1367,7 +1367,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
t("allocateTemporaryMemory", m_utils.allocationTemporaryMemoryFunction()); t("allocateTemporaryMemory", m_utils.allocationTemporaryMemoryFunction());
t("releaseTemporaryMemory", m_utils.releaseTemporaryMemoryFunction()); t("releaseTemporaryMemory", m_utils.releaseTemporaryMemoryFunction());
t("object", IRNames::creationObject(*contract)); t("object", IRNames::creationObject(*contract));
t("panic", m_utils.panicFunction()); t("panic", m_utils.panicFunction(PanicCode::ResourceError));
t("abiEncode", t("abiEncode",
m_context.abiFunctions().tupleEncoder(argumentTypes, functionType->parameterTypes(), false) m_context.abiFunctions().tupleEncoder(argumentTypes, functionType->parameterTypes(), false)
); );
@ -2023,7 +2023,7 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess)
)") )")
("index", index.name()) ("index", index.name())
("length", to_string(fixedBytesType.numBytes())) ("length", to_string(fixedBytesType.numBytes()))
("panic", m_utils.panicFunction()) ("panic", m_utils.panicFunction(PanicCode::ArrayOutOfBounds))
("array", IRVariable(_indexAccess.baseExpression()).name()) ("array", IRVariable(_indexAccess.baseExpression()).name())
("shl248", m_utils.shiftLeftFunction(256 - 8)) ("shl248", m_utils.shiftLeftFunction(256 - 8))
("result", IRVariable(_indexAccess).name()) ("result", IRVariable(_indexAccess).name())

View File

@ -9,6 +9,7 @@ set(sources
CommonIO.h CommonIO.h
Exceptions.cpp Exceptions.cpp
Exceptions.h Exceptions.h
ErrorCodes.h
FixedHash.h FixedHash.h
FunctionSelector.h FunctionSelector.h
IndentedWriter.cpp IndentedWriter.cpp

38
libsolutil/ErrorCodes.h Normal file
View File

@ -0,0 +1,38 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
#pragma once
#include <string>
namespace solidity::util
{
enum class PanicCode
{
Generic = 0x00, // generic / unspecified error.
Assert = 0x01, // generic / unspecified error. Used by assert().
UnderOverflow = 0x11, // arithmetic underflow or overflow
DivisionByZero = 0x12, // division or modulo by zero
EnumConversionError = 0x21, // enum conversion error
EmptyArrayPop = 0x31, // empty array pop
ArrayOutOfBounds = 0x32, // array out of bounds access
ResourceError = 0x41, // resource error (too large allocation or too large array)
InvalidInternalFunction = 0x51, // calling invalid internal function
};
}

View File

@ -378,6 +378,7 @@ EVMDialectTyped::EVMDialectTyped(langutil::EVMVersion _evmVersion, bool _objectA
BuiltinContext&, BuiltinContext&,
std::function<void(Expression const&)> _visitExpression std::function<void(Expression const&)> _visitExpression
) { ) {
// TODO this should use a Panic.
// A value larger than 1 causes an invalid instruction. // A value larger than 1 causes an invalid instruction.
visitArguments(_assembly, _call, _visitExpression); visitArguments(_assembly, _call, _visitExpression);
_assembly.appendConstant(2); _assembly.appendConstant(2);