-
Notifications
You must be signed in to change notification settings - Fork 6.2k
[SMTChecker] Basic support for inline assembly that over-approximates its effects. #10835
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
Conversation
libsolidity/formal/SMTEncoder.cpp
Outdated
// 7737_error, | ||
// _inlineAsm.location(), | ||
// "Assertion checker does not support inline assembly." | ||
// ); |
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.
Do we want to keep this warning, remove it, or replace with something like "Results might be imprecise because of over-approximation caused by inline assembly"?
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.
We can add the message you suggested when we see we need to reset something, and can omit the message when nothing had to be reset
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 have changed the message of the warning.
Since there will always be something over-approximated (if the assembly block is doing anything), I think it is OK to display it every time.
contract C { | ||
function f() internal pure returns (bool) { | ||
bool b; | ||
assembly { b := 1 } // This assignment is overapproximated at the moment, we don't know value of b after the assembly block |
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.
We can support the assignment when the rhs is a literal or a solidity var
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.
As agreed, this is more complicated than it seems on the first glance.
If we continue in this direction, it will be a separate PR.
{ | ||
m_context.resetVariables([&](VariableDeclaration const& _variable) { | ||
return _variable.referenceLocation() == VariableDeclaration::Location::Storage || _variable.isStateVariable(); | ||
}); |
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 is actually the intended behavior of resetStateVariables
. So maybe remove resetStateVariables
and replace by resetStorageVariables
wherever it's used?
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.
As agreed, this will be a separate PR.
// Warning 7737: (103-122): Inline assembly may cause SMTChecker to produce spurious warnings (false positives). | ||
// Warning 7737: (103-122): Inline assembly may cause SMTChecker to produce spurious warnings (false positives). | ||
// Warning 7737: (103-122): Inline assembly may cause SMTChecker to produce spurious warnings (false positives). | ||
// Warning 7737: (103-122): Inline assembly may cause SMTChecker to produce spurious warnings (false positives). |
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.
Seeing this, I think we need a mechanism in SMTChecker to display some warnings only once.
@leonardoalt , check if you agree with the error message, or I should revert it back to the original one. As I mentioned, since the inline assembly causes potentially quite a big over-approximations, I would keep displaying the warning. |
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 good.
libsolidity/formal/SMTEncoder.cpp
Outdated
{ | ||
struct AssignedExternalsCollector: public yul::ASTWalker | ||
{ | ||
using ExternalReferences = map<yul::Identifier const*, InlineAssemblyAnnotation::ExternalIdentifierInfo>; |
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.
This seems to be only used once, so do we really need the using?
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.
Removed the using.
libsolidity/formal/SMTEncoder.cpp
Outdated
{ | ||
auto const& vars = _assignment.variableNames; | ||
for (auto const& identifier: vars) | ||
if (auto it = externalReferences.find(&identifier); it != externalReferences.end()) |
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.
Can you replace this by valueOrNullptr
from libsolutil/CommonData.h
?
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.
Replaced.
libsolidity/formal/SMTEncoder.cpp
Outdated
} | ||
}; | ||
|
||
auto assignedVars = AssignedExternalsCollector(_inlineAsm).assignedVars; |
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 wonder if we really have to write the class AssignedExternalCollector
.
There is Assignments
in libyul/optimiser/NameCollector.h
, which should give you all _assignment.variableNames
. Then you could just iterate through the names, without having to write a class. What do you think?
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.
Sorry, I misread your comment initially,Assignments
looks useful, thanks for the tip!
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.
Assignments
would only give YulString
. If you really need Identifier
, then you might have to write a walker class. So not sure.
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.
Yeah, Identifier
s would be much better, since I am then accessing a map with Identifier *
type of keys. I could work around that, but collecting directly Identifier
s seems cleaner to me.
libsolidity/formal/SMTEncoder.cpp
Outdated
|
||
void SMTEncoder::resetMemoryVariables() | ||
{ | ||
m_context.resetVariables([&](VariableDeclaration const& _variable) { return _variable.referenceLocation() == VariableDeclaration::Location::Memory; }); |
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.
A line break?
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.
Fixed.
Also please rebase |
Addressed the comments and rebased. |
I think it should have a Changelog entry at least as a bugfix because it fixes the false negatives |
Some weird exceptions in Yul tests |
I don't understand these failings, and they happen in completely unrelated tests. |
Added a changelog entry and rebased. |
function f() public pure returns (bool) { | ||
bool b; | ||
int x = 42; | ||
assembly { b := 1 } | ||
assert(x == 42); // should hold | ||
assert(b); // should hold, but fails due to overapproximation | ||
return b; |
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.
function f() public pure returns (bool) {
bool b;
int x = 42;
assembly { b := 1 }
b = 5
assert(x == 42); // should hold
assert(b); // should hold
return b;
Could you also add a test like this? And a similar one for memory / storage.
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.
@hrkrshnn : Error: Type int_const 5 is not implicitly convertible to expected type bool
Did you mean b = true;
instead of b = 5;
?
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.
Yes!
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.
Added.
This PR adds basic support in SMTChecker for the analysis of Inline Assembly blocks.
The basic approach over-approximates the effects of the assembly in the following way:
sstore
), all storage variables are considered as having an unknown value at the end of the assembly block.mstore
), all memory variables are considered as having an unknown value at the end of the assembly block.This over-approximation at least eliminates false negatives, as were seen in #10619.
It is a first step towards #10596.