Change invalid opcode to revert for input validation.

This commit is contained in:
chriseth 2017-05-24 11:47:43 +02:00
parent b83f77e0e5
commit 831ed08387
7 changed files with 53 additions and 20 deletions

View File

@ -14,6 +14,7 @@ Features:
* Code Generator: Added the Whiskers template system. * Code Generator: Added the Whiskers template system.
Bugfixes: Bugfixes:
* Code generator: Use ``REVERT`` instead of ``INVALID`` for generated input validation routines.
* Type Checker: Fix address literals not being treated as compile-time constants. * Type Checker: Fix address literals not being treated as compile-time constants.
* Type Checker: Make UTF8-validation a bit more sloppy to include more valid sequences. * Type Checker: Make UTF8-validation a bit more sloppy to include more valid sequences.
* Fixed crash concerning non-callable types. * Fixed crash concerning non-callable types.

View File

@ -244,6 +244,20 @@ CompilerContext& CompilerContext::appendConditionalInvalid()
return *this; return *this;
} }
CompilerContext& CompilerContext::appendRevert()
{
return *this << u256(0) << u256(0) << Instruction::REVERT;
}
CompilerContext& CompilerContext::appendConditionalRevert()
{
*this << Instruction::ISZERO;
eth::AssemblyItem afterTag = appendConditionalJump();
appendRevert();
*this << afterTag;
return *this;
}
void CompilerContext::resetVisitedNodes(ASTNode const* _node) void CompilerContext::resetVisitedNodes(ASTNode const* _node)
{ {
stack<ASTNode const*> newStack; stack<ASTNode const*> newStack;

View File

@ -136,11 +136,15 @@ public:
/// Appends a JUMP to a new tag and @returns the tag /// Appends a JUMP to a new tag and @returns the tag
eth::AssemblyItem appendJumpToNew() { return m_asm->appendJump().tag(); } eth::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(eth::AssemblyItem::JumpType _jumpType = eth::AssemblyItem::JumpType::Ordinary); CompilerContext& appendJump(eth::AssemblyItem::JumpType _jumpType = eth::AssemblyItem::JumpType::Ordinary);
/// Appends an INVALID instruction /// Appends an INVALID instruction
CompilerContext& appendInvalid(); CompilerContext& appendInvalid();
/// Appends a conditional INVALID instruction /// Appends a conditional INVALID instruction
CompilerContext& appendConditionalInvalid(); CompilerContext& appendConditionalInvalid();
/// Appends a REVERT(0, 0) call
CompilerContext& appendRevert();
/// Appends a conditional REVERT(0, 0) call
CompilerContext& appendConditionalRevert();
/// Appends a JUMP to a specific tag /// Appends a JUMP to a specific tag
CompilerContext& appendJumpTo(eth::AssemblyItem const& _tag) { m_asm->appendJump(_tag); return *this; } CompilerContext& appendJumpTo(eth::AssemblyItem const& _tag) { m_asm->appendJump(_tag); return *this; }
/// Appends pushing of a new tag and @returns the new tag. /// Appends pushing of a new tag and @returns the new tag.

View File

@ -392,7 +392,13 @@ void CompilerUtils::pushCombinedFunctionEntryLabel(Declaration const& _function)
Instruction::OR; Instruction::OR;
} }
void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded, bool _chopSignBits) void CompilerUtils::convertType(
Type const& _typeOnStack,
Type const& _targetType,
bool _cleanupNeeded,
bool _chopSignBits,
bool _asPartOfArgumentDecoding
)
{ {
// For a type extension, we need to remove all higher-order bits that we might have ignored in // For a type extension, we need to remove all higher-order bits that we might have ignored in
// previous operations. // previous operations.
@ -450,7 +456,10 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
EnumType const& enumType = dynamic_cast<decltype(enumType)>(_typeOnStack); EnumType const& enumType = dynamic_cast<decltype(enumType)>(_typeOnStack);
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(); if (_asPartOfArgumentDecoding)
m_context.appendConditionalRevert();
else
m_context.appendConditionalInvalid();
enumOverflowCheckPending = false; enumOverflowCheckPending = false;
} }
break; break;
@ -985,7 +994,7 @@ unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCallda
m_context << shiftFactor << Instruction::MUL; m_context << shiftFactor << Instruction::MUL;
} }
if (_fromCalldata) if (_fromCalldata)
convertType(_type, _type, true); convertType(_type, _type, true, false, true);
return numBytes; return numBytes;
} }

View File

@ -135,7 +135,15 @@ public:
/// If @a _cleanupNeeded, high order bits cleanup is also done if no type conversion would be /// If @a _cleanupNeeded, high order bits cleanup is also done if no type conversion would be
/// necessary. /// necessary.
/// If @a _chopSignBits, the function resets the signed bits out of the width of the signed integer. /// If @a _chopSignBits, the function resets the signed bits out of the width of the signed integer.
void convertType(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded = false, bool _chopSignBits = false); /// If @a _asPartOfArgumentDecoding is true, failed conversions are flagged via REVERT,
/// otherwise they are flagged with INVALID.
void convertType(
Type const& _typeOnStack,
Type const& _targetType,
bool _cleanupNeeded = false,
bool _chopSignBits = false,
bool _asPartOfArgumentDecoding = false
);
/// Creates a zero-value for the given type and puts it onto the stack. This might allocate /// Creates a zero-value for the given type and puts it onto the stack. This might allocate
/// memory for memory references. /// memory for memory references.

