Merge pull request #11559 from ethereum/smt_trusted

[SMTChecker] Trusted mode for external calls
This commit is contained in:
Leo 2023-02-06 21:39:13 +01:00 committed by GitHub
commit 7176925a87
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
135 changed files with 3156 additions and 235 deletions

View File

@ -4,6 +4,7 @@ Language Features:
Compiler Features: Compiler Features:
* SMTChecker: New trusted mode that assumes that any compile-time available code is the actual used code even in external calls. This can be used via the CLI option ``--model-checker-ext-calls trusted`` or the JSON field ``settings.modelChecker.extCalls: "trusted"``.
Bugfixes: Bugfixes:

View File

@ -518,6 +518,188 @@ which has the following form:
"source2.sol": ["contract2", "contract3"] "source2.sol": ["contract2", "contract3"]
} }
Trusted External Calls
======================
By default, the SMTChecker does not assume that compile-time available code
is the same as the runtime code for external calls. Take the following contracts
as an example:
.. code-block:: solidity
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.0;
contract Ext {
uint public x;
function setX(uint _x) public { x = _x; }
}
contract MyContract {
function callExt(Ext _e) public {
_e.setX(42);
assert(_e.x() == 42);
}
}
When ``MyContract.callExt`` is called, an address is given as the argument.
At deployment time, we cannot know for sure that address ``_e`` actually
contains a deployment of contract ``Ext``.
Therefore, the SMTChecker will warn that the assertion above can be violated,
which is true, if ``_e`` contains another contract than ``Ext``.
However, it can be useful to treat these external calls as trusted, for example,
to test that different implementations of an interface conform to the same property.
This means assuming that address ``_e`` indeed was deployed as contract ``Ext``.
This mode can be enabled via the CLI option ``--model-checker-ext-calls=trusted``
or the JSON field ``settings.modelChecker.extCalls: "trusted"``.
Please be aware that enabling this mode can make the SMTChecker analysis much more
computationally costly.
An important part of this mode is that it is applied to contract types and high
level external calls to contracts, and not low level calls such as ``call`` and
``delegatecall``. The storage of an address is stored per contract type, and
the SMTChecker assumes that an externally called contract has the type of the
caller expression. Therefore, casting an ``address`` or a contract to
different contract types will yield different storage values and can give
unsound results if the assumptions are inconsistent, such as the example below:
.. code-block:: solidity
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.0;
contract D {
constructor(uint _x) { x = _x; }
uint public x;
function setX(uint _x) public { x = _x; }
}
contract E {
constructor() { x = 2; }
uint public x;
function setX(uint _x) public { x = _x; }
}
contract C {
function f() public {
address d = address(new D(42));
// `d` was deployed as `D`, so its `x` should be 42 now.
assert(D(d).x() == 42); // should hold
assert(D(d).x() == 43); // should fail
// E and D have the same interface, so the following
// call would also work at runtime.
// However, the change to `E(d)` is not reflected in `D(d)`.
E(d).setX(1024);
// Reading from `D(d)` now will show old values.
// The assertion below should fail at runtime,
// but succeeds in this mode's analysis (unsound).
assert(D(d).x() == 42);
// The assertion below should succeed at runtime,
// but fails in this mode's analysis (false positive).
assert(D(d).x() == 1024);
}
}
Due to the above, make sure that the trusted external calls to a certain
variable of ``address`` or ``contract`` type always have the same caller
expression type.
It is also helpful to cast the called contract's variable as the type of the
most derived type in case of inheritance.
.. code-block:: solidity
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.0;
interface Token {
function balanceOf(address _a) external view returns (uint);
function transfer(address _to, uint _amt) external;
}
contract TokenCorrect is Token {
mapping (address => uint) balance;
constructor(address _a, uint _b) {
balance[_a] = _b;
}
function balanceOf(address _a) public view override returns (uint) {
return balance[_a];
}
function transfer(address _to, uint _amt) public override {
require(balance[msg.sender] >= _amt);
balance[msg.sender] -= _amt;
balance[_to] += _amt;
}
}
contract Test {
function property_transfer(address _token, address _to, uint _amt) public {
require(_to != address(this));
TokenCorrect t = TokenCorrect(_token);
uint xPre = t.balanceOf(address(this));
require(xPre >= _amt);
uint yPre = t.balanceOf(_to);
t.transfer(_to, _amt);
uint xPost = t.balanceOf(address(this));
uint yPost = t.balanceOf(_to);
assert(xPost == xPre - _amt);
assert(yPost == yPre + _amt);
}
}
Note that in function ``property_transfer``, the external calls are
performed on variable ``t``
Another caveat of this mode are calls to state variables of contract type
outside the analyzed contract. In the code below, even though ``B`` deploys
``A``, it is also possible for the address stored in ``B.a`` to be called by
anyone outside of ``B`` in between transactions to ``B`` itself. To reflect the
possible changes to ``B.a``, the encoding allows an unbounded number of calls
to be made to ``B.a`` externally. The encoding will keep track of ``B.a``'s
storage, therefore assertion (2) should hold. However, currently the encoding
allows such calls to be made from ``B`` conceptually, therefore assertion (3)
fails. Making the encoding stronger logically is an extension of the trusted
mode and is under development. Note that the encoding does not keep track of
storage for ``address`` variables, therefore if ``B.a`` had type ``address``
the encoding would assume that its storage does not change in between
transactions to ``B``.
.. code-block:: solidity
pragma solidity >=0.8.0;
contract A {
uint public x;
address immutable public owner;
constructor() {
owner = msg.sender;
}
function setX(uint _x) public {
require(msg.sender == owner);
x = _x;
}
}
contract B {
A a;
constructor() {
a = new A();
assert(a.x() == 0); // (1) should hold
}
function g() public view {
assert(a.owner() == address(this)); // (2) should hold
assert(a.x() == 0); // (3) should hold, but fails due to a false positive
}
}
Reported Inferred Inductive Invariants Reported Inferred Inductive Invariants
====================================== ======================================

View File

@ -433,6 +433,10 @@ Input Description
"divModNoSlacks": false, "divModNoSlacks": false,
// Choose which model checker engine to use: all (default), bmc, chc, none. // Choose which model checker engine to use: all (default), bmc, chc, none.
"engine": "chc", "engine": "chc",
// Choose whether external calls should be considered trusted in case the
// code of the called function is available at compile-time.
// For details see the SMTChecker section.
"extCalls": "trusted",
// Choose which types of invariants should be reported to the user: contract, reentrancy. // Choose which types of invariants should be reported to the user: contract, reentrancy.
"invariants": ["contract", "reentrancy"], "invariants": ["contract", "reentrancy"],
// Choose whether to output all unproved targets. The default is `false`. // Choose whether to output all unproved targets. The default is `false`.

View File

