/* 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 . */ // SPDX-License-Identifier: GPL-3.0 /** * Class that implements an SMT-based Bounded Model Checker (BMC). * Traverses the AST such that: * - Loops are unrolled * - Internal function calls are inlined * Creates verification targets for: * - Underflow/Overflow * - Constant conditions * - Assertions */ #pragma once #include #include #include #include #include #include #include #include #include using solidity::util::h256; namespace solidity::langutil { class ErrorReporter; struct ErrorId; struct SourceLocation; } namespace solidity::frontend { class BMC: public SMTEncoder { public: BMC( smt::EncodingContext& _context, langutil::UniqueErrorReporter& _errorReporter, langutil::UniqueErrorReporter& _unsupportedErrorReporter, std::map const& _smtlib2Responses, ReadCallback::Callback const& _smtCallback, ModelCheckerSettings _settings, langutil::CharStreamProvider const& _charStreamProvider ); void analyze(SourceUnit const& _sources, std::map, smt::EncodingContext::IdCompare> _solvedTargets); /// This is used if the SMT solver is not directly linked into this binary. /// @returns a list of inputs to the SMT solver that were not part of the argument to /// the constructor. std::vector unhandledQueries() { return m_interface->unhandledQueries(); } /// @returns true if _funCall should be inlined, otherwise false. /// @param _scopeContract The contract that contains the current function being analyzed. /// @param _contextContract The most derived contract, currently being analyzed. static bool shouldInlineFunctionCall( FunctionCall const& _funCall, ContractDefinition const* _scopeContract, ContractDefinition const* _contextContract ); private: /// AST visitors. /// Only nodes that lead to verification targets being built /// or checked are visited. //@{ bool visit(ContractDefinition const& _node) override; void endVisit(ContractDefinition const& _node) override; bool visit(FunctionDefinition const& _node) override; void endVisit(FunctionDefinition const& _node) override; bool visit(IfStatement const& _node) override; bool visit(Conditional const& _node) override; bool visit(WhileStatement const& _node) override; bool visit(ForStatement const& _node) override; void endVisit(UnaryOperation const& _node) override; void endVisit(FunctionCall const& _node) override; void endVisit(Return const& _node) override; bool visit(TryStatement const& _node) override; bool visit(Break const& _node) override; bool visit(Continue const& _node) override; //@} /// Visitor helpers. //@{ void visitAssert(FunctionCall const& _funCall); void visitRequire(FunctionCall const& _funCall); void visitAddMulMod(FunctionCall const& _funCall) override; void assignment(smt::SymbolicVariable& _symVar, smtutil::Expression const& _value) override; /// Visits the FunctionDefinition of the called function /// if available and inlines the return value. void inlineFunctionCall(FunctionCall const& _funCall); /// Inlines if the function call is internal or external to `this`. /// Erases knowledge about state variables if external. void internalOrExternalFunctionCall(FunctionCall const& _funCall); /// Creates underflow/overflow verification targets. std::pair arithmeticOperation( Token _op, smtutil::Expression const& _left, smtutil::Expression const& _right, Type const* _commonType, Expression const& _expression ) override; void reset(); std::pair, std::vector> modelExpressions(); //@} /// Verification targets. //@{ struct BMCVerificationTarget: VerificationTarget { Expression const* expression; std::vector callStack; std::pair, std::vector> modelExpressions; friend bool operator<(BMCVerificationTarget const& _a, BMCVerificationTarget const& _b) { return _a.expression->id() < _b.expression->id(); } }; std::string targetDescription(BMCVerificationTarget const& _target); void checkVerificationTargets(); void checkVerificationTarget(BMCVerificationTarget& _target); void checkConstantCondition(BMCVerificationTarget& _target); void checkUnderflow(BMCVerificationTarget& _target); void checkOverflow(BMCVerificationTarget& _target); void checkDivByZero(BMCVerificationTarget& _target); void checkBalance(BMCVerificationTarget& _target); void checkAssert(BMCVerificationTarget& _target); void addVerificationTarget( VerificationTargetType _type, smtutil::Expression const& _value, Expression const* _expression ); //@} /// Solver related. //@{ /// Check that a condition can be satisfied. void checkCondition( BMCVerificationTarget const& _target, smtutil::Expression _condition, std::vector const& _callStack, std::pair, std::vector> const& _modelExpressions, langutil::SourceLocation const& _location, langutil::ErrorId _errorHappens, langutil::ErrorId _errorMightHappen, std::string const& _additionalValueName = "", smtutil::Expression const* _additionalValue = nullptr ); /// Checks that a boolean condition is not constant. Do not warn if the expression /// is a literal constant. void checkBooleanNotConstant( Expression const& _condition, smtutil::Expression const& _constraints, smtutil::Expression const& _value, std::vector const& _callStack ); std::pair> checkSatisfiableAndGenerateModel(std::vector const& _expressionsToEvaluate); smtutil::CheckResult checkSatisfiable(); //@} smtutil::Expression mergeVariablesFromLoopCheckpoints(); bool isInsideLoop() const; std::unique_ptr m_interface; /// Flags used for better warning messages. bool m_loopExecutionHappened = false; bool m_externalFunctionCallHappened = false; std::vector m_verificationTargets; /// Targets proved safe by this engine. std::map, smt::EncodingContext::IdCompare> m_safeTargets; /// Targets that were already proven before this engine started. std::map, smt::EncodingContext::IdCompare> m_solvedTargets; /// Number of verification conditions that could not be proved. size_t m_unprovedAmt = 0; enum class LoopControlKind { Continue, Break }; // Current path conditions and SSA indices for break or continue statement struct LoopControl { LoopControlKind kind; smtutil::Expression pathConditions; VariableIndices variableIndices; }; // Loop control statements for every loop std::stack> m_loopCheckpoints; }; }