View File

@ -111,7 +111,7 @@ void ContractCompiler::appendCallValueCheck()
{ {
// Throw if function is not payable but call contained ether. // Throw if function is not payable but call contained ether.
m_context << Instruction::CALLVALUE; m_context << Instruction::CALLVALUE;
m_context.appendConditionalInvalid(); m_context.appendConditionalRevert();
} }
void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _contract) void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _contract)
@ -276,7 +276,7 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac
appendReturnValuePacker(FunctionType(*fallback).returnParameterTypes(), _contract.isLibrary()); appendReturnValuePacker(FunctionType(*fallback).returnParameterTypes(), _contract.isLibrary());
} }
else else
m_context.appendInvalid(); m_context.appendRevert();
for (auto const& it: interfaceFunctions) for (auto const& it: interfaceFunctions)
{ {
@ -368,7 +368,7 @@ void ContractCompiler::appendCalldataUnpacker(TypePointers const& _typeParameter
// copy to memory // copy to memory
// move calldata type up again // move calldata type up again
CompilerUtils(m_context).moveIntoStack(calldataType->sizeOnStack()); CompilerUtils(m_context).moveIntoStack(calldataType->sizeOnStack());
CompilerUtils(m_context).convertType(*calldataType, arrayType); CompilerUtils(m_context).convertType(*calldataType, arrayType, false, false, true);
// fetch next pointer again // fetch next pointer again
CompilerUtils(m_context).moveToStackTop(arrayType.sizeOnStack()); CompilerUtils(m_context).moveToStackTop(arrayType.sizeOnStack());
} }
@ -805,8 +805,7 @@ bool ContractCompiler::visit(Throw const& _throw)
{ {
CompilerContext::LocationSetter locationSetter(m_context, _throw); CompilerContext::LocationSetter locationSetter(m_context, _throw);
// Do not send back an error detail. // Do not send back an error detail.
m_context << u256(0) << u256(0); m_context.appendRevert();
m_context << Instruction::REVERT;
return false; return false;
} }

View File

@ -587,7 +587,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
m_context << Instruction::CREATE; m_context << Instruction::CREATE;
// Check if zero (out of stack or not enough balance). // Check if zero (out of stack or not enough balance).
m_context << Instruction::DUP1 << Instruction::ISZERO; m_context << Instruction::DUP1 << Instruction::ISZERO;
m_context.appendConditionalInvalid(); m_context.appendConditionalRevert();
if (function.valueSet()) if (function.valueSet())
m_context << swapInstruction(1) << Instruction::POP; m_context << swapInstruction(1) << Instruction::POP;
break; break;
@ -651,7 +651,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
{ {
// Check if zero (out of stack or not enough balance). // Check if zero (out of stack or not enough balance).
m_context << Instruction::ISZERO; m_context << Instruction::ISZERO;
m_context.appendConditionalInvalid(); m_context.appendConditionalRevert();
} }
break; break;
case FunctionType::Kind::Selfdestruct: case FunctionType::Kind::Selfdestruct:
@ -660,9 +660,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
m_context << Instruction::SELFDESTRUCT; m_context << Instruction::SELFDESTRUCT;
break; break;
case FunctionType::Kind::Revert: case FunctionType::Kind::Revert:
// memory offset returned - zero length m_context.appendRevert();
m_context << u256(0) << u256(0);
m_context << Instruction::REVERT;
break; break;
case FunctionType::Kind::SHA3: case FunctionType::Kind::SHA3:
{ {
@ -890,7 +888,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
// condition was not met, flag an error // condition was not met, flag an error
m_context << Instruction::INVALID; m_context << Instruction::INVALID;
else else
m_context << u256(0) << u256(0) << Instruction::REVERT; m_context.appendRevert();
// the success branch // the success branch
m_context << success; m_context << success;
break; break;
@ -1695,7 +1693,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
if (funKind == FunctionType::Kind::External || funKind == FunctionType::Kind::CallCode || funKind == FunctionType::Kind::DelegateCall) if (funKind == FunctionType::Kind::External || funKind == FunctionType::Kind::CallCode || funKind == FunctionType::Kind::DelegateCall)
{ {
m_context << Instruction::DUP1 << Instruction::EXTCODESIZE << Instruction::ISZERO; m_context << Instruction::DUP1 << Instruction::EXTCODESIZE << Instruction::ISZERO;
m_context.appendConditionalInvalid(); m_context.appendConditionalRevert();
existenceChecked = true; existenceChecked = true;
} }
@ -1731,7 +1729,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
{ {
//Propagate error condition (if CALL pushes 0 on stack). //Propagate error condition (if CALL pushes 0 on stack).
m_context << Instruction::ISZERO; m_context << Instruction::ISZERO;
m_context.appendConditionalInvalid(); m_context.appendConditionalRevert();
} }
utils().popStackSlots(remainsSize); utils().popStackSlots(remainsSize);