[SMTChecker] Fix imports

This commit is contained in:
Leonardo Alt 2020-09-02 10:45:47 +02:00
parent 72f8a753a9
commit 23ee011c56
13 changed files with 195 additions and 72 deletions

View File

@ -44,6 +44,7 @@ Bugfixes:
* SMTChecker: Fix internal error on lvalue unary operators with tuples. * SMTChecker: Fix internal error on lvalue unary operators with tuples.
* SMTChecker: Fix internal error on tuple assignment. * SMTChecker: Fix internal error on tuple assignment.
* SMTChecker: Fix internal error on tuples of one element that have tuple type. * SMTChecker: Fix internal error on tuples of one element that have tuple type.
* SMTChecker: Fix internal error when using imported code.
* SMTChecker: Fix soundness of array ``pop``. * SMTChecker: Fix soundness of array ``pop``.
* Type Checker: Disallow ``using for`` directive inside interfaces. * Type Checker: Disallow ``using for`` directive inside interfaces.
* Type Checker: Disallow signed literals as exponent in exponentiation operator. * Type Checker: Disallow signed literals as exponent in exponentiation operator.

View File

@ -50,6 +50,7 @@ void CHCSmtLib2Interface::reset()
{ {
m_accumulatedOutput.clear(); m_accumulatedOutput.clear();
m_variables.clear(); m_variables.clear();
m_unhandledQueries.clear();
} }
void CHCSmtLib2Interface::registerRelation(Expression const& _expr) void CHCSmtLib2Interface::registerRelation(Expression const& _expr)

View File

