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:
* 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:

View File

@ -518,6 +518,188 @@ which has the following form:
"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
======================================

View File

@ -433,6 +433,10 @@ Input Description
"divModNoSlacks": false,
// Choose which model checker engine to use: all (default), bmc, chc, none.
"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.
"invariants": ["contract", "reentrancy"],
// 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_variableUsage.setFunctionInlining(shouldInlineFunctionCall);
createFreeConstants(sourceDependencies(_source));
state().prepareForSourceUnit(_source);
state().prepareForSourceUnit(_source, false);
m_unprovedAmt = 0;
_source.accept(*this);
@ -130,7 +130,7 @@ bool BMC::shouldInlineFunctionCall(
FunctionType const& funType = dynamic_cast<FunctionType const&>(*_funCall.expression().annotation().type);
if (funType.kind() == FunctionType::Kind::External)
return isTrustedExternalCall(&_funCall.expression());
return isExternalCallToThis(&_funCall.expression());
else if (funType.kind() != FunctionType::Kind::Internal)
return false;
@ -567,7 +567,7 @@ void BMC::internalOrExternalFunctionCall(FunctionCall const& _funCall)
auto const& funType = dynamic_cast<FunctionType const&>(*_funCall.expression().annotation().type);
if (shouldInlineFunctionCall(_funCall, currentScopeContract(), m_currentContract))
inlineFunctionCall(_funCall);
else if (isPublicGetter(_funCall.expression()))
else if (publicGetter(_funCall.expression()))
{
// Do nothing here.
// 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);
collectFreeFunctions(sources);
createFreeConstants(sources);
state().prepareForSourceUnit(_source);
state().prepareForSourceUnit(_source, encodeExternalCallsAsTrusted());
for (auto const* source: sources)
defineInterfacesAndSummaries(*source);
@ -175,15 +175,30 @@ void CHC::endVisit(ContractDefinition const& _contract)
smtutil::Expression zeroes(true);
for (auto var: stateVariablesIncludingInheritedAndPrivate(_contract))
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,
// so the balance must be at least `msg.value`, but not equals.
auto initialBalanceConstraint = state().balance(state().thisAddress()) >= state().txMember("msg.value");
addRule(smtutil::Expression::implies(
initialConstraints(_contract) && zeroes && initialBalanceConstraint,
initialConstraints(_contract) && zeroes && newAddress && initialBalanceConstraint,
predicate(entry)
), entry.functor().name);
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, "");
m_errorDest = m_constructorSummaries.at(&_contract);
// 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);
}
if (encodeExternalCallsAsTrusted())
state().writeStateVars(_contract, state().thisAddress());
connectBlocks(m_currentBlock, summary(_contract));
setCurrentBlock(*m_constructorSummaries.at(&_contract));
@ -550,10 +568,12 @@ void CHC::endVisit(FunctionCall const& _funCall)
externalFunctionCall(_funCall);
SMTEncoder::endVisit(_funCall);
break;
case FunctionType::Kind::Creation:
visitDeployment(_funCall);
break;
case FunctionType::Kind::DelegateCall:
case FunctionType::Kind::BareCallCode:
case FunctionType::Kind::BareDelegateCall:
case FunctionType::Kind::Creation:
SMTEncoder::endVisit(_funCall);
unknownFunctionCall(_funCall);
break;
@ -717,6 +737,19 @@ void CHC::visitAssert(FunctionCall const& _funCall)
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)
{
solAssert(_funCall.arguments().at(2), "");
@ -726,6 +759,66 @@ void CHC::visitAddMulMod(FunctionCall const& _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)
{
solAssert(m_currentContract, "");
@ -750,6 +843,53 @@ void CHC::internalFunctionCall(FunctionCall const& _funCall)
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)
{
/// 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.
solAssert(m_currentContract, "");
auto [callExpr, callOptions] = functionCallExpression(_funCall);
if (isTrustedExternalCall(callExpr))
{
externalFunctionCallToTrustedCode(_funCall);
return;
}
FunctionType const& funType = dynamic_cast<FunctionType const&>(*callExpr->annotation().type);
auto kind = funType.kind();
solAssert(
kind == FunctionType::Kind::External ||
@ -774,37 +909,42 @@ void CHC::externalFunctionCall(FunctionCall const& _funCall)
""
);
bool usesStaticCall = kind == FunctionType::Kind::BareStaticCall;
solAssert(m_currentContract, "");
auto function = functionCallToDefinition(_funCall, currentScopeContract(), m_currentContract);
if (function)
// Only consider high level external calls in trusted mode.
if (
kind == FunctionType::Kind::External &&
(encodeExternalCallsAsTrusted() || isExternalCallToThis(callExpr))
)
{
usesStaticCall |= function->stateMutability() == StateMutability::Pure ||
function->stateMutability() == StateMutability::View;
for (auto var: function->returnParameters())
m_context.variable(*var)->increaseIndex();
externalFunctionCallToTrustedCode(_funCall);
return;
}
// 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())
return;
if (callOptions)
{
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)));
}
if (Expression const* value = valueOption(callOptions))
decreaseBalanceFromOptionsValue(*value);
auto preCallState = vector<smtutil::Expression>{state().state()} + currentStateVariables();
if (!usesStaticCall)
if (!usesStaticCall(_funCall))
{
state().newState();
for (auto const* var: m_stateVariables)
@ -843,8 +983,14 @@ void CHC::externalFunctionCall(FunctionCall const& _funCall)
void CHC::externalFunctionCallToTrustedCode(FunctionCall const& _funCall)
{
if (publicGetter(_funCall.expression()))
visitPublicGetter(_funCall);
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();
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.
auto originalTx = state().tx();
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 transaction value as 0
m_context.addAssertion(state().txMember("msg.value") == 0);
// set the origin to be the current transaction origin
m_context.addAssertion(state().txMember("tx.origin") == txOrigin);
Expression const* value = valueOption(callOptions);
newTxConstraints(value);
auto calledAddress = contractAddressValue(_funCall);
if (value)
{
decreaseBalanceFromOptionsValue(*value);
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);
@ -878,6 +1035,17 @@ void CHC::externalFunctionCallToTrustedCode(FunctionCall const& _funCall)
(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&)
@ -1088,6 +1256,14 @@ set<unsigned> CHC::transactionVerificationTargetsIds(ASTNode const* _txRoot)
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)
{
static map<string, CHCNatspecOption> options{
@ -1128,6 +1304,11 @@ SortPointer CHC::sort(FunctionDefinition const& _function)
return functionBodySort(_function, m_currentContract, state());
}
bool CHC::encodeExternalCallsAsTrusted()
{
return m_settings.externalCalls.isTrusted();
}
SortPointer CHC::sort(ASTNode 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()));
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();
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);
}
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)
{
solAssert(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)
{
solAssert(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)
{
solAssert(m_currentContract, "");
@ -1516,18 +1753,19 @@ smtutil::Expression CHC::predicate(FunctionCall const& _funCall)
auto const& hierarchy = m_currentContract->annotation().linearizedBaseContracts;
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 += symbolicArguments(_funCall, m_currentContract);
if (!m_currentContract->isLibrary() && !usesStaticCall)
args += currentStateVariables(*contract);
args += symbolicArguments(_funCall, contract);
if (!usesStaticCall(_funCall))
{
state().newState();
for (auto const& var: m_stateVariables)
for (auto const& var: stateVariablesIncludingInheritedAndPrivate(*contract))
m_context.variable(*var)->increaseIndex();
}
args += vector<smtutil::Expression>{state().state()};
args += currentStateVariables(*m_currentContract);
args += currentStateVariables(*contract);
for (auto var: function->parameters() + function->returnParameters())
{
@ -1538,14 +1776,14 @@ smtutil::Expression CHC::predicate(FunctionCall const& _funCall)
args.push_back(currentValue(*var));
}
Predicate const& summary = *m_summaries.at(m_currentContract).at(function);
auto from = smt::function(summary, m_currentContract, m_context);
Predicate const& summary = *m_summaries.at(contract).at(function);
auto from = smt::function(summary, contract, m_context);
Predicate const& callPredicate = *createSummaryBlock(
*function,
*m_currentContract,
*contract,
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);
return callPredicate(args);
@ -1910,60 +2148,63 @@ optional<string> CHC::generateCounterexample(CHCSolverInterface::CexGraph const&
Predicate const* summaryPredicate = Predicate::predicate(summaryNode.name);
auto const& summaryArgs = summaryNode.arguments;
auto stateVars = summaryPredicate->stateVariables();
solAssert(stateVars.has_value(), "");
auto stateValues = summaryPredicate->summaryStateValues(summaryArgs);
solAssert(stateValues.size() == stateVars->size(), "");
if (first)
if (!summaryPredicate->programVariable())
{
first = false;
/// Generate counterexample message local to the failed target.
localState = formatVariableModel(*stateVars, stateValues, ", ") + "\n";
auto stateVars = summaryPredicate->stateVariables();
solAssert(stateVars.has_value(), "");
auto stateValues = summaryPredicate->summaryStateValues(summaryArgs);
solAssert(stateValues.size() == stateVars->size(), "");
if (auto calledFun = summaryPredicate->programFunction())
if (first)
{
auto inValues = summaryPredicate->summaryPostInputValues(summaryArgs);
auto const& inParams = calledFun->parameters();
if (auto inStr = formatVariableModel(inParams, inValues, "\n"); !inStr.empty())
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";
first = false;
/// Generate counterexample message local to the failed target.
localState = formatVariableModel(*stateVars, stateValues, ", ") + "\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())
if (auto calledFun = summaryPredicate->programFunction())
{
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";
auto inValues = summaryPredicate->summaryPostInputValues(summaryArgs);
auto const& inParams = calledFun->parameters();
if (auto inStr = formatVariableModel(inParams, inValues, "\n"); !inStr.empty())
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;
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
{
auto modelMsg = formatVariableModel(*stateVars, stateValues, ", ");
/// We report the state after every tx in the trace except for the last, which is reported
/// first in the code above.
if (!modelMsg.empty())
path.emplace_back("State: " + modelMsg);
else
{
auto modelMsg = formatVariableModel(*stateVars, stateValues, ", ");
/// We report the state after every tx in the trace except for the last, which is reported
/// first in the code above.
if (!modelMsg.empty())
path.emplace_back("State: " + modelMsg);
}
}
string txCex = summaryPredicate->formatSummaryCall(summaryArgs, m_charStreamProvider);
@ -1992,6 +2233,12 @@ optional<string> CHC::generateCounterexample(CHCSolverInterface::CexGraph const&
if (calls.size() > callTraceSize + 1)
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())
calls.front() += " -- reentrant call";
};
@ -2047,7 +2294,8 @@ map<unsigned, vector<unsigned>> CHC::summaryCalls(CHCSolverInterface::CexGraph c
nodePred->isInternalCall() ||
nodePred->isExternalCallTrusted() ||
nodePred->isExternalCallUntrusted() ||
rootPred->isExternalCallUntrusted()
rootPred->isExternalCallUntrusted() ||
rootPred->programVariable()
))
{
calls[root].push_back(node);
@ -2105,3 +2353,31 @@ SymbolicIntVariable& CHC::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 visitAssert(FunctionCall const& _funCall);
void visitPublicGetter(FunctionCall const& _funCall) override;
void visitAddMulMod(FunctionCall const& _funCall) override;
void visitDeployment(FunctionCall const& _funCall);
void internalFunctionCall(FunctionCall const& _funCall);
void externalFunctionCall(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 makeArrayPopVerificationTarget(FunctionCall const& _arrayPop) override;
void makeOutOfBoundsVerificationTarget(IndexAccess const& _access) override;
@ -135,6 +139,7 @@ private:
void clearIndices(ContractDefinition const* _contract, FunctionDefinition const* _function = nullptr) override;
void setCurrentBlock(Predicate const& _block);
std::set<unsigned> transactionVerificationTargetsIds(ASTNode const* _txRoot);
bool usesStaticCall(FunctionCall const& _funCall);
//@}
/// SMT Natspec and abstraction helpers.
@ -148,6 +153,10 @@ private:
/// @returns true if _function is Natspec annotated to be abstracted by
/// nondeterministic values.
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.
@ -310,6 +319,20 @@ private:
unsigned newErrorId();
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.

View File

@ -130,3 +130,12 @@ std::optional<ModelCheckerContracts> ModelCheckerContracts::fromString(string co
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;
};
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
{
ModelCheckerContracts contracts = ModelCheckerContracts::Default();
@ -151,6 +166,7 @@ struct ModelCheckerSettings
/// might prefer the precise encoding.
bool divModNoSlacks = false;
ModelCheckerEngine engine = ModelCheckerEngine::None();
ModelCheckerExtCalls externalCalls = {};
ModelCheckerInvariants invariants = ModelCheckerInvariants::Default();
bool showUnproved = false;
smtutil::SMTSolverChoice solvers = smtutil::SMTSolverChoice::Z3();
@ -164,6 +180,7 @@ struct ModelCheckerSettings
contracts == _other.contracts &&
divModNoSlacks == _other.divModNoSlacks &&
engine == _other.engine &&
externalCalls.mode == _other.externalCalls.mode &&
invariants == _other.invariants &&
showUnproved == _other.showUnproved &&
solvers == _other.solvers &&

View File

@ -144,6 +144,11 @@ FunctionCall const* Predicate::programFunctionCall() const
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
{
if (m_contractContext)
@ -214,6 +219,9 @@ string Predicate::formatSummaryCall(
{
solAssert(isSummary(), "");
if (programVariable())
return {};
if (auto funCall = programFunctionCall())
{
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());
stateLast = stateFirst + static_cast<int>(stateVars->size());
}
else if (programVariable())
return {};
else
solAssert(false, "");

View File

@ -115,6 +115,10 @@ public:
/// or nullptr otherwise.
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.
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));
}
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());
if (auto const* constructor = contract.constructor())
return _pred(currentFunctionVariablesForCall(*constructor, &contract, _context));
return _pred(currentFunctionVariablesForCall(*constructor, &contract, _context, _internal));
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();
stateExprs += vector<smtutil::Expression>{state.state()};
stateExprs += currentStateVariables(contract, _context);
@ -166,11 +166,12 @@ vector<smtutil::Expression> currentFunctionVariablesForDefinition(
vector<smtutil::Expression> currentFunctionVariablesForCall(
FunctionDefinition const& _function,
ContractDefinition const* _contract,
EncodingContext& _context
EncodingContext& _context,
bool _internal
)
{
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 += 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 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(
Predicate const& _pred,
@ -77,7 +84,8 @@ std::vector<smtutil::Expression> currentFunctionVariablesForDefinition(
std::vector<smtutil::Expression> currentFunctionVariablesForCall(
FunctionDefinition const& _function,
ContractDefinition const* _contract,
EncodingContext& _context
EncodingContext& _context,
bool _internal = true
);
std::vector<smtutil::Expression> currentBlockVariables(

View File

@ -637,7 +637,7 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall)
visitGasLeft(_funCall);
break;
case FunctionType::Kind::External:
if (isPublicGetter(_funCall.expression()))
if (publicGetter(_funCall.expression()))
visitPublicGetter(_funCall);
break;
case FunctionType::Kind::ABIDecode:
@ -696,10 +696,18 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall)
case FunctionType::Kind::ObjectCreation:
visitObjectCreation(_funCall);
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::BareCallCode:
case FunctionType::Kind::BareDelegateCall:
case FunctionType::Kind::Creation:
default:
m_errorReporter.warning(
4588_error,
@ -978,9 +986,8 @@ vector<string> structGetterReturnedMembers(StructType const& _structType)
void SMTEncoder::visitPublicGetter(FunctionCall const& _funCall)
{
MemberAccess const& access = dynamic_cast<MemberAccess const&>(_funCall.expression());
auto var = dynamic_cast<VariableDeclaration const*>(access.annotation().referencedDeclaration);
solAssert(var, "");
auto var = publicGetter(_funCall.expression());
solAssert(var && var->isStateVariable(), "");
solAssert(m_context.knownExpression(_funCall), "");
auto paramExpectedTypes = replaceUserTypes(FunctionType(*var).parameterTypes());
auto actualArguments = _funCall.arguments();
@ -1054,7 +1061,7 @@ bool SMTEncoder::shouldAnalyze(ContractDefinition const& _contract) const
return false;
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)
@ -2789,16 +2796,24 @@ MemberAccess const* SMTEncoder::isEmptyPush(Expression const& _expr) const
return nullptr;
}
bool SMTEncoder::isPublicGetter(Expression const& _expr) {
if (!isTrustedExternalCall(&_expr))
return false;
auto varDecl = dynamic_cast<VariableDeclaration const*>(
dynamic_cast<MemberAccess const&>(_expr).annotation().referencedDeclaration
);
return varDecl != nullptr;
smtutil::Expression SMTEncoder::contractAddressValue(FunctionCall const& _f)
{
FunctionType const& funType = dynamic_cast<FunctionType const&>(*_f.expression().annotation().type);
if (funType.kind() == FunctionType::Kind::Internal)
return state().thisAddress();
auto [funExpr, funOptions] = functionCallExpression(_f);
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);
if (!memberAccess)
return false;
@ -3060,7 +3075,7 @@ RationalNumberType const* SMTEncoder::isConstant(Expression const& _expr)
return nullptr;
}
set<FunctionCall const*> SMTEncoder::collectABICalls(ASTNode const* _node)
set<FunctionCall const*, ASTCompareByID<FunctionCall>> SMTEncoder::collectABICalls(ASTNode const* _node)
{
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;

View File

@ -123,7 +123,7 @@ public:
/// RationalNumberType or can be const evaluated, and nullptr otherwise.
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,
/// including itself.
@ -219,7 +219,7 @@ protected:
void visitTypeConversion(FunctionCall const& _funCall);
void visitStructConstructorCall(FunctionCall const& _funCall);
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
/// and it is not abstract.
@ -227,7 +227,12 @@ protected:
/// @returns true if @param _source is set for analysis in the settings.
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
/// visit depth.
@ -392,9 +397,9 @@ protected:
/// otherwise nullptr.
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.
static bool isTrustedExternalCall(Expression const* _expr);
static bool isExternalCallToThis(Expression const* _expr);
/// Creates symbolic expressions for the returned values
/// and set them as the components of the symbolic tuple.

View File

@ -22,8 +22,13 @@
#include <libsolidity/formal/EncodingContext.h>
#include <libsolidity/formal/SMTEncoder.h>
#include <libsmtutil/Sorts.h>
#include <range/v3/view.hpp>
using namespace std;
using namespace solidity;
using namespace solidity::util;
using namespace solidity::smtutil;
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)
{
vector<smtutil::Expression> args;
for (auto const& m: m_members)
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));
smtutil::Expression newTuple = smt::assignMember(m_tuple->currentValue(), {{_member, _value}});
m_context.addAssertion(m_tuple->increaseIndex() == newTuple);
return m_tuple->currentValue();
}
@ -75,16 +72,19 @@ void SymbolicState::reset()
{
m_error.resetIndex();
m_thisAddress.resetIndex();
m_state.reset();
m_tx.reset();
m_crypto.reset();
if (m_abi)
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
{
return m_state.member("balances");
return m_state->member("balances");
}
smtutil::Expression SymbolicState::balance() const
@ -107,24 +107,94 @@ void SymbolicState::newBalances()
auto tupleSort = dynamic_pointer_cast<TupleSort>(stateSort());
auto balanceSort = tupleSort->components.at(tupleSort->memberToIndex.at("balances"));
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)
{
unsigned indexBefore = m_state.index();
unsigned indexBefore = m_state->index();
addBalance(_from, 0 - _value);
addBalance(_to, std::move(_value));
unsigned indexAfter = m_state.index();
unsigned indexAfter = m_state->index();
solAssert(indexAfter > indexBefore, "");
m_state.newVar();
m_state->newVar();
/// Do not apply the transfer operation if _from == _to.
auto newState = smtutil::Expression::ite(
std::move(_from) == std::move(_to),
m_state.value(indexBefore),
m_state.value(indexAfter)
m_state->value(indexBefore),
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)
@ -134,7 +204,7 @@ void SymbolicState::addBalance(smtutil::Expression _address, smtutil::Expression
_address,
balance(_address) + std::move(_value)
);
m_state.assignMember("balances", newBalances);
m_state->assignMember("balances", newBalances);
}
smtutil::Expression SymbolicState::txMember(string const& _member) const
@ -194,17 +264,99 @@ smtutil::Expression SymbolicState::txFunctionConstraints(FunctionDefinition cons
return conj;
}
void SymbolicState::prepareForSourceUnit(SourceUnit const& _source)
void SymbolicState::prepareForSourceUnit(SourceUnit const& _source, bool _storage)
{
set<FunctionCall const*> abiCalls = SMTEncoder::collectABICalls(&_source);
for (auto const& source: _source.referencedSourceUnits(true))
auto allSources = _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);
for (auto node: source->nodes())
if (auto contract = dynamic_cast<ContractDefinition const*>(node.get()))
contracts.insert(contract);
}
buildState(contracts, _storage);
buildABIFunctions(abiCalls);
}
/// 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;

View File

@ -62,7 +62,8 @@ private:
* - this (the address of the currently executing contract)
* - state, represented as a tuple of:
* - 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:
* - blockhash
* - block basefee
@ -99,29 +100,41 @@ public:
/// @returns the symbolic value of the currently executing contract's address.
smtutil::Expression thisAddress() const { return m_thisAddress.currentValue(); }
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(); }
//@}
/// Blockchain state.
//@{
smtutil::Expression state() const { return m_state.value(); }
smtutil::Expression state(unsigned _idx) const { return m_state.value(_idx); }
smtutil::SortPointer const& stateSort() const { return m_state.sort(); }
void newState() { m_state.newVar(); }
smtutil::Expression state() const { solAssert(m_state, ""); return m_state->value(); }
smtutil::Expression state(unsigned _idx) const { solAssert(m_state, ""); return m_state->value(_idx); }
smtutil::SortPointer const& stateSort() const { solAssert(m_state, ""); return m_state->sort(); }
void newState() { solAssert(m_state, ""); m_state->newVar(); }
void newBalances();
/// Balance.
/// @returns the symbolic balances.
smtutil::Expression balances() const;
/// @returns the symbolic balance of address `this`.
smtutil::Expression balance() const;
/// @returns the symbolic balance of an address.
smtutil::Expression balance(smtutil::Expression _address) const;
/// Transfer _value from _from to _to.
void transfer(smtutil::Expression _from, smtutil::Expression _to, smtutil::Expression _value);
/// Adds _value to _account's balance.
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.
@ -149,11 +162,15 @@ public:
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.
//@{
/// 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);
using SymbolicABIFunction = std::tuple<
std::string,
@ -169,8 +186,15 @@ public:
//@}
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.
void buildABIFunctions(std::set<FunctionCall const*> const& _abiFunctions);
void buildABIFunctions(std::set<FunctionCall const*, ASTCompareByID<FunctionCall>> const& _abiFunctions);
EncodingContext& m_context;
@ -186,11 +210,14 @@ private:
m_context
};
BlockchainVariable m_state{
"state",
{{"balances", std::make_shared<smtutil::ArraySort>(smtutil::SortProvider::uintSort, smtutil::SortProvider::uintSort)}},
m_context
};
/// m_state is a tuple of
/// - balances: array of address to balance of address.
/// - isActive: array of address to Boolean, where element is true iff address is used.
/// - 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{
"tx",

View File

@ -618,4 +618,26 @@ optional<smtutil::Expression> symbolicTypeConversion(frontend::Type const* _from
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);
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)
{
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");
}
@ -1016,6 +1016,16 @@ std::variant<StandardCompiler::InputsAndSettings, Json::Value> StandardCompiler:
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"))
{
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_strModelCheckerDivModNoSlacks = "model-checker-div-mod-no-slacks";
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_strModelCheckerShowUnproved = "model-checker-show-unproved";
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"),
"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(),
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;
}
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))
{
string invsStr = m_args[g_strModelCheckerInvariants].as<string>();
@ -1327,6 +1343,7 @@ void CommandLineParser::processArgs()
m_args.count(g_strModelCheckerContracts) ||
m_args.count(g_strModelCheckerDivModNoSlacks) ||
m_args.count(g_strModelCheckerEngine) ||
m_args.count(g_strModelCheckerExtCalls) ||
m_args.count(g_strModelCheckerInvariants) ||
m_args.count(g_strModelCheckerShowUnproved) ||
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: 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
Transaction trace:
A.constructor()
B.constructor()
B.f(0)
--> 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\".",
"severity": "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":

View File

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

View File

@ -10,7 +10,7 @@ Counterexample:
y = 0
Transaction trace:
A.constructor()
B.constructor()
B.g(0)
--> Source:5:7:
|
@ -24,7 +24,7 @@ Counterexample:
y = 0
Transaction trace:
A.constructor()
B.constructor()
B.g(0)",
"severity": "warning",
"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":
{
"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-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 ((|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 ((|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 ((|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 |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)
@ -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)
(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))
(=> (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
@ -124,34 +151,7 @@
(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))
(=> error_target_3_0 false)))
(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| ))
"
(check-sat)"
}
},
"errors":

View File

@ -33,6 +33,12 @@ SMTCheckerTest::SMTCheckerTest(string const& _filename): SyntaxTest(_filename, E
if (!contract.empty())
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");
if (showUnproved == "no")
m_modelCheckerSettings.showUnproved = false;
@ -51,8 +57,13 @@ SMTCheckerTest::SMTCheckerTest(string const& _filename): SyntaxTest(_filename, E
m_modelCheckerSettings.solvers &= ModelChecker::availableSolvers();
/// Underflow and Overflow are not enabled by default for Solidity >=0.8.7,
/// so we explicitly enable all targets for the tests.
m_modelCheckerSettings.targets = ModelCheckerTargets::All();
/// so we explicitly enable all targets for the tests,
/// 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"));
if (engine)

View File

@ -22,4 +22,4 @@ contract C {
// SMTEngine: all
// 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: (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()
// 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
// 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()

View File

@ -20,5 +20,6 @@ contract C {
}
// ====
// SMTEngine: all
// SMTIgnoreCex: yes
// ----
// 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: (1179-1241): 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 4588: (854-886): 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 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.

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 2072: (106-112): 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