Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

blishko
Copy link
Collaborator

@blishko blishko commented Jan 22, 2021

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:

  • If any Solidity-level local variable is assigned in the assembly, it is considered as having an unknown value at the end of the assembly block.
  • If the block modifies storage (e.g., using sstore), all storage variables are considered as having an unknown value at the end of the assembly block.
  • If the block modifies memory (e.g., using mstore), all memory variables are considered as having an unknown value at the end of the assembly block.
  • In all other cases, the variables keep their value from just before the assembly block.

This over-approximation at least eliminates false negatives, as were seen in #10619.
It is a first step towards #10596.

// 7737_error,
// _inlineAsm.location(),
// "Assertion checker does not support inline assembly."
// );
Copy link
Collaborator Author

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"?

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

Copy link
Collaborator Author

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

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

Copy link
Collaborator Author

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();
});

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?

Copy link
Collaborator Author

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).
Copy link
Collaborator Author

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.

@blishko blishko changed the title Basic support for inline assembly that over-approximates its effects. [SMTChecker] Basic support for inline assembly that over-approximates its effects. Jan 25, 2021
@blishko blishko marked this pull request as ready for review January 25, 2021 15:23
@blishko blishko requested a review from leonardoalt January 25, 2021 15:31
@blishko
Copy link
Collaborator Author

blishko commented Jan 25, 2021

@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.

Copy link
Contributor

@hrkrshnn hrkrshnn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good.

{
struct AssignedExternalsCollector: public yul::ASTWalker
{
using ExternalReferences = map<yul::Identifier const*, InlineAssemblyAnnotation::ExternalIdentifierInfo>;
Copy link
Contributor

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?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the using.

{
auto const& vars = _assignment.variableNames;
for (auto const& identifier: vars)
if (auto it = externalReferences.find(&identifier); it != externalReferences.end())
Copy link
Contributor

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?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replaced.

}
};

auto assignedVars = AssignedExternalsCollector(_inlineAsm).assignedVars;
Copy link
Contributor

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?

Copy link
Collaborator Author

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!

Copy link
Contributor

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.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, Identifiers would be much better, since I am then accessing a map with Identifier * type of keys. I could work around that, but collecting directly Identifiers seems cleaner to me.


void SMTEncoder::resetMemoryVariables()
{
m_context.resetVariables([&](VariableDeclaration const& _variable) { return _variable.referenceLocation() == VariableDeclaration::Location::Memory; });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A line break?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

@leonardoalt
Copy link

Also please rebase

@blishko
Copy link
Collaborator Author

blishko commented Jan 26, 2021

Addressed the comments and rebased.
I am not sure of this should have a changelog entry or not.
It does eliminate some false negatives, but it is still a very over-approximative analysis.

@leonardoalt
Copy link

I think it should have a Changelog entry at least as a bugfix because it fixes the false negatives

@leonardoalt
Copy link

Some weird exceptions in Yul tests

@blishko
Copy link
Collaborator Author

blishko commented Jan 26, 2021

Some weird exceptions in Yul tests

I don't understand these failings, and they happen in completely unrelated tests.

@blishko
Copy link
Collaborator Author

blishko commented Jan 26, 2021

Added a changelog entry and rebased.

Comment on lines 4 to 10
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;
Copy link
Contributor

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.

Copy link
Collaborator Author

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;?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added.

@leonardoalt leonardoalt merged commit 38d1ec3 into argotorg:develop Jan 26, 2021
@blishko blishko deleted the smt-assembly branch January 27, 2021 07:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants