Skip to content

Commit 2a587b2

Browse files
author
Leo
authored
Merge pull request #12404 from ethereum/smt_fix_havocing
[SMTChecker] Fix soundness of struct storage/memory pointers
2 parents 248bc38 + 316be72 commit 2a587b2

11 files changed

+202
-23
lines changed

Changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Compiler Features:
99
Bugfixes:
1010
* Code Generator: Fix a crash when using ``@use-src`` and compiling from Yul to ewasm.
1111
* SMTChecker: Fix internal error when an unsafe target is solved more than once and the counterexample messages are different.
12+
* SMTChecker: Fix soundness of assigned storage/memory local pointers that were not erasing enough knowledge.
1213
* Fix internal error when a function has a calldata struct argument with an internal type inside.
1314

1415

libsolidity/formal/SMTEncoder.cpp

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232

3333
#include <liblangutil/CharStreamProvider.h>
3434

35+
#include <libsolutil/Algorithms.h>
36+
3537
#include <range/v3/view.hpp>
3638

3739
#include <limits>
@@ -2371,28 +2373,25 @@ void SMTEncoder::resetReferences(Type const* _type)
23712373

23722374
bool SMTEncoder::sameTypeOrSubtype(Type const* _a, Type const* _b)
23732375
{
2374-
Type const* prefix = _a;
2375-
while (
2376-
prefix->category() == Type::Category::Mapping ||
2377-
prefix->category() == Type::Category::Array
2378-
)
2379-
{
2380-
if (*typeWithoutPointer(_b) == *typeWithoutPointer(prefix))
2381-
return true;
2382-
if (prefix->category() == Type::Category::Mapping)
2383-
{
2384-
auto mapPrefix = dynamic_cast<MappingType const*>(prefix);
2385-
solAssert(mapPrefix, "");
2386-
prefix = mapPrefix->valueType();
2387-
}
2388-
else
2376+
bool foundSame = false;
2377+
2378+
solidity::util::BreadthFirstSearch<Type const*> bfs{{_a}};
2379+
bfs.run([&](auto _type, auto&& _addChild) {
2380+
if (*typeWithoutPointer(_b) == *typeWithoutPointer(_type))
23892381
{
2390-
auto arrayPrefix = dynamic_cast<ArrayType const*>(prefix);
2391-
solAssert(arrayPrefix, "");
2392-
prefix = arrayPrefix->baseType();
2382+
foundSame = true;
2383+
bfs.abort();
23932384
}
2394-
}
2395-
return false;
2385+
if (auto const* mapType = dynamic_cast<MappingType const*>(_type))
2386+
_addChild(mapType->valueType());
2387+
else if (auto const* arrayType = dynamic_cast<ArrayType const*>(_type))
2388+
_addChild(arrayType->baseType());
2389+
else if (auto const* structType = dynamic_cast<StructType const*>(_type))
2390+
for (auto const& member: structType->nativeMembers(nullptr))
2391+
_addChild(member.type);
2392+
});
2393+
2394+
return foundSame;
23962395
}
23972396

23982397
bool SMTEncoder::isSupportedType(Type const& _type) const

