Skip to content

Commit dde3378

Browse files
authored
Merge pull request #2203 from vovikhangcdv/dev
update: improve unhandled initializers in unprotected-upgrade detector
2 parents 4a3a2f6 + 1f15416 commit dde3378

File tree

38 files changed

+256
-21
lines changed

38 files changed

+256
-21
lines changed

slither/core/declarations/contract.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1372,8 +1372,6 @@ def update_read_write_using_ssa(self) -> None:
13721372
def is_upgradeable(self) -> bool:
13731373
if self._is_upgradeable is None:
13741374
self._is_upgradeable = False
1375-
if self.is_upgradeable_proxy:
1376-
return False
13771375
initializable = self.file_scope.get_contract_from_name("Initializable")
13781376
if initializable:
13791377
if initializable in self.inheritance:

slither/detectors/statements/unprotected_upgradeable.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,14 @@ def _whitelisted_modifiers(f: Function) -> bool:
5252

5353
def _initialize_functions(contract: Contract) -> List[Function]:
5454
return list(
55-
filter(_whitelisted_modifiers, [f for f in contract.functions if f.name == "initialize"])
55+
filter(
56+
_whitelisted_modifiers,
57+
[
58+
f
59+
for f in contract.functions
60+
if any((m.name in ["initializer", "reinitializer"]) for m in f.modifiers)
61+
],
62+
)
5663
)
5764