@ -43,19 +43,19 @@ using namespace solidity::frontend;
CHC::CHC( CHC::CHC(
smt::EncodingContext& _context, smt::EncodingContext& _context,
ErrorReporter& _errorReporter, ErrorReporter& _errorReporter,
map<util::h256, string> const& _smtlib2Responses, [[maybe_unused]] map<util::h256, string> const& _smtlib2Responses,
ReadCallback::Callback const& _smtCallback, [[maybe_unused]] ReadCallback::Callback const& _smtCallback,
[[maybe_unused]] smtutil::SMTSolverChoice _enabledSolvers smtutil::SMTSolverChoice _enabledSolvers
): ):
SMTEncoder(_context), SMTEncoder(_context),
m_outerErrorReporter(_errorReporter), m_outerErrorReporter(_errorReporter),
m_enabledSolvers(_enabledSolvers) m_enabledSolvers(_enabledSolvers)
{ {
#ifdef HAVE_Z3 bool usesZ3 = _enabledSolvers.z3;
if (_enabledSolvers.z3) #ifndef HAVE_Z3
m_interface = make_unique<smtutil::Z3CHCInterface>(); usesZ3 = false;
#endif #endif
if (!m_interface) if (!usesZ3)
m_interface = make_unique<smtutil::CHCSmtLib2Interface>(_smtlib2Responses, _smtCallback); m_interface = make_unique<smtutil::CHCSmtLib2Interface>(_smtlib2Responses, _smtCallback);
} }
@ -63,30 +63,8 @@ void CHC::analyze(SourceUnit const& _source)
{ {
solAssert(_source.annotation().experimentalFeatures.count(ExperimentalFeature::SMTChecker), ""); solAssert(_source.annotation().experimentalFeatures.count(ExperimentalFeature::SMTChecker), "");
bool usesZ3 = false;
#ifdef HAVE_Z3
usesZ3 = m_enabledSolvers.z3;
if (usesZ3)
{
auto z3Interface = dynamic_cast<smtutil::Z3CHCInterface const*>(m_interface.get());
solAssert(z3Interface, "");
m_context.setSolver(z3Interface->z3Interface());
}
#endif
if (!usesZ3)
{
auto smtlib2Interface = dynamic_cast<smtutil::CHCSmtLib2Interface const*>(m_interface.get());
solAssert(smtlib2Interface, "");
m_context.setSolver(smtlib2Interface->smtlib2Interface());
}
m_context.clear();
m_context.setAssertionAccumulation(false);
resetSourceAnalysis(); resetSourceAnalysis();
m_genesisPredicate = createSymbolicBlock(arity0FunctionSort(), "genesis");
addRule(genesis(), "genesis");
set<SourceUnit const*, IdCompare> sources; set<SourceUnit const*, IdCompare> sources;
sources.insert(&_source); sources.insert(&_source);
for (auto const& source: _source.referencedSourceUnits(true)) for (auto const& source: _source.referencedSourceUnits(true))
@ -131,7 +109,7 @@ bool CHC::visit(ContractDefinition const& _contract)
void CHC::endVisit(ContractDefinition const& _contract) void CHC::endVisit(ContractDefinition const& _contract)
{ {
auto implicitConstructor = (*m_implicitConstructorPredicate)({}); auto implicitConstructor = (*m_implicitConstructorPredicate)({});
connectBlocks(genesis(), implicitConstructor); addRule(implicitConstructor, implicitConstructor.name);
m_currentBlock = implicitConstructor; m_currentBlock = implicitConstructor;
m_context.addAssertion(m_error.currentValue() == 0); m_context.addAssertion(m_error.currentValue() == 0);
@ -156,7 +134,7 @@ bool CHC::visit(FunctionDefinition const& _function)
{ {
if (!_function.isImplemented()) if (!_function.isImplemented())
{ {
connectBlocks(genesis(), summary(_function)); addRule(summary(_function), "summary_function_" + to_string(_function.id()));
return false; return false;
} }
@ -184,7 +162,7 @@ bool CHC::visit(FunctionDefinition const& _function)
if (_function.isConstructor()) if (_function.isConstructor())
connectBlocks(m_currentBlock, functionPred); connectBlocks(m_currentBlock, functionPred);
else else
connectBlocks(genesis(), functionPred); addRule(functionPred, functionPred.name);
m_context.addAssertion(m_error.currentValue() == 0); m_context.addAssertion(m_error.currentValue() == 0);
for (auto const* var: m_stateVariables) for (auto const* var: m_stateVariables)
@ -676,6 +654,30 @@ void CHC::resetSourceAnalysis()
m_interfaces.clear(); m_interfaces.clear();
m_nondetInterfaces.clear(); m_nondetInterfaces.clear();
Predicate::reset(); Predicate::reset();
m_blockCounter = 0;
bool usesZ3 = false;
#ifdef HAVE_Z3
usesZ3 = m_enabledSolvers.z3;
if (usesZ3)
{
/// z3::fixedpoint does not have a reset mechanism, so we need to create another.
m_interface.reset(new smtutil::Z3CHCInterface());
auto z3Interface = dynamic_cast<smtutil::Z3CHCInterface const*>(m_interface.get());
solAssert(z3Interface, "");
m_context.setSolver(z3Interface->z3Interface());
}
#endif
if (!usesZ3)
{
auto smtlib2Interface = dynamic_cast<smtutil::CHCSmtLib2Interface*>(m_interface.get());
smtlib2Interface->reset();
solAssert(smtlib2Interface, "");
m_context.setSolver(smtlib2Interface->smtlib2Interface());
}
m_context.clear();
m_context.setAssertionAccumulation(false);
} }
void CHC::resetContractAnalysis() void CHC::resetContractAnalysis()
@ -861,27 +863,23 @@ void CHC::defineInterfacesAndSummaries(SourceUnit const& _source)
{ {
for (auto const& node: _source.nodes()) for (auto const& node: _source.nodes())
if (auto const* contract = dynamic_cast<ContractDefinition const*>(node.get())) if (auto const* contract = dynamic_cast<ContractDefinition const*>(node.get()))
{
string suffix = contract->name() + "_" + to_string(contract->id());
m_interfaces[contract] = createSymbolicBlock(interfaceSort(*contract), "interface_" + suffix);
m_nondetInterfaces[contract] = createSymbolicBlock(nondetInterfaceSort(*contract), "nondet_interface_" + suffix);
for (auto const* var: stateVariablesIncludingInheritedAndPrivate(*contract))
if (!m_context.knownVariable(*var))
createVariable(*var);
/// Base nondeterministic interface that allows
/// 0 steps to be taken, used as base for the inductive
/// rule for each function.
auto const& iface = *m_nondetInterfaces.at(contract);
auto state0 = stateVariablesAtIndex(0, *contract);
addRule(iface(state0 + state0), "base_nondet");
for (auto const* base: contract->annotation().linearizedBaseContracts) for (auto const* base: contract->annotation().linearizedBaseContracts)
{
for (auto const* var: SMTEncoder::stateVariablesIncludingInheritedAndPrivate(*base))
if (!m_context.knownVariable(*var))
createVariable(*var);
if (!m_interfaces.count(base))
{
solAssert(!m_nondetInterfaces.count(base), "");
string suffix = base->name() + "_" + to_string(base->id());
m_interfaces.emplace(base, createSymbolicBlock(interfaceSort(*base), "interface_" + suffix, base));
m_nondetInterfaces.emplace(base, createSymbolicBlock(nondetInterfaceSort(*base), "nondet_interface_" + suffix, base));
/// Base nondeterministic interface that allows
/// 0 steps to be taken, used as base for the inductive
/// rule for each function.
auto const* iface = m_nondetInterfaces.at(base);
auto state0 = stateVariablesAtIndex(0, *base);
addRule((*iface)(state0 + state0), "base_nondet");
}
for (auto const* function: base->definedFunctions()) for (auto const* function: base->definedFunctions())
{ {
for (auto var: function->parameters()) for (auto var: function->parameters())
@ -900,13 +898,11 @@ void CHC::defineInterfacesAndSummaries(SourceUnit const& _source)
!base->isInterface() !base->isInterface()
) )
{ {
auto state1 = stateVariablesAtIndex(1, *base); auto state1 = stateVariablesAtIndex(1, *contract);
auto state2 = stateVariablesAtIndex(2, *base); auto state2 = stateVariablesAtIndex(2, *contract);
auto const* iface = m_nondetInterfaces.at(base); auto nondetPre = iface(state0 + state1);
auto state0 = stateVariablesAtIndex(0, *base); auto nondetPost = iface(state0 + state2);
auto nondetPre = (*iface)(state0 + state1);
auto nondetPost = (*iface)(state0 + state2);
vector<smtutil::Expression> args{m_error.currentValue()}; vector<smtutil::Expression> args{m_error.currentValue()};
args += state1 + args += state1 +
@ -915,10 +911,10 @@ void CHC::defineInterfacesAndSummaries(SourceUnit const& _source)
applyMap(function->parameters(), [this](auto _var) { return valueAtIndex(*_var, 1); }) + applyMap(function->parameters(), [this](auto _var) { return valueAtIndex(*_var, 1); }) +
applyMap(function->returnParameters(), [this](auto _var) { return valueAtIndex(*_var, 1); }); applyMap(function->returnParameters(), [this](auto _var) { return valueAtIndex(*_var, 1); });
connectBlocks(nondetPre, nondetPost, (*m_summaries.at(base).at(function))(args)); connectBlocks(nondetPre, nondetPost, (*m_summaries.at(contract).at(function))(args));
} }
}
} }
}
} }
smtutil::Expression CHC::interface() smtutil::Expression CHC::interface()
@ -1210,6 +1206,16 @@ void CHC::addVerificationTarget(
smtutil::Expression _errorId smtutil::Expression _errorId
) )
{ {
solAssert(m_currentContract || m_currentFunction, "");
SourceUnit const* source = nullptr;
if (m_currentContract)
source = sourceUnitContaining(*m_currentContract);
else
source = sourceUnitContaining(*m_currentFunction);
solAssert(source, "");
if (!source->annotation().experimentalFeatures.count(ExperimentalFeature::SMTChecker))
return;
m_verificationTargets.emplace(_scope, CHCVerificationTarget{{_type, _from, _constraints}, _errorId}); m_verificationTargets.emplace(_scope, CHCVerificationTarget{{_type, _from, _constraints}, _errorId});
} }
@ -1459,7 +1465,11 @@ optional<string> CHC::generateCounterexample(CHCSolverInterface::CexGraph const&
/// Recurse on the next interface node which represents the previous transaction /// Recurse on the next interface node which represents the previous transaction
/// or stop. /// or stop.
if (interfaceId) if (interfaceId)
{
Predicate const* interfacePredicate = Predicate::predicate(_graph.nodes.at(*interfaceId).first);
solAssert(interfacePredicate && interfacePredicate->isInterface(), "");
node = *interfaceId; node = *interfaceId;
}
else else
break; break;
} }

