Add CLI and JSON option to select SMTChecker targets

This commit is contained in:
Leonardo Alt
2021-01-20 17:35:37 +01:00
parent 86051dc099
commit 3b23cadbdc
131 changed files with 3771 additions and 119 deletions
+2
View File
@@ -102,6 +102,8 @@ set(sources
formal/EncodingContext.h
formal/ModelChecker.cpp
formal/ModelChecker.h
formal/ModelCheckerSettings.cpp
formal/ModelCheckerSettings.h
formal/Predicate.cpp
formal/Predicate.h
formal/PredicateInstance.cpp
+45 -41
View File
@@ -39,11 +39,12 @@ BMC::BMC(
map<h256, string> const& _smtlib2Responses,
ReadCallback::Callback const& _smtCallback,
smtutil::SMTSolverChoice _enabledSolvers,
optional<unsigned> _timeout
ModelCheckerSettings const& _settings
):
SMTEncoder(_context),
m_interface(make_unique<smtutil::SMTPortfolio>(_smtlib2Responses, _smtCallback, _enabledSolvers, _timeout)),
m_outerErrorReporter(_errorReporter)
m_interface(make_unique<smtutil::SMTPortfolio>(_smtlib2Responses, _smtCallback, _enabledSolvers, _settings.timeout)),
m_outerErrorReporter(_errorReporter),
m_settings(_settings)
{
#if defined (HAVE_Z3) || defined (HAVE_CVC4)
if (_enabledSolvers.some())
@@ -58,7 +59,7 @@ BMC::BMC(
#endif
}
void BMC::analyze(SourceUnit const& _source, map<ASTNode const*, set<VerificationTarget::Type>> _solvedTargets)
void BMC::analyze(SourceUnit const& _source, map<ASTNode const*, set<VerificationTargetType>> _solvedTargets)
{
solAssert(_source.annotation().experimentalFeatures.count(ExperimentalFeature::SMTChecker), "");
@@ -193,7 +194,7 @@ bool BMC::visit(IfStatement const& _node)
// specific input values.
if (isRootFunction())
addVerificationTarget(
VerificationTarget::Type::ConstantCondition,
VerificationTargetType::ConstantCondition,
expr(_node.condition()),
&_node.condition()
);
@@ -230,7 +231,7 @@ bool BMC::visit(Conditional const& _op)
if (isRootFunction())
addVerificationTarget(
VerificationTarget::Type::ConstantCondition,
VerificationTargetType::ConstantCondition,
expr(_op.condition()),
&_op.condition()
);
@@ -263,7 +264,7 @@ bool BMC::visit(WhileStatement const& _node)
_node.condition().accept(*this);
if (isRootFunction())
addVerificationTarget(
VerificationTarget::Type::ConstantCondition,
VerificationTargetType::ConstantCondition,
expr(_node.condition()),
&_node.condition()
);
@@ -273,7 +274,7 @@ bool BMC::visit(WhileStatement const& _node)
_node.condition().accept(*this);
if (isRootFunction())
addVerificationTarget(
VerificationTarget::Type::ConstantCondition,
VerificationTargetType::ConstantCondition,
expr(_node.condition()),
&_node.condition()
);
@@ -317,7 +318,7 @@ bool BMC::visit(ForStatement const& _node)
_node.condition()->accept(*this);
if (isRootFunction())
addVerificationTarget(
VerificationTarget::Type::ConstantCondition,
VerificationTargetType::ConstantCondition,
expr(*_node.condition()),
_node.condition()
);
@@ -392,7 +393,7 @@ void BMC::endVisit(UnaryOperation const& _op)
if (_op.getOperator() == Token::Sub && smt::isInteger(*_op.annotation().type))
addVerificationTarget(
VerificationTarget::Type::UnderOverflow,
VerificationTargetType::UnderOverflow,
expr(_op),
&_op
);
@@ -438,7 +439,7 @@ void BMC::endVisit(FunctionCall const& _funCall)
smtutil::Expression thisBalance = m_context.state().balance();
addVerificationTarget(
VerificationTarget::Type::Balance,
VerificationTargetType::Balance,
thisBalance < expr(*value),
&_funCall
);
@@ -474,7 +475,7 @@ void BMC::visitAssert(FunctionCall const& _funCall)
solAssert(args.size() == 1, "");
solAssert(args.front()->annotation().type->category() == Type::Category::Bool, "");
addVerificationTarget(
VerificationTarget::Type::Assert,
VerificationTargetType::Assert,
expr(*args.front()),
&_funCall
);
@@ -487,7 +488,7 @@ void BMC::visitRequire(FunctionCall const& _funCall)
solAssert(args.front()->annotation().type->category() == Type::Category::Bool, "");
if (isRootFunction())
addVerificationTarget(
VerificationTarget::Type::ConstantCondition,
VerificationTargetType::ConstantCondition,
expr(*args.front()),
args.front().get()
);
@@ -497,7 +498,7 @@ void BMC::visitAddMulMod(FunctionCall const& _funCall)
{
solAssert(_funCall.arguments().at(2), "");
addVerificationTarget(
VerificationTarget::Type::DivByZero,
VerificationTargetType::DivByZero,
expr(*_funCall.arguments().at(2)),
&_funCall
);
@@ -572,7 +573,7 @@ pair<smtutil::Expression, smtutil::Expression> BMC::arithmeticOperation(
// Unchecked does not disable div by 0 checks.
if (_op == Token::Div || _op == Token::Mod)
addVerificationTarget(
VerificationTarget::Type::DivByZero,
VerificationTargetType::DivByZero,
_right,
&_expression
);
@@ -590,24 +591,24 @@ pair<smtutil::Expression, smtutil::Expression> BMC::arithmeticOperation(
if (_op == Token::Mod)
return values;
VerificationTarget::Type type;
VerificationTargetType type;
// The order matters here:
// If _op is Div and intType is signed, we only care about overflow.
if (_op == Token::Div)
{
if (intType->isSigned())
// Signed division can only overflow.
type = VerificationTarget::Type::Overflow;
type = VerificationTargetType::Overflow;
else
// Unsigned division cannot underflow/overflow.
return values;
}
else if (intType->isSigned())
type = VerificationTarget::Type::UnderOverflow;
type = VerificationTargetType::UnderOverflow;
else if (_op == Token::Sub)
type = VerificationTarget::Type::Underflow;
type = VerificationTargetType::Underflow;
else if (_op == Token::Add || _op == Token::Mul)
type = VerificationTarget::Type::Overflow;
type = VerificationTargetType::Overflow;
else
solAssert(false, "");
@@ -674,26 +675,26 @@ void BMC::checkVerificationTarget(BMCVerificationTarget& _target)
{
switch (_target.type)
{
case VerificationTarget::Type::ConstantCondition:
case VerificationTargetType::ConstantCondition:
checkConstantCondition(_target);
break;
case VerificationTarget::Type::Underflow:
case VerificationTargetType::Underflow:
checkUnderflow(_target);
break;
case VerificationTarget::Type::Overflow:
case VerificationTargetType::Overflow:
checkOverflow(_target);
break;
case VerificationTarget::Type::UnderOverflow:
case VerificationTargetType::UnderOverflow:
checkUnderflow(_target);
checkOverflow(_target);
break;
case VerificationTarget::Type::DivByZero:
case VerificationTargetType::DivByZero:
checkDivByZero(_target);
break;
case VerificationTarget::Type::Balance:
case VerificationTargetType::Balance:
checkBalance(_target);
break;
case VerificationTarget::Type::Assert:
case VerificationTargetType::Assert:
checkAssert(_target);
break;
default:
@@ -714,15 +715,15 @@ void BMC::checkConstantCondition(BMCVerificationTarget& _target)
void BMC::checkUnderflow(BMCVerificationTarget& _target)
{
solAssert(
_target.type == VerificationTarget::Type::Underflow ||
_target.type == VerificationTarget::Type::UnderOverflow,
_target.type == VerificationTargetType::Underflow ||
_target.type == VerificationTargetType::UnderOverflow,
""
);
if (
m_solvedTargets.count(_target.expression) && (
m_solvedTargets.at(_target.expression).count(VerificationTarget::Type::Underflow) ||
m_solvedTargets.at(_target.expression).count(VerificationTarget::Type::UnderOverflow)
m_solvedTargets.at(_target.expression).count(VerificationTargetType::Underflow) ||
m_solvedTargets.at(_target.expression).count(VerificationTargetType::UnderOverflow)
)
)
return;
@@ -747,15 +748,15 @@ void BMC::checkUnderflow(BMCVerificationTarget& _target)
void BMC::checkOverflow(BMCVerificationTarget& _target)
{
solAssert(
_target.type == VerificationTarget::Type::Overflow ||
_target.type == VerificationTarget::Type::UnderOverflow,
_target.type == VerificationTargetType::Overflow ||
_target.type == VerificationTargetType::UnderOverflow,
""
);
if (
m_solvedTargets.count(_target.expression) && (
m_solvedTargets.at(_target.expression).count(VerificationTarget::Type::Overflow) ||
m_solvedTargets.at(_target.expression).count(VerificationTarget::Type::UnderOverflow)
m_solvedTargets.at(_target.expression).count(VerificationTargetType::Overflow) ||
m_solvedTargets.at(_target.expression).count(VerificationTargetType::UnderOverflow)
)
)
return;
@@ -779,11 +780,11 @@ void BMC::checkOverflow(BMCVerificationTarget& _target)
void BMC::checkDivByZero(BMCVerificationTarget& _target)
{
solAssert(_target.type == VerificationTarget::Type::DivByZero, "");
solAssert(_target.type == VerificationTargetType::DivByZero, "");
if (
m_solvedTargets.count(_target.expression) &&
m_solvedTargets.at(_target.expression).count(VerificationTarget::Type::DivByZero)
m_solvedTargets.at(_target.expression).count(VerificationTargetType::DivByZero)
)
return;
@@ -802,7 +803,7 @@ void BMC::checkDivByZero(BMCVerificationTarget& _target)
void BMC::checkBalance(BMCVerificationTarget& _target)
{
solAssert(_target.type == VerificationTarget::Type::Balance, "");
solAssert(_target.type == VerificationTargetType::Balance, "");
checkCondition(
_target.constraints && _target.value,
_target.callStack,
@@ -817,7 +818,7 @@ void BMC::checkBalance(BMCVerificationTarget& _target)
void BMC::checkAssert(BMCVerificationTarget& _target)
{
solAssert(_target.type == VerificationTarget::Type::Assert, "");
solAssert(_target.type == VerificationTargetType::Assert, "");
if (
m_solvedTargets.count(_target.expression) &&
@@ -837,11 +838,14 @@ void BMC::checkAssert(BMCVerificationTarget& _target)
}
void BMC::addVerificationTarget(
VerificationTarget::Type _type,
VerificationTargetType _type,
smtutil::Expression const& _value,
Expression const* _expression
)
{
if (!m_settings.targets.has(_type))
return;
BMCVerificationTarget target{
{
_type,
@@ -852,7 +856,7 @@ void BMC::addVerificationTarget(
m_callStack,
modelExpressions()
};
if (_type == VerificationTarget::Type::ConstantCondition)
if (_type == VerificationTargetType::ConstantCondition)
checkVerificationTarget(target);
else
m_verificationTargets.emplace_back(move(target));
+7 -4
View File
@@ -30,6 +30,7 @@
#include <libsolidity/formal/EncodingContext.h>
#include <libsolidity/formal/ModelCheckerSettings.h>
#include <libsolidity/formal/SMTEncoder.h>
#include <libsolidity/interface/ReadFile.h>
@@ -62,10 +63,10 @@ public:
std::map<h256, std::string> const& _smtlib2Responses,
ReadCallback::Callback const& _smtCallback,
smtutil::SMTSolverChoice _enabledSolvers,
std::optional<unsigned> timeout
ModelCheckerSettings const& _settings
);
void analyze(SourceUnit const& _sources, std::map<ASTNode const*, std::set<VerificationTarget::Type>> _solvedTargets);
void analyze(SourceUnit const& _sources, std::map<ASTNode const*, std::set<VerificationTargetType>> _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
@@ -140,7 +141,7 @@ private:
void checkBalance(BMCVerificationTarget& _target);
void checkAssert(BMCVerificationTarget& _target);
void addVerificationTarget(
VerificationTarget::Type _type,
VerificationTargetType _type,
smtutil::Expression const& _value,
Expression const* _expression
);
@@ -186,7 +187,9 @@ private:
std::vector<BMCVerificationTarget> m_verificationTargets;
/// Targets that were already proven.
std::map<ASTNode const*, std::set<VerificationTarget::Type>> m_solvedTargets;
std::map<ASTNode const*, std::set<VerificationTargetType>> m_solvedTargets;
ModelCheckerSettings const& m_settings;
};
}
+25 -21
View File
@@ -54,12 +54,12 @@ CHC::CHC(
[[maybe_unused]] map<util::h256, string> const& _smtlib2Responses,
[[maybe_unused]] ReadCallback::Callback const& _smtCallback,
SMTSolverChoice _enabledSolvers,
optional<unsigned> _timeout
ModelCheckerSettings const& _settings
):
SMTEncoder(_context),
m_outerErrorReporter(_errorReporter),
m_enabledSolvers(_enabledSolvers),
m_queryTimeout(_timeout)
m_settings(_settings)
{
bool usesZ3 = _enabledSolvers.z3;
#ifdef HAVE_Z3
@@ -68,7 +68,7 @@ CHC::CHC(
usesZ3 = false;
#endif
if (!usesZ3)
m_interface = make_unique<CHCSmtLib2Interface>(_smtlib2Responses, _smtCallback, m_queryTimeout);
m_interface = make_unique<CHCSmtLib2Interface>(_smtlib2Responses, _smtCallback, m_settings.timeout);
}
void CHC::analyze(SourceUnit const& _source)
@@ -606,14 +606,14 @@ void CHC::visitAssert(FunctionCall const& _funCall)
solAssert(m_currentContract, "");
solAssert(m_currentFunction, "");
auto errorCondition = !m_context.expression(*args.front())->currentValue();
verificationTargetEncountered(&_funCall, VerificationTarget::Type::Assert, errorCondition);
verificationTargetEncountered(&_funCall, VerificationTargetType::Assert, errorCondition);
}
void CHC::visitAddMulMod(FunctionCall const& _funCall)
{
solAssert(_funCall.arguments().at(2), "");
verificationTargetEncountered(&_funCall, VerificationTarget::Type::DivByZero, expr(*_funCall.arguments().at(2)) == 0);
verificationTargetEncountered(&_funCall, VerificationTargetType::DivByZero, expr(*_funCall.arguments().at(2)) == 0);
SMTEncoder::visitAddMulMod(_funCall);
}
@@ -773,7 +773,7 @@ void CHC::makeArrayPopVerificationTarget(FunctionCall const& _arrayPop)
auto symbArray = dynamic_pointer_cast<SymbolicArrayVariable>(m_context.expression(memberAccess->expression()));
solAssert(symbArray, "");
verificationTargetEncountered(&_arrayPop, VerificationTarget::Type::PopEmptyArray, symbArray->length() <= 0);
verificationTargetEncountered(&_arrayPop, VerificationTargetType::PopEmptyArray, symbArray->length() <= 0);
}
pair<smtutil::Expression, smtutil::Expression> CHC::arithmeticOperation(
@@ -786,7 +786,7 @@ pair<smtutil::Expression, smtutil::Expression> CHC::arithmeticOperation(
{
// Unchecked does not disable div by 0 checks.
if (_op == Token::Mod || _op == Token::Div)
verificationTargetEncountered(&_expression, VerificationTarget::Type::DivByZero, _right == 0);
verificationTargetEncountered(&_expression, VerificationTargetType::DivByZero, _right == 0);
auto values = SMTEncoder::arithmeticOperation(_op, _left, _right, _commonType, _expression);
@@ -805,16 +805,16 @@ pair<smtutil::Expression, smtutil::Expression> CHC::arithmeticOperation(
return values;
if (_op == Token::Div)
verificationTargetEncountered(&_expression, VerificationTarget::Type::Overflow, values.second > intType->maxValue());
verificationTargetEncountered(&_expression, VerificationTargetType::Overflow, values.second > intType->maxValue());
else if (intType->isSigned())
{
verificationTargetEncountered(&_expression, VerificationTarget::Type::Underflow, values.second < intType->minValue());
verificationTargetEncountered(&_expression, VerificationTarget::Type::Overflow, values.second > intType->maxValue());
verificationTargetEncountered(&_expression, VerificationTargetType::Underflow, values.second < intType->minValue());
verificationTargetEncountered(&_expression, VerificationTargetType::Overflow, values.second > intType->maxValue());
}
else if (_op == Token::Sub)
verificationTargetEncountered(&_expression, VerificationTarget::Type::Underflow, values.second < intType->minValue());
verificationTargetEncountered(&_expression, VerificationTargetType::Underflow, values.second < intType->minValue());
else if (_op == Token::Add || _op == Token::Mul)
verificationTargetEncountered(&_expression, VerificationTarget::Type::Overflow, values.second > intType->maxValue());
verificationTargetEncountered(&_expression, VerificationTargetType::Overflow, values.second > intType->maxValue());
else
solAssert(false, "");
return values;
@@ -843,7 +843,7 @@ void CHC::resetSourceAnalysis()
if (usesZ3)
{
/// z3::fixedpoint does not have a reset mechanism, so we need to create another.
m_interface.reset(new Z3CHCInterface(m_queryTimeout));
m_interface.reset(new Z3CHCInterface(m_settings.timeout));
auto z3Interface = dynamic_cast<Z3CHCInterface const*>(m_interface.get());
solAssert(z3Interface, "");
m_context.setSolver(z3Interface->z3Interface());
@@ -1324,10 +1324,14 @@ pair<CheckResult, CHCSolverInterface::CexGraph> CHC::query(smtutil::Expression c
void CHC::verificationTargetEncountered(
ASTNode const* const _errorNode,
VerificationTarget::Type _type,
VerificationTargetType _type,
smtutil::Expression const& _errorCondition
)
{
if (!m_settings.targets.has(_type))
return;
solAssert(m_currentContract || m_currentFunction, "");
SourceUnit const* source = m_currentContract ? sourceUnitContaining(*m_currentContract) : sourceUnitContaining(*m_currentFunction);
solAssert(source, "");
@@ -1384,15 +1388,15 @@ void CHC::checkVerificationTargets()
string errorType;
ErrorId errorReporterId;
if (target.type == VerificationTarget::Type::PopEmptyArray)
if (target.type == VerificationTargetType::PopEmptyArray)
{
solAssert(dynamic_cast<FunctionCall const*>(target.errorNode), "");
errorType = "Empty array \"pop\"";
errorReporterId = 2529_error;
}
else if (
target.type == VerificationTarget::Type::Underflow ||
target.type == VerificationTarget::Type::Overflow
target.type == VerificationTargetType::Underflow ||
target.type == VerificationTargetType::Overflow
)
{
auto const* expr = dynamic_cast<Expression const*>(target.errorNode);
@@ -1401,23 +1405,23 @@ void CHC::checkVerificationTargets()
if (!intType)
intType = TypeProvider::uint256();
if (target.type == VerificationTarget::Type::Underflow)
if (target.type == VerificationTargetType::Underflow)
{
errorType = "Underflow (resulting value less than " + formatNumberReadable(intType->minValue()) + ")";
errorReporterId = 3944_error;
}
else if (target.type == VerificationTarget::Type::Overflow)
else if (target.type == VerificationTargetType::Overflow)
{
errorType = "Overflow (resulting value larger than " + formatNumberReadable(intType->maxValue()) + ")";
errorReporterId = 4984_error;
}
}
else if (target.type == VerificationTarget::Type::DivByZero)
else if (target.type == VerificationTargetType::DivByZero)
{
errorType = "Division by zero";
errorReporterId = 4281_error;
}
else if (target.type == VerificationTarget::Type::Assert)
else if (target.type == VerificationTargetType::Assert)
{
errorType = "Assertion violation";
errorReporterId = 6328_error;
+8 -8
View File
@@ -31,6 +31,7 @@
#pragma once
#include <libsolidity/formal/ModelCheckerSettings.h>
#include <libsolidity/formal/Predicate.h>
#include <libsolidity/formal/SMTEncoder.h>
@@ -56,13 +57,13 @@ public:
std::map<util::h256, std::string> const& _smtlib2Responses,
ReadCallback::Callback const& _smtCallback,
smtutil::SMTSolverChoice _enabledSolvers,
std::optional<unsigned> timeout
ModelCheckerSettings const& _settings
);
void analyze(SourceUnit const& _sources);
std::map<ASTNode const*, std::set<VerificationTarget::Type>> const& safeTargets() const { return m_safeTargets; }
std::map<ASTNode const*, std::set<VerificationTarget::Type>> const& unsafeTargets() const { return m_unsafeTargets; }
std::map<ASTNode const*, std::set<VerificationTargetType>> const& safeTargets() const { return m_safeTargets; }
std::map<ASTNode const*, std::set<VerificationTargetType>> const& unsafeTargets() const { return m_unsafeTargets; }
/// This is used if the Horn solver is not directly linked into this binary.
/// @returns a list of inputs to the Horn solver that were not part of the argument to
@@ -199,7 +200,7 @@ private:
/// @returns <false, model> otherwise.
std::pair<smtutil::CheckResult, smtutil::CHCSolverInterface::CexGraph> query(smtutil::Expression const& _query, langutil::SourceLocation const& _location);
void verificationTargetEncountered(ASTNode const* const _errorNode, VerificationTarget::Type _type, smtutil::Expression const& _errorCondition);
void verificationTargetEncountered(ASTNode const* const _errorNode, VerificationTargetType _type, smtutil::Expression const& _errorCondition);
void checkVerificationTargets();
// Forward declaration. Definition is below.
@@ -321,9 +322,9 @@ private:
std::map<unsigned, CHCVerificationTarget> m_verificationTargets;
/// Targets proven safe.
std::map<ASTNode const*, std::set<VerificationTarget::Type>> m_safeTargets;
std::map<ASTNode const*, std::set<VerificationTargetType>> m_safeTargets;
/// Targets proven unsafe.
std::map<ASTNode const*, std::set<VerificationTarget::Type>> m_unsafeTargets;
std::map<ASTNode const*, std::set<VerificationTargetType>> m_unsafeTargets;
//@}
/// Control-flow.
@@ -369,8 +370,7 @@ private:
/// SMT solvers that are chosen at runtime.
smtutil::SMTSolverChoice m_enabledSolvers;
/// SMT query timeout in seconds.
std::optional<unsigned> m_queryTimeout;
ModelCheckerSettings const& m_settings;
};
}
+2 -2
View File
@@ -36,8 +36,8 @@ ModelChecker::ModelChecker(
):
m_settings(_settings),
m_context(),
m_bmc(m_context, _errorReporter, _smtlib2Responses, _smtCallback, _enabledSolvers, _settings.timeout),
m_chc(m_context, _errorReporter, _smtlib2Responses, _smtCallback, _enabledSolvers, _settings.timeout)
m_bmc(m_context, _errorReporter, _smtlib2Responses, _smtCallback, _enabledSolvers, m_settings),
m_chc(m_context, _errorReporter, _smtlib2Responses, _smtCallback, _enabledSolvers, m_settings)
{
}
+1 -36
View File
@@ -26,14 +26,13 @@
#include <libsolidity/formal/BMC.h>
#include <libsolidity/formal/CHC.h>
#include <libsolidity/formal/EncodingContext.h>
#include <libsolidity/formal/ModelCheckerSettings.h>
#include <libsolidity/interface/ReadFile.h>
#include <libsmtutil/SolverInterface.h>
#include <liblangutil/ErrorReporter.h>
#include <optional>
namespace solidity::langutil
{
class ErrorReporter;
@@ -43,40 +42,6 @@ struct SourceLocation;
namespace solidity::frontend
{
struct ModelCheckerEngine
{
bool bmc = false;
bool chc = false;
static constexpr ModelCheckerEngine All() { return {true, true}; }
static constexpr ModelCheckerEngine BMC() { return {true, false}; }
static constexpr ModelCheckerEngine CHC() { return {false, true}; }
static constexpr ModelCheckerEngine None() { return {false, false}; }
bool none() const { return !any(); }
bool any() const { return bmc || chc; }
bool all() const { return bmc && chc; }
static std::optional<ModelCheckerEngine> fromString(std::string const& _engine)
{
static std::map<std::string, ModelCheckerEngine> engineMap{
{"all", All()},
{"bmc", BMC()},
{"chc", CHC()},
{"none", None()}
};
if (engineMap.count(_engine))
return engineMap.at(_engine);
return {};
}
};
struct ModelCheckerSettings
{
ModelCheckerEngine engine = ModelCheckerEngine::All();
std::optional<unsigned> timeout;
};
class ModelChecker
{
public:
@@ -0,0 +1,55 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
#include <libsolidity/formal/ModelCheckerSettings.h>
#include <optional>
#include <range/v3/view.hpp>
using namespace std;
using namespace ranges;
using namespace solidity;
using namespace solidity::frontend;
std::optional<ModelCheckerTargets> ModelCheckerTargets::fromString(string const& _targets)
{
using TargetType = VerificationTargetType;
static map<string, TargetType> const targetStrings{
{"constantCondition", TargetType::ConstantCondition},
{"underflow", TargetType::Underflow},
{"overflow", TargetType::Overflow},
{"divByZero", TargetType::DivByZero},
{"balance", TargetType::Balance},
{"assert", TargetType::Assert},
{"popEmptyArray", TargetType::PopEmptyArray}
};
set<TargetType> chosenTargets;
if (_targets == "all")
for (auto&& v: targetStrings | views::values)
chosenTargets.insert(v);
else
for (auto&& t: _targets | views::split(',') | ranges::to<vector<string>>())
{
if (!targetStrings.count(t))
return {};
chosenTargets.insert(targetStrings.at(t));
}
return ModelCheckerTargets{chosenTargets};
}
+77
View File
@@ -0,0 +1,77 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
#pragma once
#include <libsmtutil/SolverInterface.h>
#include <optional>
#include <set>
namespace solidity::frontend
{
struct ModelCheckerEngine
{
bool bmc = false;
bool chc = false;
static constexpr ModelCheckerEngine All() { return {true, true}; }
static constexpr ModelCheckerEngine BMC() { return {true, false}; }
static constexpr ModelCheckerEngine CHC() { return {false, true}; }
static constexpr ModelCheckerEngine None() { return {false, false}; }
bool none() const { return !any(); }
bool any() const { return bmc || chc; }
bool all() const { return bmc && chc; }
static std::optional<ModelCheckerEngine> fromString(std::string const& _engine)
{
static std::map<std::string, ModelCheckerEngine> engineMap{
{"all", All()},
{"bmc", BMC()},
{"chc", CHC()},
{"none", None()}
};
if (engineMap.count(_engine))
return engineMap.at(_engine);
return {};
}
};
enum class VerificationTargetType { ConstantCondition, Underflow, Overflow, UnderOverflow, DivByZero, Balance, Assert, PopEmptyArray };
struct ModelCheckerTargets
{
static ModelCheckerTargets All() { return *fromString("all"); }
static ModelCheckerTargets None() { return {}; }
static std::optional<ModelCheckerTargets> fromString(std::string const& _targets);
bool has(VerificationTargetType _type) const { return targets.count(_type); }
std::set<VerificationTargetType> targets;
};
struct ModelCheckerSettings
{
ModelCheckerEngine engine = ModelCheckerEngine::All();
ModelCheckerTargets targets = ModelCheckerTargets::All();
std::optional<unsigned> timeout;
};
}
+2 -1
View File
@@ -25,6 +25,7 @@
#include <libsolidity/formal/EncodingContext.h>
#include <libsolidity/formal/ModelCheckerSettings.h>
#include <libsolidity/formal/SymbolicVariables.h>
#include <libsolidity/formal/VariableUsage.h>
@@ -348,7 +349,7 @@ protected:
struct VerificationTarget
{
enum class Type { ConstantCondition, Underflow, Overflow, UnderOverflow, DivByZero, Balance, Assert, PopEmptyArray } type;
VerificationTargetType type;
smtutil::Expression value;
smtutil::Expression constraints;
};
+1 -1
View File
@@ -29,7 +29,7 @@
#include <libsolidity/interface/Version.h>
#include <libsolidity/interface/DebugSettings.h>
#include <libsolidity/formal/ModelChecker.h>
#include <libsolidity/formal/ModelCheckerSettings.h>
#include <libsmtutil/SolverInterface.h>
+11 -1
View File
@@ -434,7 +434,7 @@ std::optional<Json::Value> checkSettingsKeys(Json::Value const& _input)
std::optional<Json::Value> checkModelCheckerSettingsKeys(Json::Value const& _input)
{
static set<string> keys{"engine", "timeout"};
static set<string> keys{"engine", "targets", "timeout"};
return checkKeys(_input, keys, "modelChecker");
}
@@ -908,6 +908,16 @@ std::variant<StandardCompiler::InputsAndSettings, Json::Value> StandardCompiler:
ret.modelCheckerSettings.engine = *engine;
}
if (modelCheckerSettings.isMember("targets"))
{
if (!modelCheckerSettings["targets"].isString())
return formatFatalError("JSONError", "settings.modelChecker.targets must be a string.");
std::optional<ModelCheckerTargets> targets = ModelCheckerTargets::fromString(modelCheckerSettings["targets"].asString());
if (!targets)
return formatFatalError("JSONError", "Invalid model checker targets requested.");
ret.modelCheckerSettings.targets = *targets;
}
if (modelCheckerSettings.isMember("timeout"))
{
if (!modelCheckerSettings["timeout"].isUInt())