mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
[SMTChecker] Synthesize untrusted functions called externally
This commit is contained in:
+43
-13
@@ -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;
|
||||
|
||||
@@ -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(), "");
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user