View File

@ -141,8 +141,6 @@ private:
/// in a given _source. /// in a given _source.
void defineInterfacesAndSummaries(SourceUnit const& _source); void defineInterfacesAndSummaries(SourceUnit const& _source);
/// Genesis predicate.
smtutil::Expression genesis() { return (*m_genesisPredicate)({}); }
/// Interface predicate over current variables. /// Interface predicate over current variables.
smtutil::Expression interface(); smtutil::Expression interface();
smtutil::Expression interface(ContractDefinition const& _contract); smtutil::Expression interface(ContractDefinition const& _contract);
@ -259,9 +257,6 @@ private:
/// Predicates. /// Predicates.
//@{ //@{
/// Genesis predicate.
Predicate const* m_genesisPredicate = nullptr;
/// Implicit constructor predicate. /// Implicit constructor predicate.
/// Explicit constructors are handled as functions. /// Explicit constructors are handled as functions.
Predicate const* m_implicitConstructorPredicate = nullptr; Predicate const* m_implicitConstructorPredicate = nullptr;
@ -293,9 +288,6 @@ private:
"error", "error",
m_context m_context
}; };
/// Maps predicate names to the ASTNodes they came from.
std::map<std::string, ASTNode const*> m_symbolFunction;
//@} //@}
/// Variables. /// Variables.

