Merge pull request #101 from LianaHus/sol_Disallow_access_if_not_initialized

Disallow access to non-initialized references in storage
This commit is contained in:
chriseth 2015-10-02 12:45:26 +02:00
commit cae8db989a
8 changed files with 124 additions and 43 deletions

View File

@ -122,6 +122,7 @@ bool CompilerStack::parse()
}
InterfaceHandler interfaceHandler;
bool typesFine = true;
for (Source const* source: m_sourceOrder)
for (ASTPointer<ASTNode> const& node: source->ast->nodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
@ -129,14 +130,14 @@ bool CompilerStack::parse()
m_globalContext->setCurrentContract(*contract);
resolver.updateDeclaration(*m_globalContext->currentThis());
TypeChecker typeChecker;
bool typesFine = typeChecker.checkTypeRequirements(*contract);
if (!typesFine)
m_errors += typeChecker.errors();
if (!typeChecker.checkTypeRequirements(*contract))
typesFine = false;
m_errors += typeChecker.errors();
contract->setDevDocumentation(interfaceHandler.devDocumentation(*contract));
contract->setUserDocumentation(interfaceHandler.userDocumentation(*contract));
m_contracts[contract->name()].contract = contract;
}
m_parseSuccessful = m_errors.empty();
m_parseSuccessful = typesFine;
return m_parseSuccessful;
}

View File

@ -31,13 +31,13 @@ namespace dev
{
namespace solidity
{
struct Error: virtual Exception {};
struct ParserError: virtual Error {};
struct TypeError: virtual Error {};
struct DeclarationError: virtual Error {};
struct DocstringParsingError: virtual Error {};
struct Warning: virtual Error {};
struct CompilerError: virtual Exception {};
struct InternalCompilerError: virtual Exception {};
@ -53,7 +53,6 @@ public:
infos.push_back(std::make_pair(_errMsg, _sourceLocation));
return *this;
}
std::vector<errorSourceLocationInfo> infos;
};

View File

