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

Skip to content

Commit 38d1ec3

Browse files
author
Leonardo
authored
Merge pull request #10835 from blishko/smt-assembly
[SMTChecker] Basic support for inline assembly that over-approximates its effects.
2 parents 6eebd65 + 484e678 commit 38d1ec3

14 files changed

+261
-5
lines changed

Changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ Bugfixes:
2424
* Code Generator: Fix length check when decoding malformed error data in catch clause.
2525
* Control Flow Graph: Fix missing error caused by read from/write to uninitialized variables.
2626
* SMTChecker: Fix false negatives in overriding modifiers and functions.
27+
* SMTChecker: Fix false negatives in the presence of inline assembly.
2728
* SMTChecker: Fix false negatives when analyzing external function calls.
2829
* SMTChecker: Fix internal error on ``block.chainid``.
2930
* SMTChecker: Fix internal error on pushing string literal to ``bytes`` array.

libsolidity/formal/SMTEncoder.cpp

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424

2525
#include <libsolidity/analysis/ConstantEvaluator.h>
2626

27+
#include <libyul/AST.h>
28+
#include <libyul/optimiser/Semantics.h>
29+
2730
#include <libsmtutil/SMTPortfolio.h>
2831
#include <libsmtutil/Helpers.h>
2932

@@ -304,10 +307,46 @@ void SMTEncoder::endVisit(Block const& _block)
304307

305308
bool SMTEncoder::visit(InlineAssembly const& _inlineAsm)
306309
{
310+
/// This is very similar to `yul::Assignments`, except I need to collect `Identifier`s and not just names as `YulString`s.
311+
struct AssignedExternalsCollector: public yul::ASTWalker
312+
{
313+
AssignedExternalsCollector(InlineAssembly const& _inlineAsm): externalReferences(_inlineAsm.annotation().externalReferences)
314+
{
315+
this->operator()(_inlineAsm.operations());
316+
}
317+
318+
map<yul::Identifier const*, InlineAssemblyAnnotation::ExternalIdentifierInfo> const& externalReferences;
319+
set<VariableDeclaration const*> assignedVars;
320+
321+
using yul::ASTWalker::operator();
322+
void operator()(yul::Assignment const& _assignment)
323+
{
324+
auto const& vars = _assignment.variableNames;
325+
for (auto const& identifier: vars)
326+
if (auto externalInfo = valueOrNullptr(externalReferences, &identifier))
327+
if (auto varDecl = dynamic_cast<VariableDeclaration const*>(externalInfo->declaration))
328+
assignedVars.insert(varDecl);
329+
}
330+
};
331+
332+
yul::SideEffectsCollector sideEffectsCollector(_inlineAsm.dialect(), _inlineAsm.operations());
333+
if (sideEffectsCollector.invalidatesMemory())
334+
resetMemoryVariables();
335+
if (sideEffectsCollector.invalidatesStorage())
336+
resetStorageVariables();
337+
338+
auto assignedVars = AssignedExternalsCollector(_inlineAsm).assignedVars;
339+
for (auto const* var: assignedVars)
340+
{
341+
solAssert(var, "");
342+
solAssert(var->isLocalVariable(), "Non-local variable assigned in inlined assembly");
343+
m_context.resetVariable(*var);
344+
}
345+
307346
m_errorReporter.warning(
308347
7737_error,
309348
_inlineAsm.location(),
310-
"Assertion checker does not support inline assembly."
349+
"Inline assembly may cause SMTChecker to produce spurious warnings (false positives)."
311350
);
312351
return false;
313352
}
@@ -2256,6 +2295,20 @@ void SMTEncoder::resetStateVariables()
22562295
m_context.resetVariables([&](VariableDeclaration const& _variable) { return _variable.isStateVariable(); });
22572296
}
22582297