View File

@ -133,10 +133,7 @@ bool SMTEncoder::visit(FunctionDefinition const& _function)
if (_function.isConstructor()) if (_function.isConstructor())
inlineConstructorHierarchy(dynamic_cast<ContractDefinition const&>(*_function.scope())); inlineConstructorHierarchy(dynamic_cast<ContractDefinition const&>(*_function.scope()));
// Base constructors' parameters should be set by explicit calls, initializeLocalVariables(_function);
// but the most derived one needs to be initialized.
if (_function.scope() == m_currentContract)
initializeLocalVariables(_function);
_function.parameterList().accept(*this); _function.parameterList().accept(*this);
if (_function.returnParameterList()) if (_function.returnParameterList())
@ -2040,6 +2037,14 @@ vector<VariableDeclaration const*> SMTEncoder::stateVariablesIncludingInheritedA
return stateVariablesIncludingInheritedAndPrivate(dynamic_cast<ContractDefinition const&>(*_function.scope())); return stateVariablesIncludingInheritedAndPrivate(dynamic_cast<ContractDefinition const&>(*_function.scope()));
} }
SourceUnit const* SMTEncoder::sourceUnitContaining(Scopable const& _scopable)
{
for (auto const* s = &_scopable; s; s = dynamic_cast<Scopable const*>(s->scope()))
if (auto const* source = dynamic_cast<SourceUnit const*>(s->scope()))
return source;
solAssert(false, "");
}
void SMTEncoder::createReturnedExpressions(FunctionCall const& _funCall) void SMTEncoder::createReturnedExpressions(FunctionCall const& _funCall)
{ {
FunctionDefinition const* funDef = functionCallToDefinition(_funCall); FunctionDefinition const* funDef = functionCallToDefinition(_funCall);

View File

@ -65,6 +65,9 @@ public:
static std::vector<VariableDeclaration const*> stateVariablesIncludingInheritedAndPrivate(ContractDefinition const& _contract); static std::vector<VariableDeclaration const*> stateVariablesIncludingInheritedAndPrivate(ContractDefinition const& _contract);
static std::vector<VariableDeclaration const*> stateVariablesIncludingInheritedAndPrivate(FunctionDefinition const& _function); static std::vector<VariableDeclaration const*> stateVariablesIncludingInheritedAndPrivate(FunctionDefinition const& _function);
/// @returns the SourceUnit that contains _scopable.
static SourceUnit const* sourceUnitContaining(Scopable const& _scopable);
protected: protected:
// TODO: Check that we do not have concurrent reads and writes to a variable, // TODO: Check that we do not have concurrent reads and writes to a variable,
// because the order of expression evaluation is undefined // because the order of expression evaluation is undefined

View File

@ -48,6 +48,7 @@ function solcjs_test
printLog "Copying SMTChecker tests..." printLog "Copying SMTChecker tests..."
cp -Rf "$TEST_DIR"/test/libsolidity/smtCheckerTests test/ cp -Rf "$TEST_DIR"/test/libsolidity/smtCheckerTests test/
rm -rf test/smtCheckerTests/imports
# Update version (needed for some tests) # Update version (needed for some tests)
echo "Updating package.json to version $VERSION" echo "Updating package.json to version $VERSION"

View File

@ -0,0 +1,25 @@
==== Source: ====
import "B.sol";
pragma experimental SMTChecker;
contract C is B {
function h(uint _x) public view {
assert(_x < x);
}
}
==== Source: A.sol ====
contract A {
uint x;
function f(uint _x) public {
x = _x;
}
}
==== Source: B.sol ====
import "A.sol";
contract B is A {
function g(uint _x) public view {
assert(_x > x);
}
}
// ----
// Warning 6328: (103-117): Assertion violation happens here.
// Warning 6328: (B.sol:71-85): Assertion violation happens here.

View File

@ -0,0 +1,27 @@
==== Source: ====
import "B.sol";
pragma experimental SMTChecker;
contract C is B {
function h(uint _x) public view {
assert(_x < x);
}
}
==== Source: A.sol ====
contract A {
uint x;
function f(uint _x) public {
x = _x;
}
}
==== Source: B.sol ====
import "A.sol";
pragma experimental SMTChecker;
contract B is A {
function g(uint _x) public view {
assert(_x > x);
}
}
// ----
// Warning 6328: (B.sol:103-117): Assertion violation happens here.
// Warning 6328: (103-117): Assertion violation happens here.
// Warning 6328: (B.sol:103-117): Assertion violation happens here.

View File

@ -0,0 +1,26 @@
==== Source: ====
import "A.sol";
pragma experimental SMTChecker;
contract C is A {
function h(uint _x) public view {
assert(_x < x);
}
}
==== Source: A.sol ====
contract A {
uint x;
function f(uint _x) public {
x = _x;
}
}
==== Source: B.sol ====
import "A.sol";
pragma experimental SMTChecker;
contract B is A {
function g(uint _x) public view {
assert(_x > x);
}
}
// ----
// Warning 6328: (103-117): Assertion violation happens here.
// Warning 6328: (B.sol:103-117): Assertion violation happens here.

View File

@ -0,0 +1,6 @@
==== Source: A.sol ====
contract A { function f() public {} }
==== Source:====
import "A.sol";
pragma experimental SMTChecker;
contract C is A {}

View File

@ -0,0 +1,12 @@
==== Source: ====
import "A.sol";
pragma experimental SMTChecker;
contract C is A {}
==== Source: A.sol ====
contract A {
function f(uint x) public pure {
assert(x > 0);
}
}
// ----
// Warning 6328: (A.sol:49-62): Assertion violation happens here.

View File

@ -0,0 +1,14 @@
==== Source: ====
import "A.sol";
pragma experimental SMTChecker;
contract C is A {}
==== Source: A.sol ====
pragma experimental SMTChecker;
contract A {
function f(uint x) public pure {
assert(x > 0);
}
}
// ----
// Warning 6328: (A.sol:81-94): Assertion violation happens here.
// Warning 6328: (A.sol:81-94): Assertion violation happens here.