@ -274,9 +274,17 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(ASTString const*
else
m_scanner->next(); // just consume the ';'
bool const c_isConstructor = (_contractName && *name == *_contractName);
return nodeFactory.createNode<FunctionDefinition>(name, visibility, c_isConstructor, docstring,
parameters, isDeclaredConst, modifiers,
returnParameters, block);
return nodeFactory.createNode<FunctionDefinition>(
name,
visibility,
c_isConstructor,
docstring,
parameters,
isDeclaredConst,
modifiers,
returnParameters,
block
);
}
ASTPointer<StructDefinition> Parser::parseStructDefinition()
@ -753,7 +761,8 @@ ASTPointer<Statement> Parser::parseSimpleStatement()
}
ASTPointer<VariableDeclarationStatement> Parser::parseVariableDeclarationStatement(
ASTPointer<TypeName> const& _lookAheadArrayType)
ASTPointer<TypeName> const& _lookAheadArrayType
)
{
VarDeclParserOptions options;
options.allowVar = true;
@ -765,14 +774,16 @@ ASTPointer<VariableDeclarationStatement> Parser::parseVariableDeclarationStateme
}
ASTPointer<ExpressionStatement> Parser::parseExpressionStatement(
ASTPointer<Expression> const& _lookAheadIndexAccessStructure)
ASTPointer<Expression> const& _lookAheadIndexAccessStructure
)
{
ASTPointer<Expression> expression = parseExpression(_lookAheadIndexAccessStructure);
return ASTNodeFactory(*this, expression).createNode<ExpressionStatement>(expression);
}
ASTPointer<Expression> Parser::parseExpression(
ASTPointer<Expression> const& _lookAheadIndexAccessStructure)
ASTPointer<Expression> const& _lookAheadIndexAccessStructure
)
{
ASTPointer<Expression> expression = parseBinaryExpression(4, _lookAheadIndexAccessStructure);
if (!Token::isAssignmentOp(m_scanner->currentToken()))
@ -784,8 +795,10 @@ ASTPointer<Expression> Parser::parseExpression(
return nodeFactory.createNode<Assignment>(expression, assignmentOperator, rightHandSide);
}
ASTPointer<Expression> Parser::parseBinaryExpression(int _minPrecedence,
ASTPointer<Expression> const& _lookAheadIndexAccessStructure)
ASTPointer<Expression> Parser::parseBinaryExpression(
int _minPrecedence,
ASTPointer<Expression> const& _lookAheadIndexAccessStructure
)
{
ASTPointer<Expression> expression = parseUnaryExpression(_lookAheadIndexAccessStructure);
ASTNodeFactory nodeFactory(*this, expression);
@ -803,7 +816,8 @@ ASTPointer<Expression> Parser::parseBinaryExpression(int _minPrecedence,
}
ASTPointer<Expression> Parser::parseUnaryExpression(
ASTPointer<Expression> const& _lookAheadIndexAccessStructure)
ASTPointer<Expression> const& _lookAheadIndexAccessStructure
)
{
ASTNodeFactory nodeFactory = _lookAheadIndexAccessStructure ?
ASTNodeFactory(*this, _lookAheadIndexAccessStructure) : ASTNodeFactory(*this);
@ -830,7 +844,8 @@ ASTPointer<Expression> Parser::parseUnaryExpression(
}
ASTPointer<Expression> Parser::parseLeftHandSideExpression(
ASTPointer<Expression> const& _lookAheadIndexAccessStructure)
ASTPointer<Expression> const& _lookAheadIndexAccessStructure
)
{
ASTNodeFactory nodeFactory = _lookAheadIndexAccessStructure ?
ASTNodeFactory(*this, _lookAheadIndexAccessStructure) : ASTNodeFactory(*this);
@ -1014,7 +1029,9 @@ Parser::LookAheadInfo Parser::peekStatementType() const
}
ASTPointer<TypeName> Parser::typeNameIndexAccessStructure(
ASTPointer<PrimaryExpression> const& _primary, vector<pair<ASTPointer<Expression>, SourceLocation>> const& _indices)
ASTPointer<PrimaryExpression> const& _primary,
vector<pair<ASTPointer<Expression>, SourceLocation>> const& _indices
)
{
ASTNodeFactory nodeFactory(*this, _primary);
ASTPointer<TypeName> type;
@ -1033,7 +1050,9 @@ ASTPointer<TypeName> Parser::typeNameIndexAccessStructure(
}
ASTPointer<Expression> Parser::expressionFromIndexAccessStructure(
ASTPointer<PrimaryExpression> const& _primary, vector<pair<ASTPointer<Expression>, SourceLocation>> const& _indices)
ASTPointer<PrimaryExpression> const& _primary,
vector<pair<ASTPointer<Expression>, SourceLocation>> const& _indices
)
{
ASTNodeFactory nodeFactory(*this, _primary);
ASTPointer<Expression> expression(_primary);

View File

@ -90,17 +90,23 @@ private:
/// A "simple statement" can be a variable declaration statement or an expression statement.
ASTPointer<Statement> parseSimpleStatement();
ASTPointer<VariableDeclarationStatement> parseVariableDeclarationStatement(
ASTPointer<TypeName> const& _lookAheadArrayType = ASTPointer<TypeName>());
ASTPointer<TypeName> const& _lookAheadArrayType = ASTPointer<TypeName>()
);
ASTPointer<ExpressionStatement> parseExpressionStatement(
ASTPointer<Expression> const& _lookAheadIndexAccessStructure = ASTPointer<Expression>());
ASTPointer<Expression> const& _lookAheadIndexAccessStructure = ASTPointer<Expression>()
);
ASTPointer<Expression> parseExpression(
ASTPointer<Expression> const& _lookAheadIndexAccessStructure = ASTPointer<Expression>());
ASTPointer<Expression> const& _lookAheadIndexAccessStructure = ASTPointer<Expression>()
);
ASTPointer<Expression> parseBinaryExpression(int _minPrecedence = 4,
ASTPointer<Expression> const& _lookAheadIndexAccessStructure = ASTPointer<Expression>());
ASTPointer<Expression> const& _lookAheadIndexAccessStructure = ASTPointer<Expression>()
);
ASTPointer<Expression> parseUnaryExpression(
ASTPointer<Expression> const& _lookAheadIndexAccessStructure = ASTPointer<Expression>());
ASTPointer<Expression> const& _lookAheadIndexAccessStructure = ASTPointer<Expression>()
);
ASTPointer<Expression> parseLeftHandSideExpression(
ASTPointer<Expression> const& _lookAheadIndexAccessStructure = ASTPointer<Expression>());
ASTPointer<Expression> const& _lookAheadIndexAccessStructure = ASTPointer<Expression>()
);
ASTPointer<Expression> parsePrimaryExpression();
std::vector<ASTPointer<Expression>> parseFunctionCallListArguments();
std::pair<std::vector<ASTPointer<Expression>>, std::vector<ASTPointer<ASTString>>> parseFunctionCallArguments();
@ -122,11 +128,13 @@ private:
/// Returns a typename parsed in look-ahead fashion from something like "a[8][2**70]".
ASTPointer<TypeName> typeNameIndexAccessStructure(
ASTPointer<PrimaryExpression> const& _primary,
std::vector<std::pair<ASTPointer<Expression>, SourceLocation>> const& _indices);
std::vector<std::pair<ASTPointer<Expression>, SourceLocation>> const& _indices
);
/// Returns an expression parsed in look-ahead fashion from something like "a[8][2**70]".
ASTPointer<Expression> expressionFromIndexAccessStructure(
ASTPointer<PrimaryExpression> const& _primary,
std::vector<std::pair<ASTPointer<Expression>, SourceLocation>> const& _indices);
std::vector<std::pair<ASTPointer<Expression>, SourceLocation>> const& _indices
);
/// If current token value is not _value, throw exception otherwise advance token.
void expectToken(Token::Value _value);
Token::Value expectAssignmentOperator();