@ -82,7 +82,7 @@ void BMC::analyze(SourceUnit const& _source, map<ASTNode const*, set<Verificatio
m_context.setAssertionAccumulation(true); m_context.setAssertionAccumulation(true);
m_variableUsage.setFunctionInlining(shouldInlineFunctionCall); m_variableUsage.setFunctionInlining(shouldInlineFunctionCall);
createFreeConstants(sourceDependencies(_source)); createFreeConstants(sourceDependencies(_source));
state().prepareForSourceUnit(_source); state().prepareForSourceUnit(_source, false);
m_unprovedAmt = 0; m_unprovedAmt = 0;
_source.accept(*this); _source.accept(*this);
@ -130,7 +130,7 @@ bool BMC::shouldInlineFunctionCall(
FunctionType const& funType = dynamic_cast<FunctionType const&>(*_funCall.expression().annotation().type); FunctionType const& funType = dynamic_cast<FunctionType const&>(*_funCall.expression().annotation().type);
if (funType.kind() == FunctionType::Kind::External) if (funType.kind() == FunctionType::Kind::External)
return isTrustedExternalCall(&_funCall.expression()); return isExternalCallToThis(&_funCall.expression());
else if (funType.kind() != FunctionType::Kind::Internal) else if (funType.kind() != FunctionType::Kind::Internal)
return false; return false;
@ -567,7 +567,7 @@ void BMC::internalOrExternalFunctionCall(FunctionCall const& _funCall)
auto const& funType = dynamic_cast<FunctionType const&>(*_funCall.expression().annotation().type); auto const& funType = dynamic_cast<FunctionType const&>(*_funCall.expression().annotation().type);
if (shouldInlineFunctionCall(_funCall, currentScopeContract(), m_currentContract)) if (shouldInlineFunctionCall(_funCall, currentScopeContract(), m_currentContract))
inlineFunctionCall(_funCall); inlineFunctionCall(_funCall);
else if (isPublicGetter(_funCall.expression())) else if (publicGetter(_funCall.expression()))
{ {
// Do nothing here. // Do nothing here.
// The processing happens in SMT Encoder, but we need to prevent the resetting of the state variables. // The processing happens in SMT Encoder, but we need to prevent the resetting of the state variables.

View File

@ -104,7 +104,7 @@ void CHC::analyze(SourceUnit const& _source)
auto sources = sourceDependencies(_source); auto sources = sourceDependencies(_source);
collectFreeFunctions(sources); collectFreeFunctions(sources);
createFreeConstants(sources); createFreeConstants(sources);
state().prepareForSourceUnit(_source); state().prepareForSourceUnit(_source, encodeExternalCallsAsTrusted());
for (auto const* source: sources) for (auto const* source: sources)
defineInterfacesAndSummaries(*source); defineInterfacesAndSummaries(*source);
@ -175,15 +175,30 @@ void CHC::endVisit(ContractDefinition const& _contract)
smtutil::Expression zeroes(true); smtutil::Expression zeroes(true);
for (auto var: stateVariablesIncludingInheritedAndPrivate(_contract)) for (auto var: stateVariablesIncludingInheritedAndPrivate(_contract))
zeroes = zeroes && currentValue(*var) == smt::zeroValue(var->type()); zeroes = zeroes && currentValue(*var) == smt::zeroValue(var->type());
smtutil::Expression newAddress = encodeExternalCallsAsTrusted() ?
!state().addressActive(state().thisAddress()) :
smtutil::Expression(true);
// The contract's address might already have funds before deployment, // The contract's address might already have funds before deployment,
// so the balance must be at least `msg.value`, but not equals. // so the balance must be at least `msg.value`, but not equals.
auto initialBalanceConstraint = state().balance(state().thisAddress()) >= state().txMember("msg.value"); auto initialBalanceConstraint = state().balance(state().thisAddress()) >= state().txMember("msg.value");
addRule(smtutil::Expression::implies( addRule(smtutil::Expression::implies(
initialConstraints(_contract) && zeroes && initialBalanceConstraint, initialConstraints(_contract) && zeroes && newAddress && initialBalanceConstraint,
predicate(entry) predicate(entry)
), entry.functor().name); ), entry.functor().name);
setCurrentBlock(entry); setCurrentBlock(entry);
if (encodeExternalCallsAsTrusted())
{
auto const& entryAfterAddress = *createConstructorBlock(_contract, "implicit_constructor_entry_after_address");
state().setAddressActive(state().thisAddress(), true);
connectBlocks(m_currentBlock, predicate(entryAfterAddress));
setCurrentBlock(entryAfterAddress);
}
solAssert(!m_errorDest, ""); solAssert(!m_errorDest, "");
m_errorDest = m_constructorSummaries.at(&_contract); m_errorDest = m_constructorSummaries.at(&_contract);
// We need to evaluate the base constructor calls (arguments) from derived -> base // We need to evaluate the base constructor calls (arguments) from derived -> base
@ -220,6 +235,9 @@ void CHC::endVisit(ContractDefinition const& _contract)
m_context.addAssertion(errorFlag().currentValue() == 0); m_context.addAssertion(errorFlag().currentValue() == 0);
} }
if (encodeExternalCallsAsTrusted())
state().writeStateVars(_contract, state().thisAddress());
connectBlocks(m_currentBlock, summary(_contract)); connectBlocks(m_currentBlock, summary(_contract));
setCurrentBlock(*m_constructorSummaries.at(&_contract)); setCurrentBlock(*m_constructorSummaries.at(&_contract));
@ -550,10 +568,12 @@ void CHC::endVisit(FunctionCall const& _funCall)
externalFunctionCall(_funCall); externalFunctionCall(_funCall);
SMTEncoder::endVisit(_funCall); SMTEncoder::endVisit(_funCall);
break; break;
case FunctionType::Kind::Creation:
visitDeployment(_funCall);
break;
case FunctionType::Kind::DelegateCall: case FunctionType::Kind::DelegateCall:
case FunctionType::Kind::BareCallCode: case FunctionType::Kind::BareCallCode:
case FunctionType::Kind::BareDelegateCall: case FunctionType::Kind::BareDelegateCall:
case FunctionType::Kind::Creation:
SMTEncoder::endVisit(_funCall); SMTEncoder::endVisit(_funCall);
unknownFunctionCall(_funCall); unknownFunctionCall(_funCall);
break; break;
@ -717,6 +737,19 @@ void CHC::visitAssert(FunctionCall const& _funCall)
verificationTargetEncountered(&_funCall, VerificationTargetType::Assert, errorCondition); verificationTargetEncountered(&_funCall, VerificationTargetType::Assert, errorCondition);
} }
void CHC::visitPublicGetter(FunctionCall const& _funCall)
{
createExpr(_funCall);
if (encodeExternalCallsAsTrusted())
{
auto const& access = dynamic_cast<MemberAccess const&>(_funCall.expression());
auto const& contractType = dynamic_cast<ContractType const&>(*access.expression().annotation().type);
state().writeStateVars(*m_currentContract, state().thisAddress());
state().readStateVars(contractType.contractDefinition(), expr(access.expression()));
}
SMTEncoder::visitPublicGetter(_funCall);
}
void CHC::visitAddMulMod(FunctionCall const& _funCall) void CHC::visitAddMulMod(FunctionCall const& _funCall)
{ {
solAssert(_funCall.arguments().at(2), ""); solAssert(_funCall.arguments().at(2), "");
@ -726,6 +759,66 @@ void CHC::visitAddMulMod(FunctionCall const& _funCall)
SMTEncoder::visitAddMulMod(_funCall); SMTEncoder::visitAddMulMod(_funCall);
} }
void CHC::visitDeployment(FunctionCall const& _funCall)
{
if (!encodeExternalCallsAsTrusted())
{
SMTEncoder::endVisit(_funCall);
unknownFunctionCall(_funCall);
return;
}
auto [callExpr, callOptions] = functionCallExpression(_funCall);
auto funType = dynamic_cast<FunctionType const*>(callExpr->annotation().type);
ContractDefinition const* contract =
&dynamic_cast<ContractType const&>(*funType->returnParameterTypes().front()).contractDefinition();
// copy state variables from m_currentContract to state.storage.
state().writeStateVars(*m_currentContract, state().thisAddress());
errorFlag().increaseIndex();
Expression const* value = valueOption(callOptions);
if (value)
decreaseBalanceFromOptionsValue(*value);
auto originalTx = state().tx();
newTxConstraints(value);
auto prevThisAddr = state().thisAddress();
auto newAddr = state().newThisAddress();
if (auto constructor = contract->constructor())
{
auto const& args = _funCall.sortedArguments();
auto const& params = constructor->parameters();
solAssert(args.size() == params.size(), "");
for (auto [arg, param]: ranges::zip_view(args, params))
m_context.addAssertion(expr(*arg) == m_context.variable(*param)->currentValue());
}
for (auto var: stateVariablesIncludingInheritedAndPrivate(*contract))
m_context.variable(*var)->increaseIndex();
Predicate const& constructorSummary = *m_constructorSummaries.at(contract);
m_context.addAssertion(smt::constructorCall(constructorSummary, m_context, false));
solAssert(m_errorDest, "");
connectBlocks(
m_currentBlock,
predicate(*m_errorDest),
errorFlag().currentValue() > 0
);
m_context.addAssertion(errorFlag().currentValue() == 0);
m_context.addAssertion(state().newThisAddress() == prevThisAddr);
// copy state variables from state.storage to m_currentContract.
state().readStateVars(*m_currentContract, state().thisAddress());
state().newTx();
m_context.addAssertion(originalTx == state().tx());
defineExpr(_funCall, newAddr);
}
void CHC::internalFunctionCall(FunctionCall const& _funCall) void CHC::internalFunctionCall(FunctionCall const& _funCall)
{ {
solAssert(m_currentContract, ""); solAssert(m_currentContract, "");
@ -750,6 +843,53 @@ void CHC::internalFunctionCall(FunctionCall const& _funCall)
m_context.addAssertion(errorFlag().currentValue() == 0); m_context.addAssertion(errorFlag().currentValue() == 0);
} }
void CHC::addNondetCalls(ContractDefinition const& _contract)
{
for (auto var: _contract.stateVariables())
if (auto contractType = dynamic_cast<ContractType const*>(var->type()))
{
auto const& symbVar = m_context.variable(*var);
m_context.addAssertion(symbVar->currentValue() == symbVar->valueAtIndex(0));
nondetCall(contractType->contractDefinition(), *var);
}
}
void CHC::nondetCall(ContractDefinition const& _contract, VariableDeclaration const& _var)
{
auto address = m_context.variable(_var)->currentValue();
// Load the called contract's state variables from the global state.
state().readStateVars(_contract, address);
m_context.addAssertion(state().state() == state().state(0));
auto preCallState = vector<smtutil::Expression>{state().state()} + currentStateVariables(_contract);
state().newState();
for (auto const* var: _contract.stateVariables())
m_context.variable(*var)->increaseIndex();
auto error = errorFlag().increaseIndex();
Predicate const& callPredicate = *createSymbolicBlock(
nondetInterfaceSort(_contract, state()),
"nondet_call_" + uniquePrefix(),
PredicateType::FunctionSummary,
&_var,
m_currentContract
);
auto postCallState = vector<smtutil::Expression>{state().state()} + currentStateVariables(_contract);
vector<smtutil::Expression> stateExprs{error, address, state().abi(), state().crypto()};
auto nondet = (*m_nondetInterfaces.at(&_contract))(stateExprs + preCallState + postCallState);
auto nondetCall = callPredicate(stateExprs + preCallState + postCallState);
addRule(smtutil::Expression::implies(nondet, nondetCall), nondetCall.name);
m_context.addAssertion(nondetCall);
// Load the called contract's state variables into the global state.
state().writeStateVars(_contract, address);
}
void CHC::externalFunctionCall(FunctionCall const& _funCall) void CHC::externalFunctionCall(FunctionCall const& _funCall)
{ {
/// In external function calls we do not add a "predicate call" /// In external function calls we do not add a "predicate call"
@ -757,15 +897,10 @@ void CHC::externalFunctionCall(FunctionCall const& _funCall)
/// so we just add the nondet_interface predicate. /// so we just add the nondet_interface predicate.
solAssert(m_currentContract, ""); solAssert(m_currentContract, "");
auto [callExpr, callOptions] = functionCallExpression(_funCall); auto [callExpr, callOptions] = functionCallExpression(_funCall);
if (isTrustedExternalCall(callExpr))
{
externalFunctionCallToTrustedCode(_funCall);
return;
}
FunctionType const& funType = dynamic_cast<FunctionType const&>(*callExpr->annotation().type); FunctionType const& funType = dynamic_cast<FunctionType const&>(*callExpr->annotation().type);
auto kind = funType.kind(); auto kind = funType.kind();
solAssert( solAssert(
kind == FunctionType::Kind::External || kind == FunctionType::Kind::External ||
@ -774,37 +909,42 @@ void CHC::externalFunctionCall(FunctionCall const& _funCall)
"" ""
); );
bool usesStaticCall = kind == FunctionType::Kind::BareStaticCall;
solAssert(m_currentContract, ""); // Only consider high level external calls in trusted mode.
auto function = functionCallToDefinition(_funCall, currentScopeContract(), m_currentContract); if (
if (function) kind == FunctionType::Kind::External &&
(encodeExternalCallsAsTrusted() || isExternalCallToThis(callExpr))
)
{ {
usesStaticCall |= function->stateMutability() == StateMutability::Pure || externalFunctionCallToTrustedCode(_funCall);
function->stateMutability() == StateMutability::View; return;
for (auto var: function->returnParameters())
m_context.variable(*var)->increaseIndex();
} }
// Low level calls are still encoded nondeterministically.
auto function = functionCallToDefinition(_funCall, currentScopeContract(), m_currentContract);
if (function)
for (auto var: function->returnParameters())
m_context.variable(*var)->increaseIndex();
// If we see a low level call in trusted mode,
// we need to havoc the global state.
if (
kind == FunctionType::Kind::BareCall &&
encodeExternalCallsAsTrusted()
)
state().newStorage();
// No reentrancy from constructor calls.
if (!m_currentFunction || m_currentFunction->isConstructor()) if (!m_currentFunction || m_currentFunction->isConstructor())
return; return;
if (callOptions) if (Expression const* value = valueOption(callOptions))
{ decreaseBalanceFromOptionsValue(*value);
optional<unsigned> valueIndex;
for (auto&& [i, name]: callOptions->names() | ranges::views::enumerate)
if (name && *name == "value")
{
valueIndex = i;
break;
}
if (valueIndex)
state().addBalance(state().thisAddress(), 0 - expr(*callOptions->options().at(*valueIndex)));
}
auto preCallState = vector<smtutil::Expression>{state().state()} + currentStateVariables(); auto preCallState = vector<smtutil::Expression>{state().state()} + currentStateVariables();
if (!usesStaticCall) if (!usesStaticCall(_funCall))
{ {
state().newState(); state().newState();
for (auto const* var: m_stateVariables) for (auto const* var: m_stateVariables)
@ -843,8 +983,14 @@ void CHC::externalFunctionCall(FunctionCall const& _funCall)
void CHC::externalFunctionCallToTrustedCode(FunctionCall const& _funCall) void CHC::externalFunctionCallToTrustedCode(FunctionCall const& _funCall)
{ {
if (publicGetter(_funCall.expression()))
visitPublicGetter(_funCall);
solAssert(m_currentContract, ""); solAssert(m_currentContract, "");
FunctionType const& funType = dynamic_cast<FunctionType const&>(*_funCall.expression().annotation().type);
auto [callExpr, callOptions] = functionCallExpression(_funCall);
FunctionType const& funType = dynamic_cast<FunctionType const&>(*callExpr->annotation().type);
auto kind = funType.kind(); auto kind = funType.kind();
solAssert(kind == FunctionType::Kind::External || kind == FunctionType::Kind::BareStaticCall, ""); solAssert(kind == FunctionType::Kind::External || kind == FunctionType::Kind::BareStaticCall, "");
@ -854,14 +1000,25 @@ void CHC::externalFunctionCallToTrustedCode(FunctionCall const& _funCall)
// External call creates a new transaction. // External call creates a new transaction.
auto originalTx = state().tx(); auto originalTx = state().tx();
auto txOrigin = state().txMember("tx.origin"); Expression const* value = valueOption(callOptions);
state().newTx(); newTxConstraints(value);
// set the transaction sender as this contract
m_context.addAssertion(state().txMember("msg.sender") == state().thisAddress()); auto calledAddress = contractAddressValue(_funCall);
// set the transaction value as 0 if (value)
m_context.addAssertion(state().txMember("msg.value") == 0); {
// set the origin to be the current transaction origin decreaseBalanceFromOptionsValue(*value);
m_context.addAssertion(state().txMember("tx.origin") == txOrigin); state().addBalance(calledAddress, expr(*value));
}
if (encodeExternalCallsAsTrusted())
{
// The order here is important!! Write should go first.
// Load the caller contract's state variables into the global state.
state().writeStateVars(*m_currentContract, state().thisAddress());
// Load the called contract's state variables from the global state.
state().readStateVars(*function->annotation().contract, contractAddressValue(_funCall));
}
smtutil::Expression pred = predicate(_funCall); smtutil::Expression pred = predicate(_funCall);
@ -878,6 +1035,17 @@ void CHC::externalFunctionCallToTrustedCode(FunctionCall const& _funCall)
(errorFlag().currentValue() > 0) (errorFlag().currentValue() > 0)
); );
m_context.addAssertion(errorFlag().currentValue() == 0); m_context.addAssertion(errorFlag().currentValue() == 0);
if (!usesStaticCall(_funCall))
if (encodeExternalCallsAsTrusted())
{
// The order here is important!! Write should go first.
// Load the called contract's state variables into the global state.
state().writeStateVars(*function->annotation().contract, contractAddressValue(_funCall));
// Load the caller contract's state variables from the global state.
state().readStateVars(*m_currentContract, state().thisAddress());
}
} }
void CHC::unknownFunctionCall(FunctionCall const&) void CHC::unknownFunctionCall(FunctionCall const&)
@ -1088,6 +1256,14 @@ set<unsigned> CHC::transactionVerificationTargetsIds(ASTNode const* _txRoot)
return verificationTargetsIds; return verificationTargetsIds;
} }
bool CHC::usesStaticCall(FunctionCall const& _funCall)
{
FunctionType const& funType = dynamic_cast<FunctionType const&>(*_funCall.expression().annotation().type);
auto kind = funType.kind();
auto function = functionCallToDefinition(_funCall, currentScopeContract(), m_currentContract);
return (function && (function->stateMutability() == StateMutability::Pure || function->stateMutability() == StateMutability::View)) || kind == FunctionType::Kind::BareStaticCall;
}
optional<CHC::CHCNatspecOption> CHC::natspecOptionFromString(string const& _option) optional<CHC::CHCNatspecOption> CHC::natspecOptionFromString(string const& _option)
{ {
static map<string, CHCNatspecOption> options{ static map<string, CHCNatspecOption> options{
@ -1128,6 +1304,11 @@ SortPointer CHC::sort(FunctionDefinition const& _function)
return functionBodySort(_function, m_currentContract, state()); return functionBodySort(_function, m_currentContract, state());
} }
bool CHC::encodeExternalCallsAsTrusted()
{
return m_settings.externalCalls.isTrusted();
}
SortPointer CHC::sort(ASTNode const* _node) SortPointer CHC::sort(ASTNode const* _node)
{ {
if (auto funDef = dynamic_cast<FunctionDefinition const*>(_node)) if (auto funDef = dynamic_cast<FunctionDefinition const*>(_node))
@ -1232,6 +1413,62 @@ void CHC::defineExternalFunctionInterface(FunctionDefinition const& _function, C
m_context.addAssertion(smt::symbolicUnknownConstraints(state().balance(state().thisAddress()) + k.currentValue(), TypeProvider::uint256())); m_context.addAssertion(smt::symbolicUnknownConstraints(state().balance(state().thisAddress()) + k.currentValue(), TypeProvider::uint256()));
state().addBalance(state().thisAddress(), k.currentValue()); state().addBalance(state().thisAddress(), k.currentValue());
if (encodeExternalCallsAsTrusted())
{
// If the contract has state variables that are addresses to other contracts,
// we need to encode the fact that those contracts may have been called in between
// transactions to _contract.
//
// We do that by adding nondet_interface constraints for those contracts,
// in the last line of this if block.
//
// If there are state variables of container types like structs or arrays
// that indirectly contain contract types, we havoc the state for simplicity,
// in the first part of this block.
// TODO: This could actually be supported.
// For structs: simply collect the SMT expressions of all the indirect contract type members.
// For arrays: more involved, needs to traverse the array symbolically and do the same for each contract.
// For mappings: way more complicated if the element type is a contract.
auto hasContractOrAddressSubType = [&](VariableDeclaration const* _var) -> bool {
bool foundContract = false;
solidity::util::BreadthFirstSearch<Type const*> bfs{{_var->type()}};
bfs.run([&](auto _type, auto&& _addChild) {
if (
_type->category() == Type::Category::Address ||
_type->category() == Type::Category::Contract
)
{
foundContract = true;
bfs.abort();
}
if (auto const* mapType = dynamic_cast<MappingType const*>(_type))
_addChild(mapType->valueType());
else if (auto const* arrayType = dynamic_cast<ArrayType const*>(_type))
_addChild(arrayType->baseType());
else if (auto const* structType = dynamic_cast<StructType const*>(_type))
for (auto const& member: structType->nativeMembers(nullptr))
_addChild(member.type);
});
return foundContract;
};
bool found = false;
for (auto var: m_currentContract->stateVariables())
if (
var->type()->category() != Type::Category::Address &&
var->type()->category() != Type::Category::Contract &&
hasContractOrAddressSubType(var)
)
{
found = true;
break;
}
if (found)
state().newStorage();
else
addNondetCalls(*m_currentContract);
}
errorFlag().increaseIndex(); errorFlag().increaseIndex();
m_context.addAssertion(summaryCall(_function)); m_context.addAssertion(summaryCall(_function));
@ -1308,28 +1545,28 @@ smtutil::Expression CHC::summary(FunctionDefinition const& _function, ContractDe
return smt::function(*m_summaries.at(&_contract).at(&_function), &_contract, m_context); return smt::function(*m_summaries.at(&_contract).at(&_function), &_contract, m_context);
} }
smtutil::Expression CHC::summaryCall(FunctionDefinition const& _function, ContractDefinition const& _contract)
{
return smt::functionCall(*m_summaries.at(&_contract).at(&_function), &_contract, m_context);
}
smtutil::Expression CHC::externalSummary(FunctionDefinition const& _function, ContractDefinition const& _contract)
{
return smt::function(*m_externalSummaries.at(&_contract).at(&_function), &_contract, m_context);
}
smtutil::Expression CHC::summary(FunctionDefinition const& _function) smtutil::Expression CHC::summary(FunctionDefinition const& _function)
{ {
solAssert(m_currentContract, ""); solAssert(m_currentContract, "");
return summary(_function, *m_currentContract); return summary(_function, *m_currentContract);
} }
smtutil::Expression CHC::summaryCall(FunctionDefinition const& _function, ContractDefinition const& _contract)
{
return smt::functionCall(*m_summaries.at(&_contract).at(&_function), &_contract, m_context);
}
smtutil::Expression CHC::summaryCall(FunctionDefinition const& _function) smtutil::Expression CHC::summaryCall(FunctionDefinition const& _function)
{ {
solAssert(m_currentContract, ""); solAssert(m_currentContract, "");
return summaryCall(_function, *m_currentContract); return summaryCall(_function, *m_currentContract);
} }
smtutil::Expression CHC::externalSummary(FunctionDefinition const& _function, ContractDefinition const& _contract)
{
return smt::function(*m_externalSummaries.at(&_contract).at(&_function), &_contract, m_context);
}
smtutil::Expression CHC::externalSummary(FunctionDefinition const& _function) smtutil::Expression CHC::externalSummary(FunctionDefinition const& _function)
{ {
solAssert(m_currentContract, ""); solAssert(m_currentContract, "");
@ -1516,18 +1753,19 @@ smtutil::Expression CHC::predicate(FunctionCall const& _funCall)
auto const& hierarchy = m_currentContract->annotation().linearizedBaseContracts; auto const& hierarchy = m_currentContract->annotation().linearizedBaseContracts;
solAssert(kind != FunctionType::Kind::Internal || function->isFree() || (contract && contract->isLibrary()) || util::contains(hierarchy, contract), ""); solAssert(kind != FunctionType::Kind::Internal || function->isFree() || (contract && contract->isLibrary()) || util::contains(hierarchy, contract), "");
bool usesStaticCall = function->stateMutability() == StateMutability::Pure || function->stateMutability() == StateMutability::View; if (kind == FunctionType::Kind::Internal)
contract = m_currentContract;
args += currentStateVariables(*m_currentContract); args += currentStateVariables(*contract);
args += symbolicArguments(_funCall, m_currentContract); args += symbolicArguments(_funCall, contract);
if (!m_currentContract->isLibrary() && !usesStaticCall) if (!usesStaticCall(_funCall))
{ {
state().newState(); state().newState();
for (auto const& var: m_stateVariables) for (auto const& var: stateVariablesIncludingInheritedAndPrivate(*contract))
m_context.variable(*var)->increaseIndex(); m_context.variable(*var)->increaseIndex();
} }
args += vector<smtutil::Expression>{state().state()}; args += vector<smtutil::Expression>{state().state()};
args += currentStateVariables(*m_currentContract); args += currentStateVariables(*contract);
for (auto var: function->parameters() + function->returnParameters()) for (auto var: function->parameters() + function->returnParameters())
{ {
@ -1538,14 +1776,14 @@ smtutil::Expression CHC::predicate(FunctionCall const& _funCall)
args.push_back(currentValue(*var)); args.push_back(currentValue(*var));
} }
Predicate const& summary = *m_summaries.at(m_currentContract).at(function); Predicate const& summary = *m_summaries.at(contract).at(function);
auto from = smt::function(summary, m_currentContract, m_context); auto from = smt::function(summary, contract, m_context);
Predicate const& callPredicate = *createSummaryBlock( Predicate const& callPredicate = *createSummaryBlock(
*function, *function,
*m_currentContract, *contract,
kind == FunctionType::Kind::Internal ? PredicateType::InternalCall : PredicateType::ExternalCallTrusted kind == FunctionType::Kind::Internal ? PredicateType::InternalCall : PredicateType::ExternalCallTrusted
); );
auto to = smt::function(callPredicate, m_currentContract, m_context); auto to = smt::function(callPredicate, contract, m_context);
addRule(smtutil::Expression::implies(from, to), to.name); addRule(smtutil::Expression::implies(from, to), to.name);
return callPredicate(args); return callPredicate(args);
@ -1910,60 +2148,63 @@ optional<string> CHC::generateCounterexample(CHCSolverInterface::CexGraph const&
Predicate const* summaryPredicate = Predicate::predicate(summaryNode.name); Predicate const* summaryPredicate = Predicate::predicate(summaryNode.name);
auto const& summaryArgs = summaryNode.arguments; auto const& summaryArgs = summaryNode.arguments;
auto stateVars = summaryPredicate->stateVariables(); if (!summaryPredicate->programVariable())
solAssert(stateVars.has_value(), "");
auto stateValues = summaryPredicate->summaryStateValues(summaryArgs);
solAssert(stateValues.size() == stateVars->size(), "");
if (first)
{ {
first = false; auto stateVars = summaryPredicate->stateVariables();
/// Generate counterexample message local to the failed target. solAssert(stateVars.has_value(), "");
localState = formatVariableModel(*stateVars, stateValues, ", ") + "\n"; auto stateValues = summaryPredicate->summaryStateValues(summaryArgs);
solAssert(stateValues.size() == stateVars->size(), "");
if (auto calledFun = summaryPredicate->programFunction()) if (first)
{ {
auto inValues = summaryPredicate->summaryPostInputValues(summaryArgs); first = false;
auto const& inParams = calledFun->parameters(); /// Generate counterexample message local to the failed target.
if (auto inStr = formatVariableModel(inParams, inValues, "\n"); !inStr.empty()) localState = formatVariableModel(*stateVars, stateValues, ", ") + "\n";
localState += inStr + "\n";
auto outValues = summaryPredicate->summaryPostOutputValues(summaryArgs);
auto const& outParams = calledFun->returnParameters();
if (auto outStr = formatVariableModel(outParams, outValues, "\n"); !outStr.empty())
localState += outStr + "\n";
optional<unsigned> localErrorId; if (auto calledFun = summaryPredicate->programFunction())
solidity::util::BreadthFirstSearch<unsigned> bfs{{summaryId}};
bfs.run([&](auto _nodeId, auto&& _addChild) {
auto const& children = _graph.edges.at(_nodeId);
if (
children.size() == 1 &&
nodePred(children.front())->isFunctionErrorBlock()
)
{
localErrorId = children.front();
bfs.abort();
}
ranges::for_each(children, _addChild);
});
if (localErrorId.has_value())
{ {
auto const* localError = nodePred(*localErrorId); auto inValues = summaryPredicate->summaryPostInputValues(summaryArgs);
solAssert(localError && localError->isFunctionErrorBlock(), ""); auto const& inParams = calledFun->parameters();
auto const [localValues, localVars] = localError->localVariableValues(nodeArgs(*localErrorId)); if (auto inStr = formatVariableModel(inParams, inValues, "\n"); !inStr.empty())
if (auto localStr = formatVariableModel(localVars, localValues, "\n"); !localStr.empty()) localState += inStr + "\n";
localState += localStr + "\n"; auto outValues = summaryPredicate->summaryPostOutputValues(summaryArgs);
auto const& outParams = calledFun->returnParameters();
if (auto outStr = formatVariableModel(outParams, outValues, "\n"); !outStr.empty())
localState += outStr + "\n";
optional<unsigned> localErrorId;
solidity::util::BreadthFirstSearch<unsigned> bfs{{summaryId}};
bfs.run([&](auto _nodeId, auto&& _addChild) {
auto const& children = _graph.edges.at(_nodeId);
if (
children.size() == 1 &&
nodePred(children.front())->isFunctionErrorBlock()
)
{
localErrorId = children.front();
bfs.abort();
}
ranges::for_each(children, _addChild);
});
if (localErrorId.has_value())
{
auto const* localError = nodePred(*localErrorId);
solAssert(localError && localError->isFunctionErrorBlock(), "");
auto const [localValues, localVars] = localError->localVariableValues(nodeArgs(*localErrorId));
if (auto localStr = formatVariableModel(localVars, localValues, "\n"); !localStr.empty())
localState += localStr + "\n";
}
} }
} }
} else
else {
{ auto modelMsg = formatVariableModel(*stateVars, stateValues, ", ");
auto modelMsg = formatVariableModel(*stateVars, stateValues, ", "); /// We report the state after every tx in the trace except for the last, which is reported
/// We report the state after every tx in the trace except for the last, which is reported /// first in the code above.
/// first in the code above. if (!modelMsg.empty())
if (!modelMsg.empty()) path.emplace_back("State: " + modelMsg);
path.emplace_back("State: " + modelMsg); }
} }
string txCex = summaryPredicate->formatSummaryCall(summaryArgs, m_charStreamProvider); string txCex = summaryPredicate->formatSummaryCall(summaryArgs, m_charStreamProvider);
@ -1992,6 +2233,12 @@ optional<string> CHC::generateCounterexample(CHCSolverInterface::CexGraph const&
if (calls.size() > callTraceSize + 1) if (calls.size() > callTraceSize + 1)
calls.front() += ", synthesized as:"; calls.front() += ", synthesized as:";
} }
else if (pred->programVariable())
{
calls.front() += "-- action on external contract in state variable \"" + pred->programVariable()->name() + "\"";
if (calls.size() > callTraceSize + 1)
calls.front() += ", synthesized as:";
}
else if (pred->isFunctionSummary() && parentPred->isExternalCallUntrusted()) else if (pred->isFunctionSummary() && parentPred->isExternalCallUntrusted())
calls.front() += " -- reentrant call"; calls.front() += " -- reentrant call";
}; };
@ -2047,7 +2294,8 @@ map<unsigned, vector<unsigned>> CHC::summaryCalls(CHCSolverInterface::CexGraph c
nodePred->isInternalCall() || nodePred->isInternalCall() ||
nodePred->isExternalCallTrusted() || nodePred->isExternalCallTrusted() ||
nodePred->isExternalCallUntrusted() || nodePred->isExternalCallUntrusted() ||
rootPred->isExternalCallUntrusted() rootPred->isExternalCallUntrusted() ||
rootPred->programVariable()
)) ))
{ {
calls[root].push_back(node); calls[root].push_back(node);
@ -2105,3 +2353,31 @@ SymbolicIntVariable& CHC::errorFlag()
{ {
return state().errorFlag(); return state().errorFlag();
} }
void CHC::newTxConstraints(Expression const* _value)
{
auto txOrigin = state().txMember("tx.origin");
state().newTx();
// set the transaction sender as this contract
m_context.addAssertion(state().txMember("msg.sender") == state().thisAddress());
// set the origin to be the current transaction origin
m_context.addAssertion(state().txMember("tx.origin") == txOrigin);
if (_value)
// set the msg value
m_context.addAssertion(state().txMember("msg.value") == expr(*_value));
}
frontend::Expression const* CHC::valueOption(FunctionCallOptions const* _options)
{
if (_options)
for (auto&& [i, name]: _options->names() | ranges::views::enumerate)
if (name && *name == "value")
return _options->options().at(i).get();
return nullptr;
}
void CHC::decreaseBalanceFromOptionsValue(Expression const& _value)
{
state().addBalance(state().thisAddress(), 0 - expr(_value));
}

View File

@ -110,10 +110,14 @@ private:
void popInlineFrame(CallableDeclaration const& _callable) override; void popInlineFrame(CallableDeclaration const& _callable) override;
void visitAssert(FunctionCall const& _funCall); void visitAssert(FunctionCall const& _funCall);
void visitPublicGetter(FunctionCall const& _funCall) override;
void visitAddMulMod(FunctionCall const& _funCall) override; void visitAddMulMod(FunctionCall const& _funCall) override;
void visitDeployment(FunctionCall const& _funCall);
void internalFunctionCall(FunctionCall const& _funCall); void internalFunctionCall(FunctionCall const& _funCall);
void externalFunctionCall(FunctionCall const& _funCall); void externalFunctionCall(FunctionCall const& _funCall);
void externalFunctionCallToTrustedCode(FunctionCall const& _funCall); void externalFunctionCallToTrustedCode(FunctionCall const& _funCall);
void addNondetCalls(ContractDefinition const& _contract);
void nondetCall(ContractDefinition const& _contract, VariableDeclaration const& _var);
void unknownFunctionCall(FunctionCall const& _funCall); void unknownFunctionCall(FunctionCall const& _funCall);
void makeArrayPopVerificationTarget(FunctionCall const& _arrayPop) override; void makeArrayPopVerificationTarget(FunctionCall const& _arrayPop) override;
void makeOutOfBoundsVerificationTarget(IndexAccess const& _access) override; void makeOutOfBoundsVerificationTarget(IndexAccess const& _access) override;
@ -135,6 +139,7 @@ private:
void clearIndices(ContractDefinition const* _contract, FunctionDefinition const* _function = nullptr) override; void clearIndices(ContractDefinition const* _contract, FunctionDefinition const* _function = nullptr) override;
void setCurrentBlock(Predicate const& _block); void setCurrentBlock(Predicate const& _block);
std::set<unsigned> transactionVerificationTargetsIds(ASTNode const* _txRoot); std::set<unsigned> transactionVerificationTargetsIds(ASTNode const* _txRoot);
bool usesStaticCall(FunctionCall const& _funCall);
//@} //@}
/// SMT Natspec and abstraction helpers. /// SMT Natspec and abstraction helpers.
@ -148,6 +153,10 @@ private:
/// @returns true if _function is Natspec annotated to be abstracted by /// @returns true if _function is Natspec annotated to be abstracted by
/// nondeterministic values. /// nondeterministic values.
bool abstractAsNondet(FunctionDefinition const& _function); bool abstractAsNondet(FunctionDefinition const& _function);
/// @returns true if external calls should be considered trusted.
/// If that's the case, their code is used if available at compile time.
bool encodeExternalCallsAsTrusted();
//@} //@}
/// Sort helpers. /// Sort helpers.
@ -310,6 +319,20 @@ private:
unsigned newErrorId(); unsigned newErrorId();
smt::SymbolicIntVariable& errorFlag(); smt::SymbolicIntVariable& errorFlag();
/// Adds to the solver constraints that
/// - propagate tx.origin
/// - set the current contract as msg.sender
/// - set the msg.value as _value, if not nullptr
void newTxConstraints(Expression const* _value);
/// @returns the expression representing the value sent in
/// an external call if present,
/// and nullptr otherwise.
frontend::Expression const* valueOption(FunctionCallOptions const* _options);
/// Adds constraints that decrease the balance of the caller by _value.
void decreaseBalanceFromOptionsValue(Expression const& _value);
//@} //@}
/// Predicates. /// Predicates.

View File

@ -130,3 +130,12 @@ std::optional<ModelCheckerContracts> ModelCheckerContracts::fromString(string co
return ModelCheckerContracts{chosen}; return ModelCheckerContracts{chosen};
} }
std::optional<ModelCheckerExtCalls> ModelCheckerExtCalls::fromString(string const& _mode)
{
if (_mode == "untrusted")
return ModelCheckerExtCalls{Mode::UNTRUSTED};
if (_mode == "trusted")
return ModelCheckerExtCalls{Mode::TRUSTED};
return {};
}

View File

@ -140,6 +140,21 @@ struct ModelCheckerTargets
std::set<VerificationTargetType> targets; std::set<VerificationTargetType> targets;
}; };
struct ModelCheckerExtCalls
{
enum class Mode
{
UNTRUSTED,
TRUSTED
};
Mode mode = Mode::UNTRUSTED;
static std::optional<ModelCheckerExtCalls> fromString(std::string const& _mode);
bool isTrusted() const { return mode == Mode::TRUSTED; }
};
struct ModelCheckerSettings struct ModelCheckerSettings
{ {
ModelCheckerContracts contracts = ModelCheckerContracts::Default(); ModelCheckerContracts contracts = ModelCheckerContracts::Default();
@ -151,6 +166,7 @@ struct ModelCheckerSettings
/// might prefer the precise encoding. /// might prefer the precise encoding.
bool divModNoSlacks = false; bool divModNoSlacks = false;
ModelCheckerEngine engine = ModelCheckerEngine::None(); ModelCheckerEngine engine = ModelCheckerEngine::None();
ModelCheckerExtCalls externalCalls = {};
ModelCheckerInvariants invariants = ModelCheckerInvariants::Default(); ModelCheckerInvariants invariants = ModelCheckerInvariants::Default();
bool showUnproved = false; bool showUnproved = false;
smtutil::SMTSolverChoice solvers = smtutil::SMTSolverChoice::Z3(); smtutil::SMTSolverChoice solvers = smtutil::SMTSolverChoice::Z3();
@ -164,6 +180,7 @@ struct ModelCheckerSettings
contracts == _other.contracts && contracts == _other.contracts &&
divModNoSlacks == _other.divModNoSlacks && divModNoSlacks == _other.divModNoSlacks &&
engine == _other.engine && engine == _other.engine &&
externalCalls.mode == _other.externalCalls.mode &&
invariants == _other.invariants && invariants == _other.invariants &&
showUnproved == _other.showUnproved && showUnproved == _other.showUnproved &&
solvers == _other.solvers && solvers == _other.solvers &&

View File

@ -144,6 +144,11 @@ FunctionCall const* Predicate::programFunctionCall() const
return dynamic_cast<FunctionCall const*>(m_node); return dynamic_cast<FunctionCall const*>(m_node);
} }
VariableDeclaration const* Predicate::programVariable() const
{
return dynamic_cast<VariableDeclaration const*>(m_node);
}
optional<vector<VariableDeclaration const*>> Predicate::stateVariables() const optional<vector<VariableDeclaration const*>> Predicate::stateVariables() const
{ {
if (m_contractContext) if (m_contractContext)
@ -214,6 +219,9 @@ string Predicate::formatSummaryCall(
{ {
solAssert(isSummary(), ""); solAssert(isSummary(), "");
if (programVariable())
return {};
if (auto funCall = programFunctionCall()) if (auto funCall = programFunctionCall())
{ {
if (funCall->location().hasText()) if (funCall->location().hasText())
@ -348,6 +356,8 @@ vector<optional<string>> Predicate::summaryStateValues(vector<smtutil::Expressio
stateFirst = _args.begin() + 7 + static_cast<int>(stateVars->size()); stateFirst = _args.begin() + 7 + static_cast<int>(stateVars->size());
stateLast = stateFirst + static_cast<int>(stateVars->size()); stateLast = stateFirst + static_cast<int>(stateVars->size());
} }
else if (programVariable())
return {};
else else
solAssert(false, ""); solAssert(false, "");

View File

@ -115,6 +115,10 @@ public:
/// or nullptr otherwise. /// or nullptr otherwise.
FunctionCall const* programFunctionCall() const; FunctionCall const* programFunctionCall() const;
/// @returns the VariableDeclaration that this predicate represents
/// or nullptr otherwise.
VariableDeclaration const* programVariable() const;
/// @returns the program state variables in the scope of this predicate. /// @returns the program state variables in the scope of this predicate.
std::optional<std::vector<VariableDeclaration const*>> stateVariables() const; std::optional<std::vector<VariableDeclaration const*>> stateVariables() const;

View File

@ -70,14 +70,14 @@ smtutil::Expression constructor(Predicate const& _pred, EncodingContext& _contex
return _pred(stateExprs + initialStateVariables(contract, _context) + currentStateVariables(contract, _context)); return _pred(stateExprs + initialStateVariables(contract, _context) + currentStateVariables(contract, _context));
} }
smtutil::Expression constructorCall(Predicate const& _pred, EncodingContext& _context) smtutil::Expression constructorCall(Predicate const& _pred, EncodingContext& _context, bool _internal)
{ {
auto const& contract = dynamic_cast<ContractDefinition const&>(*_pred.programNode()); auto const& contract = dynamic_cast<ContractDefinition const&>(*_pred.programNode());
if (auto const* constructor = contract.constructor()) if (auto const* constructor = contract.constructor())
return _pred(currentFunctionVariablesForCall(*constructor, &contract, _context)); return _pred(currentFunctionVariablesForCall(*constructor, &contract, _context, _internal));
auto& state = _context.state(); auto& state = _context.state();
vector<smtutil::Expression> stateExprs{state.errorFlag().currentValue(), state.thisAddress(0), state.abi(0), state.crypto(0), state.tx(0), state.state()}; vector<smtutil::Expression> stateExprs{state.errorFlag().currentValue(), _internal ? state.thisAddress(0) : state.thisAddress(), state.abi(0), state.crypto(0), _internal ? state.tx(0) : state.tx(), state.state()};
state.newState(); state.newState();
stateExprs += vector<smtutil::Expression>{state.state()}; stateExprs += vector<smtutil::Expression>{state.state()};
stateExprs += currentStateVariables(contract, _context); stateExprs += currentStateVariables(contract, _context);
@ -166,11 +166,12 @@ vector<smtutil::Expression> currentFunctionVariablesForDefinition(
vector<smtutil::Expression> currentFunctionVariablesForCall( vector<smtutil::Expression> currentFunctionVariablesForCall(
FunctionDefinition const& _function, FunctionDefinition const& _function,
ContractDefinition const* _contract, ContractDefinition const* _contract,
EncodingContext& _context EncodingContext& _context,
bool _internal
) )
{ {
auto& state = _context.state(); auto& state = _context.state();
vector<smtutil::Expression> exprs{state.errorFlag().currentValue(), state.thisAddress(0), state.abi(0), state.crypto(0), state.tx(0), state.state()}; vector<smtutil::Expression> exprs{state.errorFlag().currentValue(), _internal ? state.thisAddress(0) : state.thisAddress(), state.abi(0), state.crypto(0), _internal ? state.tx(0) : state.tx(), state.state()};
exprs += _contract ? currentStateVariables(*_contract, _context) : vector<smtutil::Expression>{}; exprs += _contract ? currentStateVariables(*_contract, _context) : vector<smtutil::Expression>{};
exprs += applyMap(_function.parameters(), [&](auto _var) { return _context.variable(*_var)->currentValue(); }); exprs += applyMap(_function.parameters(), [&](auto _var) { return _context.variable(*_var)->currentValue(); });

View File

@ -37,7 +37,14 @@ smtutil::Expression interface(Predicate const& _pred, ContractDefinition const&
smtutil::Expression nondetInterface(Predicate const& _pred, ContractDefinition const& _contract, EncodingContext& _context, unsigned _preIdx, unsigned _postIdx); smtutil::Expression nondetInterface(Predicate const& _pred, ContractDefinition const& _contract, EncodingContext& _context, unsigned _preIdx, unsigned _postIdx);
smtutil::Expression constructor(Predicate const& _pred, EncodingContext& _context); smtutil::Expression constructor(Predicate const& _pred, EncodingContext& _context);
smtutil::Expression constructorCall(Predicate const& _pred, EncodingContext& _context); /// The encoding of the deployment procedure includes adding constraints
/// for base constructors if inheritance is used.
/// From the predicate point of view this is not different,
/// but some of the arguments are different.
/// @param _internal = true means that this constructor call is used in the
/// deployment procedure, whereas false means it is used in the deployment
/// of a contract.
smtutil::Expression constructorCall(Predicate const& _pred, EncodingContext& _context, bool _internal = true);
smtutil::Expression function( smtutil::Expression function(
Predicate const& _pred, Predicate const& _pred,
@ -77,7 +84,8 @@ std::vector<smtutil::Expression> currentFunctionVariablesForDefinition(
std::vector<smtutil::Expression> currentFunctionVariablesForCall( std::vector<smtutil::Expression> currentFunctionVariablesForCall(
FunctionDefinition const& _function, FunctionDefinition const& _function,
ContractDefinition const* _contract, ContractDefinition const* _contract,
EncodingContext& _context EncodingContext& _context,
bool _internal = true
); );
std::vector<smtutil::Expression> currentBlockVariables( std::vector<smtutil::Expression> currentBlockVariables(

View File

@ -637,7 +637,7 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall)
visitGasLeft(_funCall); visitGasLeft(_funCall);
break; break;
case FunctionType::Kind::External: case FunctionType::Kind::External:
if (isPublicGetter(_funCall.expression())) if (publicGetter(_funCall.expression()))
visitPublicGetter(_funCall); visitPublicGetter(_funCall);
break; break;
case FunctionType::Kind::ABIDecode: case FunctionType::Kind::ABIDecode:
@ -696,10 +696,18 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall)
case FunctionType::Kind::ObjectCreation: case FunctionType::Kind::ObjectCreation:
visitObjectCreation(_funCall); visitObjectCreation(_funCall);
return; return;
case FunctionType::Kind::Creation:
if (!m_settings.engine.chc || !m_settings.externalCalls.isTrusted())
m_errorReporter.warning(
8729_error,
_funCall.location(),
"Contract deployment is only supported in the trusted mode for external calls"
" with the CHC engine."
);
break;
case FunctionType::Kind::DelegateCall: case FunctionType::Kind::DelegateCall:
case FunctionType::Kind::BareCallCode: case FunctionType::Kind::BareCallCode:
case FunctionType::Kind::BareDelegateCall: case FunctionType::Kind::BareDelegateCall:
case FunctionType::Kind::Creation:
default: default:
m_errorReporter.warning( m_errorReporter.warning(
4588_error, 4588_error,
@ -978,9 +986,8 @@ vector<string> structGetterReturnedMembers(StructType const& _structType)
void SMTEncoder::visitPublicGetter(FunctionCall const& _funCall) void SMTEncoder::visitPublicGetter(FunctionCall const& _funCall)
{ {
MemberAccess const& access = dynamic_cast<MemberAccess const&>(_funCall.expression()); auto var = publicGetter(_funCall.expression());
auto var = dynamic_cast<VariableDeclaration const*>(access.annotation().referencedDeclaration); solAssert(var && var->isStateVariable(), "");
solAssert(var, "");
solAssert(m_context.knownExpression(_funCall), ""); solAssert(m_context.knownExpression(_funCall), "");
auto paramExpectedTypes = replaceUserTypes(FunctionType(*var).parameterTypes()); auto paramExpectedTypes = replaceUserTypes(FunctionType(*var).parameterTypes());
auto actualArguments = _funCall.arguments(); auto actualArguments = _funCall.arguments();
@ -1054,7 +1061,7 @@ bool SMTEncoder::shouldAnalyze(ContractDefinition const& _contract) const
return false; return false;
return m_settings.contracts.isDefault() || return m_settings.contracts.isDefault() ||
m_settings.contracts.has(_contract.sourceUnitName(), _contract.name()); m_settings.contracts.has(_contract.sourceUnitName());
} }
void SMTEncoder::visitTypeConversion(FunctionCall const& _funCall) void SMTEncoder::visitTypeConversion(FunctionCall const& _funCall)
@ -2789,16 +2796,24 @@ MemberAccess const* SMTEncoder::isEmptyPush(Expression const& _expr) const
return nullptr; return nullptr;
} }
bool SMTEncoder::isPublicGetter(Expression const& _expr) { smtutil::Expression SMTEncoder::contractAddressValue(FunctionCall const& _f)
if (!isTrustedExternalCall(&_expr)) {
return false; FunctionType const& funType = dynamic_cast<FunctionType const&>(*_f.expression().annotation().type);
auto varDecl = dynamic_cast<VariableDeclaration const*>( if (funType.kind() == FunctionType::Kind::Internal)
dynamic_cast<MemberAccess const&>(_expr).annotation().referencedDeclaration return state().thisAddress();
); auto [funExpr, funOptions] = functionCallExpression(_f);
return varDecl != nullptr; if (MemberAccess const* callBase = dynamic_cast<MemberAccess const*>(funExpr))
return expr(callBase->expression());
solAssert(false, "Unreachable!");
} }
bool SMTEncoder::isTrustedExternalCall(Expression const* _expr) { VariableDeclaration const* SMTEncoder::publicGetter(Expression const& _expr) const {
if (auto memberAccess = dynamic_cast<MemberAccess const*>(&_expr))
return dynamic_cast<VariableDeclaration const*>(memberAccess->annotation().referencedDeclaration);
return nullptr;
}
bool SMTEncoder::isExternalCallToThis(Expression const* _expr) {
auto memberAccess = dynamic_cast<MemberAccess const*>(_expr); auto memberAccess = dynamic_cast<MemberAccess const*>(_expr);
if (!memberAccess) if (!memberAccess)
return false; return false;
@ -3060,7 +3075,7 @@ RationalNumberType const* SMTEncoder::isConstant(Expression const& _expr)
return nullptr; return nullptr;
} }
set<FunctionCall const*> SMTEncoder::collectABICalls(ASTNode const* _node) set<FunctionCall const*, ASTCompareByID<FunctionCall>> SMTEncoder::collectABICalls(ASTNode const* _node)
{ {
struct ABIFunctions: public ASTConstVisitor struct ABIFunctions: public ASTConstVisitor
{ {
@ -3082,7 +3097,7 @@ set<FunctionCall const*> SMTEncoder::collectABICalls(ASTNode const* _node)
} }
} }
set<FunctionCall const*> abiCalls; set<FunctionCall const*, ASTCompareByID<FunctionCall>> abiCalls;
}; };
return ABIFunctions(_node).abiCalls; return ABIFunctions(_node).abiCalls;

View File

@ -123,7 +123,7 @@ public:
/// RationalNumberType or can be const evaluated, and nullptr otherwise. /// RationalNumberType or can be const evaluated, and nullptr otherwise.
static RationalNumberType const* isConstant(Expression const& _expr); static RationalNumberType const* isConstant(Expression const& _expr);
static std::set<FunctionCall const*> collectABICalls(ASTNode const* _node); static std::set<FunctionCall const*, ASTCompareByID<FunctionCall>> collectABICalls(ASTNode const* _node);
/// @returns all the sources that @param _source depends on, /// @returns all the sources that @param _source depends on,
/// including itself. /// including itself.
@ -219,7 +219,7 @@ protected:
void visitTypeConversion(FunctionCall const& _funCall); void visitTypeConversion(FunctionCall const& _funCall);
void visitStructConstructorCall(FunctionCall const& _funCall); void visitStructConstructorCall(FunctionCall const& _funCall);
void visitFunctionIdentifier(Identifier const& _identifier); void visitFunctionIdentifier(Identifier const& _identifier);
void visitPublicGetter(FunctionCall const& _funCall); virtual void visitPublicGetter(FunctionCall const& _funCall);
/// @returns true if @param _contract is set for analysis in the settings /// @returns true if @param _contract is set for analysis in the settings
/// and it is not abstract. /// and it is not abstract.
@ -227,7 +227,12 @@ protected:
/// @returns true if @param _source is set for analysis in the settings. /// @returns true if @param _source is set for analysis in the settings.
bool shouldAnalyze(SourceUnit const& _source) const; bool shouldAnalyze(SourceUnit const& _source) const;
bool isPublicGetter(Expression const& _expr); /// @returns the state variable returned by a public getter if
/// @a _expr is a call to a public getter,
/// otherwise nullptr.
VariableDeclaration const* publicGetter(Expression const& _expr) const;
smtutil::Expression contractAddressValue(FunctionCall const& _f);
/// Encodes a modifier or function body according to the modifier /// Encodes a modifier or function body according to the modifier
/// visit depth. /// visit depth.
@ -392,9 +397,9 @@ protected:
/// otherwise nullptr. /// otherwise nullptr.
MemberAccess const* isEmptyPush(Expression const& _expr) const; MemberAccess const* isEmptyPush(Expression const& _expr) const;
/// @returns true if the given identifier is a contract which is known and trusted. /// @returns true if the given expression is `this`.
/// This means we don't have to abstract away effects of external function calls to this contract. /// This means we don't have to abstract away effects of external function calls to this contract.
static bool isTrustedExternalCall(Expression const* _expr); static bool isExternalCallToThis(Expression const* _expr);
/// Creates symbolic expressions for the returned values /// Creates symbolic expressions for the returned values
/// and set them as the components of the symbolic tuple. /// and set them as the components of the symbolic tuple.

View File

@ -22,8 +22,13 @@
#include <libsolidity/formal/EncodingContext.h> #include <libsolidity/formal/EncodingContext.h>
#include <libsolidity/formal/SMTEncoder.h> #include <libsolidity/formal/SMTEncoder.h>
#include <libsmtutil/Sorts.h>
#include <range/v3/view.hpp>
using namespace std; using namespace std;
using namespace solidity; using namespace solidity;
using namespace solidity::util;
using namespace solidity::smtutil; using namespace solidity::smtutil;
using namespace solidity::frontend::smt; using namespace solidity::frontend::smt;
@ -58,16 +63,8 @@ smtutil::Expression BlockchainVariable::member(string const& _member) const
smtutil::Expression BlockchainVariable::assignMember(string const& _member, smtutil::Expression const& _value) smtutil::Expression BlockchainVariable::assignMember(string const& _member, smtutil::Expression const& _value)
{ {
vector<smtutil::Expression> args; smtutil::Expression newTuple = smt::assignMember(m_tuple->currentValue(), {{_member, _value}});
for (auto const& m: m_members) m_context.addAssertion(m_tuple->increaseIndex() == newTuple);
if (m.first == _member)
args.emplace_back(_value);
else
args.emplace_back(member(m.first));
m_tuple->increaseIndex();
auto tuple = m_tuple->currentValue();
auto sortExpr = smtutil::Expression(make_shared<smtutil::SortSort>(tuple.sort), tuple.name);
m_context.addAssertion(tuple == smtutil::Expression::tuple_constructor(sortExpr, args));
return m_tuple->currentValue(); return m_tuple->currentValue();
} }
@ -75,16 +72,19 @@ void SymbolicState::reset()
{ {
m_error.resetIndex(); m_error.resetIndex();
m_thisAddress.resetIndex(); m_thisAddress.resetIndex();
m_state.reset();
m_tx.reset(); m_tx.reset();
m_crypto.reset(); m_crypto.reset();
if (m_abi) if (m_abi)
m_abi->reset(); m_abi->reset();
/// We don't reset nor clear these pointers on purpose,
/// since it only helps to keep the already generated types.
if (m_state)
m_state->reset();
} }
smtutil::Expression SymbolicState::balances() const smtutil::Expression SymbolicState::balances() const
{ {
return m_state.member("balances"); return m_state->member("balances");
} }
smtutil::Expression SymbolicState::balance() const smtutil::Expression SymbolicState::balance() const
@ -107,24 +107,94 @@ void SymbolicState::newBalances()
auto tupleSort = dynamic_pointer_cast<TupleSort>(stateSort()); auto tupleSort = dynamic_pointer_cast<TupleSort>(stateSort());
auto balanceSort = tupleSort->components.at(tupleSort->memberToIndex.at("balances")); auto balanceSort = tupleSort->components.at(tupleSort->memberToIndex.at("balances"));
SymbolicVariable newBalances(balanceSort, "fresh_balances_" + to_string(m_context.newUniqueId()), m_context); SymbolicVariable newBalances(balanceSort, "fresh_balances_" + to_string(m_context.newUniqueId()), m_context);
m_state.assignMember("balances", newBalances.currentValue()); m_state->assignMember("balances", newBalances.currentValue());
} }
void SymbolicState::transfer(smtutil::Expression _from, smtutil::Expression _to, smtutil::Expression _value) void SymbolicState::transfer(smtutil::Expression _from, smtutil::Expression _to, smtutil::Expression _value)
{ {
unsigned indexBefore = m_state.index(); unsigned indexBefore = m_state->index();
addBalance(_from, 0 - _value); addBalance(_from, 0 - _value);
addBalance(_to, std::move(_value)); addBalance(_to, std::move(_value));
unsigned indexAfter = m_state.index(); unsigned indexAfter = m_state->index();
solAssert(indexAfter > indexBefore, ""); solAssert(indexAfter > indexBefore, "");
m_state.newVar(); m_state->newVar();
/// Do not apply the transfer operation if _from == _to. /// Do not apply the transfer operation if _from == _to.
auto newState = smtutil::Expression::ite( auto newState = smtutil::Expression::ite(
std::move(_from) == std::move(_to), std::move(_from) == std::move(_to),
m_state.value(indexBefore), m_state->value(indexBefore),
m_state.value(indexAfter) m_state->value(indexAfter)
); );
m_context.addAssertion(m_state.value() == newState); m_context.addAssertion(m_state->value() == newState);
}
smtutil::Expression SymbolicState::storage(ContractDefinition const& _contract) const
{
return smt::member(m_state->member("storage"), contractStorageKey(_contract));
}
smtutil::Expression SymbolicState::storage(ContractDefinition const& _contract, smtutil::Expression _address) const
{
return smtutil::Expression::select(storage(_contract), std::move(_address));
}
smtutil::Expression SymbolicState::addressActive(smtutil::Expression _address) const
{
return smtutil::Expression::select(m_state->member("isActive"), std::move(_address));
}
void SymbolicState::setAddressActive(
smtutil::Expression _address,
bool _active
)
{
m_state->assignMember("isActive", smtutil::Expression::store(
m_state->member("isActive"),
std::move(_address),
smtutil::Expression(_active))
);
}
void SymbolicState::newStorage()
{
auto newStorageVar = SymbolicTupleVariable(
m_state->member("storage").sort,
"havoc_storage_" + to_string(m_context.newUniqueId()),
m_context
);
m_state->assignMember("storage", newStorageVar.currentValue());
}
void SymbolicState::writeStateVars(ContractDefinition const& _contract, smtutil::Expression _address)
{
auto stateVars = SMTEncoder::stateVariablesIncludingInheritedAndPrivate(_contract);
if (stateVars.empty())
return;
map<string, smtutil::Expression> values;
for (auto var: stateVars)
values.emplace(stateVarStorageKey(*var, _contract), m_context.variable(*var)->currentValue());
smtutil::Expression thisStorage = storage(_contract, _address);
smtutil::Expression newStorage = smt::assignMember(thisStorage, values);
auto newContractStorage = smtutil::Expression::store(
storage(_contract), std::move(_address), newStorage
);
smtutil::Expression newAllStorage = smt::assignMember(m_state->member("storage"), {{contractStorageKey(_contract), newContractStorage}});
m_state->assignMember("storage", newAllStorage);
}
void SymbolicState::readStateVars(ContractDefinition const& _contract, smtutil::Expression _address)
{
auto stateVars = SMTEncoder::stateVariablesIncludingInheritedAndPrivate(_contract);
if (stateVars.empty())
return;
auto contractStorage = storage(_contract, std::move(_address));
for (auto var: stateVars)
m_context.addAssertion(
m_context.variable(*var)->increaseIndex() ==
smt::member(contractStorage, stateVarStorageKey(*var, _contract))
);
} }
void SymbolicState::addBalance(smtutil::Expression _address, smtutil::Expression _value) void SymbolicState::addBalance(smtutil::Expression _address, smtutil::Expression _value)
@ -134,7 +204,7 @@ void SymbolicState::addBalance(smtutil::Expression _address, smtutil::Expression
_address, _address,
balance(_address) + std::move(_value) balance(_address) + std::move(_value)
); );
m_state.assignMember("balances", newBalances); m_state->assignMember("balances", newBalances);
} }
smtutil::Expression SymbolicState::txMember(string const& _member) const smtutil::Expression SymbolicState::txMember(string const& _member) const
@ -194,17 +264,99 @@ smtutil::Expression SymbolicState::txFunctionConstraints(FunctionDefinition cons
return conj; return conj;
} }
void SymbolicState::prepareForSourceUnit(SourceUnit const& _source) void SymbolicState::prepareForSourceUnit(SourceUnit const& _source, bool _storage)
{ {
set<FunctionCall const*> abiCalls = SMTEncoder::collectABICalls(&_source); auto allSources = _source.referencedSourceUnits(true);
for (auto const& source: _source.referencedSourceUnits(true)) allSources.insert(&_source);
set<FunctionCall const*, ASTCompareByID<FunctionCall>> abiCalls;
set<ContractDefinition const*, ASTCompareByID<ContractDefinition>> contracts;
for (auto const& source: allSources)
{
abiCalls += SMTEncoder::collectABICalls(source); abiCalls += SMTEncoder::collectABICalls(source);
for (auto node: source->nodes())
if (auto contract = dynamic_cast<ContractDefinition const*>(node.get()))
contracts.insert(contract);
}
buildState(contracts, _storage);
buildABIFunctions(abiCalls); buildABIFunctions(abiCalls);
} }
/// Private helpers. /// Private helpers.
void SymbolicState::buildABIFunctions(set<FunctionCall const*> const& _abiFunctions) string SymbolicState::contractSuffix(ContractDefinition const& _contract) const
{
return "_" + _contract.name() + "_" + to_string(_contract.id());
}
string SymbolicState::contractStorageKey(ContractDefinition const& _contract) const
{
return "storage" + contractSuffix(_contract);
}
string SymbolicState::stateVarStorageKey(VariableDeclaration const& _var, ContractDefinition const& _contract) const
{
return _var.name() + "_" + to_string(_var.id()) + contractSuffix(_contract);
}
void SymbolicState::buildState(set<ContractDefinition const*, ASTCompareByID<ContractDefinition>> const& _contracts, bool _allStorages)
{
map<string, SortPointer> stateMembers{
{"balances", make_shared<smtutil::ArraySort>(smtutil::SortProvider::uintSort, smtutil::SortProvider::uintSort)}
};
if (_allStorages)
{
vector<string> memberNames;
vector<SortPointer> memberSorts;
for (auto contract: _contracts)
{
string suffix = contractSuffix(*contract);
// z3 doesn't like empty tuples, so if the contract has 0
// state vars we can't put it there.
auto stateVars = SMTEncoder::stateVariablesIncludingInheritedAndPrivate(*contract);
if (stateVars.empty())
continue;
auto names = applyMap(stateVars, [&](auto var) {
return var->name() + "_" + to_string(var->id()) + suffix;
});
auto sorts = applyMap(stateVars, [](auto var) { return smtSortAbstractFunction(*var->type()); });
string name = "storage" + suffix;
auto storageTuple = make_shared<smtutil::TupleSort>(
name + "_type", names, sorts
);
auto storageSort = make_shared<smtutil::ArraySort>(
smtSort(*TypeProvider::address()),
storageTuple
);
memberNames.emplace_back(name);
memberSorts.emplace_back(storageSort);
}
stateMembers.emplace(
"isActive",
make_shared<smtutil::ArraySort>(smtSort(*TypeProvider::address()), smtutil::SortProvider::boolSort)
);
stateMembers.emplace(
"storage",
make_shared<smtutil::TupleSort>(
"storage_type", memberNames, memberSorts
)
);
}
m_state = make_unique<BlockchainVariable>(
"state",
std::move(stateMembers),
m_context
);
}
void SymbolicState::buildABIFunctions(set<FunctionCall const*, ASTCompareByID<FunctionCall>> const& _abiFunctions)
{ {
map<string, SortPointer> functions; map<string, SortPointer> functions;

View File

@ -62,7 +62,8 @@ private:
* - this (the address of the currently executing contract) * - this (the address of the currently executing contract)
* - state, represented as a tuple of: * - state, represented as a tuple of:
* - balances * - balances
* - TODO: potentially storage of contracts * - array of address => bool representing whether an address is used by a contract
* - storage of contracts
* - block and transaction properties, represented as a tuple of: * - block and transaction properties, represented as a tuple of:
* - blockhash * - blockhash
* - block basefee * - block basefee
@ -99,29 +100,41 @@ public:
/// @returns the symbolic value of the currently executing contract's address. /// @returns the symbolic value of the currently executing contract's address.
smtutil::Expression thisAddress() const { return m_thisAddress.currentValue(); } smtutil::Expression thisAddress() const { return m_thisAddress.currentValue(); }
smtutil::Expression thisAddress(unsigned _idx) const { return m_thisAddress.valueAtIndex(_idx); } smtutil::Expression thisAddress(unsigned _idx) const { return m_thisAddress.valueAtIndex(_idx); }
smtutil::Expression newThisAddress() { return m_thisAddress.increaseIndex(); }
smtutil::SortPointer const& thisAddressSort() const { return m_thisAddress.sort(); } smtutil::SortPointer const& thisAddressSort() const { return m_thisAddress.sort(); }
//@} //@}
/// Blockchain state. /// Blockchain state.
//@{ //@{
smtutil::Expression state() const { return m_state.value(); } smtutil::Expression state() const { solAssert(m_state, ""); return m_state->value(); }
smtutil::Expression state(unsigned _idx) const { return m_state.value(_idx); } smtutil::Expression state(unsigned _idx) const { solAssert(m_state, ""); return m_state->value(_idx); }
smtutil::SortPointer const& stateSort() const { return m_state.sort(); } smtutil::SortPointer const& stateSort() const { solAssert(m_state, ""); return m_state->sort(); }
void newState() { m_state.newVar(); } void newState() { solAssert(m_state, ""); m_state->newVar(); }
void newBalances(); void newBalances();
/// Balance.
/// @returns the symbolic balances. /// @returns the symbolic balances.
smtutil::Expression balances() const; smtutil::Expression balances() const;
/// @returns the symbolic balance of address `this`. /// @returns the symbolic balance of address `this`.
smtutil::Expression balance() const; smtutil::Expression balance() const;
/// @returns the symbolic balance of an address. /// @returns the symbolic balance of an address.
smtutil::Expression balance(smtutil::Expression _address) const; smtutil::Expression balance(smtutil::Expression _address) const;
/// Transfer _value from _from to _to. /// Transfer _value from _from to _to.
void transfer(smtutil::Expression _from, smtutil::Expression _to, smtutil::Expression _value); void transfer(smtutil::Expression _from, smtutil::Expression _to, smtutil::Expression _value);
/// Adds _value to _account's balance. /// Adds _value to _account's balance.
void addBalance(smtutil::Expression _account, smtutil::Expression _value); void addBalance(smtutil::Expression _account, smtutil::Expression _value);
/// Storage.
smtutil::Expression storage(ContractDefinition const& _contract) const;
smtutil::Expression storage(ContractDefinition const& _contract, smtutil::Expression _address) const;
smtutil::Expression addressActive(smtutil::Expression _address) const;
void setAddressActive(smtutil::Expression _address, bool _active);
void newStorage();
void writeStateVars(ContractDefinition const& _contract, smtutil::Expression _address);
void readStateVars(ContractDefinition const& _contract, smtutil::Expression _address);
//@} //@}
/// Transaction data. /// Transaction data.
@ -149,11 +162,15 @@ public:
smtutil::Expression cryptoFunction(std::string const& _member) const { return m_crypto.member(_member); } smtutil::Expression cryptoFunction(std::string const& _member) const { return m_crypto.member(_member); }
//@} //@}
/// Calls the internal methods that build
/// - the symbolic ABI functions based on the abi.* calls
/// in _source and referenced sources.
/// - the symbolic storages for all contracts in _source and
/// referenced sources.
void prepareForSourceUnit(SourceUnit const& _source, bool _storage);
/// ABI functions. /// ABI functions.
//@{ //@{
/// Calls the internal methods that build the symbolic ABI functions
/// based on the abi.* calls in _source and referenced sources.
void prepareForSourceUnit(SourceUnit const& _source);
smtutil::Expression abiFunction(FunctionCall const* _funCall); smtutil::Expression abiFunction(FunctionCall const* _funCall);
using SymbolicABIFunction = std::tuple< using SymbolicABIFunction = std::tuple<
std::string, std::string,
@ -169,8 +186,15 @@ public:
//@} //@}
private: private:
std::string contractSuffix(ContractDefinition const& _contract) const;
std::string contractStorageKey(ContractDefinition const& _contract) const;
std::string stateVarStorageKey(VariableDeclaration const& _var, ContractDefinition const& _contract) const;
/// Builds state.storage based on _contracts.
void buildState(std::set<ContractDefinition const*, ASTCompareByID<ContractDefinition>> const& _contracts, bool _allStorages);
/// Builds m_abi based on the abi.* calls _abiFunctions. /// Builds m_abi based on the abi.* calls _abiFunctions.
void buildABIFunctions(std::set<FunctionCall const*> const& _abiFunctions); void buildABIFunctions(std::set<FunctionCall const*, ASTCompareByID<FunctionCall>> const& _abiFunctions);
EncodingContext& m_context; EncodingContext& m_context;
@ -186,11 +210,14 @@ private:
m_context m_context
}; };
BlockchainVariable m_state{ /// m_state is a tuple of
"state", /// - balances: array of address to balance of address.
{{"balances", std::make_shared<smtutil::ArraySort>(smtutil::SortProvider::uintSort, smtutil::SortProvider::uintSort)}}, /// - isActive: array of address to Boolean, where element is true iff address is used.
m_context /// - storage: tuple containing the storage of every contract, where
}; /// each element of the tuple represents a contract,
/// and is defined by an array where the index is the contract's address
/// and the element is a tuple containing the state variables of that contract.
std::unique_ptr<BlockchainVariable> m_state;
BlockchainVariable m_tx{ BlockchainVariable m_tx{
"tx", "tx",

View File

@ -618,4 +618,26 @@ optional<smtutil::Expression> symbolicTypeConversion(frontend::Type const* _from
return std::nullopt; return std::nullopt;
} }
smtutil::Expression member(smtutil::Expression const& _tuple, string const& _member)
{
TupleSort const& _sort = dynamic_cast<TupleSort const&>(*_tuple.sort);
return smtutil::Expression::tuple_get(
_tuple,
_sort.memberToIndex.at(_member)
);
}
smtutil::Expression assignMember(smtutil::Expression const _tuple, map<string, smtutil::Expression> const& _values)
{
TupleSort const& _sort = dynamic_cast<TupleSort const&>(*_tuple.sort);
vector<smtutil::Expression> args;
for (auto const& m: _sort.members)
if (auto* value = util::valueOrNullptr(_values, m))
args.emplace_back(*value);
else
args.emplace_back(member(_tuple, m));
auto sortExpr = smtutil::Expression(make_shared<smtutil::SortSort>(_tuple.sort), _tuple.name);
return smtutil::Expression::tuple_constructor(sortExpr, args);
}
} }

View File

@ -82,4 +82,8 @@ void setSymbolicUnknownValue(smtutil::Expression _expr, frontend::Type const* _t
smtutil::Expression symbolicUnknownConstraints(smtutil::Expression _expr, frontend::Type const* _type); smtutil::Expression symbolicUnknownConstraints(smtutil::Expression _expr, frontend::Type const* _type);
std::optional<smtutil::Expression> symbolicTypeConversion(frontend::Type const* _from, frontend::Type const* _to); std::optional<smtutil::Expression> symbolicTypeConversion(frontend::Type const* _from, frontend::Type const* _to);
smtutil::Expression member(smtutil::Expression const& _tuple, std::string const& _member);
smtutil::Expression assignMember(smtutil::Expression const _tuple, std::map<std::string, smtutil::Expression> const& _values);
} }

View File

@ -445,7 +445,7 @@ std::optional<Json::Value> checkSettingsKeys(Json::Value const& _input)
std::optional<Json::Value> checkModelCheckerSettingsKeys(Json::Value const& _input) std::optional<Json::Value> checkModelCheckerSettingsKeys(Json::Value const& _input)
{ {
static set<string> keys{"contracts", "divModNoSlacks", "engine", "invariants", "showUnproved", "solvers", "targets", "timeout"}; static set<string> keys{"contracts", "divModNoSlacks", "engine", "extCalls", "invariants", "showUnproved", "solvers", "targets", "timeout"};
return checkKeys(_input, keys, "modelChecker"); return checkKeys(_input, keys, "modelChecker");
} }
@ -1016,6 +1016,16 @@ std::variant<StandardCompiler::InputsAndSettings, Json::Value> StandardCompiler:
ret.modelCheckerSettings.engine = *engine; ret.modelCheckerSettings.engine = *engine;
} }
if (modelCheckerSettings.isMember("extCalls"))
{
if (!modelCheckerSettings["extCalls"].isString())
return formatFatalError(Error::Type::JSONError, "settings.modelChecker.extCalls must be a string.");
std::optional<ModelCheckerExtCalls> extCalls = ModelCheckerExtCalls::fromString(modelCheckerSettings["extCalls"].asString());
if (!extCalls)
return formatFatalError(Error::Type::JSONError, "Invalid model checker extCalls requested.");
ret.modelCheckerSettings.externalCalls = *extCalls;
}
if (modelCheckerSettings.isMember("invariants")) if (modelCheckerSettings.isMember("invariants"))
{ {
auto const& invariantsArray = modelCheckerSettings["invariants"]; auto const& invariantsArray = modelCheckerSettings["invariants"];

View File

@ -69,6 +69,7 @@ static string const g_strMetadataLiteral = "metadata-literal";
static string const g_strModelCheckerContracts = "model-checker-contracts"; static string const g_strModelCheckerContracts = "model-checker-contracts";
static string const g_strModelCheckerDivModNoSlacks = "model-checker-div-mod-no-slacks"; static string const g_strModelCheckerDivModNoSlacks = "model-checker-div-mod-no-slacks";
static string const g_strModelCheckerEngine = "model-checker-engine"; static string const g_strModelCheckerEngine = "model-checker-engine";
static string const g_strModelCheckerExtCalls = "model-checker-ext-calls";
static string const g_strModelCheckerInvariants = "model-checker-invariants"; static string const g_strModelCheckerInvariants = "model-checker-invariants";
static string const g_strModelCheckerShowUnproved = "model-checker-show-unproved"; static string const g_strModelCheckerShowUnproved = "model-checker-show-unproved";
static string const g_strModelCheckerSolvers = "model-checker-solvers"; static string const g_strModelCheckerSolvers = "model-checker-solvers";
@ -831,6 +832,12 @@ General Information)").c_str(),
po::value<string>()->value_name("all,bmc,chc,none")->default_value("none"), po::value<string>()->value_name("all,bmc,chc,none")->default_value("none"),
"Select model checker engine." "Select model checker engine."
) )
(
g_strModelCheckerExtCalls.c_str(),
po::value<string>()->value_name("untrusted,trusted")->default_value("untrusted"),
"Select whether to assume (trusted) that external calls always invoke"
" the code given by the type of the contract, if that code is available."
)
( (
g_strModelCheckerInvariants.c_str(), g_strModelCheckerInvariants.c_str(),
po::value<string>()->value_name("default,all,contract,reentrancy")->default_value("default"), po::value<string>()->value_name("default,all,contract,reentrancy")->default_value("default"),
@ -1289,6 +1296,15 @@ void CommandLineParser::processArgs()
m_options.modelChecker.settings.engine = *engine; m_options.modelChecker.settings.engine = *engine;
} }
if (m_args.count(g_strModelCheckerExtCalls))
{
string mode = m_args[g_strModelCheckerExtCalls].as<string>();
optional<ModelCheckerExtCalls> extCallsMode = ModelCheckerExtCalls::fromString(mode);
if (!extCallsMode)
solThrow(CommandLineValidationError, "Invalid option for --" + g_strModelCheckerExtCalls + ": " + mode);
m_options.modelChecker.settings.externalCalls = *extCallsMode;
}
if (m_args.count(g_strModelCheckerInvariants)) if (m_args.count(g_strModelCheckerInvariants))
{ {
string invsStr = m_args[g_strModelCheckerInvariants].as<string>(); string invsStr = m_args[g_strModelCheckerInvariants].as<string>();
@ -1327,6 +1343,7 @@ void CommandLineParser::processArgs()
m_args.count(g_strModelCheckerContracts) || m_args.count(g_strModelCheckerContracts) ||
m_args.count(g_strModelCheckerDivModNoSlacks) || m_args.count(g_strModelCheckerDivModNoSlacks) ||
m_args.count(g_strModelCheckerEngine) || m_args.count(g_strModelCheckerEngine) ||
m_args.count(g_strModelCheckerExtCalls) ||
m_args.count(g_strModelCheckerInvariants) || m_args.count(g_strModelCheckerInvariants) ||
m_args.count(g_strModelCheckerShowUnproved) || m_args.count(g_strModelCheckerShowUnproved) ||
m_args.count(g_strModelCheckerSolvers) || m_args.count(g_strModelCheckerSolvers) ||

View File

@ -1 +1,27 @@
Warning: Requested contract "C" does not exist in source "model_checker_contracts_inexistent_contract/input.sol". Warning: Requested contract "C" does not exist in source "model_checker_contracts_inexistent_contract/input.sol".
Warning: CHC: Assertion violation happens here.
Counterexample:
x = 0
Transaction trace:
B.constructor()
B.f(0)
--> model_checker_contracts_inexistent_contract/input.sol:5:3:
|
5 | assert(x > 0);
| ^^^^^^^^^^^^^
Warning: CHC: Assertion violation happens here.
Counterexample:
y = 0
Transaction trace:
A.constructor()
A.g(0)
--> model_checker_contracts_inexistent_contract/input.sol:10:3:
|
10 | assert(y > 0);
| ^^^^^^^^^^^^^

View File

@ -4,7 +4,7 @@ Counterexample:
x = 0 x = 0
Transaction trace: Transaction trace:
A.constructor() B.constructor()
B.f(0) B.f(0)
--> model_checker_contracts_only_one/input.sol:5:3: --> model_checker_contracts_only_one/input.sol:5:3:
| |

View File

@ -0,0 +1 @@
--model-checker-engine chc --model-checker-ext-calls

View File

@ -0,0 +1 @@
No input files given. If you wish to use the standard input please specify "-" explicitly.

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,15 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.0;
contract Ext {
function f() public view returns (uint) {
return 42;
}
}
contract test {
function g(Ext e) public view {
uint x = e.f();
assert(x == 42);
}
}

View File

@ -0,0 +1 @@
--model-checker-engine chc --model-checker-ext-calls trusted

View File

@ -0,0 +1,5 @@
Warning: Function state mutability can be restricted to pure
--> model_checker_ext_calls_trusted_chc/input.sol:5:2:
|
5 | function f() public view returns (uint) {
| ^ (Relevant source part starts here and spans across multiple lines).

View File

@ -0,0 +1,15 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.0;
contract Ext {
function f() public view returns (uint) {
return 42;
}
}
contract test {
function g(Ext e) public view {
uint x = e.f();
assert(x == 42);
}
}

View File

@ -0,0 +1 @@
--model-checker-engine chc --model-checker-ext-calls untrusted

View File

@ -0,0 +1,14 @@
Warning: CHC: Assertion violation happens here.
Counterexample:
e = 0
x = 1
Transaction trace:
test.constructor()
test.g(0)
e.f() -- untrusted external call
--> model_checker_ext_calls_untrusted_chc/input.sol:11:3:
|
11 | assert(x == 0);
| ^^^^^^^^^^^^^^

View File

@ -0,0 +1,13 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.0;
abstract contract Ext {
function f() virtual public view returns (uint);
}
contract test {
function g(Ext e) public view {
uint x = e.f();
assert(x == 0);
}
}

View File

@ -0,0 +1 @@
--model-checker-engine chc --model-checker-ext-calls what

View File

@ -0,0 +1 @@
Invalid option for --model-checker-ext-calls: what

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,15 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.0;
contract Ext {
function f() public view returns (uint) {
return 42;
}
}
contract test {
function g(Ext e) public view {
uint x = e.f();
assert(x == 42);
}
}

View File

@ -10,6 +10,74 @@
"message": "Requested contract \"C\" does not exist in source \"Source\".", "message": "Requested contract \"C\" does not exist in source \"Source\".",
"severity": "warning", "severity": "warning",
"type": "Warning" "type": "Warning"
},
{
"component": "general",
"errorCode": "6328",
"formattedMessage": "Warning: CHC: Assertion violation happens here.
Counterexample:
y = 0
Transaction trace:
B.constructor()
B.g(0)
--> Source:5:7:
|
5 | \t\t\t\t\t\tassert(y > 0);
| \t\t\t\t\t\t^^^^^^^^^^^^^
",
"message": "CHC: Assertion violation happens here.
Counterexample:
y = 0
Transaction trace:
B.constructor()
B.g(0)",
"severity": "warning",
"sourceLocation":
{
"end": 137,
"file": "Source",
"start": 124
},
"type": "Warning"
},
{
"component": "general",
"errorCode": "6328",
"formattedMessage": "Warning: CHC: Assertion violation happens here.
Counterexample:
x = 0
Transaction trace:
A.constructor()
A.f(0)
--> Source:10:7:
|
10 | \t\t\t\t\t\tassert(x > 0);
| \t\t\t\t\t\t^^^^^^^^^^^^^
",
"message": "CHC: Assertion violation happens here.
Counterexample:
x = 0
Transaction trace:
A.constructor()
A.f(0)",
"severity": "warning",
"sourceLocation":
{
"end": 231,
"file": "Source",
"start": 218
},
"type": "Warning"
} }
], ],
"sources": "sources":

