SMTChecker fix: Do not unroll loop after it completes

This commit is contained in:
Pawel Gebal 2023-07-21 18:02:06 +02:00
parent 0fa594e501
commit db5baebff8
8 changed files with 110 additions and 10 deletions

View File

@ -8,6 +8,7 @@ Compiler Features:
Bugfixes: Bugfixes:
* NatSpec: Fix internal error when requesting userdoc or devdoc for a contract that emits an event defined in a foreign contract or interface. * NatSpec: Fix internal error when requesting userdoc or devdoc for a contract that emits an event defined in a foreign contract or interface.
* SMTChecker: Fix encoding error that causes loops to unroll after completion.
### 0.8.21 (2023-07-19) ### 0.8.21 (2023-07-19)

View File

@ -328,13 +328,13 @@ bool BMC::visit(WhileStatement const& _node)
indicesBefore, indicesBefore,
copyVariableIndices() copyVariableIndices()
); );
loopCondition = expr(_node.condition()); loopCondition = loopCondition && expr(_node.condition());
broke = broke || brokeInCurrentIteration; broke = broke || brokeInCurrentIteration;
m_loopCheckpoints.pop(); m_loopCheckpoints.pop();
} }
} }
else { else {
smtutil::Expression loopConditionOnPreviousIteration(true); smtutil::Expression loopConditionOnPreviousIterations(true);
for (unsigned int i = 0; i < bmcLoopIterations; ++i) for (unsigned int i = 0; i < bmcLoopIterations; ++i)
{ {
m_loopCheckpoints.emplace(); m_loopCheckpoints.emplace();
@ -361,13 +361,13 @@ bool BMC::visit(WhileStatement const& _node)
// breaks in current iterations are handled when traversing loop checkpoints // breaks in current iterations are handled when traversing loop checkpoints
// handles case when the loop condition no longer holds but bmc loop iterations still unrolls the loop // handles case when the loop condition no longer holds but bmc loop iterations still unrolls the loop
mergeVariables( mergeVariables(
broke || !loopConditionOnPreviousIteration, broke || !loopConditionOnPreviousIterations,
indicesBefore, indicesBefore,
copyVariableIndices() copyVariableIndices()
); );
m_loopCheckpoints.pop(); m_loopCheckpoints.pop();
broke = broke || brokeInCurrentIteration; broke = broke || brokeInCurrentIteration;
loopConditionOnPreviousIteration = loopCondition; loopConditionOnPreviousIterations = loopConditionOnPreviousIterations && loopCondition;
} }
} }
if (bmcLoopIterations > 0) if (bmcLoopIterations > 0)
@ -384,7 +384,7 @@ bool BMC::visit(ForStatement const& _node)
smtutil::Expression broke(false); smtutil::Expression broke(false);
smtutil::Expression forCondition(true); smtutil::Expression forCondition(true);
smtutil::Expression forConditionOnPreviousIteration(true); smtutil::Expression forConditionOnPreviousIterations(true);
unsigned int bmcLoopIterations = m_settings.bmcLoopIterations.value_or(1); unsigned int bmcLoopIterations = m_settings.bmcLoopIterations.value_or(1);
for (unsigned int i = 0; i < bmcLoopIterations; ++i) for (unsigned int i = 0; i < bmcLoopIterations; ++i)
{ {
@ -427,13 +427,13 @@ bool BMC::visit(ForStatement const& _node)
// breaks in current iterations are handled when traversing loop checkpoints // breaks in current iterations are handled when traversing loop checkpoints
// handles case when the loop condition no longer holds but bmc loop iterations still unrolls the loop // handles case when the loop condition no longer holds but bmc loop iterations still unrolls the loop
mergeVariables( mergeVariables(
broke || !forConditionOnPreviousIteration, broke || !forConditionOnPreviousIterations,
indicesBefore, indicesBefore,
copyVariableIndices() copyVariableIndices()
); );
m_loopCheckpoints.pop(); m_loopCheckpoints.pop();
broke = broke || brokeInCurrentIteration; broke = broke || brokeInCurrentIteration;
forConditionOnPreviousIteration = forCondition; forConditionOnPreviousIterations = forConditionOnPreviousIterations && forCondition;
} }
if (bmcLoopIterations > 0) if (bmcLoopIterations > 0)
m_context.addAssertion(not(forCondition) || broke); m_context.addAssertion(not(forCondition) || broke);

