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

View File

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

View File

@ -274,9 +274,17 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(ASTString const*
else else
m_scanner->next(); // just consume the ';' m_scanner->next(); // just consume the ';'
bool const c_isConstructor = (_contractName && *name == *_contractName); bool const c_isConstructor = (_contractName && *name == *_contractName);
return nodeFactory.createNode<FunctionDefinition>(name, visibility, c_isConstructor, docstring, return nodeFactory.createNode<FunctionDefinition>(
parameters, isDeclaredConst, modifiers, name,
returnParameters, block); visibility,
c_isConstructor,
docstring,
parameters,
isDeclaredConst,
modifiers,
returnParameters,
block
);
} }
ASTPointer<StructDefinition> Parser::parseStructDefinition() ASTPointer<StructDefinition> Parser::parseStructDefinition()
@ -753,7 +761,8 @@ ASTPointer<Statement> Parser::parseSimpleStatement()
} }
ASTPointer<VariableDeclarationStatement> Parser::parseVariableDeclarationStatement( ASTPointer<VariableDeclarationStatement> Parser::parseVariableDeclarationStatement(
ASTPointer<TypeName> const& _lookAheadArrayType) ASTPointer<TypeName> const& _lookAheadArrayType
)
{ {
VarDeclParserOptions options; VarDeclParserOptions options;
options.allowVar = true; options.allowVar = true;
@ -765,14 +774,16 @@ ASTPointer<VariableDeclarationStatement> Parser::parseVariableDeclarationStateme
} }
ASTPointer<ExpressionStatement> Parser::parseExpressionStatement( ASTPointer<ExpressionStatement> Parser::parseExpressionStatement(
ASTPointer<Expression> const& _lookAheadIndexAccessStructure) ASTPointer<Expression> const& _lookAheadIndexAccessStructure
)
{ {
ASTPointer<Expression> expression = parseExpression(_lookAheadIndexAccessStructure); ASTPointer<Expression> expression = parseExpression(_lookAheadIndexAccessStructure);
return ASTNodeFactory(*this, expression).createNode<ExpressionStatement>(expression); return ASTNodeFactory(*this, expression).createNode<ExpressionStatement>(expression);
} }
ASTPointer<Expression> Parser::parseExpression( ASTPointer<Expression> Parser::parseExpression(
ASTPointer<Expression> const& _lookAheadIndexAccessStructure) ASTPointer<Expression> const& _lookAheadIndexAccessStructure
)
{ {
ASTPointer<Expression> expression = parseBinaryExpression(4, _lookAheadIndexAccessStructure); ASTPointer<Expression> expression = parseBinaryExpression(4, _lookAheadIndexAccessStructure);
if (!Token::isAssignmentOp(m_scanner->currentToken())) if (!Token::isAssignmentOp(m_scanner->currentToken()))
@ -784,8 +795,10 @@ ASTPointer<Expression> Parser::parseExpression(
return nodeFactory.createNode<Assignment>(expression, assignmentOperator, rightHandSide); return nodeFactory.createNode<Assignment>(expression, assignmentOperator, rightHandSide);
} }
ASTPointer<Expression> Parser::parseBinaryExpression(int _minPrecedence, ASTPointer<Expression> Parser::parseBinaryExpression(
ASTPointer<Expression> const& _lookAheadIndexAccessStructure) int _minPrecedence,
ASTPointer<Expression> const& _lookAheadIndexAccessStructure
)
{ {
ASTPointer<Expression> expression = parseUnaryExpression(_lookAheadIndexAccessStructure); ASTPointer<Expression> expression = parseUnaryExpression(_lookAheadIndexAccessStructure);
ASTNodeFactory nodeFactory(*this, expression); ASTNodeFactory nodeFactory(*this, expression);
@ -803,7 +816,8 @@ ASTPointer<Expression> Parser::parseBinaryExpression(int _minPrecedence,
} }
ASTPointer<Expression> Parser::parseUnaryExpression( ASTPointer<Expression> Parser::parseUnaryExpression(
ASTPointer<Expression> const& _lookAheadIndexAccessStructure) ASTPointer<Expression> const& _lookAheadIndexAccessStructure
)
{ {
ASTNodeFactory nodeFactory = _lookAheadIndexAccessStructure ? ASTNodeFactory nodeFactory = _lookAheadIndexAccessStructure ?
ASTNodeFactory(*this, _lookAheadIndexAccessStructure) : ASTNodeFactory(*this); ASTNodeFactory(*this, _lookAheadIndexAccessStructure) : ASTNodeFactory(*this);
@ -830,7 +844,8 @@ ASTPointer<Expression> Parser::parseUnaryExpression(
} }
ASTPointer<Expression> Parser::parseLeftHandSideExpression( ASTPointer<Expression> Parser::parseLeftHandSideExpression(
ASTPointer<Expression> const& _lookAheadIndexAccessStructure) ASTPointer<Expression> const& _lookAheadIndexAccessStructure
)
{ {
ASTNodeFactory nodeFactory = _lookAheadIndexAccessStructure ? ASTNodeFactory nodeFactory = _lookAheadIndexAccessStructure ?
ASTNodeFactory(*this, _lookAheadIndexAccessStructure) : ASTNodeFactory(*this); ASTNodeFactory(*this, _lookAheadIndexAccessStructure) : ASTNodeFactory(*this);
@ -1014,7 +1029,9 @@ Parser::LookAheadInfo Parser::peekStatementType() const
} }
ASTPointer<TypeName> Parser::typeNameIndexAccessStructure( 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); ASTNodeFactory nodeFactory(*this, _primary);
ASTPointer<TypeName> type; ASTPointer<TypeName> type;
@ -1033,7 +1050,9 @@ ASTPointer<TypeName> Parser::typeNameIndexAccessStructure(
} }
ASTPointer<Expression> Parser::expressionFromIndexAccessStructure( 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); ASTNodeFactory nodeFactory(*this, _primary);
ASTPointer<Expression> expression(_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. /// A "simple statement" can be a variable declaration statement or an expression statement.
ASTPointer<Statement> parseSimpleStatement(); ASTPointer<Statement> parseSimpleStatement();
ASTPointer<VariableDeclarationStatement> parseVariableDeclarationStatement( ASTPointer<VariableDeclarationStatement> parseVariableDeclarationStatement(
ASTPointer<TypeName> const& _lookAheadArrayType = ASTPointer<TypeName>()); ASTPointer<TypeName> const& _lookAheadArrayType = ASTPointer<TypeName>()
);
ASTPointer<ExpressionStatement> parseExpressionStatement( ASTPointer<ExpressionStatement> parseExpressionStatement(
ASTPointer<Expression> const& _lookAheadIndexAccessStructure = ASTPointer<Expression>()); ASTPointer<Expression> const& _lookAheadIndexAccessStructure = ASTPointer<Expression>()
);
ASTPointer<Expression> parseExpression( ASTPointer<Expression> parseExpression(
ASTPointer<Expression> const& _lookAheadIndexAccessStructure = ASTPointer<Expression>()); ASTPointer<Expression> const& _lookAheadIndexAccessStructure = ASTPointer<Expression>()
);
ASTPointer<Expression> parseBinaryExpression(int _minPrecedence = 4, ASTPointer<Expression> parseBinaryExpression(int _minPrecedence = 4,
ASTPointer<Expression> const& _lookAheadIndexAccessStructure = ASTPointer<Expression>()); ASTPointer<Expression> const& _lookAheadIndexAccessStructure = ASTPointer<Expression>()
);
ASTPointer<Expression> parseUnaryExpression( ASTPointer<Expression> parseUnaryExpression(
ASTPointer<Expression> const& _lookAheadIndexAccessStructure = ASTPointer<Expression>()); ASTPointer<Expression> const& _lookAheadIndexAccessStructure = ASTPointer<Expression>()
);
ASTPointer<Expression> parseLeftHandSideExpression( ASTPointer<Expression> parseLeftHandSideExpression(
ASTPointer<Expression> const& _lookAheadIndexAccessStructure = ASTPointer<Expression>()); ASTPointer<Expression> const& _lookAheadIndexAccessStructure = ASTPointer<Expression>()
);
ASTPointer<Expression> parsePrimaryExpression(); ASTPointer<Expression> parsePrimaryExpression();
std::vector<ASTPointer<Expression>> parseFunctionCallListArguments(); std::vector<ASTPointer<Expression>> parseFunctionCallListArguments();
std::pair<std::vector<ASTPointer<Expression>>, std::vector<ASTPointer<ASTString>>> parseFunctionCallArguments(); 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]". /// Returns a typename parsed in look-ahead fashion from something like "a[8][2**70]".
ASTPointer<TypeName> typeNameIndexAccessStructure( ASTPointer<TypeName> typeNameIndexAccessStructure(
ASTPointer<PrimaryExpression> const& _primary, 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]". /// Returns an expression parsed in look-ahead fashion from something like "a[8][2**70]".
ASTPointer<Expression> expressionFromIndexAccessStructure( ASTPointer<Expression> expressionFromIndexAccessStructure(
ASTPointer<PrimaryExpression> const& _primary, 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. /// If current token value is not _value, throw exception otherwise advance token.
void expectToken(Token::Value _value); void expectToken(Token::Value _value);
Token::Value expectAssignmentOperator(); Token::Value expectAssignmentOperator();

View File

@ -43,8 +43,14 @@ bool TypeChecker::checkTypeRequirements(const ContractDefinition& _contract)
if (m_errors.empty()) if (m_errors.empty())
throw; // Something is weird here, rather throw again. throw; // Something is weird here, rather throw again.
} }
bool success = true;
return m_errors.empty(); 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 TypePointer const& TypeChecker::type(Expression const& _expression) const
@ -443,6 +449,18 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
{ {
if (_variable.value()) if (_variable.value())
expectType(*_variable.value(), *varType); 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 else
{ {

View File

@ -43,10 +43,10 @@ class TypeChecker: private ASTConstVisitor
{ {
public: public:
/// Performs type checking on the given contract and all of its sub-nodes. /// 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); 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; } 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. /// @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. /// Adds a new error to the list of errors.
void typeError(ASTNode const& _node, std::string const& _description); void typeError(ASTNode const& _node, std::string const& _description);
/// Adds a new error to the list of errors and throws to abort type checking. /// Adds a new error to the list of errors and throws to abort type checking.
void fatalTypeError(ASTNode const& _node, std::string const& _description); 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 // TODO: Perhaps we should not compile unless requested
bool optimize = m_args.count("optimize") > 0; bool optimize = m_args.count("optimize") > 0;
unsigned runs = m_args["optimize-runs"].as<unsigned>(); unsigned runs = m_args["optimize-runs"].as<unsigned>();
if (!m_compiler->compile(optimize, runs)) bool successful = m_compiler->compile(optimize, runs);
{
for (auto const& error: m_compiler->errors()) for (auto const& error: m_compiler->errors())
SourceReferenceFormatter::printExceptionInformation(cerr, *error, "Error", *m_compiler); SourceReferenceFormatter::printExceptionInformation(
cerr,
*error,
(dynamic_pointer_cast<Warning const>(error)) ? "Warning" : "Error", *m_compiler
);
if (!successful)
return false; return false;
}
m_compiler->link(m_libraries); m_compiler->link(m_libraries);
} }
catch (ParserError const& _exception) catch (ParserError const& _exception)

View File

@ -45,7 +45,7 @@ namespace
{ {
pair<ASTPointer<SourceUnit>, shared_ptr<Exception const>> pair<ASTPointer<SourceUnit>, shared_ptr<Exception const>>
parseAnalyseAndReturnError(string const& _source) parseAnalyseAndReturnError(string const& _source, bool _reportWarnings = false)
{ {
Parser parser; Parser parser;
ASTPointer<SourceUnit> sourceUnit; ASTPointer<SourceUnit> sourceUnit;
@ -74,7 +74,19 @@ parseAnalyseAndReturnError(string const& _source)
TypeChecker typeChecker; TypeChecker typeChecker;
if (!typeChecker.checkTypeRequirements(*contract)) 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; break;
} }
} }
@ -101,9 +113,9 @@ ASTPointer<SourceUnit> parseAndAnalyse(string const& _source)
return sourceAndError.first; 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); BOOST_REQUIRE(!!sourceAndError.second);
return sourceAndError.second; return sourceAndError.second;
} }
@ -119,8 +131,10 @@ static ContractDefinition const* retrieveContract(ASTPointer<SourceUnit> _source
return nullptr; return nullptr;
} }
static FunctionTypePointer const& retrieveFunctionBySignature(ContractDefinition const* _contract, static FunctionTypePointer const& retrieveFunctionBySignature(
std::string const& _signature) ContractDefinition const* _contract,
std::string const& _signature
)
{ {
FixedHash<4> hash(dev::sha3(_signature)); FixedHash<4> hash(dev::sha3(_signature));
return _contract->interfaceFunctions()[hash]; return _contract->interfaceFunctions()[hash];
@ -155,8 +169,8 @@ BOOST_AUTO_TEST_CASE(double_stateVariable_declaration)
BOOST_AUTO_TEST_CASE(double_function_declaration) BOOST_AUTO_TEST_CASE(double_function_declaration)
{ {
char const* text = "contract test {\n" char const* text = "contract test {\n"
" function fun() { var x; }\n" " function fun() { uint x; }\n"
" function fun() { var x; }\n" " function fun() { uint x; }\n"
"}\n"; "}\n";
SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), DeclarationError); 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); 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() BOOST_AUTO_TEST_SUITE_END()
} }