[SMTChecker] Synthesize untrusted functions called externally

This commit is contained in:
Leonardo Alt
2021-01-15 11:56:26 +01:00
parent eaf7d7daa7
commit 007d39871b
85 changed files with 180 additions and 115 deletions
+43 -13
View File
@@ -676,6 +676,7 @@ void CHC::externalFunctionCall(FunctionCall const& _funCall)
bool usesStaticCall = kind == FunctionType::Kind::BareStaticCall ||
function->stateMutability() == StateMutability::Pure ||
function->stateMutability() == StateMutability::View;
if (!usesStaticCall)
{
state().newState();
@@ -683,13 +684,23 @@ void CHC::externalFunctionCall(FunctionCall const& _funCall)
m_context.variable(*var)->increaseIndex();
}
auto postCallState = vector<smtutil::Expression>{state().state()} + currentStateVariables();
auto error = errorFlag().increaseIndex();
Predicate const& callPredicate = *createSymbolicBlock(
nondetInterfaceSort(*m_currentContract, state()),
"nondet_call_" + uniquePrefix(),
PredicateType::ExternalCallUntrusted,
&_funCall
);
auto postCallState = vector<smtutil::Expression>{state().state()} + currentStateVariables();
vector<smtutil::Expression> stateExprs{error, state().thisAddress(), state().abi(), state().crypto()};
auto nondet = (*m_nondetInterfaces.at(m_currentContract))(stateExprs + preCallState + postCallState);
// TODO this could instead add the summary of the called function, where that summary
// basically has the nondet interface of this summary as a constraint.
m_context.addAssertion(nondet);
auto nondetCall = callPredicate(stateExprs + preCallState + postCallState);
addRule(smtutil::Expression::implies(nondet, nondetCall), nondetCall.name);
m_context.addAssertion(nondetCall);
solAssert(m_errorDest, "");
connectBlocks(m_currentBlock, predicate(*m_errorDest), errorFlag().currentValue() > 0);
// To capture the possibility of a reentrant call, we record in the call graph that the current function
@@ -1179,7 +1190,8 @@ smtutil::Expression CHC::predicate(Predicate const& _block)
return constructor(_block, m_context);
case PredicateType::FunctionSummary:
case PredicateType::InternalCall:
case PredicateType::ExternalCall:
case PredicateType::ExternalCallTrusted:
case PredicateType::ExternalCallUntrusted:
return smt::function(_block, m_currentContract, m_context);
case PredicateType::FunctionBlock:
solAssert(m_currentFunction, "");
@@ -1256,7 +1268,7 @@ smtutil::Expression CHC::predicate(FunctionCall const& _funCall)
Predicate const& callPredicate = *createSummaryBlock(
*function,
*calledContract,
kind == FunctionType::Kind::Internal ? PredicateType::InternalCall : PredicateType::ExternalCall
kind == FunctionType::Kind::Internal ? PredicateType::InternalCall : PredicateType::ExternalCallTrusted
);
auto to = smt::function(callPredicate, calledContract, m_context);
addRule(smtutil::Expression::implies(from, to), to.name);
@@ -1561,19 +1573,30 @@ optional<string> CHC::generateCounterexample(CHCSolverInterface::CexGraph const&
string txCex = summaryPredicate->formatSummaryCall(summaryArgs);
list<string> calls;
auto dfs = [&](unsigned node, unsigned depth, auto&& _dfs) -> void {
auto dfs = [&](unsigned parent, unsigned node, unsigned depth, auto&& _dfs) -> void {
auto pred = nodePred(node);
auto parentPred = nodePred(parent);
solAssert(pred && pred->isSummary(), "");
solAssert(parentPred && parentPred->isSummary(), "");
auto callTraceSize = calls.size();
if (!pred->isConstructorSummary())
for (unsigned v: callGraph[node])
_dfs(v, depth + 1, _dfs);
calls.push_front(string(depth * 2, ' ') + pred->formatSummaryCall(nodeArgs(node)));
_dfs(node, v, depth + 1, _dfs);
calls.push_front(string(depth * 4, ' ') + pred->formatSummaryCall(nodeArgs(node)));
if (pred->isInternalCall())
calls.front() += " -- internal call";
else if (pred->isExternalCall())
calls.front() += " -- external call";
else if (pred->isExternalCallTrusted())
calls.front() += " -- trusted external call";
else if (pred->isExternalCallUntrusted())
{
calls.front() += " -- untrusted external call";
if (calls.size() > callTraceSize + 1)
calls.front() += ", synthesized as:";
}
else if (pred->isFunctionSummary() && parentPred->isExternalCallUntrusted())
calls.front() += " -- reentrant call";
};
dfs(summaryId, 0, dfs);
dfs(summaryId, summaryId, 0, dfs);
path.emplace_back(boost::algorithm::join(calls, "\n"));
}
@@ -1596,7 +1619,14 @@ map<unsigned, vector<unsigned>> CHC::summaryCalls(CHCSolverInterface::CexGraph c
q.pop();
Predicate const* nodePred = Predicate::predicate(_graph.nodes.at(node).name);
if (nodePred->isSummary() && (_root == root || nodePred->isInternalCall() || nodePred->isExternalCall()))
Predicate const* rootPred = Predicate::predicate(_graph.nodes.at(root).name);
if (nodePred->isSummary() && (
_root == root ||
nodePred->isInternalCall() ||
nodePred->isExternalCallTrusted() ||
nodePred->isExternalCallUntrusted() ||
rootPred->isExternalCallUntrusted()
))
{
calls[root].push_back(node);
root = node;
+21 -4
View File
@@ -121,6 +121,11 @@ FunctionDefinition const* Predicate::programFunction() const
return nullptr;
}
FunctionCall const* Predicate::programFunctionCall() const
{
return dynamic_cast<FunctionCall const*>(m_node);
}
optional<vector<VariableDeclaration const*>> Predicate::stateVariables() const
{
if (auto const* fun = programFunction())
@@ -141,7 +146,11 @@ optional<vector<VariableDeclaration const*>> Predicate::stateVariables() const
bool Predicate::isSummary() const
{
return m_type == PredicateType::ConstructorSummary || m_type == PredicateType::FunctionSummary || m_type == PredicateType::InternalCall || m_type == PredicateType::ExternalCall;
return isFunctionSummary() ||
isInternalCall() ||
isExternalCallTrusted() ||
isExternalCallUntrusted() ||
isConstructorSummary();
}
bool Predicate::isFunctionSummary() const
@@ -154,9 +163,14 @@ bool Predicate::isInternalCall() const
return m_type == PredicateType::InternalCall;
}
bool Predicate::isExternalCall() const
bool Predicate::isExternalCallTrusted() const
{
return m_type == PredicateType::ExternalCall;
return m_type == PredicateType::ExternalCallTrusted;
}
bool Predicate::isExternalCallUntrusted() const
{
return m_type == PredicateType::ExternalCallUntrusted;
}
bool Predicate::isConstructorSummary() const
@@ -171,10 +185,13 @@ bool Predicate::isInterface() const
string Predicate::formatSummaryCall(vector<smtutil::Expression> const& _args) const
{
solAssert(isSummary(), "");
if (auto contract = programContract())
return contract->name() + ".constructor()";
solAssert(isSummary(), "");
if (auto funCall = programFunctionCall())
return funCall->location().text();
auto stateVars = stateVariables();
solAssert(stateVars.has_value(), "");
+11 -3
View File
@@ -38,7 +38,8 @@ enum class PredicateType
FunctionSummary,
FunctionBlock,
InternalCall,
ExternalCall,
ExternalCallTrusted,
ExternalCallUntrusted,
Error,
Custom
};
@@ -94,6 +95,10 @@ public:
/// or nullptr otherwise.
FunctionDefinition const* programFunction() const;
/// @returns the FunctionCall that this predicate represents
/// or nullptr otherwise.
FunctionCall const* programFunctionCall() const;
/// @returns the program state variables in the scope of this predicate.
std::optional<std::vector<VariableDeclaration const*>> stateVariables() const;
@@ -106,8 +111,11 @@ public:
/// @returns true if this predicate represents an internal function call.
bool isInternalCall() const;
/// @returns true if this predicate represents an external function call.
bool isExternalCall() const;
/// @returns true if this predicate represents a trusted external function call.
bool isExternalCallTrusted() const;
/// @returns true if this predicate represents an untrusted external function call.
bool isExternalCallUntrusted() const;
/// @returns true if this predicate represents a constructor summary.
bool isConstructorSummary() const;