-
Notifications
You must be signed in to change notification settings - Fork 10
/
multi_man_complex_9.sol
166 lines (147 loc) · 5.17 KB
/
multi_man_complex_9.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
// This is an extremely hard synthetic test case for smart contract analysis
// tools, such as fuzzers or symbolic executors. It tests various capabilities
// of the solver on achieving per-function coverage as well as finding the
// right order of the transactions. For a human this is quite easy to solve, as
// you really need to call f1 up to f9 in that order and provide the right
// inputs. One such transaction sequence would be:
// f1(12)
// f2(9)
// f3(0) with value == 1 ether
// f4(0x8000000000000000000000000000000000000001)
// f5(101, 1, 102)
// f6(address(this), 2)
// f7(0, 0xbeced09521047d05b8960b7e7bcc1d1292cf3e4b2a6b63f48335cbde5f7545d2, 42)
// f8(42)
// f9({5000, 1, 0, 0, 0})
// => now use the oracle
pragma solidity ^0.7;
contract multi_man_complex_9 {
bool state1 = false;
bool state2 = false;
bool state3 = false;
bool state4 = false;
bool state5 = false;
bool state6 = false;
uint256 state7 = 0;
bool state8 = false;
bool final_state = false;
constructor() payable {}
function f1(uint256 x) public {
// force (useless) data dependency to the final state
require(!final_state);
// must only be called once
require(!state1);
// test simple equality constraint
require(x == 12);
state1 = true;
}
function f2(uint256 y) public {
require(state1 || state2);
// test simple inequality/range constraints
require(y > 8);
require(y < 24);
state2 = true;
}
function f3(uint256 z) public payable {
require(state2);
// must not be called after itself or f4
// again -> more complex data dependencies
require(!state3 && !state4);
// simple 0 constraint
require(z == 0);
// test setting constraints on call value
require(msg.value == 1 ether);
state3 = true;
}
function f4(uint256 a, uint256 b) public {
require(state3);
require(a < b);
// test whether the solver can also produce large integers within a
// certain range.
require(a > (1 << 159));
require(a < (1 << 200));
// flip the state4 variable if f4 is called after f5 or f7
state4 = true && !state5 && !state6;
}
function f5(
uint256 a,
uint256 b,
uint256 c
) public {
require(state4);
// test whether the solver can solve arithmetic constraints
// (without falling back to the trivial solution)
require(c != 0 && a != 0 && b != 0);
require((a + b) == c);
uint256 r = a * b;
require(r > 100);
require(r < 1000);
state5 = true;
}
function f6(address x, uint256 v) public payable {
// ERC contracts often have similar constraints in some kind of
// transfer functions
require(msg.sender == x);
require(v == 2 * msg.value);
state6 = true;
// we put the require at the end ¯\_(ツ)_/¯ maybe some solver don't
// like this
require(state5);
}
function f7(
uint256,
uint256 h,
uint32 s
) public {
require(state6);
// we essentially compute a random constant here. This tests whether
// the solver supports hashes in some way and whether it can solve
// equality constraints on computed constant values, e.g., taint
// tracking (or redqueen) will solve this.
uint256 C = uint256(keccak256(abi.encode(42)));
require(h == C);
state7 = s;
}
function f8(uint256 x) public {
require(state7 != 0);
// the same value must be provided as the "s" parameter in the f6
// transaction. For dataflow or symbolic analysis this means that
// metadata must be tracked accross transactions. For a fuzzer this
// should be rather easy to solve if the selection distribution for
// uints are strongly biased towards some values.
require(state7 == x);
state8 = true;
}
function f9(uint256[] calldata arr) public {
require(state8);
// require array to be within a certain range. This is quite typical in
// real world smart contracts as to avoid excessive gas costs.
require(arr.length >= 5);
require(arr.length <= 10);
uint256 sum = 0;
for (uint256 i = 0; i < arr.length; i++) {
sum += arr[i];
}
// require the sum to be rather large, but also small when compared
// to the bitwidth of the array members.
require(sum > 5000);
require(sum < 10000);
// state4 can be reset to false again, so we check that it is still
// true
require(state4);
// set the final state, which allow to trigger the bug oracle.
final_state = true;
}
// some artificial bug oracles that should be supported by many tools
function echidna_oracle() public view returns (bool) {
return (!final_state);
}
function f_assertion() public view returns (bool) {
assert(!final_state);
return final_state;
}
function finish() public {
require(final_state);
selfdestruct(msg.sender);
}
}