test/libsolidity/smtCheckerTests/file_level/import.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,5 @@ contract C {
2222
// ====
2323
// SMTEngine: all
2424
// ----
25-
// Warning 6328: (B:238-252): CHC: Assertion violation happens here.\nCounterexample:\ndata = {x: 0}\nx = 0\ny = 0\n\nTransaction trace:\nC.constructor()\nState: data = {x: 0}\nC.g()\n C.f(7) -- internal call\n A:set({x: 0}, 7) -- internal call\n A:set({x: 0}, 8) -- internal call
26-
// Warning 6328: (B:308-322): CHC: Assertion violation happens here.\nCounterexample:\ndata = {x: 0}\nx = 0\ny = 0\n\nTransaction trace:\nC.constructor()\nState: data = {x: 0}\nC.g()\n C.f(7) -- internal call\n A:set({x: 0}, 7) -- internal call\n A:set({x: 0}, 8) -- internal call
25+
// Warning 6328: (B:238-252): CHC: Assertion violation happens here.\nCounterexample:\ndata = {x: 21238}\nx = 8\ny = 21238\n\nTransaction trace:\nC.constructor()\nState: data = {x: 0}\nC.g()\n C.f(7) -- internal call\n A:set({x: 0}, 7) -- internal call\n A:set({x: 8}, 8) -- internal call
26+
// Warning 6328: (B:308-322): CHC: Assertion violation happens here.\nCounterexample:\ndata = {x: 6}\nx = 0\ny = 6\n\nTransaction trace:\nC.constructor()\nState: data = {x: 0}\nC.g()\n C.f(7) -- internal call\n A:set({x: 0}, 7) -- internal call\n A:set({x: 0}, 8) -- internal call
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
contract C {
2+
struct S {
3+
uint sum;
4+
uint[] a;
5+
}
6+
7+
function f(S memory m, uint v) internal pure {
8+
m.sum = v;
9+
m.a = new uint[](2);
10+
}
11+
12+
constructor(uint amt) {
13+
S memory s;
14+
f(s, amt);
15+
assert(s.a.length == 2); // should hold but no aliasing support means it fails for now
16+
}
17+
}
18+
// ====
19+
// SMTEngine: all
20+
// ----
21+
// Warning 6328: (201-224): CHC: Assertion violation happens here.\nCounterexample:\n\namt = 0\ns = {sum: 0, a: []}\n\nTransaction trace:\nC.constructor(0)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
contract C {
2+
struct S {
3+
uint sum;
4+
uint[] a;
5+
}
6+
7+
struct T {
8+
S s;
9+
uint x;
10+
}
11+
12+
function f(S memory m, uint v) internal pure {
13+
m.sum = v;
14+
m.a = new uint[](2);
15+
}
16+
17+
constructor(uint amt) {
18+
T memory t;
19+
f(t.s, amt);
20+
assert(t.s.a.length == 2); // should hold but no aliasing support means it fails for now
21+
}
22+
}
23+
// ====
24+
// SMTEngine: all
25+
// ----
26+
// Warning 6328: (236-261): CHC: Assertion violation happens here.\nCounterexample:\n\namt = 0\nt = {s: {sum: 0, a: []}, x: 0}\n\nTransaction trace:\nC.constructor(0)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
contract C {
2+
struct S {
3+
uint sum;
4+
uint[] a;
5+
}
6+
7+
struct T {
8+
S s;
9+
uint x;
10+
}
11+
12+
function f(T memory m, uint v) internal pure {
13+
m.s.sum = v;
14+
m.s.a = new uint[](2);
15+
}
16+
17+
constructor(uint amt) {
18+
T memory t;
19+
f(t, amt);
20+
assert(t.s.a.length == 2); // should hold but no aliasing support means it fails for now
21+
}
22+
}
23+
// ====
24+
// SMTEngine: all
25+
// ----
26+
// Warning 6328: (238-263): CHC: Assertion violation happens here.\nCounterexample:\n\namt = 0\nt = {s: {sum: 0, a: []}, x: 0}\n\nTransaction trace:\nC.constructor(0)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
contract C {
2+
struct S {
3+
mapping(address => uint) innerM;
4+
uint sum;
5+
}
6+
7+
function f(S storage m, address i, uint v) internal {
8+
m.innerM[i] = v;
9+
m.sum += v;
10+
}
11+
12+
S s;
13+
14+
constructor(uint amt) {
15+
f(s, msg.sender, amt);
16+
}
17+
function g() public view {
18+
assert(s.sum == 0); // should hold but no aliasing support means it fails for now
19+
}
20+
}
21+
// ====
22+
// SMTEngine: all
23+
// ----
24+
// Warning 6328: (270-288): CHC: Assertion violation happens here.\nCounterexample:\ns = {innerM, sum: 21239}\n\nTransaction trace:\nC.constructor(0){ msg.sender: 0x6dc4 }\nState: s = {innerM, sum: 21239}\nC.g()
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
contract C {
2+
struct S {
3+
mapping(address => uint) innerM;
4+
uint sum;
5+
}
6+
7+
function f(mapping(address => uint) storage innerM, address i, uint v) internal {
8+
innerM[i] = v;
9+
}
10+
11+
S s;
12+
13+
constructor(uint amt) {
14+
f(s.innerM, msg.sender, amt);
15+
}
16+
function g() public view {
17+
assert(s.innerM[msg.sender] == 0); // should hold but no aliasing support means it fails for now
18+
}
19+
}
20+
// ====
21+
// SMTEngine: all
22+
// ----
23+
// Warning 6328: (289-322): CHC: Assertion violation happens here.\nCounterexample:\ns = {innerM, sum: 10}\n\nTransaction trace:\nC.constructor(0){ msg.sender: 0x6dc4 }\nState: s = {innerM, sum: 10}\nC.g(){ msg.sender: 0x0985 }
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
contract C {
2+
struct S {
3+
mapping(address => uint) innerM;
4+
uint sum;
5+
}
6+
7+
struct T {
8+
uint x;
9+
S s;
10+
}
11+
12+
function f(T storage m, address i, uint v) internal {
13+
m.s.innerM[i] = v;
14+
m.s.sum += v;
15+
}
16+
17+
T t;
18+
19+
constructor(uint amt) {
20+
f(t, msg.sender, amt);
21+
}
22+
function g() public view {
23+
assert(t.s.sum == 0); // should hold but no aliasing support means it fails for now
24+
}
25+
}
26+
// ====
27+
// SMTEngine: all
28+
// ----
29+
// Warning 6328: (307-327): CHC: Assertion violation happens here.\nCounterexample:\nt = {x: 10, s: {innerM, sum: 21239}}\n\nTransaction trace:\nC.constructor(0){ msg.sender: 0x6dc4 }\nState: t = {x: 10, s: {innerM, sum: 21239}}\nC.g()
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
contract C {
2+
struct S {
3+
mapping(address => uint) innerM;
4+
uint sum;
5+
}
6+
7+
struct T {
8+
uint x;
9+
S s;
10+
}
11+
12+
function f(S storage m, address i, uint v) internal {
13+
m.innerM[i] = v;
14+
m.sum += v;
15+
}
16+
17+
T t;
18+
19+
constructor(uint amt) {
20+
f(t.s, msg.sender, amt);
21+
}
22+
function g() public view {
23+
assert(t.s.sum == 0); // should hold but no aliasing support means it fails for now
24+
}
25+
}
26+
// ====
27+
// SMTEngine: all
28+
// ----
29+
// Warning 6328: (305-325): CHC: Assertion violation happens here.\nCounterexample:\nt = {x: 10, s: {innerM, sum: 21239}}\n\nTransaction trace:\nC.constructor(0){ msg.sender: 0x6dc4 }\nState: t = {x: 10, s: {innerM, sum: 21239}}\nC.g()

0 commit comments

Comments
 (0)