mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Add CLI and JSON option to select SMTChecker targets
This commit is contained in:
@@ -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
@@ -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));
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -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};
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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())
|
||||
|
||||
Reference in New Issue
Block a user