View File

@ -43,8 +43,14 @@ bool TypeChecker::checkTypeRequirements(const ContractDefinition& _contract)
if (m_errors.empty())
throw; // Something is weird here, rather throw again.
}
return m_errors.empty();
bool success = true;
for (auto const& it: m_errors)
if (!dynamic_cast<Warning const*>(it.get()))
{
success = false;
break;
}
return success;
}
TypePointer const& TypeChecker::type(Expression const& _expression) const
@ -443,6 +449,18 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
{
if (_variable.value())
expectType(*_variable.value(), *varType);
else
{
if (auto ref = dynamic_cast<ReferenceType const *>(varType.get()))
if (ref->dataStoredIn(DataLocation::Storage) && _variable.isLocalVariable() && !_variable.isCallableParameter())
{
auto err = make_shared<Warning>();
*err <<
errinfo_sourceLocation(_variable.location()) <<
errinfo_comment("Uninitialized storage pointer. Did you mean '<type> memory " + _variable.name() + "'?");
m_errors.push_back(err);
}
}
}
else
{

View File

@ -43,10 +43,10 @@ class TypeChecker: private ASTConstVisitor
{
public:
/// Performs type checking on the given contract and all of its sub-nodes.
/// @returns true iff all checks passed.
/// @returns true iff all checks passed. Note even if all checks passed, errors() can still contain warnings
bool checkTypeRequirements(ContractDefinition const& _contract);
/// @returns the list of errors found during type checking.
/// @returns the list of errors and warnings found during type checking.
std::vector<std::shared_ptr<Error const>> const& errors() const { return m_errors; }
/// @returns the type of an expression and asserts that it is present.
@ -57,6 +57,7 @@ public:
/// Adds a new error to the list of errors.
void typeError(ASTNode const& _node, std::string const& _description);
/// Adds a new error to the list of errors and throws to abort type checking.
void fatalTypeError(ASTNode const& _node, std::string const& _description);

View File

@ -491,12 +491,15 @@ bool CommandLineInterface::processInput()
// TODO: Perhaps we should not compile unless requested
bool optimize = m_args.count("optimize") > 0;
unsigned runs = m_args["optimize-runs"].as<unsigned>();
if (!m_compiler->compile(optimize, runs))
{
for (auto const& error: m_compiler->errors())
SourceReferenceFormatter::printExceptionInformation(cerr, *error, "Error", *m_compiler);
bool successful = m_compiler->compile(optimize, runs);
for (auto const& error: m_compiler->errors())
SourceReferenceFormatter::printExceptionInformation(
cerr,
*error,
(dynamic_pointer_cast<Warning const>(error)) ? "Warning" : "Error", *m_compiler
);
if (!successful)
return false;
}
m_compiler->link(m_libraries);
}
catch (ParserError const& _exception)

View File

@ -45,7 +45,7 @@ namespace
{
pair<ASTPointer<SourceUnit>, shared_ptr<Exception const>>
parseAnalyseAndReturnError(string const& _source)
parseAnalyseAndReturnError(string const& _source, bool _reportWarnings = false)
{
Parser parser;
ASTPointer<SourceUnit> sourceUnit;
@ -74,7 +74,19 @@ parseAnalyseAndReturnError(string const& _source)
TypeChecker typeChecker;
if (!typeChecker.checkTypeRequirements(*contract))
{
err = typeChecker.errors().front();
for (auto const& firstError: typeChecker.errors())
{
if (_reportWarnings || !dynamic_pointer_cast<Warning const>(firstError))
{
err = firstError;
break;
}
else if (_reportWarnings)
{
err = firstError;
break;
}
}
break;
}
}
@ -101,9 +113,9 @@ ASTPointer<SourceUnit> parseAndAnalyse(string const& _source)
return sourceAndError.first;
}
shared_ptr<Exception const> parseAndAnalyseReturnError(std::string const& _source)
shared_ptr<Exception const> parseAndAnalyseReturnError(std::string const& _source, bool _warning = false)
{
auto sourceAndError = parseAnalyseAndReturnError(_source);
auto sourceAndError = parseAnalyseAndReturnError(_source, _warning);
BOOST_REQUIRE(!!sourceAndError.second);
return sourceAndError.second;
}
@ -119,8 +131,10 @@ static ContractDefinition const* retrieveContract(ASTPointer<SourceUnit> _source
return nullptr;
}
static FunctionTypePointer const& retrieveFunctionBySignature(ContractDefinition const* _contract,
std::string const& _signature)
static FunctionTypePointer const& retrieveFunctionBySignature(
ContractDefinition const* _contract,
std::string const& _signature
)
{
FixedHash<4> hash(dev::sha3(_signature));
return _contract->interfaceFunctions()[hash];
@ -155,8 +169,8 @@ BOOST_AUTO_TEST_CASE(double_stateVariable_declaration)
BOOST_AUTO_TEST_CASE(double_function_declaration)
{
char const* text = "contract test {\n"
" function fun() { var x; }\n"
" function fun() { var x; }\n"
" function fun() { uint x; }\n"
" function fun() { uint x; }\n"
"}\n";
SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), DeclarationError);
}
@ -2318,6 +2332,24 @@ BOOST_AUTO_TEST_CASE(literal_string_to_storage_pointer)
SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError);
}
BOOST_AUTO_TEST_CASE(non_initialized_references)
{
char const* text = R"(
contract c
{
struct s{
uint a;
}
function f()
{
s x;
x.a = 2;
}
}
)";
SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text, true), Warning);
}
BOOST_AUTO_TEST_SUITE_END()
}