View File

@ -10,7 +10,7 @@ Counterexample:
y = 0 y = 0
Transaction trace: Transaction trace:
A.constructor() B.constructor()
B.g(0) B.g(0)
--> Source:5:7: --> Source:5:7:
| |
@ -24,7 +24,7 @@ Counterexample:
y = 0 y = 0
Transaction trace: Transaction trace:
A.constructor() B.constructor()
B.g(0)", B.g(0)",
"severity": "warning", "severity": "warning",
"sourceLocation": "sourceLocation":

View File

@ -10,7 +10,7 @@ Counterexample:
y = 0 y = 0
Transaction trace: Transaction trace:
A.constructor() B.constructor()
B.g(0) B.g(0)
--> Source:5:7: --> Source:5:7:
| |
@ -24,7 +24,7 @@ Counterexample:
y = 0 y = 0
Transaction trace: Transaction trace:
A.constructor() B.constructor()
B.g(0)", B.g(0)",
"severity": "warning", "severity": "warning",
"sourceLocation": "sourceLocation":

View File

@ -0,0 +1,30 @@
{
"language": "Solidity",
"sources":
{
"A":
{
"content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;
contract Ext {
function f() public view returns (uint) {
return 42;
}
}
contract test {
function g(Ext e) public view {
uint x = e.f();
assert(x == 42);
}
}"
}
},
"settings":
{
"modelChecker":
{
"engine": "chc",
"extCalls": ""
}
}
}