View File

@ -0,0 +1,26 @@
contract C
{
uint x;
uint y;
function condition() private returns(bool) {
x = (x + 1) % 2;
return (x == 1);
}
function f() public {
require(x == 0);
require(y == 0);
do {
++y;
} while (condition());
assert(y == 2);
}
}
// ====
// SMTEngine: bmc
// SMTSolvers: z3
// BMCLoopIterations: 5
// ----
// Warning 2661: (85-90): BMC: Overflow (resulting value larger than 2**256 - 1) happens here.
// Info 6002: BMC: 4 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them.

View File

@ -9,7 +9,7 @@ contract C {
} while (x < 3 || y == 1); } while (x < 3 || y == 1);
// BMC loop iteration setting is just enough to leave the loop // BMC loop iteration setting is just enough to leave the loop
assert(x == 3); // should hold assert(x == 3); // should hold
assert(y == 1); // should hold assert(y == 1); // should fail, when x == 3 loop is completed
} }
} }
// ==== // ====
@ -17,4 +17,5 @@ contract C {
// SMTSolvers: z3 // SMTSolvers: z3
// BMCLoopIterations: 4 // BMCLoopIterations: 4
// ---- // ----
// Info 6002: BMC: 3 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them. // Warning 4661: (244-258): BMC: Assertion violation happens here.
// Info 6002: BMC: 2 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them.

View File

@ -9,7 +9,7 @@ contract C {
} while (x < 3 || y == 1); } while (x < 3 || y == 1);
// BMC loop iteration setting is more than enough to leave the loop // BMC loop iteration setting is more than enough to leave the loop
assert(x == 3); // should hold assert(x == 3); // should hold
assert(y == 1); // should hold assert(y == 0); // should hold
} }
} }
// ==== // ====

View File

@ -0,0 +1,20 @@
contract C {
function f() public pure {
uint x = 0;
int y = 0;
do {
++x;
if (x >= 3)
y = 1;
} while (x < 3 || y == 1);
// BMC loop iteration setting is more than enough to leave the loop
assert(x == 3); // should hold
assert(y == 1); // should hold
}
}
// ====
// SMTEngine: bmc
// SMTSolvers: z3
// BMCLoopIterations: 5
// ----
// Info 6002: BMC: 3 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them.

View File

@ -0,0 +1,26 @@
contract C
{
uint x;
uint y;
function condition() private returns(bool) {
x = (x + 1) % 2;
return (x == 1);
}
function f() public {
require(x == 0);
require(y == 0);
for (; condition();) {
++y;
}
assert(y == 1);
}
}
// ====
// SMTEngine: bmc
// SMTSolvers: z3
// BMCLoopIterations: 5
// ----
// Warning 2661: (85-90): BMC: Overflow (resulting value larger than 2**256 - 1) happens here.
// Info 6002: BMC: 4 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them.

View File

@ -0,0 +1,26 @@
contract C
{
uint x;
uint y;
function condition() private returns(bool) {
x = (x + 1) % 2;
return (x == 1);
}
function f() public {
require(x == 0);
require(y == 0);
while (condition()) {
++y;
}
assert(y == 1);
}
}
// ====
// SMTEngine: bmc
// SMTSolvers: z3
// BMCLoopIterations: 5
// ----
// Warning 2661: (85-90): BMC: Overflow (resulting value larger than 2**256 - 1) happens here.
// Info 6002: BMC: 4 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them.