2298+
void SMTEncoder::resetMemoryVariables()
2299+
{
2300+
m_context.resetVariables([&](VariableDeclaration const& _variable) {
2301+
return _variable.referenceLocation() == VariableDeclaration::Location::Memory;
2302+
});
2303+
}
2304+
2305+
void SMTEncoder::resetStorageVariables()
2306+
{
2307+
m_context.resetVariables([&](VariableDeclaration const& _variable) {
2308+
return _variable.referenceLocation() == VariableDeclaration::Location::Storage || _variable.isStateVariable();
2309+
});
2310+
}
2311+
22592312
void SMTEncoder::resetReferences(VariableDeclaration const& _varDecl)
22602313
{
22612314
m_context.resetVariables([&](VariableDeclaration const& _var) {

libsolidity/formal/SMTEncoder.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,8 @@ class SMTEncoder: public ASTConstVisitor
260260
void initializeLocalVariables(FunctionDefinition const& _function);
261261
void initializeFunctionCallParameters(CallableDeclaration const& _function, std::vector<smtutil::Expression> const& _callArgs);
262262
void resetStateVariables();
263+
void resetStorageVariables();
264+
void resetMemoryVariables();
263265
/// Resets all references/pointers that have the same type or have
264266
/// a subexpression of the same type as _varDecl.
265267
void resetReferences(VariableDeclaration const& _varDecl);
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
pragma experimental SMTChecker;
2+
3+
contract C {
4+
function f() internal pure returns (bool) {
5+
bool b;
6+
assembly { b := 1 } // This assignment is overapproximated at the moment, we don't know value of b after the assembly block
7+
return b;
8+
}
9+
function g() public pure {
10+
assert(f()); // False positive currently
11+
assert(!f()); // should fail, now because of overapproximation in the analysis
12+
require(f()); // BMC constant value not detected at the moment
13+
require(!f()); // BMC constant value not ddetected at the moment
14+
}
15+
}
16+
// ----
17+
// Warning 7737: (103-122): Inline assembly may cause SMTChecker to produce spurious warnings (false positives).
18+
// Warning 6328: (272-283): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor()\nC.g()\n C.f() -- internal call
19+
// Warning 6328: (315-327): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor()\nC.g()\n C.f() -- internal call\n C.f() -- internal call
20+
// Warning 7737: (103-122): Inline assembly may cause SMTChecker to produce spurious warnings (false positives).
21+
// Warning 7737: (103-122): Inline assembly may cause SMTChecker to produce spurious warnings (false positives).
22+
// Warning 7737: (103-122): Inline assembly may cause SMTChecker to produce spurious warnings (false positives).
23+
// Warning 7737: (103-122): Inline assembly may cause SMTChecker to produce spurious warnings (false positives).
24+
// Warning 7737: (103-122): Inline assembly may cause SMTChecker to produce spurious warnings (false positives).
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
pragma experimental SMTChecker;
2+
3+
contract C {
4+
function f() public pure returns (bool) {
5+
bool b;
6+
int x = 42;
7+
assembly { b := 1 }
8+
assert(x == 42); // should hold
9+
assert(b); // should hold, but fails due to overapproximation
10+
return b;
11+
}
12+
}
13+
// ----
14+
// Warning 7737: (115-134): Inline assembly may cause SMTChecker to produce spurious warnings (false positives).
15+
// Warning 6328: (171-180): CHC: Assertion violation happens here.\nCounterexample:\n\n = false\n\nTransaction trace:\nC.constructor()\nC.f()
16+
// Warning 7737: (115-134): Inline assembly may cause SMTChecker to produce spurious warnings (false positives).
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
pragma experimental SMTChecker;
2+
3+
contract C {
4+
function f() public pure returns (bool) {
5+
bool b;
6+
bool c = true;
7+
assembly { b := c }
8+
assert(c); // should hold, c is not assigned in the assembly
9+
assert(b); // should hold, but fails currently because of overapproximation
10+
return b;
11+
}
12+
}
13+
// ----
14+
// Warning 7737: (139-158): Inline assembly may cause SMTChecker to produce spurious warnings (false positives).
15+
// Warning 6328: (236-245): CHC: Assertion violation happens here.\nCounterexample:\n\n = false\n\nTransaction trace:\nC.constructor()\nC.f()
16+
// Warning 7737: (139-158): Inline assembly may cause SMTChecker to produce spurious warnings (false positives).
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
pragma experimental SMTChecker;
2+
3+
contract C {
4+
function f() public pure returns (bool) {
5+
bool b;
6+
int x = 42;
7+
assembly { b := 1 }
8+
b = true;
9+
assert(x == 42); // should hold
10+
assert(b); // should hold
11+
return b;
12+
}
13+
}
14+
// ----
15+
// Warning 7737: (115-134): Inline assembly may cause SMTChecker to produce spurious warnings (false positives).
16+
// Warning 7737: (115-134): Inline assembly may cause SMTChecker to produce spurious warnings (false positives).
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
pragma experimental SMTChecker;
2+
3+
contract C {
4+
struct S {
5+
uint x;
6+
}
7+
8+
S s;
9+
10+
function f() public {
11+
s.x = 42;
12+
S memory sm = s;
13+
assert(sm.x == 42); // should hold
14+
uint256 i = 7;
15+
assembly {
16+
mstore(sm, i)
17+
}
18+
sm.x = 10;
19+
assert(sm.x == 10); // should hold
20+
assert(s.x == 42); // should hold, storage not changed by the assembly
21+
assert(i == 7); // should hold, not changed by the assembly
22+
}
23+
}
24+
// ----
25+
// Warning 7737: (189-220): Inline assembly may cause SMTChecker to produce spurious warnings (false positives).
26+
// Warning 7737: (189-220): Inline assembly may cause SMTChecker to produce spurious warnings (false positives).
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
pragma experimental SMTChecker;
2+
3+
contract C {
4+
struct S {
5+
uint x;
6+
}
7+
8+
S s;
9+
10+
function f() public {
11+
s.x = 42;
12+
S storage sm = s;
13+
assert(sm.x == 42); // should hold
14+
uint256 i = 7;
15+
assembly {
16+
sstore(sm.slot, i)
17+
}
18+
sm.x = 10;
19+
assert(sm.x == 10); // should hold
20+
assert(i == 7); // should hold, not changed by the assembly
21+
}
22+
}
23+
// ----
24+
// Warning 7737: (190-226): Inline assembly may cause SMTChecker to produce spurious warnings (false positives).
25+
// Warning 7737: (190-226): Inline assembly may cause SMTChecker to produce spurious warnings (false positives).
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
pragma experimental SMTChecker;
2+
3+
contract C {
4+
uint256 public z;
5+
6+
function f() public {
7+
z = 42;
8+
uint i = 32;
9+
assembly {
10+
function f() {
11+
sstore(z.slot, 7)
12+
}
13+
f()
14+
}
15+
assert(z == 42); // should fail
16+
assert(z == 7); // should hold, but the analysis cannot know this yet
17+
assert(i == 32); // should hold, not changed by the assembly
18+
}
19+
}
20+
// ----
21+
// Warning 7737: (116-182): Inline assembly may cause SMTChecker to produce spurious warnings (false positives).
22+
// Warning 6328: (185-200): CHC: Assertion violation happens here.\nCounterexample:\nz = 0\n\nTransaction trace:\nC.constructor()\nState: z = 0\nC.f()
23+
// Warning 6328: (219-233): CHC: Assertion violation happens here.\nCounterexample:\nz = 0\n\nTransaction trace:\nC.constructor()\nState: z = 0\nC.f()
24+
// Warning 7737: (116-182): Inline assembly may cause SMTChecker to produce spurious warnings (false positives).

0 commit comments

Comments
 (0)