Skip to content

Commit 80d0a97

Browse files
authored
Merge pull request #14448 from pgebal/pgebal/fix_for_loop_iterations
SMTChecker fix: Do not unroll loop after it completes
2 parents 0fa594e + db5baeb commit 80d0a97

8 files changed

+110
-10
lines changed

Changelog.md

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Compiler Features:
88

99
Bugfixes:
1010
* NatSpec: Fix internal error when requesting userdoc or devdoc for a contract that emits an event defined in a foreign contract or interface.
11+
* SMTChecker: Fix encoding error that causes loops to unroll after completion.
1112

1213

1314
### 0.8.21 (2023-07-19)

libsolidity/formal/BMC.cpp

+7-7
Original file line numberDiff line numberDiff line change
@@ -328,13 +328,13 @@ bool BMC::visit(WhileStatement const& _node)
328328
indicesBefore,
329329
copyVariableIndices()
330330
);
331-
loopCondition = expr(_node.condition());
331+
loopCondition = loopCondition && expr(_node.condition());
332332
broke = broke || brokeInCurrentIteration;
333333
m_loopCheckpoints.pop();
334334
}
335335
}
336336
else {
337-
smtutil::Expression loopConditionOnPreviousIteration(true);
337+
smtutil::Expression loopConditionOnPreviousIterations(true);
338338
for (unsigned int i = 0; i < bmcLoopIterations; ++i)
339339
{
340340
m_loopCheckpoints.emplace();
@@ -361,13 +361,13 @@ bool BMC::visit(WhileStatement const& _node)
361361
// breaks in current iterations are handled when traversing loop checkpoints
362362
// handles case when the loop condition no longer holds but bmc loop iterations still unrolls the loop
363363
mergeVariables(
364-
broke || !loopConditionOnPreviousIteration,
364+
broke || !loopConditionOnPreviousIterations,
365365
indicesBefore,
366366
copyVariableIndices()
367367
);
368368
m_loopCheckpoints.pop();
369369
broke = broke || brokeInCurrentIteration;
370-
loopConditionOnPreviousIteration = loopCondition;
370+
loopConditionOnPreviousIterations = loopConditionOnPreviousIterations && loopCondition;
371371
}
372372
}
373373
if (bmcLoopIterations > 0)
@@ -384,7 +384,7 @@ bool BMC::visit(ForStatement const& _node)
384384

385385
smtutil::Expression broke(false);
386386
smtutil::Expression forCondition(true);
387-
smtutil::Expression forConditionOnPreviousIteration(true);
387+
smtutil::Expression forConditionOnPreviousIterations(true);
388388
unsigned int bmcLoopIterations = m_settings.bmcLoopIterations.value_or(1);
389389
for (unsigned int i = 0; i < bmcLoopIterations; ++i)
390390
{
@@ -427,13 +427,13 @@ bool BMC::visit(ForStatement const& _node)
427427
// breaks in current iterations are handled when traversing loop checkpoints
428428
// handles case when the loop condition no longer holds but bmc loop iterations still unrolls the loop
429429
mergeVariables(
430-
broke || !forConditionOnPreviousIteration,
430+
broke || !forConditionOnPreviousIterations,
431431
indicesBefore,
432432
copyVariableIndices()
433433
);
434434
m_loopCheckpoints.pop();
435435
broke = broke || brokeInCurrentIteration;
436-
forConditionOnPreviousIteration = forCondition;
436+
forConditionOnPreviousIterations = forConditionOnPreviousIterations && forCondition;
437437
}
438438
if (bmcLoopIterations > 0)
439439
m_context.addAssertion(not(forCondition) || broke);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
contract C
2+
{
3+
uint x;
4+
uint y;
5+
6+
function condition() private returns(bool) {
7+
x = (x + 1) % 2;
8+
return (x == 1);
9+
}
10+
11+
function f() public {
12+
require(x == 0);
13+
require(y == 0);
14+
do {
15+
++y;
16+
} while (condition());
17+
assert(y == 2);
18+
}
19+
}
20+
// ====
21+
// SMTEngine: bmc
22+
// SMTSolvers: z3
23+
// BMCLoopIterations: 5
24+
// ----
25+
// Warning 2661: (85-90): BMC: Overflow (resulting value larger than 2**256 - 1) happens here.
26+
// Info 6002: BMC: 4 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them.

test/libsolidity/smtCheckerTests/loops/do_while_bmc_iterations_semantics_2.sol

+3-2
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,13 @@ contract C {
99
} while (x < 3 || y == 1);
1010
// BMC loop iteration setting is just enough to leave the loop
1111
assert(x == 3); // should hold
12-
assert(y == 1); // should hold
12+
assert(y == 1); // should fail, when x == 3 loop is completed
1313
}
1414
}
1515
// ====
1616
// SMTEngine: bmc
1717
// SMTSolvers: z3
1818
// BMCLoopIterations: 4
1919
// ----
20-
// Info 6002: BMC: 3 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them.
20+
// Warning 4661: (244-258): BMC: Assertion violation happens here.
21+
// Info 6002: BMC: 2 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them.

test/libsolidity/smtCheckerTests/loops/do_while_bmc_iterations_semantics_3.sol

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ contract C {
99
} while (x < 3 || y == 1);
1010
// BMC loop iteration setting is more than enough to leave the loop
1111
assert(x == 3); // should hold
12-
assert(y == 1); // should hold
12+
assert(y == 0); // should hold
1313
}
1414
}
1515
// ====
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
contract C {
2+
function f() public pure {
3+
uint x = 0;
4+
int y = 0;
5+
do {
6+
++x;
7+
if (x >= 3)
8+
y = 1;
9+
} while (x < 3 || y == 1);
10+
// BMC loop iteration setting is more than enough to leave the loop
11+
assert(x == 3); // should hold
12+
assert(y == 1); // should hold
13+
}
14+
}
15+
// ====
16+
// SMTEngine: bmc
17+
// SMTSolvers: z3
18+
// BMCLoopIterations: 5
19+
// ----
20+
// Info 6002: BMC: 3 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
contract C
2+
{
3+
uint x;
4+
uint y;
5+
6+
function condition() private returns(bool) {
7+
x = (x + 1) % 2;
8+
return (x == 1);
9+
}
10+
11+
function f() public {
12+
require(x == 0);
13+
require(y == 0);
14+
for (; condition();) {
15+
++y;
16+
}
17+
assert(y == 1);
18+
}
19+
}
20+
// ====
21+
// SMTEngine: bmc
22+
// SMTSolvers: z3
23+
// BMCLoopIterations: 5
24+
// ----
25+
// Warning 2661: (85-90): BMC: Overflow (resulting value larger than 2**256 - 1) happens here.
26+
// Info 6002: BMC: 4 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
contract C
2+
{
3+
uint x;
4+
uint y;
5+
6+
function condition() private returns(bool) {
7+
x = (x + 1) % 2;
8+
return (x == 1);
9+
}
10+
11+
function f() public {
12+
require(x == 0);
13+
require(y == 0);
14+
while (condition()) {
15+
++y;
16+
}
17+
assert(y == 1);
18+
}
19+
}
20+
// ====
21+
// SMTEngine: bmc
22+
// SMTSolvers: z3
23+
// BMCLoopIterations: 5
24+
// ----
25+
// Warning 2661: (85-90): BMC: Overflow (resulting value larger than 2**256 - 1) happens here.
26+
// Info 6002: BMC: 4 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them.

0 commit comments

Comments
 (0)