From cc1b28b12e3f8b6a66ccbab470c55092e781b2cc Mon Sep 17 00:00:00 2001 From: Erik Kundt Date: Wed, 11 Dec 2019 12:46:47 +0100 Subject: [PATCH] Fixes segfault on empty contract w/ error recovery. --- libsolidity/interface/CompilerStack.cpp | 65 +++++++++++++------------ 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index caadd210a..9d9a0a7b3 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -281,42 +281,43 @@ bool CompilerStack::analyze() { SyntaxChecker syntaxChecker(m_errorReporter, m_optimiserSettings.runYulOptimiser); for (Source const* source: m_sourceOrder) - if (!syntaxChecker.checkSyntax(*source->ast)) + if (source->ast && !syntaxChecker.checkSyntax(*source->ast)) noErrors = false; DocStringAnalyser docStringAnalyser(m_errorReporter); for (Source const* source: m_sourceOrder) - if (!docStringAnalyser.analyseDocStrings(*source->ast)) + if (source->ast && !docStringAnalyser.analyseDocStrings(*source->ast)) noErrors = false; m_globalContext = make_shared(); NameAndTypeResolver resolver(*m_globalContext, m_evmVersion, m_scopes, m_errorReporter); for (Source const* source: m_sourceOrder) - if (!resolver.registerDeclarations(*source->ast)) + if (source->ast && !resolver.registerDeclarations(*source->ast)) return false; map sourceUnitsByName; for (auto& source: m_sources) sourceUnitsByName[source.first] = source.second.ast.get(); for (Source const* source: m_sourceOrder) - if (!resolver.performImports(*source->ast, sourceUnitsByName)) + if (source->ast && !resolver.performImports(*source->ast, sourceUnitsByName)) return false; // This is the main name and type resolution loop. Needs to be run for every contract, because // the special variables "this" and "super" must be set appropriately. for (Source const* source: m_sourceOrder) - for (ASTPointer const& node: source->ast->nodes()) - { - if (!resolver.resolveNamesAndTypes(*node)) - return false; - if (ContractDefinition* contract = dynamic_cast(node.get())) - // Note that we now reference contracts by their fully qualified names, and - // thus contracts can only conflict if declared in the same source file. This - // already causes a double-declaration error elsewhere, so we do not report - // an error here and instead silently drop any additional contracts we find. - if (m_contracts.find(contract->fullyQualifiedName()) == m_contracts.end()) - m_contracts[contract->fullyQualifiedName()].contract = contract; - } + if (source->ast) + for (ASTPointer const& node: source->ast->nodes()) + { + if (!resolver.resolveNamesAndTypes(*node)) + return false; + if (ContractDefinition* contract = dynamic_cast(node.get())) + // Note that we now reference contracts by their fully qualified names, and + // thus contracts can only conflict if declared in the same source file. This + // already causes a double-declaration error elsewhere, so we do not report + // an error here and instead silently drop any additional contracts we find. + if (m_contracts.find(contract->fullyQualifiedName()) == m_contracts.end()) + m_contracts[contract->fullyQualifiedName()].contract = contract; + } // Next, we check inheritance, overrides, function collisions and other things at // contract or function level. @@ -324,10 +325,11 @@ bool CompilerStack::analyze() // type checker. ContractLevelChecker contractLevelChecker(m_errorReporter); for (Source const* source: m_sourceOrder) - for (ASTPointer const& node: source->ast->nodes()) - if (ContractDefinition* contract = dynamic_cast(node.get())) - if (!contractLevelChecker.check(*contract)) - noErrors = false; + if (source->ast) + for (ASTPointer const& node: source->ast->nodes()) + if (ContractDefinition* contract = dynamic_cast(node.get())) + if (!contractLevelChecker.check(*contract)) + noErrors = false; // New we run full type checks that go down to the expression level. This // cannot be done earlier, because we need cross-contract types and information @@ -338,17 +340,18 @@ bool CompilerStack::analyze() // which is only done one step later. TypeChecker typeChecker(m_evmVersion, m_errorReporter); for (Source const* source: m_sourceOrder) - for (ASTPointer const& node: source->ast->nodes()) - if (ContractDefinition* contract = dynamic_cast(node.get())) - if (!typeChecker.checkTypeRequirements(*contract)) - noErrors = false; + if (source->ast) + for (ASTPointer const& node: source->ast->nodes()) + if (ContractDefinition* contract = dynamic_cast(node.get())) + if (!typeChecker.checkTypeRequirements(*contract)) + noErrors = false; if (noErrors) { // Checks that can only be done when all types of all AST nodes are known. PostTypeChecker postTypeChecker(m_errorReporter); for (Source const* source: m_sourceOrder) - if (!postTypeChecker.check(*source->ast)) + if (source->ast && !postTypeChecker.check(*source->ast)) noErrors = false; } @@ -358,14 +361,14 @@ bool CompilerStack::analyze() // variable is used before it is assigned to. CFG cfg(m_errorReporter); for (Source const* source: m_sourceOrder) - if (!cfg.constructFlow(*source->ast)) + if (source->ast && !cfg.constructFlow(*source->ast)) noErrors = false; if (noErrors) { ControlFlowAnalyzer controlFlowAnalyzer(cfg, m_errorReporter); for (Source const* source: m_sourceOrder) - if (!controlFlowAnalyzer.analyze(*source->ast)) + if (source->ast && !controlFlowAnalyzer.analyze(*source->ast)) noErrors = false; } } @@ -375,7 +378,7 @@ bool CompilerStack::analyze() // Checks for common mistakes. Only generates warnings. StaticAnalyzer staticAnalyzer(m_errorReporter); for (Source const* source: m_sourceOrder) - if (!staticAnalyzer.analyze(*source->ast)) + if (source->ast && !staticAnalyzer.analyze(*source->ast)) noErrors = false; } @@ -384,7 +387,8 @@ bool CompilerStack::analyze() // Check for state mutability in every function. vector> ast; for (Source const* source: m_sourceOrder) - ast.push_back(source->ast); + if (source->ast) + ast.push_back(source->ast); if (!ViewPureChecker(ast, m_errorReporter).check()) noErrors = false; @@ -394,7 +398,8 @@ bool CompilerStack::analyze() { ModelChecker modelChecker(m_errorReporter, m_smtlib2Responses, m_readFile, m_enabledSMTSolvers); for (Source const* source: m_sourceOrder) - modelChecker.analyze(*source->ast); + if (source->ast) + modelChecker.analyze(*source->ast); m_unhandledSMTLib2Queries += modelChecker.unhandledQueries(); } }