Merge pull request #9159 from ethereum/smt_nondet_mod

[SMTChecker] Replace explicit mod by slack variables
This commit is contained in:
Daniel Kirchner 2020-06-12 17:05:13 +02:00 committed by GitHub
commit 633b170f58
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 66 additions and 26 deletions

View File

@ -32,12 +32,23 @@ EncodingContext::EncodingContext():
void EncodingContext::reset() void EncodingContext::reset()
{ {
resetAllVariables(); resetAllVariables();
resetSlackId();
m_expressions.clear(); m_expressions.clear();
m_globalContext.clear(); m_globalContext.clear();
m_state.reset(); m_state.reset();
m_assertions.clear(); m_assertions.clear();
} }
void EncodingContext::resetSlackId()
{
m_nextSlackId = 0;
}
unsigned EncodingContext::newSlackId()
{
return m_nextSlackId++;
}
void EncodingContext::clear() void EncodingContext::clear()
{ {
m_variables.clear(); m_variables.clear();

View File

@ -40,6 +40,10 @@ public:
/// alive because of state variables and inlined function calls. /// alive because of state variables and inlined function calls.
/// To be used in the beginning of a root function visit. /// To be used in the beginning of a root function visit.
void reset(); void reset();
/// Resets the fresh id for slack variables.
void resetSlackId();
/// Returns the current fresh slack id and increments it.
unsigned newSlackId();
/// Clears the entire context, erasing everything. /// Clears the entire context, erasing everything.
/// To be used before a model checking engine starts. /// To be used before a model checking engine starts.
void clear(); void clear();
@ -168,6 +172,9 @@ private:
/// Whether to conjoin assertions in the assertion stack. /// Whether to conjoin assertions in the assertion stack.
bool m_accumulateAssertions = true; bool m_accumulateAssertions = true;
//@} //@}
/// Fresh ids for slack variables to be created deterministically.
unsigned m_nextSlackId = 0;
}; };
} }

View File

@ -1204,7 +1204,7 @@ pair<smtutil::Expression, smtutil::Expression> SMTEncoder::arithmeticOperation(
smtutil::Expression const& _left, smtutil::Expression const& _left,
smtutil::Expression const& _right, smtutil::Expression const& _right,
TypePointer const& _commonType, TypePointer const& _commonType,
Expression const& Expression const& _operation
) )
{ {
static set<Token> validOperators{ static set<Token> validOperators{
@ -1227,39 +1227,66 @@ pair<smtutil::Expression, smtutil::Expression> SMTEncoder::arithmeticOperation(
else else
intType = TypeProvider::uint256(); intType = TypeProvider::uint256();
smtutil::Expression valueNoMod( auto valueUnbounded = [&]() -> smtutil::Expression {
_op == Token::Add ? _left + _right : switch (_op)
_op == Token::Sub ? _left - _right : {
_op == Token::Div ? division(_left, _right, *intType) : case Token::Add: return _left + _right;
_op == Token::Mul ? _left * _right : case Token::Sub: return _left - _right;
/*_op == Token::Mod*/ _left % _right case Token::Mul: return _left * _right;
); case Token::Div: return division(_left, _right, *intType);
case Token::Mod: return _left % _right;
default: solAssert(false, "");
}
}();
if (_op == Token::Div || _op == Token::Mod) if (_op == Token::Div || _op == Token::Mod)
{
m_context.addAssertion(_right != 0); m_context.addAssertion(_right != 0);
// mod and unsigned division never underflow/overflow
if (_op == Token::Mod || !intType->isSigned())
return {valueUnbounded, valueUnbounded};
// The only case where division overflows is
// - type is signed
// - LHS is type.min
// - RHS is -1
// the result is then -(type.min), which wraps back to type.min
smtutil::Expression maxLeft = _left == smt::minValue(*intType);
smtutil::Expression minusOneRight = _right == -1;
smtutil::Expression wrap = smtutil::Expression::ite(maxLeft && minusOneRight, smt::minValue(*intType), valueUnbounded);
return {wrap, valueUnbounded};
}
auto symbMin = smt::minValue(*intType); auto symbMin = smt::minValue(*intType);
auto symbMax = smt::maxValue(*intType); auto symbMax = smt::maxValue(*intType);
smtutil::Expression intValueRange = (0 - symbMin) + symbMax + 1; smtutil::Expression intValueRange = (0 - symbMin) + symbMax + 1;
string suffix = to_string(_operation.id()) + "_" + to_string(m_context.newSlackId());
smt::SymbolicIntVariable k(intType, intType, "k_" + suffix, m_context);
smt::SymbolicIntVariable m(intType, intType, "m_" + suffix, m_context);
// To wrap around valueUnbounded in case of overflow or underflow, we replace it with a k, given:
// 1. k + m * intValueRange = valueUnbounded
// 2. k is in range of the desired integer type
auto wrap = k.currentValue();
m_context.addAssertion(valueUnbounded == (k.currentValue() + intValueRange * m.currentValue()));
m_context.addAssertion(k.currentValue() >= symbMin);
m_context.addAssertion(k.currentValue() <= symbMax);
// TODO this could be refined:
// for unsigned types it's enough to check only the upper bound.
auto value = smtutil::Expression::ite( auto value = smtutil::Expression::ite(
valueNoMod > symbMax, valueUnbounded > symbMax,
valueNoMod % intValueRange, wrap,
smtutil::Expression::ite( smtutil::Expression::ite(
valueNoMod < symbMin, valueUnbounded < symbMin,
valueNoMod % intValueRange, wrap,
valueNoMod valueUnbounded
) )
); );
if (intType->isSigned()) return {value, valueUnbounded};
value = smtutil::Expression::ite(
value > symbMax,
value - intValueRange,
value
);
return {value, valueNoMod};
} }
void SMTEncoder::compareOperation(BinaryOperation const& _op) void SMTEncoder::compareOperation(BinaryOperation const& _op)

View File

@ -22,4 +22,3 @@ contract C
} }
} }
// ---- // ----
// Warning: (130-144): Error trying to invoke SMT solver.

View File

@ -14,6 +14,4 @@ contract C
} }
} }
// ---- // ----
// Warning: (296-309): Error trying to invoke SMT solver.
// Warning: (176-181): Overflow (resulting value larger than 2**256 - 1) happens here // Warning: (176-181): Overflow (resulting value larger than 2**256 - 1) happens here
// Warning: (296-309): Assertion violation happens here

View File

@ -19,7 +19,5 @@ contract LoopFor2 {
} }
} }
// ---- // ----
// Warning: (317-337): Error trying to invoke SMT solver.
// Warning: (317-337): Assertion violation happens here
// Warning: (341-360): Assertion violation happens here // Warning: (341-360): Assertion violation happens here
// Warning: (364-383): Assertion violation happens here // Warning: (364-383): Assertion violation happens here