-
Notifications
You must be signed in to change notification settings - Fork 6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SMTChecker: Loop conditions should be analyzed as in loop context in BMC #15862
base: develop
Are you sure you want to change the base?
Conversation
7a9f978
to
ff5ef45
Compare
ff5ef45
to
b92ef6b
Compare
libsolidity/formal/BMC.cpp
Outdated
|
||
if ( | ||
!m_settings.targets.has(VerificationTargetType::ConstantCondition) || | ||
(m_currentContract && !shouldAnalyze(*m_currentContract)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Following the change from #15880, I guess we don't want this check to be bypassed simply because it's in an unselected contract, right?
(m_currentContract && !shouldAnalyze(*m_currentContract)) | |
(m_currentContract && !m_currentContract->canBeDeployed()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct! The code has been updated.
libsolidity/formal/BMC.cpp
Outdated
// Do not check for const-ness if this is a constant. | ||
if (dynamic_cast<Literal const*>(&_condition)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks to me like this will not exclude constants (bool constant C = true;
), just literals (true
), right?
// Do not check for const-ness if this is a constant. | |
if (dynamic_cast<Literal const*>(&_condition)) | |
// Do not check for const-ness if this is a literal. | |
if (dynamic_cast<Literal const*>(&_condition)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am still confused about constants in Solidity. But I believe you are right and I've updated to comment accordingly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this test would be better off named top_level_for_loop_trivial_non_constant_condition.sol
. It would better explain why we're checking this, not just what we're testing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, I don't think that captures the purpose of the test. I tried a different name.
Changelog.md
Outdated
@@ -22,6 +22,7 @@ Bugfixes: | |||
* Metadata: Fix custom cleanup sequence missing from metadata when other optimizer settings have default values. | |||
* SMTChecker: Fix internal compiler error when analyzing overflowing expressions or bitwise negation of unsigned types involving constants. | |||
* SMTChecker: Fix reporting on targets that are safe in the context of one contract but unsafe in the context of another contract. | |||
* SMTChecker: Fix SMT logic error in BMC when loop condition is always true in one iteration and always false in the next one. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like the ICE isn't the only thing this PR fixes. 0.8.28 reports k == 0
as always false in this example:
contract Test {
function loop() public pure {
uint k = 0;
while (k == 0 ? true : false) {
++k;
}
}
}
Warning: BMC: Condition is always false.
--> test.sol:4:16:
|
4 | while (k == 0 ? true : false) {
| ^^^^^^
Note: Callstack:
The version from your branch does not.
I'm comparing with 0.8.28 binary though so there's also a chance it's the result of another, already merged fix.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right, this is the result of the current changes. I've updated the changelog entry, let me know what you think.
// Do not check for const-ness if this is a constant. | ||
if (dynamic_cast<Literal const*>(&_condition)) | ||
return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
checkBooleanNotConstant()
's docstring needs to be updated. It still mentions that the function checks this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I update the docstring. Actually, coming back to this function, I refactored it further to only perform the satisfiability checks and moved reporting of the result to the only caller.
m_loopCheckpoints.emplace(); | ||
_node.condition()->accept(*this); | ||
forCondition = expr(*_node.condition()); | ||
m_loopCheckpoints.pop(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're adding/moving these in 3 different places, but you're only adding one new test case and not updating any existing ones. I suspect that we don't have coverage for all 3.
The example I posted under the changelog would probably cover one of them. What's the other one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added you example with while
loop, which covers the change in visit(WhileStatement)
.
The one test with for loop that I added originally actually exercises both changes in visit(ForStatement)
, so no additional test is needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To clarify, there are two places in visit(ForStatement)
where the loop condition is visited:
Before each unrolling, and a final check when the loop no longer enters another iteration.
We needed to patch both places.
47bb2ac
to
343184a
Compare
Value of loop condition can change between iterations of the loop. Therefore, BMC should be in the "inside loop" state when analyzing loop conditions.
343184a
to
2b4d77d
Compare
Value of loop condition can change between iterations of the loop.
Therefore, BMC should be in the "inside loop" state when analyzing loop conditions.
Previously, when visiting loop condition of a top-level loop, BMC engine did not consider itself being "inside loop", and therefore was checking if the condition is constant (always true or always false).
When the condition is always true in the first iteration and always false in the second iteration, BMC would try to report these two contradicting statements for the same source code expression, leading to an assertion violation in the
UniqueErrorReporter
.Because the value of the condition can change between iteration, BMC should not try to check for constant condition in these cases.
As part of this PR the handling of the
ConstantCondition
verification target has been refactored (first commit).Fixes #15847.