Merge remote-tracking branch 'origin/develop' into HEAD

This commit is contained in:
chriseth
2020-10-28 18:19:31 +01:00
40 changed files with 642 additions and 146 deletions
+14 -7
View File
@@ -1306,7 +1306,7 @@ optional<string> CHC::generateCounterexample(CHCSolverInterface::CexGraph const&
{
optional<unsigned> rootId;
for (auto const& [id, node]: _graph.nodes)
if (node.first == _root)
if (node.name == _root)
{
rootId = id;
break;
@@ -1330,18 +1330,18 @@ optional<string> CHC::generateCounterexample(CHCSolverInterface::CexGraph const&
if (edges.size() == 2)
{
interfaceId = edges.at(1);
if (!Predicate::predicate(_graph.nodes.at(summaryId).first)->isSummary())
if (!Predicate::predicate(_graph.nodes.at(summaryId).name)->isSummary())
swap(summaryId, *interfaceId);
auto interfacePredicate = Predicate::predicate(_graph.nodes.at(*interfaceId).first);
auto interfacePredicate = Predicate::predicate(_graph.nodes.at(*interfaceId).name);
solAssert(interfacePredicate && interfacePredicate->isInterface(), "");
}
/// The children are unordered, so we need to check which is the summary and
/// which is the interface.
Predicate const* summaryPredicate = Predicate::predicate(_graph.nodes.at(summaryId).first);
Predicate const* summaryPredicate = Predicate::predicate(_graph.nodes.at(summaryId).name);
solAssert(summaryPredicate && summaryPredicate->isSummary(), "");
/// At this point property 2 from the function description is verified for this node.
auto summaryArgs = _graph.nodes.at(summaryId).second;
vector<smtutil::Expression> summaryArgs = _graph.nodes.at(summaryId).arguments;
FunctionDefinition const* calledFun = summaryPredicate->programFunction();
ContractDefinition const* calledContract = summaryPredicate->programContract();
@@ -1387,7 +1387,7 @@ optional<string> CHC::generateCounterexample(CHCSolverInterface::CexGraph const&
/// or stop.
if (interfaceId)
{
Predicate const* interfacePredicate = Predicate::predicate(_graph.nodes.at(*interfaceId).first);
Predicate const* interfacePredicate = Predicate::predicate(_graph.nodes.at(*interfaceId).name);
solAssert(interfacePredicate && interfacePredicate->isInterface(), "");
node = *interfaceId;
}
@@ -1403,7 +1403,14 @@ string CHC::cex2dot(CHCSolverInterface::CexGraph const& _cex)
string dot = "digraph {\n";
auto pred = [&](CHCSolverInterface::CexNode const& _node) {
return "\"" + _node.first + "(" + boost::algorithm::join(_node.second, ", ") + ")\"";
vector<string> args = applyMap(
_node.arguments,
[&](auto const& arg) {
solAssert(arg.arguments.empty(), "");
return arg.name;
}
);
return "\"" + _node.name + "(" + boost::algorithm::join(args, ", ") + ")\"";
};
for (auto const& [u, vs]: _cex.edges)
+3 -3
View File
@@ -203,7 +203,7 @@ private:
/// @returns a set of pairs _var = _value separated by _separator.
template <typename T>
std::string formatVariableModel(std::vector<T> const& _variables, std::vector<std::string> const& _values, std::string const& _separator) const
std::string formatVariableModel(std::vector<T> const& _variables, std::vector<std::optional<std::string>> const& _values, std::string const& _separator) const
{
solAssert(_variables.size() == _values.size(), "");
@@ -211,8 +211,8 @@ private:
for (unsigned i = 0; i < _values.size(); ++i)
{
auto var = _variables.at(i);
if (var && var->type()->isValueType())
assignments.emplace_back(var->name() + " = " + _values.at(i));
if (var && _values.at(i))
assignments.emplace_back(var->name() + " = " + *_values.at(i));
}
return boost::algorithm::join(assignments, _separator);
+128 -20
View File
@@ -149,7 +149,7 @@ bool Predicate::isInterface() const
return functor().name.rfind("interface", 0) == 0;
}
string Predicate::formatSummaryCall(vector<string> const& _args) const
string Predicate::formatSummaryCall(vector<smtutil::Expression> const& _args) const
{
if (programContract())
return "constructor()";
@@ -163,18 +163,19 @@ string Predicate::formatSummaryCall(vector<string> const& _args) const
/// The signature of a function summary predicate is: summary(error, this, cryptoFunctions, txData, preBlockChainState, preStateVars, preInputVars, postBlockchainState, postStateVars, postInputVars, outputVars).
/// Here we are interested in preInputVars.
vector<string>::const_iterator first = _args.begin() + 5 + static_cast<int>(stateVars->size());
vector<string>::const_iterator last = first + static_cast<int>(fun->parameters().size());
auto first = _args.begin() + 5 + static_cast<int>(stateVars->size());
auto last = first + static_cast<int>(fun->parameters().size());
solAssert(first >= _args.begin() && first <= _args.end(), "");
solAssert(last >= _args.begin() && last <= _args.end(), "");
vector<string> functionArgsCex(first, last);
auto inTypes = FunctionType(*fun).parameterTypes();
vector<optional<string>> functionArgsCex = formatExpressions(vector<smtutil::Expression>(first, last), inTypes);
vector<string> functionArgs;
auto const& params = fun->parameters();
solAssert(params.size() == functionArgsCex.size(), "");
for (unsigned i = 0; i < params.size(); ++i)
if (params[i]->type()->isValueType())
functionArgs.emplace_back(functionArgsCex[i]);
if (params.at(i) && functionArgsCex.at(i))
functionArgs.emplace_back(*functionArgsCex.at(i));
else
functionArgs.emplace_back(params[i]->name());
@@ -186,7 +187,7 @@ string Predicate::formatSummaryCall(vector<string> const& _args) const
}
vector<string> Predicate::summaryStateValues(vector<string> const& _args) const
vector<optional<string>> Predicate::summaryStateValues(vector<smtutil::Expression> const& _args) const
{
/// The signature of a function summary predicate is: summary(error, this, cryptoFunctions, txData, preBlockchainState, preStateVars, preInputVars, postBlockchainState, postStateVars, postInputVars, outputVars).
/// The signature of an implicit constructor summary predicate is: summary(error, this, cryptoFunctions, txData, preBlockchainState, postBlockchainState, postStateVars).
@@ -194,8 +195,8 @@ vector<string> Predicate::summaryStateValues(vector<string> const& _args) const
auto stateVars = stateVariables();
solAssert(stateVars.has_value(), "");
vector<string>::const_iterator stateFirst;
vector<string>::const_iterator stateLast;
vector<smtutil::Expression>::const_iterator stateFirst;
vector<smtutil::Expression>::const_iterator stateLast;
if (auto const* function = programFunction())
{
stateFirst = _args.begin() + 5 + static_cast<int>(stateVars->size()) + static_cast<int>(function->parameters().size()) + 1;
@@ -212,12 +213,13 @@ vector<string> Predicate::summaryStateValues(vector<string> const& _args) const
solAssert(stateFirst >= _args.begin() && stateFirst <= _args.end(), "");
solAssert(stateLast >= _args.begin() && stateLast <= _args.end(), "");
vector<string> stateArgs(stateFirst, stateLast);
vector<smtutil::Expression> stateArgs(stateFirst, stateLast);
solAssert(stateArgs.size() == stateVars->size(), "");
return stateArgs;
auto stateTypes = applyMap(*stateVars, [&](auto const& _var) { return _var->type(); });
return formatExpressions(stateArgs, stateTypes);
}
vector<string> Predicate::summaryPostInputValues(vector<string> const& _args) const
vector<optional<string>> Predicate::summaryPostInputValues(vector<smtutil::Expression> const& _args) const
{
/// The signature of a function summary predicate is: summary(error, this, cryptoFunctions, txData, preBlockchainState, preStateVars, preInputVars, postBlockchainState, postStateVars, postInputVars, outputVars).
/// Here we are interested in postInputVars.
@@ -229,18 +231,19 @@ vector<string> Predicate::summaryPostInputValues(vector<string> const& _args) co
auto const& inParams = function->parameters();
vector<string>::const_iterator first = _args.begin() + 5 + static_cast<int>(stateVars->size()) * 2 + static_cast<int>(inParams.size()) + 1;
vector<string>::const_iterator last = first + static_cast<int>(inParams.size());
auto first = _args.begin() + 5 + static_cast<int>(stateVars->size()) * 2 + static_cast<int>(inParams.size()) + 1;
auto last = first + static_cast<int>(inParams.size());
solAssert(first >= _args.begin() && first <= _args.end(), "");
solAssert(last >= _args.begin() && last <= _args.end(), "");
vector<string> inValues(first, last);
vector<smtutil::Expression> inValues(first, last);
solAssert(inValues.size() == inParams.size(), "");
return inValues;
auto inTypes = FunctionType(*function).parameterTypes();
return formatExpressions(inValues, inTypes);
}
vector<string> Predicate::summaryPostOutputValues(vector<string> const& _args) const
vector<optional<string>> Predicate::summaryPostOutputValues(vector<smtutil::Expression> const& _args) const
{
/// The signature of a function summary predicate is: summary(error, this, cryptoFunctions, txData, preBlockchainState, preStateVars, preInputVars, postBlockchainState, postStateVars, postInputVars, outputVars).
/// Here we are interested in outputVars.
@@ -252,11 +255,116 @@ vector<string> Predicate::summaryPostOutputValues(vector<string> const& _args) c
auto const& inParams = function->parameters();
vector<string>::const_iterator first = _args.begin() + 5 + static_cast<int>(stateVars->size()) * 2 + static_cast<int>(inParams.size()) * 2 + 1;
auto first = _args.begin() + 5 + static_cast<int>(stateVars->size()) * 2 + static_cast<int>(inParams.size()) * 2 + 1;
solAssert(first >= _args.begin() && first <= _args.end(), "");
vector<string> outValues(first, _args.end());
vector<smtutil::Expression> outValues(first, _args.end());
solAssert(outValues.size() == function->returnParameters().size(), "");
return outValues;
auto outTypes = FunctionType(*function).returnParameterTypes();
return formatExpressions(outValues, outTypes);
}
vector<optional<string>> Predicate::formatExpressions(vector<smtutil::Expression> const& _exprs, vector<TypePointer> const& _types) const
{
solAssert(_exprs.size() == _types.size(), "");
vector<optional<string>> strExprs;
for (unsigned i = 0; i < _exprs.size(); ++i)
strExprs.push_back(expressionToString(_exprs.at(i), _types.at(i)));
return strExprs;
}
optional<string> Predicate::expressionToString(smtutil::Expression const& _expr, TypePointer _type) const
{
if (smt::isNumber(*_type))
{
solAssert(_expr.sort->kind == Kind::Int, "");
solAssert(_expr.arguments.empty(), "");
// TODO assert that _expr.name is a number.
return _expr.name;
}
if (smt::isBool(*_type))
{
solAssert(_expr.sort->kind == Kind::Bool, "");
solAssert(_expr.arguments.empty(), "");
solAssert(_expr.name == "true" || _expr.name == "false", "");
return _expr.name;
}
if (smt::isFunction(*_type))
{
solAssert(_expr.arguments.empty(), "");
return _expr.name;
}
if (smt::isArray(*_type))
{
auto const& arrayType = dynamic_cast<ArrayType const&>(*_type);
solAssert(_expr.name == "tuple_constructor", "");
auto const& tupleSort = dynamic_cast<TupleSort const&>(*_expr.sort);
solAssert(tupleSort.components.size() == 2, "");
auto length = stoul(_expr.arguments.at(1).name);
// Limit this counterexample size to 1k.
// Some OSs give you "unlimited" memory through swap and other virtual memory,
// so purely relying on bad_alloc being thrown is not a good idea.
// In that case, the array allocation might cause OOM and the program is killed.
if (length >= 1024)
return {};
try
{
vector<string> array(length);
if (!fillArray(_expr.arguments.at(0), array, arrayType))
return {};
return "[" + boost::algorithm::join(array, ", ") + "]";
}
catch (bad_alloc const&)
{
// Solver gave a concrete array but length is too large.
}
}
return {};
}
bool Predicate::fillArray(smtutil::Expression const& _expr, vector<string>& _array, ArrayType const& _type) const
{
// Base case
if (_expr.name == "const_array")
{
auto length = _array.size();
optional<string> elemStr = expressionToString(_expr.arguments.at(1), _type.baseType());
if (!elemStr)
return false;
_array.clear();
_array.resize(length, *elemStr);
return true;
}
// Recursive case.
if (_expr.name == "store")
{
if (!fillArray(_expr.arguments.at(0), _array, _type))
return false;
optional<string> indexStr = expressionToString(_expr.arguments.at(1), TypeProvider::uint256());
if (!indexStr)
return false;
// Sometimes the solver assigns huge lengths that are not related,
// we should catch and ignore those.
unsigned index;
try
{
index = stoul(*indexStr);
}
catch (out_of_range const&)
{
return true;
}
optional<string> elemStr = expressionToString(_expr.arguments.at(2), _type.baseType());
if (!elemStr)
return false;
if (index < _array.size())
_array.at(index) = *elemStr;
return true;
}
solAssert(false, "");
}
+16 -4
View File
@@ -107,21 +107,33 @@ public:
/// @returns a formatted string representing a call to this predicate
/// with _args.
std::string formatSummaryCall(std::vector<std::string> const& _args) const;
std::string formatSummaryCall(std::vector<smtutil::Expression> const& _args) const;
/// @returns the values of the state variables from _args at the point
/// where this summary was reached.
std::vector<std::string> summaryStateValues(std::vector<std::string> const& _args) const;
std::vector<std::optional<std::string>> summaryStateValues(std::vector<smtutil::Expression> const& _args) const;
/// @returns the values of the function input variables from _args at the point
/// where this summary was reached.
std::vector<std::string> summaryPostInputValues(std::vector<std::string> const& _args) const;
std::vector<std::optional<std::string>> summaryPostInputValues(std::vector<smtutil::Expression> const& _args) const;
/// @returns the values of the function output variables from _args at the point
/// where this summary was reached.
std::vector<std::string> summaryPostOutputValues(std::vector<std::string> const& _args) const;
std::vector<std::optional<std::string>> summaryPostOutputValues(std::vector<smtutil::Expression> const& _args) const;
private:
/// @returns the formatted version of the given SMT expressions. Those expressions must be SMT constants.
std::vector<std::optional<std::string>> formatExpressions(std::vector<smtutil::Expression> const& _exprs, std::vector<TypePointer> const& _types) const;
/// @returns a string representation of the SMT expression based on a Solidity type.
std::optional<std::string> expressionToString(smtutil::Expression const& _expr, TypePointer _type) const;
/// Recursively fills _array from _expr.
/// _expr should have the form `store(store(...(const_array(x_0), i_0, e_0), i_m, e_m), i_k, e_k)`.
/// @returns true if the construction worked,
/// and false if at least one element could not be built.
bool fillArray(smtutil::Expression const& _expr, std::vector<std::string>& _array, ArrayType const& _type) const;
/// The actual SMT expression.
smt::SymbolicFunctionVariable m_predicate;
+50 -14
View File
@@ -574,8 +574,8 @@ bool SMTEncoder::visit(Conditional const& _op)
defineExpr(_op, smtutil::Expression::ite(
expr(_op.condition()),
expr(_op.trueExpression()),
expr(_op.falseExpression())
expr(_op.trueExpression(), _op.annotation().type),
expr(_op.falseExpression(), _op.annotation().type)
));
return false;
@@ -1056,10 +1056,9 @@ bool SMTEncoder::visit(MemberAccess const& _memberAccess)
auto const& exprType = _memberAccess.expression().annotation().type;
solAssert(exprType, "");
auto identifier = dynamic_cast<Identifier const*>(&_memberAccess.expression());
if (exprType->category() == Type::Category::Magic)
{
if (identifier)
if (auto const* identifier = dynamic_cast<Identifier const*>(&_memberAccess.expression()))
{
auto const& name = identifier->name();
solAssert(name == "block" || name == "msg" || name == "tx", "");
@@ -1104,13 +1103,23 @@ bool SMTEncoder::visit(MemberAccess const& _memberAccess)
}
else if (exprType->category() == Type::Category::TypeType)
{
if (identifier && dynamic_cast<EnumDefinition const*>(identifier->annotation().referencedDeclaration))
auto const* decl = expressionToDeclaration(_memberAccess.expression());
if (dynamic_cast<EnumDefinition const*>(decl))
{
auto enumType = dynamic_cast<EnumType const*>(accessType);
solAssert(enumType, "");
defineExpr(_memberAccess, enumType->memberValue(_memberAccess.memberName()));
return false;
}
else if (dynamic_cast<ContractDefinition const*>(decl))
{
if (auto const* var = dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration))
{
defineExpr(_memberAccess, currentValue(*var));
return false;
}
}
return false;
}
else if (exprType->category() == Type::Category::Address)
{
@@ -1240,6 +1249,23 @@ void SMTEncoder::arrayAssignment()
void SMTEncoder::indexOrMemberAssignment(Expression const& _expr, smtutil::Expression const& _rightHandSide)
{
if (auto const* memberAccess = dynamic_cast<MemberAccess const*>(&_expr))
{
if (dynamic_cast<ContractDefinition const*>(expressionToDeclaration(memberAccess->expression())))
{
if (auto const* var = dynamic_cast<VariableDeclaration const*>(memberAccess->annotation().referencedDeclaration))
{
if (var->hasReferenceOrMappingType())
resetReferences(*var);
m_context.addAssertion(m_context.newValue(*var) == _rightHandSide);
m_context.expression(_expr)->increaseIndex();
defineExpr(_expr, currentValue(*var));
return;
}
}
}
auto toStore = _rightHandSide;
auto const* lastExpr = &_expr;
while (true)
@@ -1301,7 +1327,7 @@ void SMTEncoder::indexOrMemberAssignment(Expression const& _expr, smtutil::Expre
m_context.addAssertion(m_context.newValue(*varDecl) == toStore);
m_context.expression(*id)->increaseIndex();
defineExpr(*id,currentValue(*varDecl));
defineExpr(*id, currentValue(*varDecl));
break;
}
else
@@ -1622,7 +1648,8 @@ smtutil::Expression SMTEncoder::bitwiseOperation(
result = bvLeft << bvRight;
break;
case Token::SHR:
solAssert(false, "");
result = bvLeft >> bvRight;
break;
case Token::SAR:
result = isSigned ?
smtutil::Expression::ashr(bvLeft, bvRight) :
@@ -2282,16 +2309,25 @@ set<VariableDeclaration const*> SMTEncoder::touchedVariables(ASTNode const& _nod
return m_variableUsage.touchedVariables(_node, callStack);
}
Declaration const* SMTEncoder::expressionToDeclaration(Expression const& _expr)
{
if (auto const* identifier = dynamic_cast<Identifier const*>(&_expr))
return identifier->annotation().referencedDeclaration;
if (auto const* outerMemberAccess = dynamic_cast<MemberAccess const*>(&_expr))
return outerMemberAccess->annotation().referencedDeclaration;
return nullptr;
}
VariableDeclaration const* SMTEncoder::identifierToVariable(Expression const& _expr)
{
if (auto identifier = dynamic_cast<Identifier const*>(&_expr))
{
if (auto decl = dynamic_cast<VariableDeclaration const*>(identifier->annotation().referencedDeclaration))
// We do not use `expressionToDeclaration` here because we are not interested in
// struct.field, for example.
if (auto const* identifier = dynamic_cast<Identifier const*>(&_expr))
if (auto const* varDecl = dynamic_cast<VariableDeclaration const*>(identifier->annotation().referencedDeclaration))
{
solAssert(m_context.knownVariable(*decl), "");
return decl;
solAssert(m_context.knownVariable(*varDecl), "");
return varDecl;
}
}
return nullptr;
}
+5 -1
View File
@@ -280,7 +280,11 @@ protected:
/// @returns variables that are touched in _node's subtree.
std::set<VariableDeclaration const*> touchedVariables(ASTNode const& _node);
/// @returns the VariableDeclaration referenced by an Identifier or nullptr.
/// @returns the declaration referenced by _expr, if any,
/// and nullptr otherwise.
Declaration const* expressionToDeclaration(Expression const& _expr);
/// @returns the VariableDeclaration referenced by an Expression or nullptr.
VariableDeclaration const* identifierToVariable(Expression const& _expr);
/// Creates symbolic expressions for the returned values
+20 -2
View File
@@ -245,7 +245,25 @@ SymbolicTupleVariable::SymbolicTupleVariable(
solAssert(m_sort->kind == Kind::Tuple, "");
}
vector<SortPointer> const& SymbolicTupleVariable::components()
smtutil::Expression SymbolicTupleVariable::currentValue(frontend::TypePointer const& _targetType) const
{
if (!_targetType || sort() == smtSort(*_targetType))
return SymbolicVariable::currentValue();
auto thisTuple = dynamic_pointer_cast<TupleSort>(sort());
auto otherTuple = dynamic_pointer_cast<TupleSort>(smtSort(*_targetType));
solAssert(thisTuple && otherTuple, "");
solAssert(thisTuple->components.size() == otherTuple->components.size(), "");
vector<smtutil::Expression> args;
for (size_t i = 0; i < thisTuple->components.size(); ++i)
args.emplace_back(component(i, type(), _targetType));
return smtutil::Expression::tuple_constructor(
smtutil::Expression(make_shared<smtutil::SortSort>(smtSort(*_targetType)), ""),
args
);
}
vector<SortPointer> const& SymbolicTupleVariable::components() const
{
auto tupleSort = dynamic_pointer_cast<TupleSort>(m_sort);
solAssert(tupleSort, "");
@@ -256,7 +274,7 @@ smtutil::Expression SymbolicTupleVariable::component(
size_t _index,
TypePointer _fromType,
TypePointer _toType
)
) const
{
optional<smtutil::Expression> conversion = symbolicTypeConversion(_fromType, _toType);
if (conversion)
+4 -2
View File
@@ -225,12 +225,14 @@ public:
EncodingContext& _context
);
std::vector<smtutil::SortPointer> const& components();
smtutil::Expression currentValue(frontend::TypePointer const& _targetType = TypePointer{}) const override;
std::vector<smtutil::SortPointer> const& components() const;
smtutil::Expression component(
size_t _index,
TypePointer _fromType = nullptr,
TypePointer _toType = nullptr
);
) const;
};
/**