View File

@ -0,0 +1,12 @@
{
"errors":
[
{
"component": "general",
"formattedMessage": "Invalid model checker extCalls requested.",
"message": "Invalid model checker extCalls requested.",
"severity": "error",
"type": "JSONError"
}
]
}

View File

@ -0,0 +1,30 @@
{
"language": "Solidity",
"sources":
{
"A":
{
"content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;
contract Ext {
function f() public view returns (uint) {
return 42;
}
}
contract test {
function g(Ext e) public view {
uint x = e.f();
assert(x == 42);
}
}"
}
},
"settings":
{
"modelChecker":
{
"engine": "chc",
"extCalls": "trusted"
}
}
}

View File

@ -0,0 +1,32 @@
{
"errors":
[
{
"component": "general",
"errorCode": "2018",
"formattedMessage": "Warning: Function state mutability can be restricted to pure
--> A:4:7:
|
4 | \t\t\t\t\t\tfunction f() public view returns (uint) {
| \t\t\t\t\t\t^ (Relevant source part starts here and spans across multiple lines).
",
"message": "Function state mutability can be restricted to pure",
"severity": "warning",
"sourceLocation":
{
"end": 152,
"file": "A",
"start": 85
},
"type": "Warning"
}
],
"sources":
{
"A":
{
"id": 0
}
}
}

View File

@ -0,0 +1,28 @@
{
"language": "Solidity",
"sources":
{
"A":
{
"content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;
abstract contract Ext {
function f() virtual public view returns (uint);
}
contract test {
function g(Ext e) public view {
uint x = e.f();
assert(x == 0);
}
}"
}
},
"settings":
{
"modelChecker":
{
"engine": "chc",
"extCalls": "untrusted"
}
}
}

View File

@ -0,0 +1,50 @@
{
"errors":
[
{
"component": "general",
"errorCode": "6328",
"formattedMessage": "Warning: CHC: Assertion violation happens here.
Counterexample:
e = 0
x = 1
Transaction trace:
test.constructor()
test.g(0)
e.f() -- untrusted external call
--> A:10:8:
|
10 | \t\t\t\t\t\t\tassert(x == 0);
| \t\t\t\t\t\t\t^^^^^^^^^^^^^^
",
"message": "CHC: Assertion violation happens here.
Counterexample:
e = 0
x = 1
Transaction trace:
test.constructor()
test.g(0)
e.f() -- untrusted external call",
"severity": "warning",
"sourceLocation":
{
"end": 254,
"file": "A",
"start": 240
},
"type": "Warning"
}
],
"sources":
{
"A":
{
"id": 0
}
}
}

View File

@ -0,0 +1,30 @@
{
"language": "Solidity",
"sources":
{
"A":
{
"content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;
contract Ext {
function f() public view returns (uint) {
return 42;
}
}
contract test {
function g(Ext e) public view {
uint x = e.f();
assert(x == 42);
}
}"
}
},
"settings":
{
"modelChecker":
{
"engine": "chc",
"extCalls": "what"
}
}
}

View File

@ -0,0 +1,12 @@
{
"errors":
[
{
"component": "general",
"formattedMessage": "Invalid model checker extCalls requested.",
"message": "Invalid model checker extCalls requested.",
"severity": "error",
"type": "JSONError"
}
]
}

View File

@ -0,0 +1,30 @@
{
"language": "Solidity",
"sources":
{
"A":
{
"content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;
contract Ext {
function f() public view returns (uint) {
return 42;
}
}
contract test {
function g(Ext e) public view {
uint x = e.f();
assert(x == 42);
}
}"
}
},
"settings":
{
"modelChecker":
{
"engine": "chc",
"extCalls": 2
}
}
}

View File

@ -0,0 +1,12 @@
{
"errors":
[
{
"component": "general",
"formattedMessage": "settings.modelChecker.extCalls must be a string.",
"message": "settings.modelChecker.extCalls must be a string.",
"severity": "error",
"type": "JSONError"
}
]
}

View File

@ -3,14 +3,41 @@
{ {
"smtlib2queries": "smtlib2queries":
{ {
"0x0ebc730de380833af1e52ed063befb32994bc637929c942b7fd089b7cd3ba64e": "(set-logic HORN) "0x75b95497d56c30e254a59358d72ddd4e78f9e90db621cfe677e85d05b2252411": "(set-option :produce-models true)
(set-logic ALL)
(declare-fun |x_3_3| () Int)
(declare-fun |error_0| () Int)
(declare-fun |this_0| () Int)
(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int)))))
(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.basefee| Int) (|block.chainid| Int) (|block.coinbase| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.prevrandao| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int)))))
(declare-fun |tx_0| () |tx_type|)
(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int)))))
(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int))))))
(declare-fun |crypto_0| () |crypto_type|)
(declare-datatypes ((|abi_type| 0)) (((|abi_type|))))
(declare-fun |abi_0| () |abi_type|)
(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) (declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int))))))
(declare-fun |state_0| () |state_type|)
(declare-fun |x_3_4| () Int)
(declare-fun |x_3_0| () Int)
(declare-fun |expr_7_0| () Int)
(declare-fun |expr_8_0| () Int)
(declare-fun |expr_9_1| () Bool)
(assert (and (and (and true true) (and (= expr_9_1 (> expr_7_0 expr_8_0)) (and (=> (and true true) true) (and (= expr_8_0 0) (and (=> (and true true) (and (>= expr_7_0 0) (<= expr_7_0 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_7_0 x_3_0) (and (and (>= x_3_0 0) (<= x_3_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (and (and (and (and (and (and (and (and (and (and (and (> (|block.prevrandao| tx_0) 18446744073709551616) (and (>= (|block.basefee| tx_0) 0) (<= (|block.basefee| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.prevrandao| tx_0) 0) (<= (|block.prevrandao| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (and (and (and (and (and (= (|msg.value| tx_0) 0) (= (|msg.sig| tx_0) 3017696395)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 0) 179)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 1) 222)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 2) 100)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 3) 139)) (>= (|bytes_tuple_accessor_length| (|msg.data| tx_0)) 4))) true)))))))) (not expr_9_1)))
(declare-const |EVALEXPR_0| Int)
(assert (= |EVALEXPR_0| x_3_0))
(check-sat)
(get-value (|EVALEXPR_0| ))
",
"0xfb06d3f02a20bd362abded9ab80638bdc9dd43ccbf644517f3006206c0c47f67": "(set-logic HORN)
(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) (declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int)))))
(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.basefee| Int) (|block.chainid| Int) (|block.coinbase| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.prevrandao| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) (declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.basefee| Int) (|block.chainid| Int) (|block.coinbase| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.prevrandao| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int)))))
(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) (declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int)))))
(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) (declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int))))))
(declare-datatypes ((|abi_type| 0)) (((|abi_type|)))) (declare-datatypes ((|abi_type| 0)) (((|abi_type|))))
(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int))))))
(declare-fun |interface_0_C_14_0| (Int |abi_type| |crypto_type| |state_type| ) Bool) (declare-fun |interface_0_C_14_0| (Int |abi_type| |crypto_type| |state_type| ) Bool)
(declare-fun |nondet_interface_1_C_14_0| (Int Int |abi_type| |crypto_type| |state_type| |state_type| ) Bool) (declare-fun |nondet_interface_1_C_14_0| (Int Int |abi_type| |crypto_type| |state_type| |state_type| ) Bool)
(declare-fun |summary_constructor_2_C_14_0| (Int Int |abi_type| |crypto_type| |tx_type| |state_type| |state_type| ) Bool) (declare-fun |summary_constructor_2_C_14_0| (Int Int |abi_type| |crypto_type| |tx_type| |state_type| |state_type| ) Bool)
@ -97,7 +124,7 @@
(declare-fun |implicit_constructor_entry_13_C_14_0| (Int Int |abi_type| |crypto_type| |tx_type| |state_type| |state_type| ) Bool) (declare-fun |implicit_constructor_entry_13_C_14_0| (Int Int |abi_type| |crypto_type| |tx_type| |state_type| |state_type| ) Bool)
(assert (assert
(forall ( (abi_0 |abi_type|) (crypto_0 |crypto_type|) (error_0 Int) (error_1 Int) (expr_7_0 Int) (expr_8_0 Int) (expr_9_1 Bool) (funds_2_0 Int) (state_0 |state_type|) (state_1 |state_type|) (state_2 |state_type|) (state_3 |state_type|) (this_0 Int) (tx_0 |tx_type|) (x_3_0 Int) (x_3_1 Int) (x_3_2 Int)) (forall ( (abi_0 |abi_type|) (crypto_0 |crypto_type|) (error_0 Int) (error_1 Int) (expr_7_0 Int) (expr_8_0 Int) (expr_9_1 Bool) (funds_2_0 Int) (state_0 |state_type|) (state_1 |state_type|) (state_2 |state_type|) (state_3 |state_type|) (this_0 Int) (tx_0 |tx_type|) (x_3_0 Int) (x_3_1 Int) (x_3_2 Int))
(=> (and (and (and (and (= state_1 state_0) (= error_0 0)) true) true) (>= (select (|balances| state_1) this_0) (|msg.value| tx_0))) (implicit_constructor_entry_13_C_14_0 error_0 this_0 abi_0 crypto_0 tx_0 state_0 state_1)))) (=> (and (and (and (and (and (= state_1 state_0) (= error_0 0)) true) true) true) (>= (select (|balances| state_1) this_0) (|msg.value| tx_0))) (implicit_constructor_entry_13_C_14_0 error_0 this_0 abi_0 crypto_0 tx_0 state_0 state_1))))
(assert (assert
@ -124,34 +151,7 @@
(assert (assert
(forall ( (abi_0 |abi_type|) (crypto_0 |crypto_type|) (error_0 Int) (error_1 Int) (expr_7_0 Int) (expr_8_0 Int) (expr_9_1 Bool) (funds_2_0 Int) (state_0 |state_type|) (state_1 |state_type|) (state_2 |state_type|) (state_3 |state_type|) (this_0 Int) (tx_0 |tx_type|) (x_3_0 Int) (x_3_1 Int) (x_3_2 Int)) (forall ( (abi_0 |abi_type|) (crypto_0 |crypto_type|) (error_0 Int) (error_1 Int) (expr_7_0 Int) (expr_8_0 Int) (expr_9_1 Bool) (funds_2_0 Int) (state_0 |state_type|) (state_1 |state_type|) (state_2 |state_type|) (state_3 |state_type|) (this_0 Int) (tx_0 |tx_type|) (x_3_0 Int) (x_3_1 Int) (x_3_2 Int))
(=> error_target_3_0 false))) (=> error_target_3_0 false)))
(check-sat)", (check-sat)"
"0xcb822e6220a39244d26887a0fa6f62b06718359056555679fb06dd7dff18bb86": "(set-option :produce-models true)
(set-logic ALL)
(declare-fun |x_3_3| () Int)
(declare-fun |error_0| () Int)
(declare-fun |this_0| () Int)
(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int))))))
(declare-fun |state_0| () |state_type|)
(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int)))))
(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.basefee| Int) (|block.chainid| Int) (|block.coinbase| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.prevrandao| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int)))))
(declare-fun |tx_0| () |tx_type|)
(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int)))))
(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int))))))
(declare-fun |crypto_0| () |crypto_type|)
(declare-datatypes ((|abi_type| 0)) (((|abi_type|))))
(declare-fun |abi_0| () |abi_type|)
(declare-fun |x_3_4| () Int)
(declare-fun |x_3_0| () Int)
(declare-fun |expr_7_0| () Int)
(declare-fun |expr_8_0| () Int)
(declare-fun |expr_9_1| () Bool)
(assert (and (and (and true true) (and (= expr_9_1 (> expr_7_0 expr_8_0)) (and (=> (and true true) true) (and (= expr_8_0 0) (and (=> (and true true) (and (>= expr_7_0 0) (<= expr_7_0 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_7_0 x_3_0) (and (and (>= x_3_0 0) (<= x_3_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (and (and (and (and (and (and (and (and (and (and (and (> (|block.prevrandao| tx_0) 18446744073709551616) (and (>= (|block.basefee| tx_0) 0) (<= (|block.basefee| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.prevrandao| tx_0) 0) (<= (|block.prevrandao| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (and (and (and (and (and (= (|msg.value| tx_0) 0) (= (|msg.sig| tx_0) 3017696395)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 0) 179)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 1) 222)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 2) 100)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 3) 139)) (>= (|bytes_tuple_accessor_length| (|msg.data| tx_0)) 4))) true)))))))) (not expr_9_1)))
(declare-const |EVALEXPR_0| Int)
(assert (= |EVALEXPR_0| x_3_0))
(check-sat)
(get-value (|EVALEXPR_0| ))
"
} }
}, },
"errors": "errors":

View File

@ -33,6 +33,12 @@ SMTCheckerTest::SMTCheckerTest(string const& _filename): SyntaxTest(_filename, E
if (!contract.empty()) if (!contract.empty())
m_modelCheckerSettings.contracts.contracts[""] = {contract}; m_modelCheckerSettings.contracts.contracts[""] = {contract};
auto extCallsMode = ModelCheckerExtCalls::fromString(m_reader.stringSetting("SMTExtCalls", "untrusted"));
if (extCallsMode)
m_modelCheckerSettings.externalCalls = *extCallsMode;
else
BOOST_THROW_EXCEPTION(runtime_error("Invalid SMT external calls mode."));
auto const& showUnproved = m_reader.stringSetting("SMTShowUnproved", "yes"); auto const& showUnproved = m_reader.stringSetting("SMTShowUnproved", "yes");
if (showUnproved == "no") if (showUnproved == "no")
m_modelCheckerSettings.showUnproved = false; m_modelCheckerSettings.showUnproved = false;
@ -51,8 +57,13 @@ SMTCheckerTest::SMTCheckerTest(string const& _filename): SyntaxTest(_filename, E
m_modelCheckerSettings.solvers &= ModelChecker::availableSolvers(); m_modelCheckerSettings.solvers &= ModelChecker::availableSolvers();
/// Underflow and Overflow are not enabled by default for Solidity >=0.8.7, /// Underflow and Overflow are not enabled by default for Solidity >=0.8.7,
/// so we explicitly enable all targets for the tests. /// so we explicitly enable all targets for the tests,
m_modelCheckerSettings.targets = ModelCheckerTargets::All(); /// if the targets were not explicitly set by the test.
auto targets = ModelCheckerTargets::fromString(m_reader.stringSetting("SMTTargets", "all"));
if (targets)
m_modelCheckerSettings.targets = *targets;
else
BOOST_THROW_EXCEPTION(runtime_error("Invalid SMT targets."));
auto engine = ModelCheckerEngine::fromString(m_reader.stringSetting("SMTEngine", "all")); auto engine = ModelCheckerEngine::fromString(m_reader.stringSetting("SMTEngine", "all"));
if (engine) if (engine)

View File

@ -22,4 +22,4 @@ contract C {
// SMTEngine: all // SMTEngine: all
// SMTIgnoreOS: macos // SMTIgnoreOS: macos
// ---- // ----
// Info 1180: Contract invariant(s) for :C:\n!(s1.arr.length <= 0)\n!(s2.arr.length <= 0)\n(((s2.arr[0].length + ((- 1) * s1.arr[0].length)) <= 0) && ((s1.arr[0].length + ((- 1) * s2.arr[0].length)) <= 0))\n // Info 1180: Contract invariant(s) for :C:\n!(s1.arr.length <= 0)\n!(s2.arr.length <= 0)\n(((s1.arr[0].length + ((- 1) * s2.arr[0].length)) <= 0) && ((s2.arr[0].length + ((- 1) * s1.arr[0].length)) <= 0))\n

View File

@ -31,4 +31,4 @@ contract C {
// Warning 6328: (349-375): CHC: Assertion violation happens here.\nCounterexample:\narr = [[], [], [], [], [], [], [], [], []]\nx = 0\ny = 0\nz = 9\nt = 0\n\nTransaction trace:\nC.constructor()\nState: arr = [[], [], [], [], [], [], [], [], []]\nC.f() // Warning 6328: (349-375): CHC: Assertion violation happens here.\nCounterexample:\narr = [[], [], [], [], [], [], [], [], []]\nx = 0\ny = 0\nz = 9\nt = 0\n\nTransaction trace:\nC.constructor()\nState: arr = [[], [], [], [], [], [], [], [], []]\nC.f()
// Warning 6328: (379-402): CHC: Assertion violation happens here.\nCounterexample:\narr = [[], [], [], [], [], [], [], [], []]\nx = 0\ny = 0\nz = 9\nt = 0\n\nTransaction trace:\nC.constructor()\nState: arr = [[], [], [], [], [], [], [], [], []]\nC.f() // Warning 6328: (379-402): CHC: Assertion violation happens here.\nCounterexample:\narr = [[], [], [], [], [], [], [], [], []]\nx = 0\ny = 0\nz = 9\nt = 0\n\nTransaction trace:\nC.constructor()\nState: arr = [[], [], [], [], [], [], [], [], []]\nC.f()
// Warning 6328: (406-432): CHC: Assertion violation happens here.\nCounterexample:\narr = [[], [], [], [], [], [], [], [], []]\nx = 0\ny = 0\nz = 9\nt = 0\n\nTransaction trace:\nC.constructor()\nState: arr = [[], [], [], [], [], [], [], [], []]\nC.f() // Warning 6328: (406-432): CHC: Assertion violation happens here.\nCounterexample:\narr = [[], [], [], [], [], [], [], [], []]\nx = 0\ny = 0\nz = 9\nt = 0\n\nTransaction trace:\nC.constructor()\nState: arr = [[], [], [], [], [], [], [], [], []]\nC.f()
// Info 1180: Contract invariant(s) for :C:\n!(arr.length <= 7)\n!(arr.length <= 8)\n // Info 1180: Contract invariant(s) for :C:\n!(arr.length <= 5)\n!(arr.length <= 7)\n!(arr.length <= 8)\n

View File

@ -18,5 +18,6 @@ contract C {
// ==== // ====
// SMTEngine: all // SMTEngine: all
// SMTIgnoreOS: macos // SMTIgnoreOS: macos
// SMTIgnoreCex: yes
// ---- // ----
// Warning 6328: (199-229): CHC: Assertion violation happens here.\nCounterexample:\nb = [1]\n\nTransaction trace:\nC.constructor()\nState: b = []\nC.g() // Warning 6328: (199-229): CHC: Assertion violation happens here.\nCounterexample:\nb = [1]\n\nTransaction trace:\nC.constructor()\nState: b = []\nC.g()

View File

@ -20,5 +20,6 @@ contract C {
} }
// ==== // ====
// SMTEngine: all // SMTEngine: all
// SMTIgnoreCex: yes
// ---- // ----
// Warning 6328: (362-420): CHC: Assertion violation happens here. // Warning 6328: (362-420): CHC: Assertion violation happens here.

View File

@ -83,6 +83,6 @@ contract InternalCall {
// Warning 2018: (1111-1173): Function state mutability can be restricted to pure // Warning 2018: (1111-1173): Function state mutability can be restricted to pure
// Warning 2018: (1179-1241): Function state mutability can be restricted to pure // Warning 2018: (1179-1241): Function state mutability can be restricted to pure
// Warning 2018: (1247-1309): Function state mutability can be restricted to pure // Warning 2018: (1247-1309): Function state mutability can be restricted to pure
// Warning 4588: (681-716): Assertion checker does not yet implement this type of function call. // Warning 8729: (681-716): Contract deployment is only supported in the trusted mode for external calls with the CHC engine.
// Warning 4588: (854-886): Assertion checker does not yet implement this type of function call. // Warning 8729: (854-886): Contract deployment is only supported in the trusted mode for external calls with the CHC engine.
// Warning 5729: (1370-1375): BMC does not yet implement this type of function call. // Warning 5729: (1370-1375): BMC does not yet implement this type of function call.

View File

@ -0,0 +1,18 @@
contract D {
uint x;
function f() public view returns (uint) { return x; }
}
contract C {
function g() public {
D d = new D();
uint y = d.f();
assert(y == 0); // should fail in BMC
}
}
// ====
// SMTEngine: bmc
// SMTExtCalls: trusted
// ----
// Warning 8729: (124-131): Contract deployment is only supported in the trusted mode for external calls with the CHC engine.
// Warning 4661: (153-167): BMC: Assertion violation happens here.

View File

@ -0,0 +1,17 @@
contract D {
uint x;
function f() public view returns (uint) { return x; }
}
contract C {
function g() public {
D d = new D();
uint y = d.f();
assert(y == 0); // should fail in ext calls untrusted mode
}
}
// ====
// SMTEngine: bmc
// ----
// Warning 8729: (124-131): Contract deployment is only supported in the trusted mode for external calls with the CHC engine.
// Warning 4661: (153-167): BMC: Assertion violation happens here.

View File

@ -0,0 +1,16 @@
contract D {
uint x;
function f() public view returns (uint) { return x; }
}
contract C {
function g() public {
D d = new D();
uint y = d.f();
assert(y == 0); // should hold in ext calls trusted mode
}
}
// ====
// SMTEngine: all
// SMTExtCalls: trusted
// ----

View File

@ -0,0 +1,19 @@
contract D {
uint x;
}
contract C {
function f() public {
D d1 = new D();
D d2 = new D();
assert(d1 != d2); // should hold in ext calls trusted mode
assert(address(this) != address(d1)); // should hold in ext calls trusted mode
assert(address(this) != address(d2)); // should hold in ext calls trusted mode
}
}
// ====
// SMTEngine: all
// SMTExtCalls: trusted
// ----
// Info 1180: Contract invariant(s) for :C:\n(:var 0).isActive[address(this)]\n

View File

@ -0,0 +1,32 @@
contract D {
uint x;
function inc() public { ++x; }
function f() public view returns (uint) { return x; }
}
contract C {
function f() public {
D d = new D();
assert(d.f() == 0); // should hold
d.inc();
assert(d.f() == 1); // should hold
d = new D();
assert(d.f() == 0); // should hold
assert(d.f() == 1); // should fail
}
}
// ====
// SMTEngine: all
// SMTExtCalls: trusted
// SMTIgnoreOS: macos
// ----
// Warning 4984: (47-50): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here.
// Warning 6328: (167-185): CHC: Assertion violation might happen here.
// Warning 6328: (215-233): CHC: Assertion violation might happen here.
// Warning 6328: (267-285): CHC: Assertion violation might happen here.
// Warning 6328: (304-322): CHC: Assertion violation might happen here.
// Warning 2661: (47-50): BMC: Overflow (resulting value larger than 2**256 - 1) happens here.
// Warning 4661: (167-185): BMC: Assertion violation happens here.
// Warning 4661: (215-233): BMC: Assertion violation happens here.
// Warning 4661: (267-285): BMC: Assertion violation happens here.
// Warning 4661: (304-322): BMC: Assertion violation happens here.

View File

@ -0,0 +1,17 @@
contract D {
uint x;
}
contract C {
uint y;
function g() public {
D d = new D();
assert(y == 0); // should hold
}
}
// ====
// SMTEngine: all
// SMTExtCalls: trusted
// ----
// Warning 2072: (72-75): Unused local variable.
// Info 1180: Contract invariant(s) for :C:\n(y <= 0)\n

View File

@ -0,0 +1,24 @@
contract D {
uint x;
function inc() public { ++x; }
function f() public view returns (uint) { return x; }
}
contract C {
D d;
constructor() {
d = new D();
assert(d.f() == 0); // should hold
}
function g() public view {
assert(d.f() == 0); // should fail
}
}
// ====
// SMTEngine: all
// SMTExtCalls: trusted
// SMTIgnoreOS: macos
// ----
// Warning 4984: (47-50): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here.
// Warning 6328: (233-251): CHC: Assertion violation happens here.
// Warning 2661: (47-50): BMC: Overflow (resulting value larger than 2**256 - 1) happens here.

View File

@ -0,0 +1,20 @@
contract D {
uint x;
function f() public view returns (uint) { return x; }
}
contract C {
D d;
constructor() {
d = new D();
assert(d.f() == 0); // should hold
}
function g() public view {
assert(d.f() == 0); // should hold
}
}
// ====
// SMTEngine: all
// SMTExtCalls: trusted
// ----
// Info 1180: Contract invariant(s) for :C:\n((:var 1).storage.storage_D_12[d].x_3_D_12 <= 0)\nReentrancy property(ies) for :D:\n((x' <= 0) || !(x <= 0))\n

View File

@ -0,0 +1,21 @@
contract D {
uint x;
function s(uint _x) public { x = _x; }
function f() public view returns (uint) { return x; }
}
contract C {
D d;
constructor() {
d = new D();
}
function g() public view {
assert(d.f() == 0); // should fail
}
}
// ====
// SMTContract: C
// SMTEngine: all
// SMTExtCalls: trusted
// ----
// Warning 6328: (204-222): CHC: Assertion violation happens here.

View File

@ -0,0 +1,20 @@
contract D {
bool b;
function s() public { b = true; }
function f() public view returns (bool) { return b; }
}
contract C {
D d;
constructor() {
d = new D();
}
function g() public view {
assert(d.f()); // should fail
}
}
// ====
// SMTEngine: all
// SMTExtCalls: trusted
// ----
// Warning 6328: (199-212): CHC: Assertion violation happens here.\nCounterexample:\nd = (- 1)\n\nTransaction trace:\nC.constructor()\nState: d = (- 1)\nC.g()\n D.f() -- trusted external call

View File

@ -0,0 +1,17 @@
contract D {
uint x;
function f() public view returns (uint) { return x; }
}
contract C {
function g() public {
D d = new D();
uint y = d.f();
assert(y == 0); // should fail in ext calls untrusted mode
}
}
// ====
// SMTEngine: all
// ----
// Warning 8729: (124-131): Contract deployment is only supported in the trusted mode for external calls with the CHC engine.
// Warning 6328: (153-167): CHC: Assertion violation happens here.\nCounterexample:\n\nd = 0\ny = 1\n\nTransaction trace:\nC.constructor()\nC.g()\n d.f() -- untrusted external call

View File

@ -0,0 +1,22 @@
contract D {
uint x;
}
contract C {
function f() public {
D d1 = new D();
D d2 = new D();
assert(d1 != d2); // should fail in ext calls untrusted mode
assert(address(this) != address(d1)); // should fail in ext calls untrusted mode
assert(address(this) != address(d2)); // should fail in ext calls untrusted mode
}
}
// ====
// SMTEngine: all
// ----
// Warning 8729: (70-77): Contract deployment is only supported in the trusted mode for external calls with the CHC engine.
// Warning 8729: (88-95): Contract deployment is only supported in the trusted mode for external calls with the CHC engine.
// Warning 6328: (100-116): CHC: Assertion violation happens here.\nCounterexample:\n\nd1 = 0\nd2 = 0\n\nTransaction trace:\nC.constructor()\nC.f()
// Warning 6328: (163-199): CHC: Assertion violation happens here.\nCounterexample:\n\nd1 = 21238\nd2 = 21238\n\nTransaction trace:\nC.constructor()\nC.f()
// Warning 6328: (246-282): CHC: Assertion violation happens here.\nCounterexample:\n\nd1 = 21238\nd2 = 21238\n\nTransaction trace:\nC.constructor()\nC.f()

View File

@ -0,0 +1,17 @@
contract D {
uint x;
}
contract C {
uint y;
function g() public {
D d = new D();
assert(y == 0); // should fail in ext calls untrusted mode
}
}
// ====
// SMTEngine: all
// ----
// Warning 2072: (72-75): Unused local variable.
// Warning 8729: (78-85): Contract deployment is only supported in the trusted mode for external calls with the CHC engine.
// Warning 6328: (89-103): CHC: Assertion violation happens here.\nCounterexample:\ny = 1\nd = 0\n\nTransaction trace:\nC.constructor()\nState: y = 0\nC.g()

View File

@ -0,0 +1,20 @@
contract A {
constructor() payable {}
}
contract B {
function f() public payable {
require(address(this).balance == 100);
A a = new A{value: 50}();
assert(address(this).balance == 50); // should hold
assert(address(this).balance == 60); // should fail
assert(address(a).balance >= 50); // should hold
assert(address(a).balance == 50); // should fail
}
}
// ====
// SMTEngine: chc
// SMTExtCalls: trusted
// ----
// Warning 6328: (211-246): CHC: Assertion violation happens here.
// Warning 6328: (316-348): CHC: Assertion violation happens here.

View File

@ -0,0 +1,28 @@
contract D {
constructor(uint _x) { x = _x; }
uint public x;
}
contract E {
constructor() { x = 2; }
uint public x;
}
contract C {
constructor() {
address d = address(new D(42));
assert(D(d).x() == 42); // should hold
assert(D(d).x() == 43); // should fail
uint y = E(d).x();
assert(y == 2); // should fail, it would still call D.x() == 42
assert(y == 42); // should hold, but fails due to false positive
}
}
// ====
// SMTEngine: chc
// SMTExtCalls: trusted
// SMTIgnoreCex: yes
// ----
// Warning 6328: (231-253): CHC: Assertion violation happens here.
// Warning 6328: (293-307): CHC: Assertion violation happens here.
// Warning 6328: (359-374): CHC: Assertion violation happens here.

View File

@ -0,0 +1,25 @@
contract D {
constructor(uint _x) { x = _x; }
function setD(uint _x) public { x = _x; }
uint public x;
}
contract C {
constructor() {
address d = address(new D(42));
assert(D(d).x() == 42); // should hold
assert(D(d).x() == 21); // should fail
d.call(abi.encodeCall(D.setD, (21)));
assert(D(d).x() == 21); // should hold, but false positive cus low level calls are not handled precisely
assert(D(d).x() == 42); // should fail
}
}
// ====
// SMTEngine: chc
// SMTExtCalls: trusted
// SMTIgnoreCex: yes
// ----
// Warning 9302: (257-293): Return value of low-level calls not used.
// Warning 6328: (216-238): CHC: Assertion violation happens here.
// Warning 6328: (297-319): CHC: Assertion violation happens here.
// Warning 6328: (404-426): CHC: Assertion violation happens here.

View File

@ -0,0 +1,28 @@
contract D {
constructor(uint _x) { x = _x; }
uint public x;
}
contract E {
constructor() { x = 2; }
uint public x;
}
contract C {
function f() public {
address d = address(new D(42));
assert(D(d).x() == 42); // should hold
assert(D(d).x() == 43); // should fail
uint y = E(d).x();
assert(y == 2); // should fail, it would still call D.x() == 42
assert(y == 42); // should hold, but fails due to false positive
}
}
// ====
// SMTEngine: chc
// SMTExtCalls: trusted
// SMTIgnoreCex: yes
// ----
// Warning 6328: (237-259): CHC: Assertion violation happens here.
// Warning 6328: (299-313): CHC: Assertion violation happens here.
// Warning 6328: (365-380): CHC: Assertion violation happens here.

View File

@ -0,0 +1,25 @@
contract D {
constructor(uint _x) { x = _x; }
function setD(uint _x) public { x = _x; }
uint public x;
}
contract C {
function f() public {
address d = address(new D(42));
assert(D(d).x() == 42); // should hold
assert(D(d).x() == 21); // should fail
d.call(abi.encodeCall(D.setD, (21)));
assert(D(d).x() == 21); // should hold, but false positive cus low level calls are not handled precisely
assert(D(d).x() == 42); // should fail
}
}
// ====
// SMTEngine: chc
// SMTExtCalls: trusted
// SMTIgnoreCex: yes
// ----
// Warning 9302: (263-299): Return value of low-level calls not used.
// Warning 6328: (222-244): CHC: Assertion violation happens here.
// Warning 6328: (303-325): CHC: Assertion violation happens here.
// Warning 6328: (410-432): CHC: Assertion violation happens here.

View File

@ -0,0 +1,29 @@
contract D {
constructor(uint _x) { x = _x; }
function setD(uint _x) public { x = _x; }
uint public x;
}
contract C {
uint x;
function f() public {
x = 666;
address d = address(new D(42));
assert(D(d).x() == 42); // should hold
assert(D(d).x() == 21); // should fail
d.call(abi.encodeCall(D.setD, (21)));
assert(D(d).x() == 21); // should hold, but false positive cus low level calls are not handled precisely
assert(D(d).x() == 42); // should fail
assert(x == 666); // should hold, C's storage should not have been havoced
}
}
// ====
// SMTEngine: chc
// SMTExtCalls: trusted
// SMTIgnoreCex: yes
// ----
// Warning 9302: (284-320): Return value of low-level calls not used.
// Warning 6328: (243-265): CHC: Assertion violation happens here.
// Warning 6328: (324-346): CHC: Assertion violation happens here.
// Warning 6328: (431-453): CHC: Assertion violation happens here.

View File

@ -15,4 +15,4 @@ contract C {
// Warning 2519: (106-112): This declaration shadows an existing declaration. // Warning 2519: (106-112): This declaration shadows an existing declaration.
// Warning 2072: (106-112): Unused local variable. // Warning 2072: (106-112): Unused local variable.
// Warning 2072: (114-131): Unused local variable. // Warning 2072: (114-131): Unused local variable.
// Info 1180: Contract invariant(s) for :C:\n(x <= 0)\nReentrancy property(ies) for :C:\n((!(x <= 0) || (x' <= 0)) && (!(x <= 0) || (<errorCode> <= 0)))\n<errorCode> = 0 -> no errors\n<errorCode> = 1 -> Assertion failed at assert(x == 0)\n // Info 1180: Contract invariant(s) for :C:\n(x <= 0)\nReentrancy property(ies) for :C:\n((!(x <= 0) || (x' <= 0)) && ((<errorCode> <= 0) || !(x <= 0)))\n<errorCode> = 0 -> no errors\n<errorCode> = 1 -> Assertion failed at assert(x == 0)\n

View File

@ -0,0 +1,21 @@
contract State {
function f(uint _x) public pure returns (uint) {
assert(_x < 100); // should fail
return _x;
}
}
contract C {
State s;
uint z = s.f(2);
function f() public view {
assert(z == 2); // should hold in trusted mode
}
}
// ====
// SMTContract: C
// SMTEngine: all
// SMTExtCalls: trusted
// SMTIgnoreInv: yes
// ----
// Warning 6328: (69-85): CHC: Assertion violation happens here.\nCounterexample:\n\n_x = 100\n = 0\n\nTransaction trace:\nState.constructor()\nState.f(100)

View File

@ -0,0 +1,17 @@
contract C {
uint z = this.g(2);
function g(uint _x) public pure returns (uint) {
assert(_x > 0); // should fail
return _x;
}
function f() public view {
assert(z == 2); // should hold
}
}
// ====
// SMTEngine: all
// ----
// Warning 6328: (87-101): CHC: Assertion violation happens here.\nCounterexample:\nz = 2\n_x = 0\n = 0\n\nTransaction trace:\nC.constructor()\nState: z = 2\nC.g(0)
// Info 1180: Contract invariant(s) for :C:\n(!(z >= 3) && !(z <= 1))\n

View File

@ -0,0 +1,25 @@
contract State {
function f(uint _x) public pure returns (uint) {
assert(_x < 100); // should fail
return _x;
}
}
contract C {
State s;
uint z;
constructor() {
z = s.f(2);
}
function f() public view {
assert(z == 2); // should hold in trusted mode
}
}
// ====
// SMTContract: C
// SMTEngine: all
// SMTExtCalls: trusted
// ----
// Warning 6328: (69-85): CHC: Assertion violation happens here.\nCounterexample:\n\n_x = 100\n = 0\n\nTransaction trace:\nState.constructor()\nState.f(100)
// Info 1180: Contract invariant(s) for :C:\n(!(z >= 3) && !(z <= 1))\n

View File

@ -0,0 +1,41 @@
contract A {
uint x;
function setX(uint _x) public {
x = _x;
}
function getX() public view returns (uint) {
return x;
}
}
contract B {
A a;
constructor() {
a = new A();
assert(a.getX() == 0); // should hold
}
function g() public view {
assert(a.getX() == 0); // should fail because A.setX() can be called without B
}
function getX() public view returns (uint) {
return a.getX();
}
}
contract C {
B b;
constructor() {
b = new B();
assert(b.getX() == 0); // should hold
}
function f() public view {
assert(b.getX() == 0); // should fail because A.setX() can be called without A
}
}
// ====
// SMTEngine: chc
// SMTExtCalls: trusted
// SMTIgnoreOS: macos
// ----
// Warning 6328: (256-277): CHC: Assertion violation happens here.
// Warning 6328: (533-554): CHC: Assertion violation might happen here.

View File

@ -0,0 +1,52 @@
contract A {
uint x;
address immutable owner;
constructor() {
owner = msg.sender;
}
function setX(uint _x) public {
require(msg.sender == owner);
x = _x;
}
function getX() public view returns (uint) {
return x;
}
}
contract B {
A a;
constructor() {
a = new A();
assert(a.getX() == 0); // should hold
}
function g() public view {
assert(a.getX() == 0); // should hold, but fails because
// the nondet_interface constraint added for `A a` in between
// txs of `B` does not have the constraint that `msg.sender != address(this)`
// so `A.setX` is allowed with `msg.sender = address(this)` inside
// the current rules defining nondet_interface.
// If we want to support that, we likely need a new type of nondet_interface
// `nondet_interface_with_tx` that contains tx data as well as restricts
// every further `nondet_interface_with_tx` to not have that `msg.sender`.
}
function getX() public view returns (uint) {
return a.getX();
}
}
contract C {
B b;
constructor() {
b = new B();
assert(b.getX() == 0); // should hold
}
function f() public view {
assert(b.getX() == 0); // should hold
}
}
// ====
// SMTEngine: chc
// SMTExtCalls: trusted
// ----
// Warning 6328: (434-455): CHC: Assertion violation happens here.
// Warning 6328: (1270-1291): CHC: Assertion violation might happen here.

View File

@ -0,0 +1,45 @@
contract A {
uint x;
address immutable owner;
constructor() {
owner = msg.sender;
}
function setX(uint _x) public {
require(msg.sender == owner);
x = _x;
}
function getX() public view returns (uint) {
return x;
}
}
contract B {
A a;
constructor() {
a = new A();
assert(a.getX() == 0); // should hold
}
function g() public {
a.setX(42);
}
function getX() public view returns (uint) {
return a.getX();
}
}
contract C {
B b;
constructor() {
b = new B();
assert(b.getX() == 0); // should hold
}
function f() public view {
assert(b.getX() == 0); // should fail
}
}
// ====
// SMTEngine: chc
// SMTExtCalls: trusted
// SMTIgnoreOS: macos
// ----
// Warning 6328: (561-582): CHC: Assertion violation might happen here.

View File

@ -0,0 +1,48 @@
contract A {
uint x;
address immutable owner;
constructor() {
owner = msg.sender;
}
function setX(uint _x) public {
require(msg.sender == owner);
x = _x;
}
function getX() public view returns (uint) {
return x;
}
}
contract B {
A a;
address immutable owner;
constructor() {
owner = msg.sender;
a = new A();
assert(a.getX() == 0); // should hold
}
function g() public {
require(msg.sender == owner);
a.setX(42);
}
function getX() public view returns (uint) {
return a.getX();
}
}
contract C {
B b;
constructor() {
b = new B();
assert(b.getX() == 0); // should hold
}
function f() public view {
assert(b.getX() == 0); // should hold
}
}
// ====
// SMTEngine: chc
// SMTExtCalls: trusted
// SMTIgnoreOS: macos
// ----
// Warning 6328: (641-662): CHC: Assertion violation might happen here.

View File

@ -0,0 +1,50 @@
contract A {
uint x;
address immutable owner;
constructor() {
owner = msg.sender;
}
function setX(uint _x) public {
require(msg.sender == owner);
x = _x;
}
function getX() public view returns (uint) {
return x;
}
}
contract B {
A a;
address immutable owner;
constructor() {
owner = msg.sender;
a = new A();
}
function g() public {
require(msg.sender == owner);
a.setX(42);
}
function getX() public view returns (uint) {
return a.getX();
}
}
contract C {
B b;
constructor() {
b = new B();
assert(b.getX() == 0); // should hold
}
function f() public view {
assert(b.getX() == 0); // should fail
}
function h() public {
b.g();
}
}
// ====
// SMTContract: C
// SMTEngine: chc
// SMTExtCalls: trusted
// ----
// Warning 6328: (601-622): CHC: Assertion violation might happen here.

View File

@ -0,0 +1,17 @@
contract C {
uint x;
function i() public { ++x; }
function f() public {
x = 0;
this.i();
assert(x == 1); // should hold in trusted mode
assert(x != 1); // should fail
}
}
// ====
// SMTEngine: chc
// SMTExtCalls: trusted
// SMTTargets: assert
// SMTIgnoreCex: yes
// ----
// Warning 6328: (147-161): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\n\nTransaction trace:\nC.constructor()\nState: x = 0\nC.f()\n C.i() -- trusted external call

View File

@ -0,0 +1,16 @@
contract C {
uint x;
function i() public { ++x; }
function f() public {
x = 0;
((this)).i();
assert(x == 1); // should hold in trusted mode
assert(x != 1); // should fail
}
}
// ====
// SMTEngine: chc
// SMTExtCalls: trusted
// SMTTargets: assert
// ----
// Warning 6328: (151-165): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\n\nTransaction trace:\nC.constructor()\nState: x = 0\nC.f()\n C.i() -- trusted external call

View File

@ -0,0 +1,17 @@
contract C {
uint x;
function i() public { ++x; }
function f() public {
x = 0;
C c = this;
c.i();
assert(x == 1); // should hold in trusted mode
assert(x != 1); // should fail
}
}
// ====
// SMTEngine: chc
// SMTExtCalls: trusted
// SMTTargets: assert
// ----
// Warning 6328: (158-172): CHC: Assertion violation happens here.

View File

@ -0,0 +1,23 @@
contract D {
uint public x;
}
contract C {
struct S {
address d;
}
S[] ss;
constructor() {
ss.push(S(address(new D())));
assert(D(ss[0].d).x() == 0); // should hold
}
function f() public view {
assert(D(ss[0].d).x() == 0); // should hold, but fails because we havoc the state
}
}
// ====
// SMTEngine: chc
// SMTExtCalls: trusted
// SMTTargets: assert
// ----
// Warning 6328: (210-237): CHC: Assertion violation happens here.

View File

@ -0,0 +1,24 @@
contract D {
uint public x;
function setD(uint _x) public { x = _x; }
}
contract C {
struct S {
address d;
}
S[] ss;
constructor() {
ss.push(S(address(new D())));
assert(D(ss[0].d).x() == 0); // should hold
}
function f() public view {
assert(D(ss[0].d).x() == 0); // should fail
}
}
// ====
// SMTEngine: chc
// SMTExtCalls: trusted
// SMTTargets: assert
// ----
// Warning 6328: (253-280): CHC: Assertion violation happens here.\nCounterexample:\nss = [{d: 0x4706}]\n\nTransaction trace:\nC.constructor()\nState: ss = [{d: 0x4706}]\nC.f()

View File

@ -0,0 +1,21 @@
contract D {
uint public x;
function setD(uint _x) public { x = _x; }
}
contract C {
address[] ds;
constructor() {
ds.push(address(new D()));
assert(D(ds[0]).x() == 0); // should hold
}
function f() public view {
assert(D(ds[0]).x() == 0); // should fail
}
}
// ====
// SMTEngine: chc
// SMTExtCalls: trusted
// SMTTargets: assert
// ----
// Warning 6328: (226-251): CHC: Assertion violation happens here.\nCounterexample:\nds = [0x0]\n\nTransaction trace:\nC.constructor()\nState: ds = [0x0]\nC.f()

View File

@ -0,0 +1,20 @@
contract D {
uint public x;
}
contract C {
address[] ds;
constructor() {
ds.push(address(new D()));
assert(D(ds[0]).x() == 0); // should hold
}
function f() public view {
assert(D(ds[0]).x() == 0); // should hold, but fails because we havoc the state
}
}
// ====
// SMTEngine: chc
// SMTExtCalls: trusted
// SMTTargets: assert
// ----
// Warning 6328: (183-208): CHC: Assertion violation happens here.\nCounterexample:\nds = [0x25]\n\nTransaction trace:\nC.constructor()\nState: ds = [0x25]\nC.f()

View File

@ -0,0 +1,24 @@
contract D {
uint public x;
function setD(uint _x) public { x = _x; }
}
contract C {
struct S {
address d;
}
S s;
constructor() {
s.d = address(new D());
assert(D(s.d).x() == 0); // should hold
}
function f() public view {
assert(D(s.d).x() == 0); // should fail
}
}
// ====
// SMTEngine: chc
// SMTExtCalls: trusted
// SMTTargets: assert
// ----
// Warning 6328: (240-263): CHC: Assertion violation happens here.\nCounterexample:\ns = {d: 0x5039}\n\nTransaction trace:\nC.constructor()\nState: s = {d: 0x5039}\nC.f()

View File

@ -0,0 +1,23 @@
contract D {
uint public x;
}
contract C {
struct S {
address d;
}
S s;
constructor() {
s.d = address(new D());
assert(D(s.d).x() == 0); // should hold
}
function f() public view {
assert(D(s.d).x() == 0); // should hold, but fails because we havoc the state
}
}
// ====
// SMTEngine: chc
// SMTExtCalls: trusted
// SMTTargets: assert
// ----
// Warning 6328: (197-220): CHC: Assertion violation happens here.\nCounterexample:\ns = {d: 0x5039}\n\nTransaction trace:\nC.constructor()\nState: s = {d: 0x5039}\nC.f()

View File

@ -0,0 +1,26 @@
contract D {
uint public x;
}
contract C {
struct S {
address d;
}
struct T {
S s;
}
T t;
constructor() {
t.s.d = address(new D());
assert(D(t.s.d).x() == 0); // should hold
}
function f() public view {
assert(D(t.s.d).x() == 0); // should hold, but fails because we havoc the state
}
}
// ====
// SMTEngine: chc
// SMTExtCalls: trusted
// SMTTargets: assert
// ----
// Warning 6328: (223-248): CHC: Assertion violation happens here.\nCounterexample:\nt = {s: {d: 0x5039}}\n\nTransaction trace:\nC.constructor()\nState: t = {s: {d: 0x5039}}\nC.f()

View File

@ -0,0 +1,27 @@
contract D {
uint public x;
function setD(uint _x) public { x = _x; }
}
contract C {
struct S {
address d;
}
struct T {
S s;
}
T t;
constructor() {
t.s.d = address(new D());
assert(D(t.s.d).x() == 0); // should hold
}
function f() public view {
assert(D(t.s.d).x() == 0); // should fail
}
}
// ====
// SMTEngine: chc
// SMTExtCalls: trusted
// SMTTargets: assert
// ----
// Warning 6328: (266-291): CHC: Assertion violation happens here.\nCounterexample:\nt = {s: {d: 0x5039}}\n\nTransaction trace:\nC.constructor()\nState: t = {s: {d: 0x5039}}\nC.f()

View File

@ -0,0 +1,23 @@
contract D {
uint public x;
}
contract C {
struct S {
D d;
}
S[] ss;
constructor() {
ss.push(S(new D()));
assert(ss[0].d.x() == 0); // should hold
}
function f() public view {
assert(ss[0].d.x() == 0); // should hold, but fails because we havoc the state
}
}
// ====
// SMTEngine: chc
// SMTExtCalls: trusted
// SMTTargets: assert
// ----
// Warning 6328: (192-216): CHC: Assertion violation happens here.

View File

@ -0,0 +1,24 @@
contract D {
uint public x;
function setD(uint _x) public { x = _x; }
}
contract C {
struct S {
D d;
}
S[] ss;
constructor() {
ss.push(S(new D()));
assert(ss[0].d.x() == 0); // should hold
}
function f() public view {
assert(ss[0].d.x() == 0); // should fail
}
}
// ====
// SMTEngine: chc
// SMTExtCalls: trusted
// SMTTargets: assert
// ----
// Warning 6328: (235-259): CHC: Assertion violation happens here.\nCounterexample:\nss = [{d: 20819}]\n\nTransaction trace:\nC.constructor()\nState: ss = [{d: 20819}]\nC.f()

View File

@ -0,0 +1,21 @@
contract D {
uint public x;
function setD(uint _x) public { x = _x; }
}
contract C {
D[] ds;
constructor() {
ds.push(new D());
assert(ds[0].x() == 0); // should hold
}
function f() public view {
assert(ds[0].x() == 0); // should fail
}
}
// ====
// SMTEngine: chc
// SMTExtCalls: trusted
// SMTTargets: assert
// ----
// Warning 6328: (208-230): CHC: Assertion violation happens here.\nCounterexample:\nds = [39]\n\nTransaction trace:\nC.constructor()\nState: ds = [39]\nC.f()

View File

@ -0,0 +1,21 @@
contract D {
uint public x;
}
contract C {
D[] ds;
constructor() {
ds.push(new D());
assert(ds[0].x() == 0); // should hold
}
function f() public view {
assert(ds[0].x() == 0); // should hold, but fails because we havoc the state
}
}
// ====
// SMTEngine: chc
// SMTExtCalls: trusted
// SMTTargets: assert
// SMTIgnoreCex: yes
// ----
// Warning 6328: (165-187): CHC: Assertion violation happens here.

View File

@ -0,0 +1,24 @@
contract D {
uint public x;
function setD(uint _x) public { x = _x; }
}
contract C {
struct S {
D d;
}
S s;
constructor() {
s.d = new D();
assert(s.d.x() == 0); // should hold
}
function f() public view {
assert(s.d.x() == 0); // should fail
}
}
// ====
// SMTEngine: chc
// SMTExtCalls: trusted
// SMTTargets: assert
// ----
// Warning 6328: (222-242): CHC: Assertion violation happens here.\nCounterexample:\ns = {d: 20819}\n\nTransaction trace:\nC.constructor()\nState: s = {d: 20819}\nC.f()

Some files were not shown because too many files have changed in this diff Show More