5865

Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
AnyInitializer (tests/e2e/detectors/test_data/unprotected-upgrade/0.4.25/AnyInitializer.sol#3-15) is an upgradeable contract that does not protect its initialize functions: AnyInitializer.anyName() (tests/e2e/detectors/test_data/unprotected-upgrade/0.4.25/AnyInitializer.sol#6-9). Anyone can delete the contract with: AnyInitializer.kill() (tests/e2e/detectors/test_data/unprotected-upgrade/0.4.25/AnyInitializer.sol#11-14)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Reinitializer (tests/e2e/detectors/test_data/unprotected-upgrade/0.4.25/Reinitializer.sol#3-15) is an upgradeable contract that does not protect its initialize functions: Reinitializer.initialize() (tests/e2e/detectors/test_data/unprotected-upgrade/0.4.25/Reinitializer.sol#6-9). Anyone can delete the contract with: Reinitializer.kill() (tests/e2e/detectors/test_data/unprotected-upgrade/0.4.25/Reinitializer.sol#11-14)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
AnyInitializer (tests/e2e/detectors/test_data/unprotected-upgrade/0.5.16/AnyInitializer.sol#3-15) is an upgradeable contract that does not protect its initialize functions: AnyInitializer.anyName() (tests/e2e/detectors/test_data/unprotected-upgrade/0.5.16/AnyInitializer.sol#6-9). Anyone can delete the contract with: AnyInitializer.kill() (tests/e2e/detectors/test_data/unprotected-upgrade/0.5.16/AnyInitializer.sol#11-14)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Reinitializer (tests/e2e/detectors/test_data/unprotected-upgrade/0.5.16/Reinitializer.sol#3-15) is an upgradeable contract that does not protect its initialize functions: Reinitializer.initialize() (tests/e2e/detectors/test_data/unprotected-upgrade/0.5.16/Reinitializer.sol#6-9). Anyone can delete the contract with: Reinitializer.kill() (tests/e2e/detectors/test_data/unprotected-upgrade/0.5.16/Reinitializer.sol#11-14)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
AnyInitializer (tests/e2e/detectors/test_data/unprotected-upgrade/0.6.11/AnyInitializer.sol#3-15) is an upgradeable contract that does not protect its initialize functions: AnyInitializer.anyName() (tests/e2e/detectors/test_data/unprotected-upgrade/0.6.11/AnyInitializer.sol#6-9). Anyone can delete the contract with: AnyInitializer.kill() (tests/e2e/detectors/test_data/unprotected-upgrade/0.6.11/AnyInitializer.sol#11-14)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Reinitializer (tests/e2e/detectors/test_data/unprotected-upgrade/0.6.11/Reinitializer.sol#3-15) is an upgradeable contract that does not protect its initialize functions: Reinitializer.initialize() (tests/e2e/detectors/test_data/unprotected-upgrade/0.6.11/Reinitializer.sol#6-9). Anyone can delete the contract with: Reinitializer.kill() (tests/e2e/detectors/test_data/unprotected-upgrade/0.6.11/Reinitializer.sol#11-14)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
AnyInitializer (tests/e2e/detectors/test_data/unprotected-upgrade/0.7.6/AnyInitializer.sol#3-15) is an upgradeable contract that does not protect its initialize functions: AnyInitializer.anyName() (tests/e2e/detectors/test_data/unprotected-upgrade/0.7.6/AnyInitializer.sol#6-9). Anyone can delete the contract with: AnyInitializer.kill() (tests/e2e/detectors/test_data/unprotected-upgrade/0.7.6/AnyInitializer.sol#11-14)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Reinitializer (tests/e2e/detectors/test_data/unprotected-upgrade/0.7.6/Reinitializer.sol#3-15) is an upgradeable contract that does not protect its initialize functions: Reinitializer.initialize() (tests/e2e/detectors/test_data/unprotected-upgrade/0.7.6/Reinitializer.sol#6-9). Anyone can delete the contract with: Reinitializer.kill() (tests/e2e/detectors/test_data/unprotected-upgrade/0.7.6/Reinitializer.sol#11-14)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
AnyInitializer (tests/e2e/detectors/test_data/unprotected-upgrade/0.8.15/AnyInitializer.sol#3-15) is an upgradeable contract that does not protect its initialize functions: AnyInitializer.anyName() (tests/e2e/detectors/test_data/unprotected-upgrade/0.8.15/AnyInitializer.sol#6-9). Anyone can delete the contract with: AnyInitializer.kill() (tests/e2e/detectors/test_data/unprotected-upgrade/0.8.15/AnyInitializer.sol#11-14)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Reinitializer (tests/e2e/detectors/test_data/unprotected-upgrade/0.8.15/Reinitializer.sol#3-15) is an upgradeable contract that does not protect its initialize functions: Reinitializer.initialize() (tests/e2e/detectors/test_data/unprotected-upgrade/0.8.15/Reinitializer.sol#6-9). Anyone can delete the contract with: Reinitializer.kill() (tests/e2e/detectors/test_data/unprotected-upgrade/0.8.15/Reinitializer.sol#11-14)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import "./Initializable.sol";
2+
3+
contract AnyInitializer is Initializable {
4+
address owner;
5+
6+
function anyName() external initializer {
7+
require(owner == address(0));
8+
owner = msg.sender;
9+
}
10+
11+
function kill() external {
12+
require(msg.sender == owner);
13+
selfdestruct(owner);
14+
}
15+
}
Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1-
contract Initializable{
2-
modifier initializer() {
3-
_;
4-
}
5-
}
1+
contract Initializable {
2+
modifier initializer() {
3+
_;
4+
}
5+
6+
modifier reinitializer(uint64 version) {
7+
_;
8+
}
9+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import "./Initializable.sol";
2+
3+
contract Reinitializer is Initializable {
4+
address owner;
5+
6+
function initialize() external reinitializer(2) {
7+
require(owner == address(0));
8+
owner = msg.sender;
9+
}
10+
11+
function kill() external {
12+
require(msg.sender == owner);
13+
selfdestruct(owner);
14+
}
15+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import "./Initializable.sol";
2+
3+
contract AnyInitializer is Initializable {
4+
address payable owner;
5+
6+
function anyName() external initializer {
7+
require(owner == address(0));
8+
owner = msg.sender;
9+
}
10+
11+
function kill() external {
12+
require(msg.sender == owner);
13+
selfdestruct(owner);
14+
}
15+
}
Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1-
contract Initializable{
2-
modifier initializer() {
3-
_;
4-
}
5-
}
1+
contract Initializable {
2+
modifier initializer() {
3+
_;
4+
}
5+
6+
modifier reinitializer(uint64 version) {
7+
_;
8+
}
9+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import "./Initializable.sol";
2+
3+
contract Reinitializer is Initializable {
4+
address payable owner;
5+
6+
function initialize() external reinitializer(2) {
7+
require(owner == address(0));
8+
owner = msg.sender;
9+
}
10+
11+
function kill() external {
12+
require(msg.sender == owner);
13+
selfdestruct(owner);
14+
}
15+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import "./Initializable.sol";
2+
3+
contract AnyInitializer is Initializable {
4+
address payable owner;
5+
6+
function anyName() external initializer {
7+
require(owner == address(0));
8+
owner = payable(msg.sender);
9+
}
10+
11+
function kill() external {
12+
require(msg.sender == owner);
13+
selfdestruct(owner);
14+
}
15+
}
Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1-
contract Initializable{
2-
modifier initializer() {
3-
_;
4-
}
5-
}
1+
contract Initializable {
2+
modifier initializer() {
3+
_;
4+
}
5+
6+
modifier reinitializer(uint64 version) {
7+
_;
8+
}
9+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import "./Initializable.sol";
2+
3+
contract Reinitializer is Initializable {
4+
address payable owner;
5+
6+
function initialize() external reinitializer(2) {
7+
require(owner == address(0));
8+
owner = payable(msg.sender);
9+
}
10+
11+
function kill() external {
12+
require(msg.sender == owner);
13+
selfdestruct(owner);
14+
}
15+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import "./Initializable.sol";
2+
3+
contract AnyInitializer is Initializable {
4+
address payable owner;
5+
6+
function anyName() external initializer {
7+
require(owner == address(0));
8+
owner = payable(msg.sender);
9+
}
10+
11+
function kill() external {
12+
require(msg.sender == owner);
13+
selfdestruct(owner);
14+
}
15+
}
Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
1-
contract Initializable{
1+
contract Initializable {
22
uint8 private _initialized;
33
bool private _initializing;
44

55
modifier initializer() {
66
_;
77
}
88

9+
modifier reinitializer(uint64 version) {
10+
_;
11+
}
12+
913
function _disableInitializers() internal virtual {
1014
require(!_initializing, "Initializable: contract is initializing");
1115
if (_initialized < type(uint8).max) {
1216
_initialized = type(uint8).max;
1317
}
1418
}
15-
}
19+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import "./Initializable.sol";
2+
3+
contract Reinitializer is Initializable {
4+
address payable owner;
5+
6+
function initialize() external reinitializer(2) {
7+
require(owner == address(0));
8+
owner = payable(msg.sender);
9+
}
10+
11+
function kill() external {
12+
require(msg.sender == owner);
13+
selfdestruct(owner);
14+
}
15+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import "./Initializable.sol";
2+
3+
contract AnyInitializer is Initializable {
4+
address payable owner;
5+
6+
function anyName() external initializer {
7+
require(owner == address(0));
8+
owner = payable(msg.sender);
9+
}
10+
11+
function kill() external {
12+
require(msg.sender == owner);
13+
selfdestruct(owner);
14+
}
15+
}

tests/e2e/detectors/test_data/unprotected-upgrade/0.8.15/Initializable.sol

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,14 @@ contract Initializable {
66
_;
77
}
88

9+
modifier reinitializer(uint64 version) {
10+
_;
11+
}
12+
913
function _disableInitializers() internal virtual {
1014
require(!_initializing, "Initializable: contract is initializing");
1115
if (_initialized < type(uint8).max) {
1216
_initialized = type(uint8).max;
1317
}
1418
}
15-
}
19+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import "./Initializable.sol";
2+
3+
contract Reinitializer is Initializable {
4+
address payable owner;
5+
6+
function initialize() external reinitializer(2) {
7+
require(owner == address(0));
8+
owner = payable(msg.sender);
9+
}
10+
11+
function kill() external {
12+
require(msg.sender == owner);
13+
selfdestruct(owner);
14+
}
15+
}

tests/e2e/detectors/test_detectors.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -938,6 +938,16 @@ def id_test(test_item: Test):
938938
"whitelisted.sol",
939939
"0.4.25",
940940
),
941+
Test(
942+
all_detectors.UnprotectedUpgradeable,
943+
"Reinitializer.sol",
944+
"0.4.25",
945+
),
946+
Test(
947+
all_detectors.UnprotectedUpgradeable,
948+
"AnyInitializer.sol",
949+
"0.4.25",
950+
),
941951
Test(
942952
all_detectors.UnprotectedUpgradeable,
943953
"Buggy.sol",
@@ -953,6 +963,16 @@ def id_test(test_item: Test):
953963
"whitelisted.sol",
954964
"0.5.16",
955965
),
966+
Test(
967+
all_detectors.UnprotectedUpgradeable,
968+
"Reinitializer.sol",
969+
"0.5.16",
970+
),
971+
Test(
972+
all_detectors.UnprotectedUpgradeable,
973+
"AnyInitializer.sol",
974+
"0.5.16",
975+
),
956976
Test(
957977
all_detectors.UnprotectedUpgradeable,
958978
"Buggy.sol",
@@ -968,6 +988,16 @@ def id_test(test_item: Test):
968988
"whitelisted.sol",
969989
"0.6.11",
970990
),
991+
Test(
992+
all_detectors.UnprotectedUpgradeable,
993+
"Reinitializer.sol",
994+
"0.6.11",
995+
),
996+
Test(
997+
all_detectors.UnprotectedUpgradeable,
998+
"AnyInitializer.sol",
999+
"0.6.11",
1000+
),
9711001
Test(
9721002
all_detectors.UnprotectedUpgradeable,
9731003
"Buggy.sol",
@@ -978,6 +1008,16 @@ def id_test(test_item: Test):
9781008
"Fixed.sol",
9791009
"0.7.6",
9801010
),
1011+
Test(
1012+
all_detectors.UnprotectedUpgradeable,
1013+
"Reinitializer.sol",
1014+
"0.7.6",
1015+
),
1016+
Test(
1017+
all_detectors.UnprotectedUpgradeable,
1018+
"AnyInitializer.sol",
1019+
"0.7.6",
1020+
),
9811021
Test(
9821022
all_detectors.UnprotectedUpgradeable,
9831023
"whitelisted.sol",
@@ -998,6 +1038,16 @@ def id_test(test_item: Test):
9981038
"whitelisted.sol",
9991039
"0.8.15",
10001040
),
1041+
Test(
1042+
all_detectors.UnprotectedUpgradeable,
1043+
"Reinitializer.sol",
1044+
"0.8.15",
1045+
),
1046+
Test(
1047+
all_detectors.UnprotectedUpgradeable,
1048+
"AnyInitializer.sol",
1049+
"0.8.15",
1050+
),
10011051
Test(
10021052
all_detectors.ABIEncoderV2Array,
10031053
"storage_ABIEncoderV2_array.sol",

0 commit comments

Comments
 (0)