From 0abe7727a316254606bd9fa11f215315df59ec9c Mon Sep 17 00:00:00 2001 From: Jayakumar2812 Date: Wed, 12 Feb 2025 17:25:41 +0530 Subject: [PATCH 01/30] Fixes issue 2524, Slot Calculation for Variables that Cross 32-Byte Boundaries --- slither/tools/read_storage/read_storage.py | 2 +- .../read-storage/test_data/StorageLayout.bin | 2 +- .../read-storage/test_data/StorageLayout.sol | 6 ++++ .../test_data/TEST_storage_layout.json | 28 +++++++++++++++++++ 4 files changed, 36 insertions(+), 2 deletions(-) diff --git a/slither/tools/read_storage/read_storage.py b/slither/tools/read_storage/read_storage.py index 728636f2e4..9f9786726f 100644 --- a/slither/tools/read_storage/read_storage.py +++ b/slither/tools/read_storage/read_storage.py @@ -582,7 +582,7 @@ def _find_struct_var_slot( var_type = var.type if isinstance(var_type, ElementaryType): size = var_type.size - if offset >= 256: + if size > (256 - offset): slot += 1 offset = 0 if struct_var == var.name: diff --git a/tests/tools/read-storage/test_data/StorageLayout.bin b/tests/tools/read-storage/test_data/StorageLayout.bin index 11163c2f00..978c2eeb20 100644 --- a/tests/tools/read-storage/test_data/StorageLayout.bin +++ b/tests/tools/read-storage/test_data/StorageLayout.bin @@ -1 +1 @@ -608060405260016000806101000a8154817effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff02191690837effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16021790555060016000601f6101000a81548160ff02191690831515021790555060405180604001604052806000601f9054906101000a900460ff161515815260200160008054906101000a90047effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16815250600160008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a8154817effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff02191690837effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16021790555050506040518060400160405280601481526020017f736c69746865722d726561642d73746f7261676500000000000000000000000081525060079080519060200190620001b6929190620002b2565b5060088060006101000a81548160ff021916908360ff1602179055507f6161616161616161000000000000000000000000000000000000000000000000600860016101000a81548167ffffffffffffffff021916908360c01c02179055506000600860096101000a81548160ff021916908360028111156200023d576200023c62000362565b5b021790555060016008600a6101000a81548160ff021916908360028111156200026b576200026a62000362565b5b021790555060026008600b6101000a81548160ff0219169083600281111562000299576200029862000362565b5b0217905550348015620002ab57600080fd5b50620003f6565b828054620002c090620003c0565b90600052602060002090601f016020900481019282620002e4576000855562000330565b82601f10620002ff57805160ff191683800117855562000330565b8280016001018555821562000330579182015b828111156200032f57825182559160200191906001019062000312565b5b5090506200033f919062000343565b5090565b5b808211156200035e57600081600090555060010162000344565b5090565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680620003d957607f821691505b60208210811415620003f057620003ef62000391565b5b50919050565b610fd780620004066000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063975057e714610030575b600080fd5b61003861003a565b005b600073ffffffffffffffffffffffffffffffffffffffff16600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161461009557600080fd5b33600660006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506001600260008060009054906101000a90047effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000820160009054906101000a900460ff168160000160006101000a81548160ff0219169083151502179055506000820160019054906101000a90047effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff168160000160016101000a8154817effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff02191690837effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff160217905550905050600160036000600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008060009054906101000a90047effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000820160009054906101000a900460ff168160000160006101000a81548160ff0219169083151502179055506000820160019054906101000a90047effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff168160000160016101000a8154817effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff02191690837effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff160217905550905050600160046000600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006001815260200190815260200160002060006101000a81548160ff021916908315150217905550600160046000600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006002815260200190815260200160002060006101000a81548160ff0219169083151502179055506040518060600160405280600160ff168152602001600260ff168152602001600360ff1681525060099060036104b8929190610ddd565b50600c600990806001815401808255809150506001900390600052602060002090600302016000909190919091509060036104f4929190610e22565b50600c6040518060600160405280600460ff168152602001600560ff168152602001600660ff168152509080600181540180825580915050600190039060005260206000209060030201600090919091909150906003610555929190610ddd565b50600d60006003811061056b5761056a610f72565b5b0160079080600181540180825580915050600190039060005260206000200160009091909190915055600d6001600381106105a9576105a8610f72565b5b0160089080600181540180825580915050600190039060005260206000200160009091909190915055600d6001600381106105e7576105e6610f72565b5b0160099080600181540180825580915050600190039060005260206000200160009091909190915055600d60026003811061062557610624610f72565b5b01600a9080600181540180825580915050600190039060005260206000200160009091909190915055600d60026003811061066357610662610f72565b5b01600b9080600181540180825580915050600190039060005260206000200160009091909190915055600d6002600381106106a1576106a0610f72565b5b01600c908060018154018082558091505060019003906000526020600020016000909190919091505560106040518060200160405280600d60ff1681525090806001815401808255809150506001900390600052602060002001600090919091909150906001610712929190610e5f565b5060106040518060400160405280600e60ff168152602001600f60ff1681525090806001815401808255809150506001900390600052602060002001600090919091909150906002610765929190610eb1565b5060106040518060600160405280601060ff168152602001601160ff168152602001601260ff16815250908060018154018082558091505060019003906000526020600020016000909190919091509060036107c2929190610f03565b5060116001908060018154018082558091505060019003906000526020600020016000909190919091506000820160009054906101000a900460ff168160000160006101000a81548160ff0219169083151502179055506000820160019054906101000a90047effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff168160000160016101000a8154817effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff02191690837effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff160217905550505060116040518060400160405280600015158152602001600a7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff168152509080600181540180825580915050600190039060005260206000200160009091909190915060008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a8154817effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff02191690837effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16021790555050506001601260006003811061099a57610999610f72565b5b016000820160009054906101000a900460ff168160000160006101000a81548160ff0219169083151502179055506000820160019054906101000a90047effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff168160000160016101000a8154817effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff02191690837effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1602179055509050506040518060400160405280600015158152602001600a7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff168152506012600160038110610aa257610aa1610f72565b5b0160008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a8154817effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff02191690837effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16021790555090505060056000600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206011600081548110610b9957610b98610f72565b5b90600052602060002001908060018154018082558091505060019003906000526020600020016000909190919091506000820160009054906101000a900460ff168160000160006101000a81548160ff0219169083151502179055506000820160019054906101000a90047effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff168160000160016101000a8154817effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff02191690837effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff160217905550505060056000600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206011600181548110610cf557610cf4610f72565b5b90600052602060002001908060018154018082558091505060019003906000526020600020016000909190919091506000820160009054906101000a900460ff168160000160006101000a81548160ff0219169083151502179055506000820160019054906101000a90047effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff168160000160016101000a8154817effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff02191690837effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1602179055505050565b8260038101928215610e11579160200282015b82811115610e10578251829060ff16905591602001919060010190610df0565b5b509050610e1e9190610f55565b5090565b8260038101928215610e4e579182015b82811115610e4d578254825591600101919060010190610e32565b5b509050610e5b9190610f55565b5090565b828054828255906000526020600020908101928215610ea0579160200282015b82811115610e9f578251829060ff16905591602001919060010190610e7f565b5b509050610ead9190610f55565b5090565b828054828255906000526020600020908101928215610ef2579160200282015b82811115610ef1578251829060ff16905591602001919060010190610ed1565b5b509050610eff9190610f55565b5090565b828054828255906000526020600020908101928215610f44579160200282015b82811115610f43578251829060ff16905591602001919060010190610f23565b5b509050610f519190610f55565b5090565b5b80821115610f6e576000816000905550600101610f56565b5090565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fdfea26469706673582212201cafaf685feba709fe5b26911b38f3251b1d43d43cec2d0d839fb6ff480488d164736f6c634300080a0033 \ No newline at end of file +608060405260016000806101000a8154817effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff02191690837effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16021790555060016000601f6101000a81548160ff02191690831515021790555060405180604001604052806000601f9054906101000a900460ff161515815260200160008054906101000a90047effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16815250600160008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a8154817effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff02191690837effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16021790555050506040518060400160405280601481526020017f736c69746865722d726561642d73746f7261676500000000000000000000000081525060079080519060200190620001b6929190620002b2565b5060088060006101000a81548160ff021916908360ff1602179055507f6161616161616161000000000000000000000000000000000000000000000000600860016101000a81548167ffffffffffffffff021916908360c01c02179055506000600860096101000a81548160ff021916908360028111156200023d576200023c62000362565b5b021790555060016008600a6101000a81548160ff021916908360028111156200026b576200026a62000362565b5b021790555060026008600b6101000a81548160ff0219169083600281111562000299576200029862000362565b5b0217905550348015620002ab57600080fd5b50620003f6565b828054620002c090620003c0565b90600052602060002090601f016020900481019282620002e4576000855562000330565b82601f10620002ff57805160ff191683800117855562000330565b8280016001018555821562000330579182015b828111156200032f57825182559160200191906001019062000312565b5b5090506200033f919062000343565b5090565b5b808211156200035e57600081600090555060010162000344565b5090565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680620003d957607f821691505b60208210811415620003f057620003ef62000391565b5b50919050565b61103680620004066000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063975057e714610030575b600080fd5b61003861003a565b005b600073ffffffffffffffffffffffffffffffffffffffff16600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161461009557600080fd5b33600660006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506001600260008060009054906101000a90047effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000820160009054906101000a900460ff168160000160006101000a81548160ff0219169083151502179055506000820160019054906101000a90047effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff168160000160016101000a8154817effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff02191690837effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff160217905550905050600160036000600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008060009054906101000a90047effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000820160009054906101000a900460ff168160000160006101000a81548160ff0219169083151502179055506000820160019054906101000a90047effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff168160000160016101000a8154817effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff02191690837effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff160217905550905050600160046000600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006001815260200190815260200160002060006101000a81548160ff021916908315150217905550600160046000600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006002815260200190815260200160002060006101000a81548160ff0219169083151502179055506040518060600160405280600160ff168152602001600260ff168152602001600360ff1681525060099060036104b8929190610e3c565b50600c600990806001815401808255809150506001900390600052602060002090600302016000909190919091509060036104f4929190610e81565b50600c6040518060600160405280600460ff168152602001600560ff168152602001600660ff168152509080600181540180825580915050600190039060005260206000209060030201600090919091909150906003610555929190610e3c565b50600d60006003811061056b5761056a610fd1565b5b0160079080600181540180825580915050600190039060005260206000200160009091909190915055600d6001600381106105a9576105a8610fd1565b5b0160089080600181540180825580915050600190039060005260206000200160009091909190915055600d6001600381106105e7576105e6610fd1565b5b0160099080600181540180825580915050600190039060005260206000200160009091909190915055600d60026003811061062557610624610fd1565b5b01600a9080600181540180825580915050600190039060005260206000200160009091909190915055600d60026003811061066357610662610fd1565b5b01600b9080600181540180825580915050600190039060005260206000200160009091909190915055600d6002600381106106a1576106a0610fd1565b5b01600c908060018154018082558091505060019003906000526020600020016000909190919091505560106040518060200160405280600d60ff1681525090806001815401808255809150506001900390600052602060002001600090919091909150906001610712929190610ebe565b5060106040518060400160405280600e60ff168152602001600f60ff1681525090806001815401808255809150506001900390600052602060002001600090919091909150906002610765929190610f10565b5060106040518060600160405280601060ff168152602001601160ff168152602001601260ff16815250908060018154018082558091505060019003906000526020600020016000909190919091509060036107c2929190610f62565b5060116001908060018154018082558091505060019003906000526020600020016000909190919091506000820160009054906101000a900460ff168160000160006101000a81548160ff0219169083151502179055506000820160019054906101000a90047effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff168160000160016101000a8154817effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff02191690837effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff160217905550505060116040518060400160405280600015158152602001600a7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff168152509080600181540180825580915050600190039060005260206000200160009091909190915060008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a8154817effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff02191690837effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16021790555050506001601260006003811061099a57610999610fd1565b5b016000820160009054906101000a900460ff168160000160006101000a81548160ff0219169083151502179055506000820160019054906101000a90047effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff168160000160016101000a8154817effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff02191690837effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1602179055509050506040518060400160405280600015158152602001600a7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff168152506012600160038110610aa257610aa1610fd1565b5b0160008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a8154817effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff02191690837effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16021790555090505060056000600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206011600081548110610b9957610b98610fd1565b5b90600052602060002001908060018154018082558091505060019003906000526020600020016000909190919091506000820160009054906101000a900460ff168160000160006101000a81548160ff0219169083151502179055506000820160019054906101000a90047effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff168160000160016101000a8154817effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff02191690837effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff160217905550505060056000600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206011600181548110610cf557610cf4610fd1565b5b90600052602060002001908060018154018082558091505060019003906000526020600020016000909190919091506000820160009054906101000a900460ff168160000160006101000a81548160ff0219169083151502179055506000820160019054906101000a90047effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff168160000160016101000a8154817effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff02191690837effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16021790555050506040518060400160405280600a67ffffffffffffffff1681526020016064815250601560008201518160000160006101000a81548167ffffffffffffffff021916908367ffffffffffffffff16021790555060208201518160010155905050565b8260038101928215610e70579160200282015b82811115610e6f578251829060ff16905591602001919060010190610e4f565b5b509050610e7d9190610fb4565b5090565b8260038101928215610ead579182015b82811115610eac578254825591600101919060010190610e91565b5b509050610eba9190610fb4565b5090565b828054828255906000526020600020908101928215610eff579160200282015b82811115610efe578251829060ff16905591602001919060010190610ede565b5b509050610f0c9190610fb4565b5090565b828054828255906000526020600020908101928215610f51579160200282015b82811115610f50578251829060ff16905591602001919060010190610f30565b5b509050610f5e9190610fb4565b5090565b828054828255906000526020600020908101928215610fa3579160200282015b82811115610fa2578251829060ff16905591602001919060010190610f82565b5b509050610fb09190610fb4565b5090565b5b80821115610fcd576000816000905550600101610fb5565b5090565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fdfea264697066735822122024ecc82448debfc839e8b1fae417ed0ba914db697e4fd1f8c5ee852149de083664736f6c634300080a0033 \ No newline at end of file diff --git a/tests/tools/read-storage/test_data/StorageLayout.sol b/tests/tools/read-storage/test_data/StorageLayout.sol index 0940b67692..96ddf6ce2e 100644 --- a/tests/tools/read-storage/test_data/StorageLayout.sol +++ b/tests/tools/read-storage/test_data/StorageLayout.sol @@ -36,6 +36,11 @@ contract StorageLayout { uint[][] multidimensionalArray; PackedStruct[] dynamicArrayOfStructs; PackedStruct[3] fixedArrayOfStructs; + struct BiggerStruct { + uint64 x; + uint256 y; + } + BiggerStruct big; function store() external { require(_address == address(0)); @@ -71,5 +76,6 @@ contract StorageLayout { mappingDynamicArrayOfStructs[_address].push(dynamicArrayOfStructs[0]); mappingDynamicArrayOfStructs[_address].push(dynamicArrayOfStructs[1]); + big = BiggerStruct(10,100); } } diff --git a/tests/tools/read-storage/test_data/TEST_storage_layout.json b/tests/tools/read-storage/test_data/TEST_storage_layout.json index 9de3aacfd1..50d5ffccbc 100644 --- a/tests/tools/read-storage/test_data/TEST_storage_layout.json +++ b/tests/tools/read-storage/test_data/TEST_storage_layout.json @@ -572,5 +572,33 @@ } } } + }, + "big": { + "name": "big", + "type_string": "StorageLayout.BiggerStruct", + "slot": 21, + "size": 512, + "offset": 0, + "value": "000000000000000000000000000000000000000000000000000000000000000a", + "elems": { + "x": { + "name": "big.x", + "type_string": "uint64", + "slot": 21, + "size": 64, + "offset": 0, + "value": 10, + "elems": {} + }, + "y": { + "name": "big.y", + "type_string": "uint256", + "slot": 22, + "size": 256, + "offset": 0, + "value": 100, + "elems": {} + } } } +} From 46f3caf8060a460c9e13d36c2b02f74f52dc333c Mon Sep 17 00:00:00 2001 From: Simone Date: Mon, 17 Feb 2025 12:51:50 +0100 Subject: [PATCH 02/30] Initialize size to 0 --- slither/tools/read_storage/read_storage.py | 1 + 1 file changed, 1 insertion(+) diff --git a/slither/tools/read_storage/read_storage.py b/slither/tools/read_storage/read_storage.py index 728636f2e4..06521e6565 100644 --- a/slither/tools/read_storage/read_storage.py +++ b/slither/tools/read_storage/read_storage.py @@ -578,6 +578,7 @@ def _find_struct_var_slot( slot = int.from_bytes(slot_as_bytes, "big") offset = 0 type_to = "" + size = 0 for var in elems: var_type = var.type if isinstance(var_type, ElementaryType): From b59cf81c9e038d54482fcd6b0b8e4c74fd08d92a Mon Sep 17 00:00:00 2001 From: Feist Josselin Date: Thu, 20 Feb 2025 11:57:31 +0100 Subject: [PATCH 03/30] Improve the support for sstore/sload with simple slot access During the IR generation, convert the sstore/sload into direct IR assignement when we have a state_variable.slot direct access This allow the SSA to be generated correctly, and for the var read/written analyses to use the right variables. Fix #2642 #2491 #2470 #2160. Replace #2669 Revert #2329 --- slither/core/cfg/node.py | 16 ---- .../visitors/slithir/expression_to_slithir.py | 19 +++++ ...ouldBeConstant_0_8_0_unused_yul_sol__0.txt | 0 .../constable-states/0.8.0/unused_yul.sol | 60 ++++++++++++++ .../0.8.0/unused_yul.sol-0.8.0.zip | Bin 0 -> 4446 bytes tests/e2e/detectors/test_detectors.py | 5 ++ tests/e2e/solc_parsing/test_ast_parsing.py | 1 + .../compile/yul-solady.sol-0.8.27-compact.zip | Bin 0 -> 5144 bytes .../yul-solady.sol-0.8.27-compact.json | 9 +++ .../e2e/solc_parsing/test_data/yul-solady.sol | 75 ++++++++++++++++++ .../test_data/assembly_sstore_sload.sol | 29 +++++++ .../slithir/test_yul_parser_assembly_slot.py | 26 ++++++ 12 files changed, 224 insertions(+), 16 deletions(-) create mode 100644 tests/e2e/detectors/snapshots/detectors__detector_CouldBeConstant_0_8_0_unused_yul_sol__0.txt create mode 100644 tests/e2e/detectors/test_data/constable-states/0.8.0/unused_yul.sol create mode 100644 tests/e2e/detectors/test_data/constable-states/0.8.0/unused_yul.sol-0.8.0.zip create mode 100644 tests/e2e/solc_parsing/test_data/compile/yul-solady.sol-0.8.27-compact.zip create mode 100644 tests/e2e/solc_parsing/test_data/expected/yul-solady.sol-0.8.27-compact.json create mode 100644 tests/e2e/solc_parsing/test_data/yul-solady.sol create mode 100644 tests/unit/slithir/test_data/assembly_sstore_sload.sol diff --git a/slither/core/cfg/node.py b/slither/core/cfg/node.py index fc178db4a7..83c896a42e 100644 --- a/slither/core/cfg/node.py +++ b/slither/core/cfg/node.py @@ -11,7 +11,6 @@ SolidityFunction, ) from slither.core.expressions.expression import Expression -from slither.core.expressions import CallExpression, Identifier, AssignmentOperation from slither.core.solidity_types import ElementaryType from slither.core.source_mapping.source_mapping import SourceMapping from slither.core.variables.local_variable import LocalVariable @@ -889,21 +888,6 @@ def _find_read_write_call(self) -> None: # pylint: disable=too-many-statements # TODO: consider removing dependancy of solidity_call to internal_call self._solidity_calls.append(ir) self._internal_calls.append(ir) - if ( - isinstance(ir, SolidityCall) - and ir.function == SolidityFunction("sstore(uint256,uint256)") - and isinstance(ir.node.expression, CallExpression) - and isinstance(ir.node.expression.arguments[0], Identifier) - ): - self._vars_written.append(ir.arguments[0]) - if ( - isinstance(ir, SolidityCall) - and ir.function == SolidityFunction("sload(uint256)") - and isinstance(ir.node.expression, AssignmentOperation) - and isinstance(ir.node.expression.expression_right, CallExpression) - and isinstance(ir.node.expression.expression_right.arguments[0], Identifier) - ): - self._vars_read.append(ir.arguments[0]) if isinstance(ir, LowLevelCall): assert isinstance(ir.destination, (Variable, SolidityVariable)) self._low_level_calls.append(ir) diff --git a/slither/visitors/slithir/expression_to_slithir.py b/slither/visitors/slithir/expression_to_slithir.py index 1d68336bd6..2a132339cb 100644 --- a/slither/visitors/slithir/expression_to_slithir.py +++ b/slither/visitors/slithir/expression_to_slithir.py @@ -406,6 +406,25 @@ def _post_call_expression(self, expression: CallExpression) -> None: self._result.append(var) set_val(expression, val) + elif (called.name == "sload(uint256)" or called.name == "sstore(uint256,uint256)") and (len(args)>0 and isinstance(args[0], StateVariable)): + # parse_yul._parse_yul_magic_suffixes does a best effort tentative to retrieve + # the right state variable on .slot access + # + # Solidity does not allow state variable to be directly used through sstore/sload + # As you need to specify the slot number (ex you can't do " sload(some_state_variable)") + # + # So we can make the assumption that if a state variable appear on the first argument + # of sstore/sload, we can convert the call to sstore to a normal assignment / read + + if called.name == "sload(uint256)": + val = TemporaryVariable(self._node) + var = Assignment(val, args[0], ElementaryType("uint256")) + self._result.append(var) + set_val(expression, val) + else: + var = Assignment(args[0], args[1], ElementaryType("uint256")) + self._result.append(var) + set_val(expression, args[0]) else: # If tuple if expression.type_call.startswith("tuple(") and expression.type_call != "tuple()": diff --git a/tests/e2e/detectors/snapshots/detectors__detector_CouldBeConstant_0_8_0_unused_yul_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_CouldBeConstant_0_8_0_unused_yul_sol__0.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/e2e/detectors/test_data/constable-states/0.8.0/unused_yul.sol b/tests/e2e/detectors/test_data/constable-states/0.8.0/unused_yul.sol new file mode 100644 index 0000000000..6eb4ca98cb --- /dev/null +++ b/tests/e2e/detectors/test_data/constable-states/0.8.0/unused_yul.sol @@ -0,0 +1,60 @@ +// adapted from https://github.com/crytic/slither/issues/2325 + +contract L1Block { + + address internal constant cDEPOSITOR_ACCOUNT = 0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001; + + /// @notice Address of the special depositor account. + function DEPOSITOR_ACCOUNT() public pure returns (address addr_) { + addr_ = cDEPOSITOR_ACCOUNT; + } + + /// @notice The latest L1 block number known by the L2 system. + uint64 public number; + + /// @notice The latest L1 base fee. + uint256 public basefee; + + /// @notice The latest L1 blockhash. + bytes32 public hash; + + /// @notice The number of L2 blocks in the same epoch. + uint64 public sequenceNumber; + + /// @notice The versioned hash to authenticate the batcher by. + bytes32 public batcherHash; + + /// @notice The latest L1 blob base fee. + uint256 public blobBaseFee; + + /// @notice Updates the L1 block values for an Ecotone upgraded chain. + /// Params are packed and passed in as raw msg.data instead of ABI to reduce calldata size. + /// Params are expected to be in the following order: + /// 1. _baseFeeScalar L1 base fee scalar + /// 2. _blobBaseFeeScalar L1 blob base fee scalar + /// 3. _sequenceNumber Number of L2 blocks since epoch start. + /// 4. _timestamp L1 timestamp. + /// 5. _number L1 blocknumber. + /// 6. _basefee L1 base fee. + /// 7. _blobBaseFee L1 blob base fee. + /// 8. _hash L1 blockhash. + /// 9. _batcherHash Versioned hash to authenticate batcher by. + function _setL1BlockValuesEcotone() internal { + address depositor = DEPOSITOR_ACCOUNT(); + assembly { + // Revert if the caller is not the depositor account. + if xor(caller(), depositor) { + mstore(0x00, 0x3cc50b45) // 0x3cc50b45 is the 4-byte selector of "NotDepositor()" + revert(0x1C, 0x04) // returns the stored 4-byte selector from above + } + // sequencenum (uint64), blobBaseFeeScalar (uint32), baseFeeScalar (uint32) + sstore(sequenceNumber.slot, shr(128, calldataload(4))) + // number (uint64) and timestamp (uint64) + sstore(number.slot, shr(128, calldataload(20))) + sstore(basefee.slot, calldataload(36)) // uint256 + sstore(blobBaseFee.slot, calldataload(68)) // uint256 + sstore(hash.slot, calldataload(100)) // bytes32 + sstore(batcherHash.slot, calldataload(132)) // bytes32 + } + } +} \ No newline at end of file diff --git a/tests/e2e/detectors/test_data/constable-states/0.8.0/unused_yul.sol-0.8.0.zip b/tests/e2e/detectors/test_data/constable-states/0.8.0/unused_yul.sol-0.8.0.zip new file mode 100644 index 0000000000000000000000000000000000000000..c3d44f0c11836b859f2d8a80c44597060ada0312 GIT binary patch literal 4446 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 List[str]: Test("scope/inherited_function_scope.sol", ["0.8.24"]), Test("using_for_global_user_defined_operator_1.sol", ["0.8.24"]), Test("require-error.sol", ["0.8.27"]), + Test("yul-solady.sol", ["0.8.27"]), ] # create the output folder if needed try: diff --git a/tests/e2e/solc_parsing/test_data/compile/yul-solady.sol-0.8.27-compact.zip b/tests/e2e/solc_parsing/test_data/compile/yul-solady.sol-0.8.27-compact.zip new file mode 100644 index 0000000000000000000000000000000000000000..849144170f733ca1e6f723187c16bd5278aac3db GIT binary patch literal 5144 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\n1[label=\"Node Type: INLINE ASM 1\n\"];\n1->2;\n2[label=\"Node Type: NEW VARIABLE 2\n\"];\n2->3;\n3[label=\"Node Type: EXPRESSION 3\n\"];\n3->4;\n4[label=\"Node Type: BEGIN_LOOP 4\n\"];\n4->6;\n5[label=\"Node Type: END_LOOP 5\n\"];\n5->25;\n6[label=\"Node Type: NEW VARIABLE 6\n\"];\n6->7;\n7[label=\"Node Type: EXPRESSION 7\n\"];\n7->8;\n8[label=\"Node Type: IF_LOOP 8\n\"];\n8->5[label=\"True\"];\n8->9[label=\"False\"];\n9[label=\"Node Type: IF 9\n\"];\n9->11[label=\"True\"];\n9->10[label=\"False\"];\n10[label=\"Node Type: END_IF 10\n\"];\n10->23;\n11[label=\"Node Type: IF 11\n\"];\n11->13[label=\"True\"];\n11->12[label=\"False\"];\n12[label=\"Node Type: END_IF 12\n\"];\n12->15;\n13[label=\"Node Type: EXPRESSION 13\n\"];\n13->14;\n14[label=\"Node Type: EXPRESSION 14\n\"];\n14->12;\n15[label=\"Node Type: EXPRESSION 15\n\"];\n15->16;\n16[label=\"Node Type: IF 16\n\"];\n16->18[label=\"True\"];\n16->17[label=\"False\"];\n17[label=\"Node Type: END_IF 17\n\"];\n17->20;\n18[label=\"Node Type: EXPRESSION 18\n\"];\n18->19;\n19[label=\"Node Type: BREAK 19\n\"];\n19->17;\n20[label=\"Node Type: EXPRESSION 20\n\"];\n20->21;\n21[label=\"Node Type: EXPRESSION 21\n\"];\n21->22;\n22[label=\"Node Type: BREAK 22\n\"];\n22->10;\n23[label=\"Node Type: NEW VARIABLE 23\n\"];\n23->24;\n24[label=\"Node Type: EXPRESSION 24\n\"];\n24->8;\n25[label=\"Node Type: END INLINE ASM 25\n\"];\n25->26;\n26[label=\"Node Type: RETURN 26\n\"];\n}\n" + }, + "Initializable": { + "_initializableSlot()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n", + "initializer()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: NEW VARIABLE 1\n\"];\n1->2;\n2[label=\"Node Type: INLINE ASM 2\n\"];\n2->3;\n3[label=\"Node Type: NEW VARIABLE 3\n\"];\n3->4;\n4[label=\"Node Type: EXPRESSION 4\n\"];\n4->5;\n5[label=\"Node Type: EXPRESSION 5\n\"];\n5->6;\n6[label=\"Node Type: IF 6\n\"];\n6->8[label=\"True\"];\n6->7[label=\"False\"];\n7[label=\"Node Type: END_IF 7\n\"];\n7->13;\n8[label=\"Node Type: IF 8\n\"];\n8->10[label=\"True\"];\n8->9[label=\"False\"];\n9[label=\"Node Type: END_IF 9\n\"];\n9->12;\n10[label=\"Node Type: EXPRESSION 10\n\"];\n10->11;\n11[label=\"Node Type: EXPRESSION 11\n\"];\n11->9;\n12[label=\"Node Type: EXPRESSION 12\n\"];\n12->7;\n13[label=\"Node Type: END INLINE ASM 13\n\"];\n13->14;\n14[label=\"Node Type: _ 14\n\"];\n14->15;\n15[label=\"Node Type: INLINE ASM 15\n\"];\n15->16;\n16[label=\"Node Type: IF 16\n\"];\n16->18[label=\"True\"];\n16->17[label=\"False\"];\n17[label=\"Node Type: END_IF 17\n\"];\n17->21;\n18[label=\"Node Type: EXPRESSION 18\n\"];\n18->19;\n19[label=\"Node Type: EXPRESSION 19\n\"];\n19->20;\n20[label=\"Node Type: EXPRESSION 20\n\"];\n20->17;\n21[label=\"Node Type: END INLINE ASM 21\n\"];\n}\n" + } +} \ No newline at end of file diff --git a/tests/e2e/solc_parsing/test_data/yul-solady.sol b/tests/e2e/solc_parsing/test_data/yul-solady.sol new file mode 100644 index 0000000000..b82dddcf45 --- /dev/null +++ b/tests/e2e/solc_parsing/test_data/yul-solady.sol @@ -0,0 +1,75 @@ +contract C { + // Snippet of the _checkpointPushDiff function that was making slither crashes + // https://github.com/Vectorized/solady/blob/9298d096feb87de9a8873a704ff98f6892064c65/src/tokens/ERC20Votes.sol#L339-L361 + function _checkpointPushDiff(uint256 lengthSlot, uint256 key, uint256 amount, bool isAdd) + private + returns(uint256 newValue) + { + /// @solidity memory-safe-assembly + assembly { + let lengthSlotPacked := sload(lengthSlot) + for { let n := shr(208, shl(160, lengthSlotPacked)) } 1 {} { + if iszero(n) { + if iszero(or(isAdd, iszero(amount))) { + mstore(0x00, 0x5915f686) // `ERC5805CheckpointValueUnderflow()`. + revert(0x1c, 0x04) + } + newValue := amount + if iszero(or(eq(newValue, address()), shr(160, newValue))) { + sstore(lengthSlot, or(or(key, shl(48, 1)), shl(96, newValue))) + break + } + sstore(lengthSlot, or(or(key, shl(48, 1)), shl(96, address()))) + sstore(not(lengthSlot), newValue) + break + } + let checkpointSlot := add(sub(n, 1), lengthSlot) + } + } + } +} + + +// Snippet of the Initializable contract that was making slither crashes +// https://github.com/Vectorized/solady/blob/9298d096feb87de9a8873a704ff98f6892064c65/src/utils/Initializable.sol#L7 +contract Initializable { + bytes32 private constant _INTIALIZED_EVENT_SIGNATURE = + 0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2; + bytes32 private constant _INITIALIZABLE_SLOT = + 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffbf601132; + + + function _initializableSlot() internal pure virtual returns (bytes32) { + return _INITIALIZABLE_SLOT; + } + + modifier initializer() virtual { + bytes32 s = _initializableSlot(); + /// @solidity memory-safe-assembly + assembly { + let i := sload(s) + // Set `initializing` to 1, `initializedVersion` to 1. + sstore(s, 3) + // If `!(initializing == 0 && initializedVersion == 0)`. + if i { + // If `!(address(this).code.length == 0 && initializedVersion == 1)`. + if iszero(lt(extcodesize(address()), eq(shr(1, i), 1))) { + mstore(0x00, 0xf92ee8a9) // `InvalidInitialization()`. + revert(0x1c, 0x04) + } + s := shl(shl(255, i), s) // Skip initializing if `initializing == 1`. + } + } + _; + /// @solidity memory-safe-assembly + assembly { + if s { + // Set `initializing` to 0, `initializedVersion` to 1. + sstore(s, 2) + // Emit the {Initialized} event. + mstore(0x20, 1) + log1(0x20, 0x20, _INTIALIZED_EVENT_SIGNATURE) + } + } + } +} \ No newline at end of file diff --git a/tests/unit/slithir/test_data/assembly_sstore_sload.sol b/tests/unit/slithir/test_data/assembly_sstore_sload.sol new file mode 100644 index 0000000000..bf32d1f9da --- /dev/null +++ b/tests/unit/slithir/test_data/assembly_sstore_sload.sol @@ -0,0 +1,29 @@ +contract Test{ + + uint variable; + + function read() internal { + assembly { + let read_value := sload(variable.slot) + } + } + + function read_parameter(uint slot) internal { + assembly { + let read_value := sload(slot) + } + } + + function write() internal { + assembly { + sstore(variable.slot, 1) + } + } + + function write_parameter(uint slot) internal { + assembly { + sstore(slot, 1) + } + } + +} \ No newline at end of file diff --git a/tests/unit/slithir/test_yul_parser_assembly_slot.py b/tests/unit/slithir/test_yul_parser_assembly_slot.py index da800b55e0..a2500288fd 100644 --- a/tests/unit/slithir/test_yul_parser_assembly_slot.py +++ b/tests/unit/slithir/test_yul_parser_assembly_slot.py @@ -38,3 +38,29 @@ def test_yul_parser_assembly_slot(solc_binary_path) -> None: assert isinstance(value.value, StateVariable) elif value.value.name == "bucketId": assert isinstance(value.value, LocalVariable) + +def test_yul_parser_assembly_slot(solc_binary_path) -> None: + # mstore(0x0, bucketId) + # mstore(0x20, _counters.slot) + data = {"0x0": "bucketId", "0x20": "_counters"} + + solc_path = solc_binary_path("0.8.18") + slither = Slither(Path(TEST_DATA_DIR, "assembly_sstore_sload.sol").as_posix(), solc=solc_path) + + contract = slither.get_contract_from_name("Test")[0] + + read = contract.get_function_from_full_name("read()") + # Sload is converted to an assignement + assert len(read.all_solidity_calls()) == 0 + + read_parameter = contract.get_function_from_full_name("read_parameter(uint256)") + # Sload is kept because the slot is dynamic + assert len(read_parameter.all_solidity_calls()) == 1 + + write = contract.get_function_from_full_name("write()") + # Sstore is converted to an assignement + assert len(write.all_solidity_calls()) == 0 + + write_parameter = contract.get_function_from_full_name("write_parameter(uint256)") + # Sstore is kept because the slot is dynamic + assert len(write_parameter.all_solidity_calls()) == 1 From 78eefb116afd819d636bdb86c974ef3fc8f3a789 Mon Sep 17 00:00:00 2001 From: Josselin Feist Date: Thu, 20 Feb 2025 12:03:56 +0100 Subject: [PATCH 04/30] Update slither/visitors/slithir/expression_to_slithir.py Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- slither/visitors/slithir/expression_to_slithir.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/slither/visitors/slithir/expression_to_slithir.py b/slither/visitors/slithir/expression_to_slithir.py index 2a132339cb..67f591c9f8 100644 --- a/slither/visitors/slithir/expression_to_slithir.py +++ b/slither/visitors/slithir/expression_to_slithir.py @@ -406,7 +406,9 @@ def _post_call_expression(self, expression: CallExpression) -> None: self._result.append(var) set_val(expression, val) - elif (called.name == "sload(uint256)" or called.name == "sstore(uint256,uint256)") and (len(args)>0 and isinstance(args[0], StateVariable)): + elif (called.name == "sload(uint256)" or called.name == "sstore(uint256,uint256)") and ( + len(args) > 0 and isinstance(args[0], StateVariable) + ): # parse_yul._parse_yul_magic_suffixes does a best effort tentative to retrieve # the right state variable on .slot access # From 33b4bbaa394e773b958332dfa18e92a0e582af32 Mon Sep 17 00:00:00 2001 From: Josselin Feist Date: Thu, 20 Feb 2025 12:04:03 +0100 Subject: [PATCH 05/30] Update tests/unit/slithir/test_yul_parser_assembly_slot.py Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- tests/unit/slithir/test_yul_parser_assembly_slot.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/unit/slithir/test_yul_parser_assembly_slot.py b/tests/unit/slithir/test_yul_parser_assembly_slot.py index a2500288fd..56f6a5c6de 100644 --- a/tests/unit/slithir/test_yul_parser_assembly_slot.py +++ b/tests/unit/slithir/test_yul_parser_assembly_slot.py @@ -39,6 +39,7 @@ def test_yul_parser_assembly_slot(solc_binary_path) -> None: elif value.value.name == "bucketId": assert isinstance(value.value, LocalVariable) + def test_yul_parser_assembly_slot(solc_binary_path) -> None: # mstore(0x0, bucketId) # mstore(0x20, _counters.slot) From 116a31ac426ba422ec0b467afd2b26587df0b57f Mon Sep 17 00:00:00 2001 From: Feist Josselin Date: Thu, 20 Feb 2025 12:09:47 +0100 Subject: [PATCH 06/30] fix linters --- slither/visitors/slithir/expression_to_slithir.py | 6 ++++-- tests/unit/slithir/test_yul_parser_assembly_slot.py | 5 +---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/slither/visitors/slithir/expression_to_slithir.py b/slither/visitors/slithir/expression_to_slithir.py index 67f591c9f8..f4346f81dc 100644 --- a/slither/visitors/slithir/expression_to_slithir.py +++ b/slither/visitors/slithir/expression_to_slithir.py @@ -406,8 +406,10 @@ def _post_call_expression(self, expression: CallExpression) -> None: self._result.append(var) set_val(expression, val) - elif (called.name == "sload(uint256)" or called.name == "sstore(uint256,uint256)") and ( - len(args) > 0 and isinstance(args[0], StateVariable) + elif ( + called.name in ["sload(uint256)", "sstore(uint256,uint256)"] + and len(args) > 0 + and isinstance(args[0], StateVariable) ): # parse_yul._parse_yul_magic_suffixes does a best effort tentative to retrieve # the right state variable on .slot access diff --git a/tests/unit/slithir/test_yul_parser_assembly_slot.py b/tests/unit/slithir/test_yul_parser_assembly_slot.py index 56f6a5c6de..593b65a5dc 100644 --- a/tests/unit/slithir/test_yul_parser_assembly_slot.py +++ b/tests/unit/slithir/test_yul_parser_assembly_slot.py @@ -40,10 +40,7 @@ def test_yul_parser_assembly_slot(solc_binary_path) -> None: assert isinstance(value.value, LocalVariable) -def test_yul_parser_assembly_slot(solc_binary_path) -> None: - # mstore(0x0, bucketId) - # mstore(0x20, _counters.slot) - data = {"0x0": "bucketId", "0x20": "_counters"} +def test_yul_parser_sstore_sload(solc_binary_path) -> None: solc_path = solc_binary_path("0.8.18") slither = Slither(Path(TEST_DATA_DIR, "assembly_sstore_sload.sol").as_posix(), solc=solc_path) From 43dc3bdb76e14cdfd9b83143d24dd7269b5bcd9d Mon Sep 17 00:00:00 2001 From: Simone Date: Thu, 20 Feb 2025 13:27:15 +0100 Subject: [PATCH 07/30] Move checking for function scoped variables after yul scoped --- slither/solc_parsing/yul/parse_yul.py | 29 +++++++++--------- .../test_data/assembly-functions.sol | 10 ++++++ .../assembly-functions.sol-0.6.9-compact.zip | Bin 1607 -> 2022 bytes .../assembly-functions.sol-0.6.9-legacy.zip | Bin 1433 -> 1788 bytes .../assembly-functions.sol-0.7.6-compact.zip | Bin 1588 -> 1993 bytes .../assembly-functions.sol-0.7.6-legacy.zip | Bin 1414 -> 1763 bytes .../assembly-functions.sol-0.8.16-compact.zip | Bin 1605 -> 2036 bytes .../assembly-functions.sol-0.6.9-compact.json | 5 +++ .../assembly-functions.sol-0.6.9-legacy.json | 3 ++ .../assembly-functions.sol-0.7.6-compact.json | 5 +++ .../assembly-functions.sol-0.7.6-legacy.json | 3 ++ ...assembly-functions.sol-0.8.16-compact.json | 5 +++ 12 files changed, 45 insertions(+), 15 deletions(-) diff --git a/slither/solc_parsing/yul/parse_yul.py b/slither/solc_parsing/yul/parse_yul.py index 5dcc33a9a4..666a3510ac 100644 --- a/slither/solc_parsing/yul/parse_yul.py +++ b/slither/solc_parsing/yul/parse_yul.py @@ -798,27 +798,12 @@ def parse_yul_identifier(root: YulScope, _node: YulNode, ast: Dict) -> Optional[ if name in builtins: return Identifier(YulBuiltin(name)) - # check function-scoped variables - parent_func = root.parent_func - if parent_func: - local_variable = parent_func.get_local_variable_from_name(name) - if local_variable: - return Identifier(local_variable) - - if isinstance(parent_func, FunctionContract): - # Variables must be looked from the contract declarer - assert parent_func.contract_declarer - state_variable = parent_func.contract_declarer.get_state_variable_from_name(name) - if state_variable: - return Identifier(state_variable) - # check yul-scoped variable variable = root.get_yul_local_variable_from_name(name) if variable: return Identifier(variable.underlying) # check yul-scoped function - func = root.get_yul_local_function_from_name(name) if func: return Identifier(func.underlying) @@ -840,6 +825,20 @@ def parse_yul_identifier(root: YulScope, _node: YulNode, ast: Dict) -> Optional[ if func: return Identifier(func.underlying) + # check function-scoped variables + parent_func = root.parent_func + if parent_func: + local_variable = parent_func.get_local_variable_from_name(name) + if local_variable: + return Identifier(local_variable) + + if isinstance(parent_func, FunctionContract): + # Variables must be looked from the contract declarer + assert parent_func.contract_declarer + state_variable = parent_func.contract_declarer.get_state_variable_from_name(name) + if state_variable: + return Identifier(state_variable) + magic_suffix = _parse_yul_magic_suffixes(name, root) if magic_suffix: return magic_suffix diff --git a/tests/e2e/solc_parsing/test_data/assembly-functions.sol b/tests/e2e/solc_parsing/test_data/assembly-functions.sol index 224e16bab8..a27cbf3023 100644 --- a/tests/e2e/solc_parsing/test_data/assembly-functions.sol +++ b/tests/e2e/solc_parsing/test_data/assembly-functions.sol @@ -10,3 +10,13 @@ contract A { } } } + +// Issue https://github.com/crytic/slither/issues/2655 +contract B { + function test(int256 a) internal { + assembly { + function a() {} + function b() { a() } + } + } +} \ No newline at end of file diff --git a/tests/e2e/solc_parsing/test_data/compile/assembly-functions.sol-0.6.9-compact.zip b/tests/e2e/solc_parsing/test_data/compile/assembly-functions.sol-0.6.9-compact.zip index 8389eb6f593dc3d0adfaf206f8ba933852ff32db..4330e49aff40b717f1d40d7255f91e78b24d1412 100644 GIT binary patch delta 1738 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 diff --git a/tests/e2e/solc_parsing/test_data/compile/assembly-functions.sol-0.6.9-legacy.zip b/tests/e2e/solc_parsing/test_data/compile/assembly-functions.sol-0.6.9-legacy.zip index a05a1cd47d26e92672998a5c155ebf594645644a..eb515c68f9012d15b7fbbe09f8ecb6112c303256 100644 GIT binary patch delta 1495 zcmV;|1t|KN3;YcjP)h>@KL7#%4ggtcR9dD*K^{g1003Sbkr?oQ4d8r?;R9xCLA_%5 zM>gSJa_fUKr;Z|_Vf)dw)HKRcdroMw#fd#!p$ZD9WhG@<2RZjYG3;QGC`c@p0UMwg zQb7=iYIohsKAzCa=FILm9U4Mz>qbqb#RCnjB|HxPV}oiAmBVqSsXleNHTuVds@cWd z*AjWVPmWk$hswQwhr}j9xSe8nO!VPk-<+Rp<;oF;lx205*iJRQ&Lg*mX{i7n5b*0d zaw30GX1;-#-&=kz9h=;Ku^_VX_cqxp*UT6}nz0IRf##9)yne}{?*_rv<)=P6HDie9 z8Q4pue!MH4QUmTDDsoT~#Y@smd$d6arq}Pd%2*}Hl?|GIo6hR;v?6D75AAbtS_FpN zFMi7l_M`}*0Fk|#)cAAzk0)OY^W*752SS+Q1?d9Joxf#f?p6W!@lKhi7-P8L;hwCSaC5Yl~59~k&tyOHA)5E zKSJ@SeAQUE#E)Fjr*>50%n*aF{@>B9tdPgtt*XD&sNe97 zhn{%>`1K9sw38;%{B9#&`Q8* z=QX|t%@vq{vjn=elg}LM*dTSeI6q5hx|RF+9^FQtAxQB#p_(B_gyTzNd{+;x)!`VF z<0+-ETH7x(J!3bv+_j^A{68}4t5|qR`{L<_m78Qb`$V5^W~Fod$Je9yfU<3&4*fyV`kxKicOu6-}a7zr5I!yNA}_z zAcXN0*bIRu(T??tmzCD4Cv0_0|H%P=<`}jg|4@{xl7M8*lf=cfF9FO)KxdEHff(|+a{c4n{mv-rYh!AT;ce8U`PF`2b)bT2g%ngwTVDk_dlP3n|9Daf$2nQ~%_FxQwM&SG` zatT6h{dibDPvP@dIp24cXt-d1IGGZ2+XpsBFMIc%_p0K$NAR%2%iSl@o|k2;FXeck zP{bmB2@KL7#%4ghhsuU7Oje8S@d0077mkr?oQ3(rJF$!~`u+Y%y@ z;+a3=EG$ztIp|3S$j2AL@-Na(Z93HE^5o3x9|SQy=H-Lx)wd(%?`QaKFg;| zO#1E6+IoL-yFu?|bxgdg^wEMmnLf~SK+gqwYb&=5_oXQnb%(Y}KOh+0bFdk7m zbM$BkI7)`OITP?FwjR1k9)e=9@y=I-Y5us@h+s-n;a?W~7Mw!aV&m&TzB2fw0k?By zY5nH#>|2X}SRy~S_9uJZF;wJsDn*sHs#(Gk z1dk_-ZQS0@xp99d-Hy_n+?@q1&lBFN{Q=14@G9sSSR`FxCh~T`QRaPE72;**87Pd_ zzML1~gn(7;ixQvbC!#>Ix@t3)qO`i=q;LLwgM#UQaSY9hz*9g-vVHjn$6MtG*4&ND z!!Tw73b`K)6%8ADX0fidgis<^qpxTv7QLyM#d^Z>un}aFo%*1^c+A@3t$Ye?W_>`E z2gg_H@dJSfs-=?6G?`Rm4xH^OOS5;2Kv{An7s_s;Sh^QPEr05c-N=yU+4TWQ?`#)q zZ>Yq7J((qNQDpPub~=$_FE*{t%;EZO-#^ks_mvD@0Y(DS0`newjDsbK{~MQ2TY^L> zj*~50>dxyJIWTk5BX0H9va&d^ZUAz#1is9&jyFgqRtVX8)(eGl4J^|3 z1zM-zZgp-nuVE&MYNZ8PZA)P%Y;-v^AGJ}3BRCebo!SfZ1VQ)7en-C09Uy2wuWfvP z6LFga1P19Wo8b5K6bo8vW-w5(ci{p-5X0#_%8GO5tjOFm_FaK~-1|pwHha@_=5jd` z#g%6l9l=PSc~5odcr#oSo%At}PasES`YMbeC6};EbwT;rA38HhI7rXff9ki0`Uso> zrq#uOZdC46oe7NxVXGG40iLyQg84&5QchvUm03wvJ={ZJrVwmBboefw*Yx@#U=CC- ziTA5_MbVpWwh{lo-huK^O928u13v%)01g0gwy#$7GJL}01ONcY5|gL~P6jIl00000 Dpd}?6 diff --git a/tests/e2e/solc_parsing/test_data/compile/assembly-functions.sol-0.7.6-compact.zip b/tests/e2e/solc_parsing/test_data/compile/assembly-functions.sol-0.7.6-compact.zip index b21bdae6fcaee090d2eba382547f8ca5b262bcd6..249f6845e9d45f60cacc879a173635fa23808cc7 100644 GIT binary patch delta 1716 zcmV;l221(049O20P)h>@KL7#%4ggzeR9X!{v~3dy000Fh001hJ@dYiBNV$JE3?6k; z-8fw2ay^C)wo`=;XXQ{r6hs_+nz`bQjjI#z411LcMj(%hsy)zIen4+5p1g4`y8*xJ zg{1K+?en2Y=BXZ_g(2_X3J6TvQax@?Y9Eg`OPH2K&iTmil}M69JTk($;&1gM8UqV8 zrBPG;*Q)PpOLSDhJfB{hN9}(ywG5AW1O;tanV+kQ#G|D6z2PS(ZqR9Amr8{WXS-o3 zHY@0iKlNCa5%!h>X}5$fdTEQi&)CMy&I7?iiK*_=PZ&ZCikAn<*{}!b3rMU-!MyDk9@Y=TZghqn*#Z# zBlhIyB>#cbSyWQ`8Ge7XeOK38@&(Vgw~OwnL4udW*Iz5`9^HK&s2;N#N6(fe-%ElE z0g(&LkTAitKv2CEW+l>(CYr*mmBUmgr$wDehbM4Ey-K3z!{*UE`?V`Wn1_R|!0KZ< z#Wq}s8R=OulK_n_T%Mv>Z9YXzMJcof+L}51`o^rAMA`q*)?9y5s~7>Wm$Vpu5oe+a z>*X@d8p5N{0Xudpdh}~vLk4ua(q5SR>B5*z0xEH{&SL!(Z{4d4aoXPJ{1XCy3%{UY zg8sG+&js89hW*^iz|^VAK2E>nT%%9j0R?6R8q1}sBGvuP#r0;(d{f@4C0>%sCW9o9 zr&+m2WB5++p<{ogZ!HLKaOq$Q%?Xi0GqCfa6A5@o$(ac}A%e8mgNlh6sF5qSGD(>6 zvo@97MJEz>^vTg9)8)`qW_&{ZvIltW3OdqpE&xVdhV@FQIuk*|A$8WOCM0Mfli*Dd z4bgNwF79<@7R}~XM4ITWG<^UGaraYmyx7CoyBbn#Gn#)8CyMYXzn$7?J)r!@XFa%k z2t|k^=*a^0`XSxrTvTJjA$`(nd3S5fkv5_D3;&Tp{t500zf8?*WyW`fhST8MT*YS#4Kp9X_^Gt}F2kZEab%ym?9_a0 z6Tpf@|BrYb?65MFKo4kA>@SbzbM(l{{Jg{GQ;P37b@ zM{$4t-^FZ9#1C2s`h!+jK+NHd9gWa2yg?+i9RMkMRkG^aZ;S z9=O9?4Tc{9UmBpAX6|nE2^d#J`tWn3x>`liwaQW`EKRV`!1GCwpA4(OkkPweo|P58 znysTx%8Cw2I$L!IXHM*r%4djv3RFRJcN%{OzW$rb#3<^)-{L?)#+nPh;kFa*Goij- z36_}B?2_;&ynkZ?zam0ZXYRaeLtYFuOwS#E`ZxT0tQN{v5=05{Jg zXuJz6;QRe8D{-pfjiLBOrhOgaahZkmw+;cLI>vXG`ZQF3whYN?mYhmld&{!*Q{jJ> z*epiwnY?_&5QxXItXfu?l{DrNGtv(s_5p_&Gp(bH^^rO%@FL7z$)tvCz6HMuQtmO(7>4XmTnp2 zZNdoW0`JsnZ2!)#_lWfS|H&^Colr{w0zU&k00ICG09$EPS`9$7Z4(Co00jXili&qT K23rRJ0002w5jwX3 delta 1323 zcmV+`1=RY<53~#zP)h>@KL7#%4ghhsuT~h7v)FzG008M4kr?TJG{>G%G-v;a@Wwq^ zW|V=C)q-FzELeD;^S#y(P;;ngl6mE`fo+#9g7p{e^M^3~z*`77I7tiURNbHVt8e|5 z+RN%8Z%`C@x}}bFfRhqpy^g#$dZFC z2sqpeu3CR8h&E!lxt56VdlOTAYD_Sl(1BeBm`qb%-A&kjRwxk~`1=`(&AjavBvrrS z?leQx=HU#|Ex-si*h0|UZu`}NwPSm9=(u>H@uLCT*$i2KKqlPVHR94c5Ju)-lEw-}<+UM?z3-q-N1X)#=_UL$U@tm64`cRCnQtp`E<3*nMlO)=`1x z;JYKn1aiZx^pxw2`9&=JA?e~uz4!GZcF-XPA+y#6xTJvW@O;R(^tChO=bWHd^`e__ zzDdA;ZST+!IFL!@Z&UEAls7?l~UI8uS?+YGdW8Q zbUs5ZdW=}Puw1Dq#Oz(+3cscwqN)w0Ivso|DR^M@)*iRaF6|F6-+On`fxqmT&K_1+ z8ZgUHf0Eri^^cDZ8OFDbCni_33w(Ef$!&aSU^%5h&;XI`f>WGn%I7acINt}W#XbM< z?j8VH^e&LX(YMg>fp^B0EkpY$C$+FedDLAD$i*@?zMqHO&lF7XVK#O7V7*UElOXGv z3v~vDb$MD(+3QF6B}sucePm%n%dUN~y7aNvinD|E@*K0=r2G3~d6SRQ&H=}Nzb_an zA;Mqyd69LqqDmWU#%;e0?V(y{@KL7#%4ggzeR9drZe%LJr008V8kr?oQ-{Tud1}~9kIo>T{ z1}t9^=2v)uYCr{ds=Up}m#;oH79CZpO^;MOlFVG6%dN)artYc}?Y;Nj*|~v1dCyt; z7@by!2c!Ep!Mpe5ybtXdVK{5__RPIW_r~QWmv-btg33125OBKSC_c&8b-f5EO(aYX zE~#lj{S2qjqEIS-Hj8j`|AWE}M#X0mf!BZ^;F_+2F`9$)rE+z_y+(9RP%$?3V)uaQ7wspB&P?}-37MF3gj|{lOoanxg z4dcJnY-KSzKHQ@*LT^uY*e4Sl z6sT@5`*iP`Y@Oipt%>b}+FUz+TJ+eE&4tX2?Ou9+^U!U8#0Mi$NX~MEm2L9bU~MaR zVMteNyK3ycvfwS-rU^+Va553Yus3J9Q&*haS)s^#vI_=z?Fum#^g?uCk}Opti&>`u zbuLu~b={YQ9?r5f8K)(dipZtNzi#bFQn2jZL}f@znk^$tluI=qJ@4!J7}Ad2TdYar z9>Csz?M5iI(VEXS`|s^A0}=d!`jRLC7YU(XH5jB4xbVh0dTjhtMkU7_B;c_)-ixVs z)s{pI3ox$~nFpVqfMr0sK#PlPN3)`AOuat+)>cd2$N4D^nDXtHI!89Ta37#4(-DZP-?=rLY%8%-$Bxm+W4cKIC*Z-%!Cati-xU12qi3-!~_2$!m zkabGgGWvm(f7`^q=187DLjer)Wm{i`au*Jv{)#SVfU7^Kh|K1urJLDaz*WiTXOuD9 ze31mAx&)m(h3K-wF>_OWZ-_}tZrupH7Bg}>2|@fU)n zMa+%w^yV!C7j-!0Kr47X+u2Yl5MzP_nLB8p$#<~+mGPW~w}zFT`;#m6 z`=Qpl(u`~sf-==V*w4Y{x2h)}E?=NQNI+?)(nNI3! z-ILv#tRXUSyk=PIzT~ARIQ2i%6ueCMSnuoKdFFpX?8qa^-bKbpuBBT`gr-b`&bnV- zH*535`A{%+jY5Necrh04;e0QD4M}oLePXLUM;6_3iFm@JxTzH1TA#7|OLa~ZeJ)RS zcd8F-cl9v4^xgThD3703V;2_D?~Ug8SzJ|$SS^$`v1hg_$l|nDc&=|Foh~N91PG5E zRkQPx^w)Akko+D2@)w(mb{@NEn1aq=z=SnG8~Oi@75_|7O928u13v%~0ssyGTWM5U Yvul3XEd~Gp>>HCZ1x^Na1^@s608PT!NdN!< delta 1125 zcmV-r1e*Kf4TcLFP)h>@KL7#%4ghhsuT}?yUb@f(006lX001hJpaw0GNWg#HuRcLb zXeqVaTv_9aWT}sSA;lH|G5xdB{P9Q@{u-Zfo38L^2wps*>pac^4b4WCjeP^b!dcTM zK;NBz@S>DYQT9mx`(+qKH$bi1fJt81Q}x-3&uE^!nBy2S{)m3Z1NPK&S!tuGV6{Z& zCB$P?VaSxAadwVHjdJ*qJJYwz|& zNg?#|U90Q$x_(TLGChBuNvnS4a2l{Vj%A|YVhTMbbit1pa(7BbTU~Jv0d*g%3vRWI zxf@2!iV5*#ySd%Lr$qDW zrm*pnKc6+5jgl3lYBC*r*ot-~%fY(JINPOu9|9?HWz7g*6jkS1a(tF&x`CQ+DJw^> zstL;u4pVVn5RiZNJVxO~pEk&7>g7M~D}w4)eB~9RK_S)den-j&t|}8$Z+I^uEF)#F z?{Q)p`Gr{0cv(~Zd58!@Ni2y@_OJ?>d0qV`p=I{7G~=VOiFUl-dBOBnZ*K1y4vT#~ zPg8S{bTe~O4g|a#q7va#f53x<5LiI&ZF@Yx>w3N)z`Y*5SBk)!sl&^MzP}>;5C|3U%()uBwMEP~#6N`_LpX z-radK80&oXd6@RzN&a{LlR0gdPKe044T*3N+Y2&8!Lj?P$)5wT_y4jZbvL-5?N9&0 rZ;ZlFO928u13v%)01g0gwy#zPgkHMP1ONcJ5|fVxP6ikS00000*Xtf> diff --git a/tests/e2e/solc_parsing/test_data/compile/assembly-functions.sol-0.8.16-compact.zip b/tests/e2e/solc_parsing/test_data/compile/assembly-functions.sol-0.8.16-compact.zip index a2b78d7b0698f3f623f5a2a37a95314adaa14197..52648f99ca74c6deaa4b9f6142a13168f5d29aa7 100644 GIT binary patch delta 1729 zcmV;y20r=44D=5eP)h>@KL7#%4gg(gR9X*rOUph7000&yu^0gXe>Izx#&V3kkreBF z1uT~TIu9ByX={O%xY(7*OQ&v-Y?F!JZVo-E^yw-4y9je{RnS^cat>sNo&YGftFPPs zqw^fxwwCm*qPorn-Q1wmt#fl7QkGkA`vZUbxrr1wk$4ys&>gOJ&FEIsoLm*_Z~nLH zc=q^lIlMLVz4N5|e|AcSu2nc-m3>*C_Xt=<+h4X>Cxe;BA}z?{iK(C^*1uRx=h!( zX9Bphvslp=;OG_ur9;rY_a(Y#u(=RA%`wIdA32Z;)YJq1-2c(d6?O^vTEA^0jczt0 zyU=DMkbaEiXvLE|a}`Dy{L$>bmb2orPmr+T#Vz<2l$s)ONCk(4p+rY~^yHjCy+e@b zBuY>9K_gz@fA%^(oUG>tsT8rEZHZ<4{V$b%j6;UAt)o7L(PM)N>Mv)6I5;qE=3Iji zbIQL%SZf)|*oRSg=ShtS0kz*EuD-6MY^UX=PvN3suf|qtx8F)&H>%d_6fPHZikSG# zLK4c|)67Lb*v^qVyJ;^S1g%tpN{!gXnu~}(ciuzhe>!0;efp;$ik2)a$lI{}R#Tqh z{3?vhJE;RUk6?#I@vtNzdVo$Z;0(8y>1t+XmEFE?Lc zxWF{D;uDgIK2lb$GFuZb)N$3wFEWh@WFRc~$60=!7-}E%d4z`5D5)K125DeEe8>yc zJ4>{de*#oe+9J*feD%LCDpOPE4V*70sT~8_12;if^TI9U(&d`fWEMe2aM!Xt!%f~R z>Q*5P2}_;<;lTr0@k@>Q1era^HR&HSJD#FYcw>@oV6Tm2={75(l(xMgHebz)wcoUR zz6Q{G6HXXd@TSXjDl8Y5F%ZtXO192_s5$JFloeMR?)xQzt7(Djk|B^cYN{QqvtNVvYBr3wbI(;@JexZ+YAy&KB$=g-m-2naOYi)}B#{RScS z;4Gbx#UrIC`pD)t36lImtZK#qog$yrz_R9aYpTI`?_b-N*oXXiH$B0=h}HF&^xQ&Y4Od9brnFML|3odh6$-P7R|7 zU^n6y1ZWj~wy>>64+l#c9=7Moe+s@Q>G1Az$fY_y^#}q?o_C)Zqq{kgj4kh9pur^% zn`u^Y_K;lTKYtDwG-PW@%T9D?#*~{SHjgP&XyUOC#STB5|mKmrOZL z_6v_(!Kr3dZAwMe)9qXHWcp|U24Nrt-}7X5N`jq+0F%4NP96kehINUWe-_NZf>ItU zqF@oSANQ%yZO}i_;`IWCBQqBr_APyV3-Uvv_G!&*M*;%S5FWn}aT{2BEgH{OClPbG zW;MmUApheIhzr+eF>tg)r+m=BQCu3XN3uNA)7Byfb`hnFD_yOeA}p`e6v_7I+wt4x zeRs;+B~8<=Iy~&UGZeyne@gk4EOW2ZV7GS_bOWM*e^U1S5he4ISX2LuoM&o8uPC+J z5Y9PyG;bKz8oWu^=<|rdxvdZOkO<64RF!>p=s|0g*-#w_Jn+*C>>#+wUliEElBEcM z*Y@h!(L&LteHt!2{{ca3V0J}L6sSGcvZ;a4%h~?iX?jpg0Rle*6h8n001g0MX;fMd XcT3AY2LJ#TCX?|6P6mbt00000=>Sh3 delta 1300 zcmV+v1?&3s55)`{P)h>@KL7#%4ghhsuU3)0TTYJ!006@o001hJu?H=YNYQ^uOsdv} zX?A7K7@VK8fF)x{CDm;X{tBQf!Z&J8{9Wb)hvlN1yJUFlYYGaBk7 zA3dloIt|B-LBKvGL_F-Eh?#p+_+W~0vK>xcmR@gbesMR$L_8BwFg1jkRtvuA{M6Ac zd!>5wi@a?m!IoyMw|>?z`S^bzz_7CQl*UB>u_r)Zlbn%(Ph?WJN4m5UDY=v87E&24 z>~DKe63iLza44p3dm<|qgPLus)qD0-))LC{XRyf6q?%Mw;DE6v;AqFDBD`g$GJmK~ zn2I)dBUew?9u4ZEj7{G%%dMvCo|A`23WLAw0RdeA6Z&w+9r1m-`FVe=VVYJSxcY2h z1SRg*Cp{3^EdM!%o+h=KK)^FzjW)i;dH}qbJF^2>GYF##3f|8HlVGq2%zjYp7yxv?(xU@Y1n--*{VqvGRKU?< zU@3goA8w#m*N+`ly7+DlOysBqqlr%RC3e(rD}2#qZ_ayE7DyGqxW#w z+f#}cB=`|jIk})i(;$25F1K)06&Yr?8emN&$Gz6bZ}I5CxZi)Ps(ddHB-P!zEqSJ-may4+tZ_f_q!l2apS7Z(^m2zZl}8JuyREs?Z{E~f3MMYI_Y_-R-s`A3(z0u zJbNcU~x>E`y1u%&ZTVIhIuH-|;ODX}7&e4>k`a`D$j zC3ZK50W}RJUZ2r{mKSlO*v#Z>Vu^%LDM#LfQ^(vmbK<}IQnVbS^RxGnN~N>o$aE#r z61-zVd|`hrM*|gn>nNn3PXD3!AOeI@RackwIPH3Ot$zA%=X5*i^Q<>dJWJu`ihN`) zoHtK_Foq=G&x;b<()4&{)n8q1GTRN5R|8Ku((*71G358zbQlzEfq#Y%?|Gr1-f;tE z(29WOUIL66c9y65E*cafyxkxl6^2?v3|oGby)J*4UTpqx8f!PG5JGJukLnHple)i8 z-+Js?33nglLD6^e^P=3HfRISn{n|M603pkkEAnI|_e6@+Z`FB<*Y9N~Rp!JGo1uYYCKs zv1DQT$w48JhX6A^A|V3(FaZ#&(~Mfk+AjtgOxX)qN@a!#;QHJ+Vu|yHL1)hhe}X!0 zm%jW0Z8FzRZ`i00rNoIw;Q#rINZ3$I0Rle*KL7#%4ghhsuU3)0TTYJ!006@olb{Dq K2G<1u00021;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n}\n", "foo.asm_0.w.b()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n", "foo.asm_0.g()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n" + }, + "B": { + "test(int256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: INLINE ASM 1\n\"];\n1->2;\n2[label=\"Node Type: END INLINE ASM 2\n\"];\n}\n", + "test.asm_0.a()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n}\n", + "test.asm_0.b()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n" } } \ No newline at end of file diff --git a/tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.6.9-legacy.json b/tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.6.9-legacy.json index 344d6e29cd..25a852eeea 100644 --- a/tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.6.9-legacy.json +++ b/tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.6.9-legacy.json @@ -1,5 +1,8 @@ { "A": { "foo()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: INLINE ASM 1\n\"];\n1->2;\n2[label=\"Node Type: END INLINE ASM 2\n\"];\n}\n" + }, + "B": { + "test(int256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: INLINE ASM 1\n\"];\n1->2;\n2[label=\"Node Type: END INLINE ASM 2\n\"];\n}\n" } } \ No newline at end of file diff --git a/tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.7.6-compact.json b/tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.7.6-compact.json index 0c50d020c3..9cf2555b40 100644 --- a/tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.7.6-compact.json +++ b/tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.7.6-compact.json @@ -8,5 +8,10 @@ "foo.asm_0.w.a()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n}\n", "foo.asm_0.w.b()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n", "foo.asm_0.g()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n" + }, + "B": { + "test(int256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: INLINE ASM 1\n\"];\n1->2;\n2[label=\"Node Type: END INLINE ASM 2\n\"];\n}\n", + "test.asm_0.a()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n}\n", + "test.asm_0.b()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n" } } \ No newline at end of file diff --git a/tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.7.6-legacy.json b/tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.7.6-legacy.json index 344d6e29cd..25a852eeea 100644 --- a/tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.7.6-legacy.json +++ b/tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.7.6-legacy.json @@ -1,5 +1,8 @@ { "A": { "foo()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: INLINE ASM 1\n\"];\n1->2;\n2[label=\"Node Type: END INLINE ASM 2\n\"];\n}\n" + }, + "B": { + "test(int256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: INLINE ASM 1\n\"];\n1->2;\n2[label=\"Node Type: END INLINE ASM 2\n\"];\n}\n" } } \ No newline at end of file diff --git a/tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.8.16-compact.json b/tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.8.16-compact.json index 0c50d020c3..9cf2555b40 100644 --- a/tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.8.16-compact.json +++ b/tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.8.16-compact.json @@ -8,5 +8,10 @@ "foo.asm_0.w.a()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n}\n", "foo.asm_0.w.b()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n", "foo.asm_0.g()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n" + }, + "B": { + "test(int256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: INLINE ASM 1\n\"];\n1->2;\n2[label=\"Node Type: END INLINE ASM 2\n\"];\n}\n", + "test.asm_0.a()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n}\n", + "test.asm_0.b()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n" } } \ No newline at end of file From ba9e002f517d54de15c6c74158bd268e4c3ac783 Mon Sep 17 00:00:00 2001 From: Feist Josselin Date: Tue, 25 Feb 2025 11:08:21 +0100 Subject: [PATCH 08/30] WIP docs --- secure_contracts_docs/src/README.md | 19 + secure_contracts_docs/src/SUMMARY.md | 26 + secure_contracts_docs/src/Usage.md | 163 + .../src/api/Data-dependency.md | 51 + .../src/api/Developer-installation.md | 0 secure_contracts_docs/src/api/JSON-output.md | 160 + secure_contracts_docs/src/api/README.md | 7 + secure_contracts_docs/src/api/SlithIR-SSA.md | 6 + secure_contracts_docs/src/api/SlithIR.md | 186 ++ secure_contracts_docs/src/api/api.md | 208 ++ .../src/api/static_analysis.md | 124 + .../src/detectors/Adding-a-new-detector.md | 63 + .../src/detectors/Detector-Documentation.md | 2761 +++++++++++++++++ .../src/printers/Printer-documentation.md | 468 +++ .../src/tools/Adding-a-new-utility.md | 21 + .../src/tools/Code-Similarity-Detector.md | 151 + .../src/tools/Contract-Flattening.md | 33 + .../src/tools/ERC-Conformance.md | 72 + .../src/tools/Path-Finding-Utility.md | 26 + .../src/tools/Property-generation.md | 158 + .../src/tools/Slither-format.md | 17 + .../src/tools/Upgradeability-Checks.md | 546 ++++ secure_contracts_docs/src/tutorials/README.md | 54 + .../src/tutorials/examples/coin.sol | 27 + ...pected_results_print_basic_information.txt | 39 + .../examples/print_basic_information.py | 18 + .../src/tutorials/exercise1.md | 33 + .../src/tutorials/exercise2.md | 21 + .../src/tutorials/exercise3.md | 13 + .../tutorials/exercises/exercise1/coin.sol | 27 + .../exercises/exercise1/expected_results.txt | 1 + .../tutorials/exercises/exercise1/solution.py | 15 + .../tutorials/exercises/exercise2/coin.sol | 52 + .../exercises/exercise2/expected_results.txt | 1 + .../tutorials/exercises/exercise2/solution.py | 15 + .../exercises/exercise3/expected_results.txt | 1 + .../tutorials/exercises/exercise3/find.sol | 15 + .../tutorials/exercises/exercise3/solution.py | 20 + .../src/tutorials/images/ast.png | Bin 0 -> 20508 bytes .../src/tutorials/images/cfg.png | Bin 0 -> 8912 bytes .../src/tutorials/scripts/gh_action_test.sh | 73 + 41 files changed, 5691 insertions(+) create mode 100644 secure_contracts_docs/src/README.md create mode 100644 secure_contracts_docs/src/SUMMARY.md create mode 100644 secure_contracts_docs/src/Usage.md create mode 100644 secure_contracts_docs/src/api/Data-dependency.md create mode 100644 secure_contracts_docs/src/api/Developer-installation.md create mode 100644 secure_contracts_docs/src/api/JSON-output.md create mode 100644 secure_contracts_docs/src/api/README.md create mode 100644 secure_contracts_docs/src/api/SlithIR-SSA.md create mode 100644 secure_contracts_docs/src/api/SlithIR.md create mode 100644 secure_contracts_docs/src/api/api.md create mode 100644 secure_contracts_docs/src/api/static_analysis.md create mode 100644 secure_contracts_docs/src/detectors/Adding-a-new-detector.md create mode 100644 secure_contracts_docs/src/detectors/Detector-Documentation.md create mode 100644 secure_contracts_docs/src/printers/Printer-documentation.md create mode 100644 secure_contracts_docs/src/tools/Adding-a-new-utility.md create mode 100644 secure_contracts_docs/src/tools/Code-Similarity-Detector.md create mode 100644 secure_contracts_docs/src/tools/Contract-Flattening.md create mode 100644 secure_contracts_docs/src/tools/ERC-Conformance.md create mode 100644 secure_contracts_docs/src/tools/Path-Finding-Utility.md create mode 100644 secure_contracts_docs/src/tools/Property-generation.md create mode 100644 secure_contracts_docs/src/tools/Slither-format.md create mode 100644 secure_contracts_docs/src/tools/Upgradeability-Checks.md create mode 100644 secure_contracts_docs/src/tutorials/README.md create mode 100644 secure_contracts_docs/src/tutorials/examples/coin.sol create mode 100644 secure_contracts_docs/src/tutorials/examples/expected_results_print_basic_information.txt create mode 100644 secure_contracts_docs/src/tutorials/examples/print_basic_information.py create mode 100644 secure_contracts_docs/src/tutorials/exercise1.md create mode 100644 secure_contracts_docs/src/tutorials/exercise2.md create mode 100644 secure_contracts_docs/src/tutorials/exercise3.md create mode 100644 secure_contracts_docs/src/tutorials/exercises/exercise1/coin.sol create mode 100644 secure_contracts_docs/src/tutorials/exercises/exercise1/expected_results.txt create mode 100644 secure_contracts_docs/src/tutorials/exercises/exercise1/solution.py create mode 100644 secure_contracts_docs/src/tutorials/exercises/exercise2/coin.sol create mode 100644 secure_contracts_docs/src/tutorials/exercises/exercise2/expected_results.txt create mode 100644 secure_contracts_docs/src/tutorials/exercises/exercise2/solution.py create mode 100644 secure_contracts_docs/src/tutorials/exercises/exercise3/expected_results.txt create mode 100644 secure_contracts_docs/src/tutorials/exercises/exercise3/find.sol create mode 100644 secure_contracts_docs/src/tutorials/exercises/exercise3/solution.py create mode 100644 secure_contracts_docs/src/tutorials/images/ast.png create mode 100644 secure_contracts_docs/src/tutorials/images/cfg.png create mode 100644 secure_contracts_docs/src/tutorials/scripts/gh_action_test.sh diff --git a/secure_contracts_docs/src/README.md b/secure_contracts_docs/src/README.md new file mode 100644 index 0000000000..7b4c6a126b --- /dev/null +++ b/secure_contracts_docs/src/README.md @@ -0,0 +1,19 @@ +# [Slither, the smart contract static analyzer](https://crytic.github.io/slither/slither.html) + +Slither Static Analysis Framework Logo + +[**Slither**](https://github.com/crytic/slither) is a Solidity & Vyper static analysis framework written in Python3. It runs a suite of vulnerability detectors, prints visual information about contract details, and provides an API to easily write custom analyses. Slither enables developers to find vulnerabilities, enhance their code comprehension, and quickly prototype custom analyses. + +If you are looking to use Slither's cli: +- [Usage](./Usage.md) the most common flags + +If you are looking to leverage Slither inbuilt features: +- [Detectors](./detectors/): Vulnerabilities detectors +- [Printers](./printers): Printers (code vizualiation) +- [Tools](./tools): Custom tools + +If you are looking to learn how to extend Slither's capabilities: +- [Developper-installation](./Developer-installation.md): How to install slither in a dev mode +- [API](./api): Introduction to static analysis & Slither's API +- [Tutorial](./tutorials/): Hands-on exercises + diff --git a/secure_contracts_docs/src/SUMMARY.md b/secure_contracts_docs/src/SUMMARY.md new file mode 100644 index 0000000000..e86e2dd5c0 --- /dev/null +++ b/secure_contracts_docs/src/SUMMARY.md @@ -0,0 +1,26 @@ + - [Introduction](./README.md) + - [Usage](./Usage.md) + - [API](./api/README.md) + - [Static Analysis](./api/static_analysis.md) + - [API](./api/api.md) + - [SlithIR](./api/SlithIR.md) + - [SSA](./api/SlithIR-SSA.md) + - [Data dependency](./api/Data-dependency.md) + - [JSON output](./api/JSON-output.md) + - [Detectors](./detectors/Detector-Documentation.md) + - [Detectors](./detectors/Detector-Documentation.md) + - [Adding a detector](./detectors/Adding-a-new-detector.md) + - [Printers](./printers/Printer-documentation.md) + - [Tools](./tools/Adding-a-new-utility.md) + - [Addning a new tool](./tools/Adding-a-new-utility.md) + - [Path Finding Utility](./tools/Path-Finding-Utility.md) + - [Code Similarity](./tools/Code-Similarity-detector.md) + - [Contract Flattening](./tools/Contract-Flattening.md) + - [Format](./tools/Slither-format.md) + - [ERC Conformance](./tools/ERC-Conformance.md) + - [Property Generation](./tools/Property-generation.md) + - [Upgradeability checks](./tools/Upgradeability-Checks.md) + - [Tutorials](./tutorials/README.md) + - [Exercise 1](./tutorials/exercise1.md) + - [Exercise 2](./tutorials/exercise2.md) + - [Exercise 3](./tutorials/exercise3.md) diff --git a/secure_contracts_docs/src/Usage.md b/secure_contracts_docs/src/Usage.md new file mode 100644 index 0000000000..87f3134176 --- /dev/null +++ b/secure_contracts_docs/src/Usage.md @@ -0,0 +1,163 @@ +## Usage + +- [How to run Slither](#how-to-run-slither) + - [Truffle/Dapp/Etherlime](#truffledappetherlime) + - [Embark](#embark) + - [solc](#solc) + - [Etherscan](#etherscan) + - [AST input](#ast-file) +- [Options](#options) + - [Detector selection](#detector-selection) + - [Printer selection](#printer-selection) + - [Path Filtering](#path-filtering) + - [Triage mode](#triage-mode) + - [Configuration file](#configuration-file) +- [IDE integrations](#ide-integration) + +## How to run Slither + +All the [`crytic-compile`](https://github.com/crytic/crytic-compile/wiki/Configuration) options are available through Slither. + +### Foundry/hardhat + +To run Slither on a Foundry/hardhat directory: +``` +slither . +``` + +### solc + +To run Slither from a Solidity file: + +``` +slither file.sol +``` + +### Etherscan + +To run Slither from a contract hosted on Etherscan, run + +``` +slither 0x7F37f78cBD74481E593F9C737776F7113d76B315 +``` + +We recommend installing [solc-select](https://github.com/crytic/solc-select/) so Slither can switch to the expected solc version automatically. + + +### Detector selection + +Slither runs all its detectors by default. + +To run only selected detectors, use `--detect detector1,detector2`. For example: +``` +slither file.sol --detect arbitrary-send,pragma +``` + +To exclude detectors, use `--exclude detector1,detector2`. For example: +``` +slither file.sol --exclude naming-convention,unused-state,suicidal +``` + +To exclude detectors with an informational or low severity, use `--exclude-informational` or `--exclude-low`. + +`--list-detectors` lists [available detectors](https://github.com/crytic/slither/wiki/Detector-Documentation). + +### Printer selection + +By default, no printers are run. + +To run selected printers, use `--print printer1,printer2`. For example: +``` +slither file.sol --print inheritance-graph +``` + +`--list-printers` lists [available printers](https://github.com/crytic/slither/wiki/Printer-Documentation). + +### Path filtering + +`--filter-paths path1` will exclude all the results that are only related to `path1`. The path specified can be a path directory or a filename. Direct string comparison and [Python regular expression](https://docs.python.org/3/library/re.html) are used. + +Examples: +``` +slither . --filter-paths "openzepellin" +``` +Filter all the results only related to openzepellin. +``` +slither . --filter-paths "Migrations.sol|ConvertLib.sol" +``` +Filter all the results only related to the file `SafeMath.sol` or `ConvertLib.sol`. + + +### Triage mode + +Slither offers two ways to remove results: +- By adding `//slither-disable-next-line DETECTOR_NAME` before the issue +- By adding `// slither-disable-start [detector] ... // slither-disable-end [detector]` around the code to disable the detector on a large section +- By adding `@custom:security non-reentrant` before the variable declaration will indicate to Slither that the external calls from this variable are non-reentrant +- By running the triage mode (see below) + +### Triage mode + +`--triage-mode` runs Slither in its triage mode. For every finding, Slither will ask if the result should be shown for the next run. Results are saved in `slither.db.json`. + +Examples: +``` +slither . --triage-mode +[...] +0: C.destination (test.sol#3) is never initialized. It is used in: + - f (test.sol#5-7) +Reference: https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#uninitialized-state-variables +Results to hide during next runs: "0,1,..." or "All" (enter to not hide results): 0 +[...] +``` + +The second run of Slither will hide the above result. + +To show the hidden results again, delete `slither.db.json`. + +### Configuration File + +Some options can be set through a json configuration file. By default, `slither.config.json` is used if present (it can be changed through `--config-file file.config.json`). + +Options passed via the CLI have priority over options set in the configuration file. + +The following flags are supported: + +``` +{ + "detectors_to_run": "all", + "printers_to_run": None, + "detectors_to_exclude": None, + "detectors_to_include": None, + "exclude_dependencies": False, + "exclude_informational": False, + "exclude_optimization": False, + "exclude_low": False, + "exclude_medium": False, + "exclude_high": False, + "fail_on": FailOnLevel.PEDANTIC, + "json": None, + "sarif": None, + "disable_color": False, + "filter_paths": None, + "include_paths": None, + "generate_patches": False, + "skip_assembly": False, + "legacy_ast": False, + "zip": None, + "zip_type": "lzma", + "show_ignored_findings": False, + "sarif_input": "export.sarif", + "sarif_triage": "export.sarif.sarifexplorer", + "triage_database": "slither.db.json", + # codex + "codex": False, + "codex_contracts": "all", + "codex_model": "text-davinci-003", + "codex_temperature": 0, + "codex_max_tokens": 300, + "codex_log": False, +} +``` + +For flags related to the compilation, see the [`crytic-compile` configuration](https://github.com/crytic/crytic-compile/blob/master/crytic_compile/cryticparser/defaults.py) \ No newline at end of file diff --git a/secure_contracts_docs/src/api/Data-dependency.md b/secure_contracts_docs/src/api/Data-dependency.md new file mode 100644 index 0000000000..2caa72bba4 --- /dev/null +++ b/secure_contracts_docs/src/api/Data-dependency.md @@ -0,0 +1,51 @@ +# Data dependency + +Data dependency allows knowing if the value of a given variable is influenced by another variable's value. + +Because smart contracts have a state machine based architecture, the results of the data dependency depend on the context (function/contract) of the analysis. Consider the following example: +```solidity +contract MyContract{ + uint a = 0; + uint b = 0; + + function setA(uint input_a) public{ + a = input_a; + } + + function setB() public{ + b = a; + } + +} +``` + +In this example, if we consider only `setA`, we have the following dependency: +- `a` is dependent on `input_a` + +If we consider only `setB`, we have: +- `b` is dependent on `a` + +If we consider the contract entirely (with all the functions), we have: +- `a` is dependent on `input_a` +- `b` is dependent on `a` and `input_a` (by transitivity) + +`slither.analyses.is_dependent(variable, variable_source, context)` allows to know if `variable` is dependent on `variable_source` on the given context. + +As a result, in our previous example, `is_dependent(b, a, funcA)` will return `False`, while `is_dependent(b, a, myContract)` will return `True`: +``` +from slither import Slither +from slither.analyses import is_dependent + +slither = Slither('data_dependency_simple_example.sol') + +myContract = slither.get_contract_from_name('MyContract') +funcA = myContract.get_function_from_signature('setA(uint256)') +input_a = funcA.parameters[0] + +a = myContract.get_state_variable_from_name('a') +b = myContract.get_state_variable_from_name('b') + +print(f'{b.name} is dependant from {input_a.name}?: {is_dependent(b, a, funcA)}') +print(f'{b.name} is dependant from {input_a.name}?: {is_dependent(b, a, myContract)}') +``` + diff --git a/secure_contracts_docs/src/api/Developer-installation.md b/secure_contracts_docs/src/api/Developer-installation.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/secure_contracts_docs/src/api/JSON-output.md b/secure_contracts_docs/src/api/JSON-output.md new file mode 100644 index 0000000000..d781a76cfd --- /dev/null +++ b/secure_contracts_docs/src/api/JSON-output.md @@ -0,0 +1,160 @@ +- [Top-level Command Output](https://github.com/crytic/slither/wiki/JSON-output#top-level-command-output): Standard top-level (for detectors, printers and tools) +- [Detectors output](https://github.com/crytic/slither/wiki/JSON-output#detector-results) +- [Upgradeability output](https://github.com/crytic/slither/wiki/JSON-output#slither-check-upgradeability) + +## Top-level Command Output +At the top level, the JSON output provided by slither will appear in the following format: +```json +{ + "success": true, + "error": null, + "results": {} +} +``` +- `success` (boolean): `true` if `results` were output successfully, `false` if an `error` occurred. +- `error` (string | null): If `success` is `false`, this will be a string with relevant error information. Otherwise, it will be `null`. +- `results` (command-results, see below): If `success` is `true`, this will be an object populated with different types of results, depending on the JSON arguments specified. + +## Command Results +The underlying `results` item above will appear in the following format: +```json +{ + "detectors": [], + "upgradeability-check": {} +} +``` +- `detectors` (OPTIONAL, vulnerability-results, see below): The results of any detector analysis. +- `upgradeability-check` (OPTIONAL, upgradeability-results, see below): The results of `slither-check-upgradeability`. + +## Detector Results +A detector result found in the `detectors` array above will be of the following format: + +``` +{ + "check": "...", + "impact": "...", + "confidence": "...", + "description": "...", + "elements": [] +} +``` +- `check` (string): The detector identifier (see the [list of detectors](https://github.com/trailofbits/slither#detectors)) +- `impact` (string): representation of the impact (`High`/ `Medium`/ `Low`/ `Informational`) +- `confidence` (string): representation of the confidence (`High`/ `Medium`/ `Low`) +- `description` (string): output of the slither +- `elements`: (element array, see below): an array of relevant items for this finding which map to some source code. + - NOTE: When writing a detector, the first element should be carefully chosen to represent the most significant portion of mapped code for the finding (the area of source on which external tooling should primarily focus for the issue). +- `additional_fields`: (OPTIONAL, any): Offers additional detector-specific information, does not always exist. + +## Detector Result Elements +Each element found in `elements` above is of the form: +```json +{ + "type": "...", + "name": "...", + "source_mapping": {}, + "type_specific_fields": {}, + "additional_fields": {} +} +``` +- `type` (string): Refers to the type of element, this can be either: (`contract`, `function`, `variable`, `node`, `pragma`, `enum`, `struct`, `event`). +- `name` (string): Refers to the name of the element. + - For `contract`/`function`/`variable`/`enum`/`struct`/`event` types, this refers to the definition name. + - For `node` types, this refers to a string representation of any underlying expression. A blank string is used if there is no underlying expression. + - For `pragma` types, this refers to a string representation of the `version` portion of the pragma (ie: `^0.5.0`). +- `source_mapping` (source mapping, see below): Refers to a source mapping object which defines the source range which represents this element. +- `type_specific_fields` (OPTIONAL, any): + - For `function`/`event` type elements: + - `parent` (result-element): Refers to the parent contract of this definition. + - `signature` (string): Refers to the full signature of this function + - For `enum`/`struct` type elements: + - `parent` (result-element): Refers to the parent contract of this definition. + - For `variable` type elements: + - `parent` (result-element): Refers to the parent contract if this variable is a state variable. Refers to the parent function if this variable is a local variable. + - For `node` type elements: + - `parent` (result-element): Refers to the parent function of this node. + - For `pragma` type elements: + - `directive` (string array): Fully serialized pragma directive (ie: `["solidity", "^", "0.4", ".9"]`) +- `additional_fields` (OPTIONAL, any): Offers additional detector-specific element information, does not always exist. + +## Source Mapping +Each `source_mapping` object is used to map an element to some portion of source. It is of the form: +``` +"source_mapping": { + "start": 45 + "length": 58, + "filename_relative": "contracts/tests/constant.sol", + "filename_absolute": "/tmp/contracts/tests/constant.sol", + "filename_short": "tests/constant.sol", + "filename_used": "contracts/tests/constant.sol", + "lines": [ + 5, + 6, + 7 + ], + "starting_column": 1, + "ending_column": 24, +} +``` +- `start` (integer): Refers to the starting byte position of the mapped source. +- `length` (integer): Refers to the byte-length of the mapped source. +- `filename_relative` (string): A relative file path from the analysis directory. +- `filename_absolute` (string): An absolute file path to the file. +- `filename_short` (string): A short version of the filename used for display purposes. Hides platform-specific directories (ex: `node_modules`). +- `filename_used` (string): The path used by the platform for analysis (non-standard). +- `lines` (integer array): An array of line numbers which the mapped source spans. Line numbers begin from 1. +- `starting_column` (integer): The starting column/character position for the first mapped source line. Begins from 1. +- `ending_column` (integer): The ending column/character position for the last mapped source line. Begins from 1. + +## Detector-specific additional fields +Some detectors have custom elements output via the `additional_fields` field of their result, or result elements. Annotations here will specify _result_ or _result-element_ to specify the location of the additional fields. +- `constant-function`: + - `contain_assembly` (result, boolean): Specifies if the result is due to the function containing assembly. +- `naming-convention`: + - `convention` (result-element, string): Used to denote the convention used to find the result element/issue. Valid conventions are: + - `CapWords` + - `mixedCase` + - `l_O_I_should_not_be_used` + - `UPPER_CASE_WITH_UNDERSCORES` + - `target` (result-element, string): Used to denote the type of finding (constant, parameter, etc). Valid targets are: + - `contract` + - `structure` + - `event` + - `function` + - `variable` + - `variable_constant` + - `parameter` + - `enum` + - `modifier` + +- `reentrancy` (all variants): + - `underlying_type` (result-element, string): Specifies the type of result element. Is one of `external_calls`, `external_calls_sending_eth`, or `variables_written`. + + +## Slither Check Upgradeability + +The `slither-check-upgradeability` tool also produces JSON output (with the use of the `--json` option). At the top level, this JSON output will appear in the format similar to that of Slither above: +```json +{ + "success": true, + "error": null, + "results": { + "upgradeability-check": {} + } +} +``` +- `success` (boolean): `true` if `results` were output successfully, `false` if an `error` occurred. +- `error` (string | null): If `success` is `false`, this will be a string with relevant error information. Otherwise, it will be `null`. +- `results` (upgradeability-check-results, see below): If `success` is `true`, this will contain an `upgradeability-check` object populated with the different upgradeability checks. If `success` is `false`, `upgradeability-check` object will be empty. + +## Command Results +The underlying `upgradeability-check` item above will appear in the following format: +```json +{ + "check-initialization": {}, + "check-initialization-v2": {}, + "compare-function-ids": {}, + "compare-variables-order-proxy": {}, + "compare-variables-order-implementation": {} +} +``` diff --git a/secure_contracts_docs/src/api/README.md b/secure_contracts_docs/src/api/README.md new file mode 100644 index 0000000000..4e16d63551 --- /dev/null +++ b/secure_contracts_docs/src/api/README.md @@ -0,0 +1,7 @@ +Slither is fully customizable: +- [Static Analysis](./static_analysis.md): learn about the basics of static analysis +- [API](./api.md) +- [SlithIR](./SlithIR.md) +- [SSA](./SlithIR-SSA.md) +- [Data dependency](./Data-dependency.md) +- [JSON output](./JSON-output.md) \ No newline at end of file diff --git a/secure_contracts_docs/src/api/SlithIR-SSA.md b/secure_contracts_docs/src/api/SlithIR-SSA.md new file mode 100644 index 0000000000..155361fad5 --- /dev/null +++ b/secure_contracts_docs/src/api/SlithIR-SSA.md @@ -0,0 +1,6 @@ +## SSA + +Slither possess a Static Single Assignment (SSA) form representation of SlithIR. SSA is a commonly used representation in compilation and static analysis in general. It requires that each variable is assigned at least one time. SSA is a key component for building an efficient data-dependency analysis. + +The [SSA printer](https://github.com/trailofbits/slither/wiki/Printer-documentation#slithir-ssa) allows to visualize this representation. + diff --git a/secure_contracts_docs/src/api/SlithIR.md b/secure_contracts_docs/src/api/SlithIR.md new file mode 100644 index 0000000000..bb5d1aa957 --- /dev/null +++ b/secure_contracts_docs/src/api/SlithIR.md @@ -0,0 +1,186 @@ +# SlithIR +Slither translates Solidity an intermediate representation, SlithIR, to enable high-precision analysis via a simple API. It supports taint and value tracking to enable detection of complex patterns. + +SlithIR is a work in progress, although it is usable today. New developments in SlithIR are driven by needs identified by new detector modules. Please help us bugtest and enhance SlithIR! + +## What is an IR? + +In language design, compilers often operate on an “intermediate representation” (IR) of a language that carries extra details about the program as it is parsed. For example, a compiler creates a parse tree of a program that represents a program as written. However, the compiler can continue to enrich this tree with information, such as taint information, source location, and other items that could have impacted an item from control flow. Additionally, languages such as Solidity have inheritance, meaning that functions and methods may be defined outside the scope of a given contract. An IR could linearize these methods, allowing additional transformations and processing of the contract’s source code. + +A demonstrative example would be LLVM’s IR vs C or x86 code. While C will clearly demonstrate a function call, it may be missing details about the underlying system, the path to a location, and so on. Likewise, while an analog to the same call would be clear in x86, this would lose all transient details of which variables and which path an application had taken to arrive at a specific call. LLVM’s IR solves this by abstracting away the specific details of a call instruction, while still capturing the variables, environmental state, and other values that lead to this position. In this way, LLVM can perform additional introspection of the resulting code, and use these analyses to drive other optimizations or information to other passes of the compiler. + +## Why does Slither translate to an IR? + +Solidity is a quirky language with a number of edge cases, both in terms of syntax and semantics. By translating to an IR, Slither normalizes many of these quirks to better analyze the contract. For example, Solidity’s grammar defines an array push as a function call to the array. A straightforward representation of this semantic would be indistinguishable from a normal function call. Slither, in contrast, treats array pushes as a specific operation, allowing further analysis of the accesses to arrays and their impact to the security of a program. Moreover, the operators in SlithIR have a hierarchy, so, for example in a few lines of code you can track all the operators that write to a variable, which makes it trivial to write precise taint analysis. + +Additionally, Slither can include non-trivial variable tracking by default by translating to an IR. This can build richer representations of contracts and allow for deeper analysis of potential vulnerabilities. For example, answering the question “can a user control a variable” is central to uncovering more complex vulnerabilities from a static position. Slither will propagate information from function parameters to program state in an iterative fashion, which captures the control flow of information across potentially multiple transactions. In this way, Slither can enrich information and statically provide a large amount of assurance to contracts that standard vulnerabilities exist and are reachable under certain conditions. + +## Example +`$ slither file.sol --print slithir` will output the IR for every function. + +``` +$ slither examples/printers/slihtir.sol --printers slithir +Contract UnsafeMath + Function add(uint256,uint256) + Expression: a + b + IRs: + TMP_0(uint256) = a + b + RETURN TMP_0 + Function min(uint256,uint256) + Expression: a - b + IRs: + TMP_0(uint256) = a - b + RETURN TMP_0 +Contract MyContract + Function transfer(address,uint256) + Expression: balances[msg.sender] = balances[msg.sender].min(val) + IRs: + REF_3(uint256) -> balances[msg.sender] + REF_1(uint256) -> balances[msg.sender] + TMP_1(uint256) = LIBRARY_CALL, dest:UnsafeMath, function:min, arguments:['REF_1', 'val'] + REF_3 := TMP_1 + Expression: balances[to] = balances[to].add(val) + IRs: + REF_3(uint256) -> balances[to] + REF_1(uint256) -> balances[to] + TMP_1(uint256) = LIBRARY_CALL, dest:UnsafeMath, function:add, arguments:['REF_1', 'val'] + REF_3 := TMP_1 +``` + +# SlithIR Specification + +## Variables +- `StateVariable` +- `LocalVariable` +- `Constant` (`string` or `int`) +- `SolidityVariable` +- `TupleVariable` +- `TemporaryVariable` (variables added by SlithIR) +- `ReferenceVariable` (variables added by SlithIR, for mapping/index accesses) + +In the following we use: +- `LVALUE` can be: `StateVariable`, `LocalVariable`, `TemporaryVariable`, `ReferenceVariable` or `TupleVariable` +- `RVALUE` can be: `StateVariable`, `LocalVariable`, `Constant`, `SolidityVariable`, `TemporaryVariable` or `ReferenceVariable` + +## Operators + +- All the operators inherit from `Operation` and have a `read` attribute returning the list of variables read (see [slither/slithir/operations/operation.py](https://github.com/crytic/slither/blob/master/slither/slithir/operations/operation.py)). +- All the operators writing to a `LVALUE` inherit from `OperationWithLValue` and have the `lvalue` attribute (see [slither/slithir/operations/lvalue.py](https://github.com/crytic/slither/blob/master/slither/slithir/operations/lvalue.py)). + +### Assignment + +- `LVALUE := RVALUE` +- `LVALUE := Tuple` +- `LVALUE := Function` (for dynamic function) + +### Binary Operation + +- `LVALUE = RVALUE ** RVALUE` +- `LVALUE = RVALUE * RVALUE` +- `LVALUE = RVALUE / RVALUE` +- `LVALUE = RVALUE % RVALUE` +- `LVALUE = RVALUE + RVALUE` +- `LVALUE = RVALUE - RVALUE` +- `LVALUE = RVALUE << RVALUE` +- `LVALUE = RVALUE >> RVALUE` +- `LVALUE = RVALUE & RVALUE` +- `LVALUE = RVALUE ^ RVALUE` +- `LVALUE = RVALUE | RVALUE` +- `LVALUE = RVALUE < RVALUE` +- `LVALUE = RVALUE > RVALUE` +- `LVALUE = RVALUE <= RVALUE` +- `LVALUE = RVALUE >= RVALUE` +- `LVALUE = RVALUE == RVALUE` +- `LVALUE = RVALUE != RVALUE` +- `LVALUE = RVALUE && RVALUE` +- `LVALUE = RVALUE -- RVALUE` + +### Unary Operation +- `LVALUE = ! RVALUE` +- `LVALUE = ~ RVALUE` + +### Index +- `REFERENCE -> LVALUE [ RVALUE ]` + +Note: The reference points to the memory location + +### Member +- `REFERENCE -> LVALUE . RVALUE` +- `REFERENCE -> CONTRACT . RVALUE` +- `REFERENCE -> ENUM . RVALUE` + +Note: The reference points to the memory location + +### New Operators +- `LVALUE = NEW_ARRAY ARRAY_TYPE DEPTH(:int)` + +`ARRAY_TYPE` is a [solidity_types](https://github.com/crytic/slither/tree/master/slither/core/solidity_types) + +`DEPTH` is used for arrays of multiple dimensions. + +- `LVALUE = NEW_CONTRACT CONSTANT` +- `LVALUE = NEW_STRUCTURE STRUCTURE` +- `LVALUE = NEW_ELEMENTARY_TYPE ELEMENTARY_TYPE` + +`ELEMENTARY_TYPE` is defined in [slither/core/solidity_types/elementary_type.py](https://github.com/crytic/slither/blob/master/slither/core/solidity_types/elementary_type.py) + +### Push Operator +- `PUSH LVALUE RVALUE` +- `PUSH LVALUE Function` (for dynamic function) + +### Delete Operator +- `DELETE LVALUE` + +### Conversion +- `CONVERT LVALUE RVALUE TYPE` + +TYPE is a [solidity_types](https://github.com/crytic/slither/tree/master/slither/core/solidity_types) + +### Unpack +- `LVALUE = UNPACK TUPLEVARIABLE INDEX(:int)` + +### Array Initialization +- `LVALUE = INIT_VALUES` + +`INIT_VALUES` is a list of `RVALUE`, or a list of lists in case of a multidimensional array. + +### Calls Operators + +In the following, `ARG` is a variable as defined in [SlithIR#variables](https://github.com/crytic/slither/wiki/SlithIR#variables) + +- `LVALUE = HIGH_LEVEL_CALL DESTINATION FUNCTION [ARG, ..]` +- `LVALUE = LOW_LEVEL_CALL DESTINATION FUNCTION_NAME [ARG, ..]` + +`FUNCTION_NAME` can only be `call`/`delegatecall`/`codecall` + +- `LVALUE = SOLIDITY_CALL SOLIDITY_FUNCTION [ARG, ..]` + +`SOLIDITY_FUNCTION` is defined in [slither/core/declarations/solidity_variables.py](https://github.com/crytic/slither/blob/master/slither/core/declarations/solidity_variables.py) + +- `LVALUE = INTERNAL_CALL FUNCTION [ARG, ..]` +- `LVALUE = INTERNAL_DYNAMIC_CALL FUNCTION_TYPE [ARG, ..]` + +`INTERNAL_DYNAMIC_CALL` represents the pointer of function. + +`FUNCTION_TYPE` is defined in [slither/core/solidity_types/function_type.py](https://github.com/crytic/slither/blob/master/slither/core/solidity_types/function_type.py) + +- `LVALUE = LIBRARY_CALL DESTINATION FUNCTION_NAME [ARG, ..]` +- `LVALUE = EVENT_CALL EVENT_NAME [ARG, ..]` +- `LVALUE = SEND DESTINATION VALUE` +- `TRANSFER DESTINATION VALUE` + +Optional arguments: +- `GAS` and `VALUE` for `HIGH_LEVEL_CALL` / `LOW_LEVEL_CALL`. + +### Return +- `RETURN RVALUE` +- `RETURN TUPLE` +- `RETURN None` + +`Return None` represents an empty return statement. + +### Condition +- `CONDITION RVALUE` + +`CONDITION` holds the condition in a conditional node. + diff --git a/secure_contracts_docs/src/api/api.md b/secure_contracts_docs/src/api/api.md new file mode 100644 index 0000000000..d347d7903c --- /dev/null +++ b/secure_contracts_docs/src/api/api.md @@ -0,0 +1,208 @@ +# API Basics + +Slither has an API that allows you to explore basic attributes of contracts and their functions. + +On a high level there are 6 layers: + +- `Slither` - main slither object +- `SlitherCompilationUnit` - group of files used by one call to solc +- `Contract` - contract level +- `Function` - function level +- `Node` - control flow graph +- `SlithrIR` - intermediate representation + +Watch our [API walkthrough](https://www.youtube.com/watch?v=Ijf0pellvgw) for more details + +## Slither object + +To load a codebase: + +```python +from slither import Slither +slither = Slither('/path/to/project') +``` + +To load a contract deployed: + +```python +from slither import Slither +slither = Slither('0x..') # assuming the code is verified on etherscan +``` + +Use `etherscan_api_key` to provide an [Etherscan API KEY](https://docs.etherscan.io/getting-started/viewing-api-usage-statistics) + +```python +slither = Slither('0x..', etherscan_api_key='..') +``` + +You can retrieve the list of compilation units with: + +- `sl.compilation_units # array of SlitherCompilationUnit` + +## SlitherCompilationUnit object + +- ~ group of files used by one call to solc +- Most targets have 1 compilation, but not always true + - Partial compilation for optimization + - Multiple solc version used + - Etc.. +- Why compilation unit matters? + - Some APIs might be not intuitive + - Ex: looking for a contract based on the name? + - Can have multiple contracts +- For hacking you can (probably) use the first compilation unit + - `compilation_unit = sl.compilation_units[0]` + +A [`SlitherCompilationUnit`](https://github.com/crytic/slither/blob/master/slither/core/compilation_unit.py) has: + +- `contracts (list(Contract))`: A list of contracts +- `contracts_derived (list(Contract))`: A list of contracts that are not inherited by another contract (a subset of contracts) +- `get_contract_from_name (str)`: Returns a list of contracts matching the name +- `[structures | enums | events | variables | functions]_top_level`: Top level object + +Example + +```python +from slither import Slither +sl = Slither("0xdac17f958d2ee523a2206206994597c13d831ec7") +compilation_unit = sl.compilation_units[0] + +# Print all the contracts from the USDT address +print([str(c) for c in compilation_unit.contracts]) + +# Print the most derived contracts from the USDT address +print([str(c) for c in compilation_unit.contracts_derived]) +``` + +```bash +% python test.py +['SafeMath', 'Ownable', 'ERC20Basic', 'ERC20', 'BasicToken', 'StandardToken', 'Pausable', 'BlackList', 'UpgradedStandardToken', 'TetherToken'] + +['SafeMath', 'UpgradedStandardToken', 'TetherToken'] +``` + +## Contract Object + +A [`Contract`](https://github.com/crytic/slither/blob/master/slither/core/declarations/contract.py) object has: + +- `name: str`: The name of the contract +- `functions: list[Function]`: A list of functions +- `modifiers: list[Modifier]`: A list of modifiers +- `all_functions_called: list[Function/Modifier]`: A list of all internal functions reachable by the contract +- `inheritance: list[Contract]`: A list of inherited contracts (c3 linearization order) +- `derived_contracts: list[Contract]`: contracts derived from it +- `get_function_from_signature(str): Function`: Returns a Function from its signature +- `get_modifier_from_signature(str): Modifier`: Returns a Modifier from its signature +- `get_state_variable_from_name(str): StateVariable`: Returns a StateVariable from its name +- `state_variables: List[StateVariable]`: list of accessible variables +- `state_variables_ordered: List[StateVariable]`: all variable ordered by declaration + +Example + +```python +from slither import Slither +sl = Slither("0xdac17f958d2ee523a2206206994597c13d831ec7") +compilation_unit = sl.compilation_units[0] + +# Print all the state variables of the USDT token +contract = compilation_unit.get_contract_from_name("TetherToken")[0] +print([str(v) for v in contract.state_variables]) +``` + +```bash +% python test.py +['owner', 'paused', '_totalSupply', 'balances', 'basisPointsRate', 'maximumFee', 'allowed', 'MAX_UINT', 'isBlackListed', 'name', 'symbol', 'decimals', 'upgradedAddress', 'deprecated'] +``` + +## Function object + +A [`Function`](https://github.com/crytic/slither/blob/master/slither/core/declarations/function.py) or a `Modifier` object has: + +- `name: str`: The name of the function +- `contract: Contract`: The contract where the function is declared +- `nodes: list[Node]`: A list of nodes composing the CFG of the function/modifier +- `entry_point: Node`: The entry point of the CFG +- `[state |local]_variable_[read |write]: list[StateVariable]`: A list of local/state variables read/write + - All can be prefixed by “all\_” for recursive lookup + - Ex: `all_state_variable_read`: return all the state variables read in internal calls +- `slithir_operations: List[Operation]`: list of IR operations + +```python +from slither import Slither +sl = Slither("0xdac17f958d2ee523a2206206994597c13d831ec7") +compilation_unit = sl.compilation_units[0] +contract = compilation_unit.get_contract_from_name("TetherToken")[0] + +transfer = contract.get_function_from_signature("transfer(address,uint256)") + +# Print all the state variables read by the transfer function +print([str(v) for v in transfer.state_variables_read]) +# Print all the state variables read by the transfer function and its internal calls +print([str(v) for v in transfer.all_state_variables_read]) +``` + +```bash +% python test.py +['deprecated', 'isBlackListed', 'upgradedAddress'] +['owner', 'basisPointsRate', 'deprecated', 'paused', 'isBlackListed', 'maximumFee', 'upgradedAddress', 'balances'] +``` + +## Node object + +[Node](https://github.com/crytic/slither/blob/master/slither/core/cfg/node.py) + +To explore the nodes: + +- If order does not matter + - `for node in function.nodes` +- If order matters, walk through the nodes + +```python +def visit_node(node: Node, visited: List[Node]): + + if node in visited: + return + visited += [node] + + # custom action + for son in node.sons: + visit_node(son, visited) +``` + +- If need to iterate more than once (advanced usages) +- Bound the iteration X times +- Create a fix-point - abstract interpretation style analysis + +## SlithIR + +- [slither/slithir](https://github.com/crytic/slither/tree/master/slither/slithir) +- Every IR operation has its own methods +- Check if an operation is of a type: + - `isinstance(ir, TYPE)` + - Ex: `isinstance(ir, Call)` +- Check if the operation is an addition +- `isinstance(ir, Binary) & ir.type == BinaryType.ADDITION` +- Check if the operation is a call to MyContract +- `isinstance(ir, HighLevelCall) & ir.destination == MyContract` + +```python +from slither import Slither +sl = Slither("0xdac17f958d2ee523a2206206994597c13d831ec7") +compilation_unit = sl.compilation_units[0] +contract = compilation_unit.get_contract_from_name("TetherToken")[0] +totalSupply = contract.get_function_from_signature("totalSupply()") + +# Print the external call made in the totalSupply function +for ir in totalSupply.slithir_operations: + if isinstance(ir, HighLevelCall): + print(f"External call found {ir} ({ir.node.source_mapping})") +``` + +```bash +% python test.py +External call found HIGH_LEVEL_CALL, […] (...TetherToken.sol#339) +``` + +### Example: Print Basic Information + +[print_basic_information.py](https://github.com/crytic/building-secure-contracts/tree/master/program-analysis/slither/examples/print_basic_information.py) demonstrates how to print basic information about a project. diff --git a/secure_contracts_docs/src/api/static_analysis.md b/secure_contracts_docs/src/api/static_analysis.md new file mode 100644 index 0000000000..85cf4b2814 --- /dev/null +++ b/secure_contracts_docs/src/api/static_analysis.md @@ -0,0 +1,124 @@ +## Static analysis + +The capabilities and design of the Slither static analysis framework have been described in blog posts ([1](https://blog.trailofbits.com/2018/10/19/slither-a-solidity-static-analysis-framework/), [2](https://blog.trailofbits.com/2019/05/27/slither-the-leading-static-analyzer-for-smart-contracts/)) and an [academic paper](https://github.com/trailofbits/publications/blob/master/papers/wetseb19.pdf). + +Static analysis comes in different flavors. You may already know that compilers like [clang](https://clang-analyzer.llvm.org/) and [gcc](https://lwn.net/Articles/806099/) rely on these research techniques, as do tools like [Infer](https://fbinfer.com/), [CodeClimate](https://codeclimate.com/), [FindBugs](http://findbugs.sourceforge.net/), and tools based on formal methods like [Frama-C](https://frama-c.com/) and [Polyspace](https://www.mathworks.com/products/polyspace.html). + +In this article, we will not provide an exhaustive review of static analysis techniques and research. Instead, we'll focus on what you need to understand about how Slither works, so you can more effectively use it to find bugs and understand code. + +- [Code representation](#code-representation) +- [Code analysis](#analysis) +- [Intermediate representation](#intermediate-representation) + +### Code representation + +Unlike dynamic analysis, which reasons about a single execution path, static analysis reasons about all paths at once. To do so, it relies on a different code representation. The two most common ones are the abstract syntax tree (AST) and the control flow graph (CFG). + +### Abstract Syntax Trees (AST) + +ASTs are used every time a compiler parses code. They are arguably the most basic structure upon which static analysis can be performed. + +In a nutshell, an AST is a structured tree where, usually, each leaf contains a variable or a constant, and internal nodes are operands or control flow operations. Consider the following code: + +```solidity +function safeAdd(uint256 a, uint256 b) internal pure returns (uint256) { + if (a + b <= a) { + revert(); + } + return a + b; +} +``` + +The corresponding AST is shown in the following illustration: + +![AST](./images/ast.png) + +Slither uses the AST exported by solc. + +While simple to build, the AST is a nested structure that's not always straightforward to analyze. For example, to identify the operations used by the expression `a + b <= a`, you must first analyze `<=` and then `+`. A common approach is to use the so-called visitor pattern, which navigates through the tree recursively. Slither contains a generic visitor in [`ExpressionVisitor`](https://github.com/crytic/slither/blob/master/slither/visitors/expression/expression.py). + +The following code uses `ExpressionVisitor` to detect if an expression contains an addition: + +```python +from slither.visitors.expression.expression import ExpressionVisitor +from slither.core.expressions.binary_operation import BinaryOperationType + +class HasAddition(ExpressionVisitor): + + def result(self): + return self._result + + def _post_binary_operation(self, expression): + if expression.type == BinaryOperationType.ADDITION: + self._result = True + +visitor = HasAddition(expression) # expression is the expression to be tested +print(f'The expression {expression} has an addition: {visitor.result()}') +``` + +### Control Flow Graph (CFG) + +The second most common code representation is the control flow graph (CFG). As its name suggests, it is a graph-based representation that reveals all the execution paths. Each node contains one or multiple instructions, and edges in the graph represent control flow operations (if/then/else, loop, etc). The CFG of our previous example is as follows: + +![CFG](./images/cfg.png) + +Most analyses are built on top of the CFG representation. + +There are many other code representations, each with its advantages and drawbacks depending on the desired analysis. + +### Analysis + +The simplest types of analyses that can be performed with Slither are syntactic analyses. + +### Syntax analysis + +Slither can navigate through the different components of the code and their representation to find inconsistencies and flaws using a pattern matching-like approach. + +For example, the following detectors look for syntax-related issues: + +- [State variable shadowing](https://github.com/crytic/slither/wiki/Detector-Documentation#state-variable-shadowing): iterates over all state variables and checks if any shadow a variable from an inherited contract ([state.py#L51-L62](https://github.com/crytic/slither/blob/0441338e055ab7151b30ca69258561a5a793f8ba/slither/detectors/shadowing/state.py#L51-L62)) + +- [Incorrect ERC20 interface](https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-erc20-interface): searches for incorrect ERC20 function signatures ([incorrect_erc20_interface.py#L34-L55](https://github.com/crytic/slither/blob/0441338e055ab7151b30ca69258561a5a793f8ba/slither/detectors/erc/incorrect_erc20_interface.py#L34-L55)) + +### Semantic analysis + +In contrast to syntax analysis, semantic analysis delves deeper and analyzes the "meaning" of the code. This category includes a broad range of analyses that yield more powerful and useful results but are more complex to write. + +Semantic analyses are used for advanced vulnerability detection. + +#### Data dependency analysis + +A variable `variable_a` is said to be data-dependent on `variable_b` if there is a path for which the value of `variable_a` is influenced by `variable_b`. + +In the following code, `variable_a` is dependent on `variable_b`: + +```solidity +// ... +variable_a = variable_b + 1; +``` + +Slither comes with built-in [data dependency](https://github.com/crytic/slither/wiki/data-dependency) capabilities, thanks to its intermediate representation (discussed later). + +An example of data dependency usage can be found in the [dangerous strict equality detector](https://github.com/crytic/slither/wiki/Detector-Documentation#dangerous-strict-equalities). Slither looks for strict equality comparisons to dangerous values ([incorrect_strict_equality.py#L86-L87](https://github.com/crytic/slither/blob/6d86220a53603476f9567c3358524ea4db07fb25/slither/detectors/statements/incorrect_strict_equality.py#L86-L87)) and informs the user that they should use `>=` or `<=` instead of `==` to prevent attackers from trapping the contract. Among other things, the detector considers the return value of a call to `balanceOf(address)` to be dangerous ([incorrect_strict_equality.py#L63-L64](https://github.com/crytic/slither/blob/6d86220a53603476f9567c3358524ea4db07fb25/slither/detectors/statements/incorrect_strict_equality.py#L63-L64)) and uses the data dependency engine to track its usage. + +#### Fixed-point computation + +If your analysis navigates through the CFG and follows the edges, you're likely to encounter already visited nodes. For example, if a loop is presented as shown below: + +```solidity +for(uint256 i; i < range; ++) { + variable_a += 1; +} +``` + +Your analysis will need to know when to stop. There are two main strategies: (1) iterate on each node a finite number of times, (2) compute a so-called _fixpoint_. A fixpoint essentially means that analyzing the node doesn't provide any meaningful information. + +An example of a fixpoint used can be found in the reentrancy detectors: Slither explores the nodes and looks for external calls, reads, and writes to storage. Once it has reached a fixpoint ([reentrancy.py#L125-L131](https://github.com/crytic/slither/blob/master/slither/detectors/reentrancy/reentrancy.py#L125-L131)), it stops the exploration and analyzes the results to see if a reentrancy is present, through different reentrancy patterns ([reentrancy_benign.py](https://github.com/crytic/slither/blob/b275bcc824b1b932310cf03b6bfb1a1fef0ebae1/slither/detectors/reentrancy/reentrancy_benign.py), [reentrancy_read_before_write.py](https://github.com/crytic/slither/blob/b275bcc824b1b932310cf03b6bfb1a1fef0ebae1/slither/detectors/reentrancy/reentrancy_read_before_write.py), [reentrancy_eth.py](https://github.com/crytic/slither/blob/b275bcc824b1b932310cf03b6bfb1a1fef0ebae1/slither/detectors/reentrancy/reentrancy_eth.py)). + +Writing analyses using efficient fixed-point computation requires a good understanding of how the analysis propagates its information. + +### Intermediate representation + +An intermediate representation (IR) is a language designed to be more amenable to static analysis than the original one. Slither translates Solidity to its own IR: [SlithIR](https://github.com/crytic/slither/wiki/SlithIR). + +Understanding SlithIR is not necessary if you only want to write basic checks. However, it becomes essential if you plan to write advanced semantic analyses. The [SlithIR](https://github.com/crytic/slither/wiki/Printer-documentation#slithir) and [SSA](https://github.com/crytic/slither/wiki/Printer-documentation#slithir-ssa) printers can help you understand how the code is translated. diff --git a/secure_contracts_docs/src/detectors/Adding-a-new-detector.md b/secure_contracts_docs/src/detectors/Adding-a-new-detector.md new file mode 100644 index 0000000000..c905a604d6 --- /dev/null +++ b/secure_contracts_docs/src/detectors/Adding-a-new-detector.md @@ -0,0 +1,63 @@ +Slither's plugin architecture lets you integrate new detectors that run from the command line. + +## Detector Skeleton + +The skeleton for a detector is: + +```python +from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification + + +class Skeleton(AbstractDetector): + """ + Documentation + """ + + ARGUMENT = 'mydetector' # slither will launch the detector with slither.py --detect mydetector + HELP = 'Help printed by slither' + IMPACT = DetectorClassification.HIGH + CONFIDENCE = DetectorClassification.HIGH + + WIKI = '' + + WIKI_TITLE = '' + WIKI_DESCRIPTION = '' + WIKI_EXPLOIT_SCENARIO = '' + WIKI_RECOMMENDATION = '' + + def _detect(self): + info = ['This is an example'] + res = self.generate_result(info) + + return [res] +``` + +- `ARGUMENT` lets you run the detector from the command line +- `HELP` is the information printed from the command line +- `IMPACT` indicates the impact of the issue. Allowed values are: + - `DetectorClassification.OPTIMIZATION`: printed in green + - `DetectorClassification.INFORMATIONAL`: printed in green + - `DetectorClassification.LOW`: printed in green + - `DetectorClassification.MEDIUM`: printed in yellow + - `DetectorClassification.HIGH`: printed in red +- `CONFIDENCE` indicates your confidence in the analysis. Allowed values are: + - `DetectorClassification.LOW` + - `DetectorClassification.MEDIUM` + - `DetectorClassification.HIGH` +- `WIKI` constants are used to generate automatically the documentation. + +`_detect()` needs to return a list of findings. A finding is an element generated with `self.generate_result(info)`, where `info` is a list of text or contract's object (contract, function, node, ...) + +An `AbstractDetector` object has the `slither` attribute, which returns the current `Slither` object. + +## Integration + +You can integrate your detector into Slither by: +- Adding it in [slither/detectors/all_detectors.py](https://github.com/trailofbits/slither/blob/ae7c410938b616d993e6c27678f6e48d9a4d7dd6/slither/detectors/all_detectors.py) +- or, by creating a plugin package (see the [skeleton example](https://github.com/trailofbits/slither/tree/ae7c410938b616d993e6c27678f6e48d9a4d7dd6/plugin_example)). + +### Test the detector +See [CONTRIBUTING.md#development-environment](https://github.com/crytic/slither/blob/master/CONTRIBUTING.md#development-environment) + +## Example +[backdoor.py](https://github.com/crytic/slither/blob/ae7c410938b616d993e6c27678f6e48d9a4d7dd6/slither/detectors/examples/backdoor.py) will detect any function with `backdoor` in its name. diff --git a/secure_contracts_docs/src/detectors/Detector-Documentation.md b/secure_contracts_docs/src/detectors/Detector-Documentation.md new file mode 100644 index 0000000000..f17109f338 --- /dev/null +++ b/secure_contracts_docs/src/detectors/Detector-Documentation.md @@ -0,0 +1,2761 @@ + +# Public Detectors + +List of public detectors + + + + +## Storage ABIEncoderV2 Array +### Configuration +* Check: `abiencoderv2-array` +* Severity: `High` +* Confidence: `High` + +### Description +`solc` versions `0.4.7`-`0.5.9` contain a [compiler bug](https://blog.ethereum.org/2019/06/25/solidity-storage-array-bugs) leading to incorrect ABI encoder usage. + +### Exploit Scenario: + +```solidity +contract A { + uint[2][3] bad_arr = [[1, 2], [3, 4], [5, 6]]; + + /* Array of arrays passed to abi.encode is vulnerable */ + function bad() public { + bytes memory b = abi.encode(bad_arr); + } +} +``` +`abi.encode(bad_arr)` in a call to `bad()` will incorrectly encode the array as `[[1, 2], [2, 3], [3, 4]]` and lead to unintended behavior. + + +### Recommendation +Use a compiler >= `0.5.10`. + +## Arbitrary `from` in transferFrom +### Configuration +* Check: `arbitrary-send-erc20` +* Severity: `High` +* Confidence: `High` + +### Description +Detect when `msg.sender` is not used as `from` in transferFrom. + +### Exploit Scenario: + +```solidity + function a(address from, address to, uint256 amount) public { + erc20.transferFrom(from, to, am); + } +``` +Alice approves this contract to spend her ERC20 tokens. Bob can call `a` and specify Alice's address as the `from` parameter in `transferFrom`, allowing him to transfer Alice's tokens to himself. + +### Recommendation + +Use `msg.sender` as `from` in transferFrom. + + +## Modifying storage array by value +### Configuration +* Check: `array-by-reference` +* Severity: `High` +* Confidence: `High` + +### Description +Detect arrays passed to a function that expects reference to a storage array + +### Exploit Scenario: + +```solidity +contract Memory { + uint[1] public x; // storage + + function f() public { + f1(x); // update x + f2(x); // do not update x + } + + function f1(uint[1] storage arr) internal { // by reference + arr[0] = 1; + } + + function f2(uint[1] arr) internal { // by value + arr[0] = 2; + } +} +``` + +Bob calls `f()`. Bob assumes that at the end of the call `x[0]` is 2, but it is 1. +As a result, Bob's usage of the contract is incorrect. + +### Recommendation +Ensure the correct usage of `memory` and `storage` in the function parameters. Make all the locations explicit. + +## ABI encodePacked Collision +### Configuration +* Check: `encode-packed-collision` +* Severity: `High` +* Confidence: `High` + +### Description +Detect collision due to dynamic type usages in `abi.encodePacked` + +### Exploit Scenario: + +```solidity +contract Sign { + function get_hash_for_signature(string name, string doc) external returns(bytes32) { + return keccak256(abi.encodePacked(name, doc)); + } +} +``` +Bob calls `get_hash_for_signature` with (`bob`, `This is the content`). The hash returned is used as an ID. +Eve creates a collision with the ID using (`bo`, `bThis is the content`) and compromises the system. + + +### Recommendation +Do not use more than one dynamic type in `abi.encodePacked()` +(see the [Solidity documentation](https://solidity.readthedocs.io/en/v0.5.10/abi-spec.html?highlight=abi.encodePacked#non-standard-packed-modeDynamic)). +Use `abi.encode()`, preferably. + +## Incorrect shift in assembly. +### Configuration +* Check: `incorrect-shift` +* Severity: `High` +* Confidence: `High` + +### Description +Detect if the values in a shift operation are reversed + +### Exploit Scenario: + +```solidity +contract C { + function f() internal returns (uint a) { + assembly { + a := shr(a, 8) + } + } +} +``` +The shift statement will right-shift the constant 8 by `a` bits + +### Recommendation +Swap the order of parameters. + +## Multiple constructor schemes +### Configuration +* Check: `multiple-constructors` +* Severity: `High` +* Confidence: `High` + +### Description +Detect multiple constructor definitions in the same contract (using new and old schemes). + +### Exploit Scenario: + +```solidity +contract A { + uint x; + constructor() public { + x = 0; + } + function A() public { + x = 1; + } + + function test() public returns(uint) { + return x; + } +} +``` +In Solidity [0.4.22](https://github.com/ethereum/solidity/releases/tag/v0.4.23), a contract with both constructor schemes will compile. The first constructor will take precedence over the second, which may be unintended. + +### Recommendation +Only declare one constructor, preferably using the new scheme `constructor(...)` instead of `function (...)`. + +## Name reused +### Configuration +* Check: `name-reused` +* Severity: `High` +* Confidence: `High` + +### Description +If a codebase has two contracts the similar names, the compilation artifacts +will not contain one of the contracts with the duplicate name. + +### Exploit Scenario: + +Bob's `truffle` codebase has two contracts named `ERC20`. +When `truffle compile` runs, only one of the two contracts will generate artifacts in `build/contracts`. +As a result, the second contract cannot be analyzed. + + +### Recommendation +Rename the contract. + +## Protected Variables +### Configuration +* Check: `protected-vars` +* Severity: `High` +* Confidence: `High` + +### Description +Detect unprotected variable that are marked protected + +### Exploit Scenario: + +```solidity +contract Buggy{ + + /// @custom:security write-protection="onlyOwner()" + address owner; + + function set_protected() public onlyOwner(){ + owner = msg.sender; + } + + function set_not_protected() public{ + owner = msg.sender; + } +} +``` +`owner` must be always written by function using `onlyOwner` (`write-protection="onlyOwner()"`), however anyone can call `set_not_protected`. + + +### Recommendation +Add access controls to the vulnerable function + +## Public mappings with nested variables +### Configuration +* Check: `public-mappings-nested` +* Severity: `High` +* Confidence: `High` + +### Description +Prior to Solidity 0.5, a public mapping with nested structures returned [incorrect values](https://github.com/ethereum/solidity/issues/5520). + +### Exploit Scenario: +Bob interacts with a contract that has a public mapping with nested structures. The values returned by the mapping are incorrect, breaking Bob's usage + +### Recommendation +Do not use public mapping with nested structures. + +## Right-to-Left-Override character +### Configuration +* Check: `rtlo` +* Severity: `High` +* Confidence: `High` + +### Description +An attacker can manipulate the logic of the contract by using a right-to-left-override character (`U+202E)`. + +### Exploit Scenario: + +```solidity +contract Token +{ + + address payable o; // owner + mapping(address => uint) tokens; + + function withdraw() external returns(uint) + { + uint amount = tokens[msg.sender]; + address payable d = msg.sender; + tokens[msg.sender] = 0; + _withdraw(/*owner‮/*noitanitsed*/ d, o/*‭ + /*value */, amount); + } + + function _withdraw(address payable fee_receiver, address payable destination, uint value) internal + { + fee_receiver.transfer(1); + destination.transfer(value); + } +} +``` + +`Token` uses the right-to-left-override character when calling `_withdraw`. As a result, the fee is incorrectly sent to `msg.sender`, and the token balance is sent to the owner. + + + +### Recommendation +Special control characters must not be allowed. + +## State variable shadowing +### Configuration +* Check: `shadowing-state` +* Severity: `High` +* Confidence: `High` + +### Description +Detection of state variables shadowed. + +### Exploit Scenario: + +```solidity +contract BaseContract{ + address owner; + + modifier isOwner(){ + require(owner == msg.sender); + _; + } + +} + +contract DerivedContract is BaseContract{ + address owner; + + constructor(){ + owner = msg.sender; + } + + function withdraw() isOwner() external{ + msg.sender.transfer(this.balance); + } +} +``` +`owner` of `BaseContract` is never assigned and the modifier `isOwner` does not work. + +### Recommendation +Remove the state variable shadowing. + +## Suicidal +### Configuration +* Check: `suicidal` +* Severity: `High` +* Confidence: `High` + +### Description +Unprotected call to a function executing `selfdestruct`/`suicide`. + +### Exploit Scenario: + +```solidity +contract Suicidal{ + function kill() public{ + selfdestruct(msg.sender); + } +} +``` +Bob calls `kill` and destructs the contract. + +### Recommendation +Protect access to all sensitive functions. + +## Uninitialized state variables +### Configuration +* Check: `uninitialized-state` +* Severity: `High` +* Confidence: `High` + +### Description +Uninitialized state variables. + +### Exploit Scenario: + +```solidity +contract Uninitialized{ + address destination; + + function transfer() payable public{ + destination.transfer(msg.value); + } +} +``` +Bob calls `transfer`. As a result, the Ether are sent to the address `0x0` and are lost. + + +### Recommendation + +Initialize all the variables. If a variable is meant to be initialized to zero, explicitly set it to zero to improve code readability. + + +## Uninitialized storage variables +### Configuration +* Check: `uninitialized-storage` +* Severity: `High` +* Confidence: `High` + +### Description +An uninitialized storage variable will act as a reference to the first state variable, and can override a critical variable. + +### Exploit Scenario: + +```solidity +contract Uninitialized{ + address owner = msg.sender; + + struct St{ + uint a; + } + + function func() { + St st; + st.a = 0x0; + } +} +``` +Bob calls `func`. As a result, `owner` is overridden to `0`. + + +### Recommendation +Initialize all storage variables. + +## Unprotected upgradeable contract +### Configuration +* Check: `unprotected-upgrade` +* Severity: `High` +* Confidence: `High` + +### Description +Detects logic contract that can be destructed. + +### Exploit Scenario: + +```solidity +contract Buggy is Initializable{ + address payable owner; + + function initialize() external initializer{ + require(owner == address(0)); + owner = msg.sender; + } + function kill() external{ + require(msg.sender == owner); + selfdestruct(owner); + } +} +``` +Buggy is an upgradeable contract. Anyone can call initialize on the logic contract, and destruct the contract. + + +### Recommendation +Add a constructor to ensure `initialize` cannot be called on the logic contract. + +## Arbitrary `from` in transferFrom used with permit +### Configuration +* Check: `arbitrary-send-erc20-permit` +* Severity: `High` +* Confidence: `Medium` + +### Description +Detect when `msg.sender` is not used as `from` in transferFrom and permit is used. + +### Exploit Scenario: + +```solidity + function bad(address from, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s, address to) public { + erc20.permit(from, address(this), value, deadline, v, r, s); + erc20.transferFrom(from, to, value); + } +``` +If an ERC20 token does not implement permit and has a fallback function e.g. WETH, transferFrom allows an attacker to transfer all tokens approved for this contract. + +### Recommendation + +Ensure that the underlying ERC20 token correctly implements a permit function. + + +## Functions that send Ether to arbitrary destinations +### Configuration +* Check: `arbitrary-send-eth` +* Severity: `High` +* Confidence: `Medium` + +### Description +Unprotected call to a function sending Ether to an arbitrary address. + +### Exploit Scenario: + +```solidity +contract ArbitrarySendEth{ + address destination; + function setDestination(){ + destination = msg.sender; + } + + function withdraw() public{ + destination.transfer(this.balance); + } +} +``` +Bob calls `setDestination` and `withdraw`. As a result he withdraws the contract's balance. + +### Recommendation +Ensure that an arbitrary user cannot withdraw unauthorized funds. + +## Array Length Assignment +### Configuration +* Check: `controlled-array-length` +* Severity: `High` +* Confidence: `Medium` + +### Description +Detects the direct assignment of an array's length. + +### Exploit Scenario: + +```solidity +contract A { + uint[] testArray; // dynamic size array + + function f(uint usersCount) public { + // ... + testArray.length = usersCount; + // ... + } + + function g(uint userIndex, uint val) public { + // ... + testArray[userIndex] = val; + // ... + } +} +``` +Contract storage/state-variables are indexed by a 256-bit integer. +The user can set the array length to `2**256-1` in order to index all storage slots. +In the example above, one could call the function `f` to set the array length, then call the function `g` to control any storage slot desired. +Note that storage slots here are indexed via a hash of the indexers; nonetheless, all storage will still be accessible and could be controlled by the attacker. + +### Recommendation +Do not allow array lengths to be set directly set; instead, opt to add values as needed. +Otherwise, thoroughly review the contract to ensure a user-controlled variable cannot reach an array length assignment. + +## Controlled Delegatecall +### Configuration +* Check: `controlled-delegatecall` +* Severity: `High` +* Confidence: `Medium` + +### Description +`Delegatecall` or `callcode` to an address controlled by the user. + +### Exploit Scenario: + +```solidity +contract Delegatecall{ + function delegate(address to, bytes data){ + to.delegatecall(data); + } +} +``` +Bob calls `delegate` and delegates the execution to his malicious contract. As a result, Bob withdraws the funds of the contract and destructs it. + +### Recommendation +Avoid using `delegatecall`. Use only trusted destinations. + +## Payable functions using `delegatecall` inside a loop +### Configuration +* Check: `delegatecall-loop` +* Severity: `High` +* Confidence: `Medium` + +### Description +Detect the use of `delegatecall` inside a loop in a payable function. + +### Exploit Scenario: + +```solidity +contract DelegatecallInLoop{ + + mapping (address => uint256) balances; + + function bad(address[] memory receivers) public payable { + for (uint256 i = 0; i < receivers.length; i++) { + address(this).delegatecall(abi.encodeWithSignature("addBalance(address)", receivers[i])); + } + } + + function addBalance(address a) public payable { + balances[a] += msg.value; + } + +} +``` +When calling `bad` the same `msg.value` amount will be accredited multiple times. + +### Recommendation + +Carefully check that the function called by `delegatecall` is not payable/doesn't use `msg.value`. + + +## Incorrect exponentiation +### Configuration +* Check: `incorrect-exp` +* Severity: `High` +* Confidence: `Medium` + +### Description +Detect use of bitwise `xor ^` instead of exponential `**` + +### Exploit Scenario: + +```solidity +contract Bug{ + uint UINT_MAX = 2^256 - 1; + ... +} +``` +Alice deploys a contract in which `UINT_MAX` incorrectly uses `^` operator instead of `**` for exponentiation + +### Recommendation +Use the correct operator `**` for exponentiation. + +## Incorrect return in assembly +### Configuration +* Check: `incorrect-return` +* Severity: `High` +* Confidence: `Medium` + +### Description +Detect if `return` in an assembly block halts unexpectedly the execution. + +### Exploit Scenario: + +```solidity +contract C { + function f() internal returns (uint a, uint b) { + assembly { + return (5, 6) + } + } + + function g() returns (bool){ + f(); + return true; + } +} +``` +The return statement in `f` will cause execution in `g` to halt. +The function will return 6 bytes starting from offset 5, instead of returning a boolean. + +### Recommendation +Use the `leave` statement. + +## `msg.value` inside a loop +### Configuration +* Check: `msg-value-loop` +* Severity: `High` +* Confidence: `Medium` + +### Description +Detect the use of `msg.value` inside a loop. + +### Exploit Scenario: + +```solidity +contract MsgValueInLoop{ + + mapping (address => uint256) balances; + + function bad(address[] memory receivers) public payable { + for (uint256 i=0; i < receivers.length; i++) { + balances[receivers[i]] += msg.value; + } + } + +} +``` + + +### Recommendation + +Provide an explicit array of amounts alongside the receivers array, and check that the sum of all amounts matches `msg.value`. + + +## Reentrancy vulnerabilities +### Configuration +* Check: `reentrancy-eth` +* Severity: `High` +* Confidence: `Medium` + +### Description + +Detection of the [reentrancy bug](https://github.com/trailofbits/not-so-smart-contracts/tree/master/reentrancy). +Do not report reentrancies that don't involve Ether (see `reentrancy-no-eth`) + +### Exploit Scenario: + +```solidity + function withdrawBalance(){ + // send userBalance[msg.sender] Ether to msg.sender + // if msg.sender is a contract, it will call its fallback function + if( ! (msg.sender.call.value(userBalance[msg.sender])() ) ){ + throw; + } + userBalance[msg.sender] = 0; + } +``` + +Bob uses the re-entrancy bug to call `withdrawBalance` two times, and withdraw more than its initial deposit to the contract. + +### Recommendation +Apply the [`check-effects-interactions pattern`](http://solidity.readthedocs.io/en/v0.4.21/security-considerations.html#re-entrancy). + +## Return instead of leave in assembly +### Configuration +* Check: `return-leave` +* Severity: `High` +* Confidence: `Medium` + +### Description +Detect if a `return` is used where a `leave` should be used. + +### Exploit Scenario: + +```solidity +contract C { + function f() internal returns (uint a, uint b) { + assembly { + return (5, 6) + } + } + +} +``` +The function will halt the execution, instead of returning a two uint. + +### Recommendation +Use the `leave` statement. + +## Storage Signed Integer Array +### Configuration +* Check: `storage-array` +* Severity: `High` +* Confidence: `Medium` + +### Description +`solc` versions `0.4.7`-`0.5.9` contain [a compiler bug](https://blog.ethereum.org/2019/06/25/solidity-storage-array-bugs) +leading to incorrect values in signed integer arrays. + +### Exploit Scenario: + +```solidity +contract A { + int[3] ether_balances; // storage signed integer array + function bad0() private { + // ... + ether_balances = [-1, -1, -1]; + // ... + } +} +``` +`bad0()` uses a (storage-allocated) signed integer array state variable to store the ether balances of three accounts. +`-1` is supposed to indicate uninitialized values but the Solidity bug makes these as `1`, which could be exploited by the accounts. + + +### Recommendation +Use a compiler version >= `0.5.10`. + +## Unchecked transfer +### Configuration +* Check: `unchecked-transfer` +* Severity: `High` +* Confidence: `Medium` + +### Description +The return value of an external transfer/transferFrom call is not checked + +### Exploit Scenario: + +```solidity +contract Token { + function transferFrom(address _from, address _to, uint256 _value) public returns (bool success); +} +contract MyBank{ + mapping(address => uint) balances; + Token token; + function deposit(uint amount) public{ + token.transferFrom(msg.sender, address(this), amount); + balances[msg.sender] += amount; + } +} +``` +Several tokens do not revert in case of failure and return false. If one of these tokens is used in `MyBank`, `deposit` will not revert if the transfer fails, and an attacker can call `deposit` for free.. + +### Recommendation +Use `SafeERC20`, or ensure that the transfer/transferFrom return value is checked. + +## Weak PRNG +### Configuration +* Check: `weak-prng` +* Severity: `High` +* Confidence: `Medium` + +### Description +Weak PRNG due to a modulo on `block.timestamp`, `now` or `blockhash`. These can be influenced by miners to some extent so they should be avoided. + +### Exploit Scenario: + +```solidity +contract Game { + + uint reward_determining_number; + + function guessing() external{ + reward_determining_number = uint256(block.blockhash(10000)) % 10; + } +} +``` +Eve is a miner. Eve calls `guessing` and re-orders the block containing the transaction. +As a result, Eve wins the game. + +### Recommendation +Do not use `block.timestamp`, `now` or `blockhash` as a source of randomness + +## Codex +### Configuration +* Check: `codex` +* Severity: `High` +* Confidence: `Low` + +### Description +Use [codex](https://openai.com/blog/openai-codex/) to find vulnerabilities + +### Exploit Scenario: +N/A + +### Recommendation +Review codex's message. + +## Domain separator collision +### Configuration +* Check: `domain-separator-collision` +* Severity: `Medium` +* Confidence: `High` + +### Description +An ERC20 token has a function whose signature collides with EIP-2612's DOMAIN_SEPARATOR(), causing unanticipated behavior for contracts using `permit` functionality. + +### Exploit Scenario: + +```solidity +contract Contract{ + function some_collisions() external() {} +} +``` +`some_collision` clashes with EIP-2612's DOMAIN_SEPARATOR() and will interfere with contract's using `permit`. + +### Recommendation +Remove or rename the function that collides with DOMAIN_SEPARATOR(). + +## Dangerous enum conversion +### Configuration +* Check: `enum-conversion` +* Severity: `Medium` +* Confidence: `High` + +### Description +Detect out-of-range `enum` conversion (`solc` < `0.4.5`). + +### Exploit Scenario: + +```solidity + pragma solidity 0.4.2; + contract Test{ + + enum E{a} + + function bug(uint a) public returns(E){ + return E(a); + } +} +``` +Attackers can trigger unexpected behaviour by calling `bug(1)`. + +### Recommendation +Use a recent compiler version. If `solc` <`0.4.5` is required, check the `enum` conversion range. + +## Incorrect erc20 interface +### Configuration +* Check: `erc20-interface` +* Severity: `Medium` +* Confidence: `High` + +### Description +Incorrect return values for `ERC20` functions. A contract compiled with Solidity > 0.4.22 interacting with these functions will fail to execute them, as the return value is missing. + +### Exploit Scenario: + +```solidity +contract Token{ + function transfer(address to, uint value) external; + //... +} +``` +`Token.transfer` does not return a boolean. Bob deploys the token. Alice creates a contract that interacts with it but assumes a correct `ERC20` interface implementation. Alice's contract is unable to interact with Bob's contract. + +### Recommendation +Set the appropriate return values and types for the defined `ERC20` functions. + +## Incorrect erc721 interface +### Configuration +* Check: `erc721-interface` +* Severity: `Medium` +* Confidence: `High` + +### Description +Incorrect return values for `ERC721` functions. A contract compiled with solidity > 0.4.22 interacting with these functions will fail to execute them, as the return value is missing. + +### Exploit Scenario: + +```solidity +contract Token{ + function ownerOf(uint256 _tokenId) external view returns (bool); + //... +} +``` +`Token.ownerOf` does not return an address like `ERC721` expects. Bob deploys the token. Alice creates a contract that interacts with it but assumes a correct `ERC721` interface implementation. Alice's contract is unable to interact with Bob's contract. + +### Recommendation +Set the appropriate return values and vtypes for the defined `ERC721` functions. + +## Dangerous strict equalities +### Configuration +* Check: `incorrect-equality` +* Severity: `Medium` +* Confidence: `High` + +### Description +Use of strict equalities that can be easily manipulated by an attacker. + +### Exploit Scenario: + +```solidity +contract Crowdsale{ + function fund_reached() public returns(bool){ + return this.balance == 100 ether; + } +``` +`Crowdsale` relies on `fund_reached` to know when to stop the sale of tokens. +`Crowdsale` reaches 100 Ether. Bob sends 0.1 Ether. As a result, `fund_reached` is always false and the `crowdsale` never ends. + +### Recommendation +Don't use strict equality to determine if an account has enough Ether or tokens. + +## Contracts that lock Ether +### Configuration +* Check: `locked-ether` +* Severity: `Medium` +* Confidence: `High` + +### Description +Contract with a `payable` function, but without a withdrawal capacity. + +### Exploit Scenario: + +```solidity +pragma solidity 0.4.24; +contract Locked{ + function receive() payable public{ + } +} +``` +Every Ether sent to `Locked` will be lost. + +### Recommendation +Remove the payable attribute or add a withdraw function. + +## Deletion on mapping containing a structure +### Configuration +* Check: `mapping-deletion` +* Severity: `Medium` +* Confidence: `High` + +### Description +A deletion in a structure containing a mapping will not delete the mapping (see the [Solidity documentation](https://solidity.readthedocs.io/en/latest/types.html##delete)). The remaining data may be used to compromise the contract. + +### Exploit Scenario: + +```solidity + struct BalancesStruct{ + address owner; + mapping(address => uint) balances; + } + mapping(address => BalancesStruct) public stackBalance; + + function remove() internal{ + delete stackBalance[msg.sender]; + } +``` +`remove` deletes an item of `stackBalance`. +The mapping `balances` is never deleted, so `remove` does not work as intended. + +### Recommendation +Use a lock mechanism instead of a deletion to disable structure containing a mapping. + +## Pyth deprecated functions +### Configuration +* Check: `pyth-deprecated-functions` +* Severity: `Medium` +* Confidence: `High` + +### Description +Detect when a Pyth deprecated function is used + +### Exploit Scenario: + +```solidity +import "@pythnetwork/pyth-sdk-solidity/IPyth.sol"; +import "@pythnetwork/pyth-sdk-solidity/PythStructs.sol"; + +contract C { + + IPyth pyth; + + constructor(IPyth _pyth) { + pyth = _pyth; + } + + function A(bytes32 priceId) public { + PythStructs.Price memory price = pyth.getPrice(priceId); + ... + } +} +``` +The function `A` uses the deprecated `getPrice` Pyth function. + + +### Recommendation +Do not use deprecated Pyth functions. Visit https://api-reference.pyth.network/. + +## Pyth unchecked confidence level +### Configuration +* Check: `pyth-unchecked-confidence` +* Severity: `Medium` +* Confidence: `High` + +### Description +Detect when the confidence level of a Pyth price is not checked + +### Exploit Scenario: + +```solidity +import "@pythnetwork/pyth-sdk-solidity/IPyth.sol"; +import "@pythnetwork/pyth-sdk-solidity/PythStructs.sol"; + +contract C { + IPyth pyth; + + constructor(IPyth _pyth) { + pyth = _pyth; + } + + function bad(bytes32 id, uint256 age) public { + PythStructs.Price memory price = pyth.getEmaPriceNoOlderThan(id, age); + // Use price + } +} +``` +The function `A` uses the price without checking its confidence level. + + +### Recommendation +Check the confidence level of a Pyth price. Visit https://docs.pyth.network/price-feeds/best-practices#confidence-intervals for more information. + +## Pyth unchecked publishTime +### Configuration +* Check: `pyth-unchecked-publishtime` +* Severity: `Medium` +* Confidence: `High` + +### Description +Detect when the publishTime of a Pyth price is not checked + +### Exploit Scenario: + +```solidity +import "@pythnetwork/pyth-sdk-solidity/IPyth.sol"; +import "@pythnetwork/pyth-sdk-solidity/PythStructs.sol"; + +contract C { + IPyth pyth; + + constructor(IPyth _pyth) { + pyth = _pyth; + } + + function bad(bytes32 id) public { + PythStructs.Price memory price = pyth.getEmaPriceUnsafe(id); + // Use price + } +} +``` +The function `A` uses the price without checking its `publishTime` coming from the `getEmaPriceUnsafe` function. + + +### Recommendation +Check the publishTime of a Pyth price. + +## State variable shadowing from abstract contracts +### Configuration +* Check: `shadowing-abstract` +* Severity: `Medium` +* Confidence: `High` + +### Description +Detection of state variables shadowed from abstract contracts. + +### Exploit Scenario: + +```solidity +contract BaseContract{ + address owner; +} + +contract DerivedContract is BaseContract{ + address owner; +} +``` +`owner` of `BaseContract` is shadowed in `DerivedContract`. + +### Recommendation +Remove the state variable shadowing. + +## Tautological compare +### Configuration +* Check: `tautological-compare` +* Severity: `Medium` +* Confidence: `High` + +### Description +A variable compared to itself is probably an error as it will always return `true` for `==`, `>=`, `<=` and always `false` for `<`, `>` and `!=`. + +### Exploit Scenario: + +```solidity + function check(uint a) external returns(bool){ + return (a >= a); + } +``` +`check` always return true. + +### Recommendation +Remove comparison or compare to different value. + +## Tautology or contradiction +### Configuration +* Check: `tautology` +* Severity: `Medium` +* Confidence: `High` + +### Description +Detects expressions that are tautologies or contradictions. + +### Exploit Scenario: + +```solidity +contract A { + function f(uint x) public { + // ... + if (x >= 0) { // bad -- always true + // ... + } + // ... + } + + function g(uint8 y) public returns (bool) { + // ... + return (y < 512); // bad! + // ... + } +} +``` +`x` is a `uint256`, so `x >= 0` will be always true. +`y` is a `uint8`, so `y <512` will be always true. + + +### Recommendation +Fix the incorrect comparison by changing the value type or the comparison. + +## Write after write +### Configuration +* Check: `write-after-write` +* Severity: `Medium` +* Confidence: `High` + +### Description +Detects variables that are written but never read and written again. + +### Exploit Scenario: + + ```solidity + contract Buggy{ + function my_func() external initializer{ + // ... + a = b; + a = c; + // .. + } + } + ``` + `a` is first asigned to `b`, and then to `c`. As a result the first write does nothing. + +### Recommendation +Fix or remove the writes. + +## Misuse of a Boolean constant +### Configuration +* Check: `boolean-cst` +* Severity: `Medium` +* Confidence: `Medium` + +### Description +Detects the misuse of a Boolean constant. + +### Exploit Scenario: + +```solidity +contract A { + function f(uint x) public { + // ... + if (false) { // bad! + // ... + } + // ... + } + + function g(bool b) public returns (bool) { + // ... + return (b || true); // bad! + // ... + } +} +``` +Boolean constants in code have only a few legitimate uses. +Other uses (in complex expressions, as conditionals) indicate either an error or, most likely, the persistence of faulty code. + +### Recommendation +Verify and simplify the condition. + +## Chronicle unchecked price +### Configuration +* Check: `chronicle-unchecked-price` +* Severity: `Medium` +* Confidence: `Medium` + +### Description +Chronicle oracle is used and the price returned is not checked to be valid. For more information https://docs.chroniclelabs.org/Resources/FAQ/Oracles#how-do-i-check-if-an-oracle-becomes-inactive-gets-deprecated. + +### Exploit Scenario: + +```solidity +contract C { + IChronicle chronicle; + + constructor(address a) { + chronicle = IChronicle(a); + } + + function bad() public { + uint256 price = chronicle.read(); + } +``` +The `bad` function gets the price from Chronicle by calling the read function however it does not check if the price is valid. + +### Recommendation +Validate that the price returned by the oracle is valid. + +## Constant functions using assembly code +### Configuration +* Check: `constant-function-asm` +* Severity: `Medium` +* Confidence: `Medium` + +### Description + +Functions declared as `constant`/`pure`/`view` using assembly code. + +`constant`/`pure`/`view` was not enforced prior to Solidity 0.5. +Starting from Solidity 0.5, a call to a `constant`/`pure`/`view` function uses the `STATICCALL` opcode, which reverts in case of state modification. + +As a result, a call to an [incorrectly labeled function may trap a contract compiled with Solidity 0.5](https://solidity.readthedocs.io/en/develop/050-breaking-changes.html#interoperability-with-older-contracts). + +### Exploit Scenario: + +```solidity +contract Constant{ + uint counter; + function get() public view returns(uint){ + counter = counter +1; + return counter + } +} +``` +`Constant` was deployed with Solidity 0.4.25. Bob writes a smart contract that interacts with `Constant` in Solidity 0.5.0. +All the calls to `get` revert, breaking Bob's smart contract execution. + +### Recommendation +Ensure the attributes of contracts compiled prior to Solidity 0.5.0 are correct. + +## Constant functions changing the state +### Configuration +* Check: `constant-function-state` +* Severity: `Medium` +* Confidence: `Medium` + +### Description + +Functions declared as `constant`/`pure`/`view` change the state. + +`constant`/`pure`/`view` was not enforced prior to Solidity 0.5. +Starting from Solidity 0.5, a call to a `constant`/`pure`/`view` function uses the `STATICCALL` opcode, which reverts in case of state modification. + +As a result, a call to an [incorrectly labeled function may trap a contract compiled with Solidity 0.5](https://solidity.readthedocs.io/en/develop/050-breaking-changes.html#interoperability-with-older-contracts). + +### Exploit Scenario: + +```solidity +contract Constant{ + uint counter; + function get() public view returns(uint){ + counter = counter +1; + return counter + } +} +``` +`Constant` was deployed with Solidity 0.4.25. Bob writes a smart contract that interacts with `Constant` in Solidity 0.5.0. +All the calls to `get` revert, breaking Bob's smart contract execution. + +### Recommendation +Ensure that attributes of contracts compiled prior to Solidity 0.5.0 are correct. + +## Divide before multiply +### Configuration +* Check: `divide-before-multiply` +* Severity: `Medium` +* Confidence: `Medium` + +### Description +Solidity's integer division truncates. Thus, performing division before multiplication can lead to precision loss. + +### Exploit Scenario: + +```solidity +contract A { + function f(uint n) public { + coins = (oldSupply / n) * interest; + } +} +``` +If `n` is greater than `oldSupply`, `coins` will be zero. For example, with `oldSupply = 5; n = 10, interest = 2`, coins will be zero. +If `(oldSupply * interest / n)` was used, `coins` would have been `1`. +In general, it's usually a good idea to re-arrange arithmetic to perform multiplication before division, unless the limit of a smaller type makes this dangerous. + +### Recommendation +Consider ordering multiplication before division. + +## Gelato unprotected randomness +### Configuration +* Check: `gelato-unprotected-randomness` +* Severity: `Medium` +* Confidence: `Medium` + +### Description +Detect calls to `_requestRandomness` within an unprotected function. + +### Exploit Scenario: + +```solidity +contract C is GelatoVRFConsumerBase { + function _fulfillRandomness( + uint256 randomness, + uint256, + bytes memory extraData + ) internal override { + // Do something with the random number + } + + function bad() public { + _requestRandomness(abi.encode(msg.sender)); + } +} +``` +The function `bad` is uprotected and requests randomness. + +### Recommendation +Function that request randomness should be allowed only to authorized users. + +## Out-of-order retryable transactions +### Configuration +* Check: `out-of-order-retryable` +* Severity: `Medium` +* Confidence: `Medium` + +### Description +Out-of-order retryable transactions + +### Exploit Scenario: + +```solidity +contract L1 { + function doStuffOnL2() external { + // Retryable A + IInbox(inbox).createRetryableTicket({ + to: l2contract, + l2CallValue: 0, + maxSubmissionCost: maxSubmissionCost, + excessFeeRefundAddress: msg.sender, + callValueRefundAddress: msg.sender, + gasLimit: gasLimit, + maxFeePerGas: maxFeePerGas, + data: abi.encodeCall(l2contract.claim_rewards, ()) + }); + // Retryable B + IInbox(inbox).createRetryableTicket({ + to: l2contract, + l2CallValue: 0, + maxSubmissionCost: maxSubmissionCost, + excessFeeRefundAddress: msg.sender, + callValueRefundAddress: msg.sender, + gasLimit: gas, + maxFeePerGas: maxFeePerGas, + data: abi.encodeCall(l2contract.unstake, ()) + }); + } +} + +contract L2 { + function claim_rewards() public { + // rewards is computed based on balance and staking period + uint unclaimed_rewards = _compute_and_update_rewards(); + token.safeTransfer(msg.sender, unclaimed_rewards); + } + + // Call claim_rewards before unstaking, otherwise you lose your rewards + function unstake() public { + _free_rewards(); // clean up rewards related variables + balance = balance[msg.sender]; + balance[msg.sender] = 0; + staked_token.safeTransfer(msg.sender, balance); + } +} +``` +Bob calls `doStuffOnL2` but the first retryable ticket calling `claim_rewards` fails. The second retryable ticket calling `unstake` is executed successfully. As a result, Bob loses his rewards. + +### Recommendation +Do not rely on the order or successful execution of retryable tickets. + +## Reentrancy vulnerabilities +### Configuration +* Check: `reentrancy-no-eth` +* Severity: `Medium` +* Confidence: `Medium` + +### Description + +Detection of the [reentrancy bug](https://github.com/trailofbits/not-so-smart-contracts/tree/master/reentrancy). +Do not report reentrancies that involve Ether (see `reentrancy-eth`). + +### Exploit Scenario: + +```solidity + function bug(){ + require(not_called); + if( ! (msg.sender.call() ) ){ + throw; + } + not_called = False; + } +``` + + +### Recommendation +Apply the [`check-effects-interactions` pattern](http://solidity.readthedocs.io/en/v0.4.21/security-considerations.html#re-entrancy). + +## Reused base constructors +### Configuration +* Check: `reused-constructor` +* Severity: `Medium` +* Confidence: `Medium` + +### Description +Detects if the same base constructor is called with arguments from two different locations in the same inheritance hierarchy. + +### Exploit Scenario: + +```solidity +pragma solidity ^0.4.0; + +contract A{ + uint num = 5; + constructor(uint x) public{ + num += x; + } +} + +contract B is A{ + constructor() A(2) public { /* ... */ } +} + +contract C is A { + constructor() A(3) public { /* ... */ } +} + +contract D is B, C { + constructor() public { /* ... */ } +} + +contract E is B { + constructor() A(1) public { /* ... */ } +} +``` +The constructor of `A` is called multiple times in `D` and `E`: +- `D` inherits from `B` and `C`, both of which construct `A`. +- `E` only inherits from `B`, but `B` and `E` construct `A`. +. + +### Recommendation +Remove the duplicate constructor call. + +## Dangerous usage of `tx.origin` +### Configuration +* Check: `tx-origin` +* Severity: `Medium` +* Confidence: `Medium` + +### Description +`tx.origin`-based protection can be abused by a malicious contract if a legitimate user interacts with the malicious contract. + +### Exploit Scenario: + +```solidity +contract TxOrigin { + address owner = msg.sender; + + function bug() { + require(tx.origin == owner); + } +``` +Bob is the owner of `TxOrigin`. Bob calls Eve's contract. Eve's contract calls `TxOrigin` and bypasses the `tx.origin` protection. + +### Recommendation +Do not use `tx.origin` for authorization. + +## Unchecked low-level calls +### Configuration +* Check: `unchecked-lowlevel` +* Severity: `Medium` +* Confidence: `Medium` + +### Description +The return value of a low-level call is not checked. + +### Exploit Scenario: + +```solidity +contract MyConc{ + function my_func(address payable dst) public payable{ + dst.call.value(msg.value)(""); + } +} +``` +The return value of the low-level call is not checked, so if the call fails, the Ether will be locked in the contract. +If the low level is used to prevent blocking operations, consider logging failed calls. + + +### Recommendation +Ensure that the return value of a low-level call is checked or logged. + +## Unchecked Send +### Configuration +* Check: `unchecked-send` +* Severity: `Medium` +* Confidence: `Medium` + +### Description +The return value of a `send` is not checked. + +### Exploit Scenario: + +```solidity +contract MyConc{ + function my_func(address payable dst) public payable{ + dst.send(msg.value); + } +} +``` +The return value of `send` is not checked, so if the send fails, the Ether will be locked in the contract. +If `send` is used to prevent blocking operations, consider logging the failed `send`. + + +### Recommendation +Ensure that the return value of `send` is checked or logged. + +## Uninitialized local variables +### Configuration +* Check: `uninitialized-local` +* Severity: `Medium` +* Confidence: `Medium` + +### Description +Uninitialized local variables. + +### Exploit Scenario: + +```solidity +contract Uninitialized is Owner{ + function withdraw() payable public onlyOwner{ + address to; + to.transfer(this.balance) + } +} +``` +Bob calls `transfer`. As a result, all Ether is sent to the address `0x0` and is lost. + +### Recommendation +Initialize all the variables. If a variable is meant to be initialized to zero, explicitly set it to zero to improve code readability. + +## Unused return +### Configuration +* Check: `unused-return` +* Severity: `Medium` +* Confidence: `Medium` + +### Description +The return value of an external call is not stored in a local or state variable. + +### Exploit Scenario: + +```solidity +contract MyConc{ + using SafeMath for uint; + function my_func(uint a, uint b) public{ + a.add(b); + } +} +``` +`MyConc` calls `add` of `SafeMath`, but does not store the result in `a`. As a result, the computation has no effect. + +### Recommendation +Ensure that all the return values of the function calls are used. + +## Chainlink Feed Registry usage +### Configuration +* Check: `chainlink-feed-registry` +* Severity: `Low` +* Confidence: `High` + +### Description +Detect when Chainlink Feed Registry is used. At the moment is only available on Ethereum Mainnet. + +### Exploit Scenario: + +```solidity +import "chainlink/contracts/src/v0.8/interfaces/FeedRegistryInteface.sol" + +contract A { + FeedRegistryInterface public immutable registry; + + constructor(address _registry) { + registry = _registry; + } + + function getPrice(address base, address quote) public return(uint256) { + (, int256 price,,,) = registry.latestRoundData(base, quote); + // Do price validation + return uint256(price); + } +} +``` +If the contract is deployed on a different chain than Ethereum Mainnet the `getPrice` function will revert. + + +### Recommendation +Do not use Chainlink Feed Registry outside of Ethereum Mainnet. + +## Incorrect modifier +### Configuration +* Check: `incorrect-modifier` +* Severity: `Low` +* Confidence: `High` + +### Description +If a modifier does not execute `_` or revert, the execution of the function will return the default value, which can be misleading for the caller. + +### Exploit Scenario: + +```solidity + modidfier myModif(){ + if(..){ + _; + } + } + function get() myModif returns(uint){ + + } +``` +If the condition in `myModif` is false, the execution of `get()` will return 0. + +### Recommendation +All the paths in a modifier must execute `_` or revert. + +## Optimism deprecated predeploy or function +### Configuration +* Check: `optimism-deprecation` +* Severity: `Low` +* Confidence: `High` + +### Description +Detect when deprecated Optimism predeploy or function is used. + +### Exploit Scenario: + +```solidity +interface GasPriceOracle { + function scalar() external view returns (uint256); +} + +contract Test { + GasPriceOracle constant OPT_GAS = GasPriceOracle(0x420000000000000000000000000000000000000F); + + function a() public { + OPT_GAS.scalar(); + } +} +``` +The call to the `scalar` function of the Optimism GasPriceOracle predeploy always revert. + + +### Recommendation +Do not use the deprecated components. + +## Builtin Symbol Shadowing +### Configuration +* Check: `shadowing-builtin` +* Severity: `Low` +* Confidence: `High` + +### Description +Detection of shadowing built-in symbols using local variables, state variables, functions, modifiers, or events. + +### Exploit Scenario: + +```solidity +pragma solidity ^0.4.24; + +contract Bug { + uint now; // Overshadows current time stamp. + + function assert(bool condition) public { + // Overshadows built-in symbol for providing assertions. + } + + function get_next_expiration(uint earlier_time) private returns (uint) { + return now + 259200; // References overshadowed timestamp. + } +} +``` +`now` is defined as a state variable, and shadows with the built-in symbol `now`. The function `assert` overshadows the built-in `assert` function. Any use of either of these built-in symbols may lead to unexpected results. + +### Recommendation +Rename the local variables, state variables, functions, modifiers, and events that shadow a builtin symbol. + +## Local variable shadowing +### Configuration +* Check: `shadowing-local` +* Severity: `Low` +* Confidence: `High` + +### Description +Detection of shadowing using local variables. + +### Exploit Scenario: + +```solidity +pragma solidity ^0.4.24; + +contract Bug { + uint owner; + + function sensitive_function(address owner) public { + // ... + require(owner == msg.sender); + } + + function alternate_sensitive_function() public { + address owner = msg.sender; + // ... + require(owner == msg.sender); + } +} +``` +`sensitive_function.owner` shadows `Bug.owner`. As a result, the use of `owner` in `sensitive_function` might be incorrect. + +### Recommendation +Rename the local variables that shadow another component. + +## Uninitialized function pointers in constructors +### Configuration +* Check: `uninitialized-fptr-cst` +* Severity: `Low` +* Confidence: `High` + +### Description +solc versions `0.4.5`-`0.4.26` and `0.5.0`-`0.5.8` contain a compiler bug leading to unexpected behavior when calling uninitialized function pointers in constructors. + +### Exploit Scenario: + +```solidity +contract bad0 { + + constructor() public { + /* Uninitialized function pointer */ + function(uint256) internal returns(uint256) a; + a(10); + } + +} +``` +The call to `a(10)` will lead to unexpected behavior because function pointer `a` is not initialized in the constructor. + +### Recommendation +Initialize function pointers before calling. Avoid function pointers if possible. + +## Pre-declaration usage of local variables +### Configuration +* Check: `variable-scope` +* Severity: `Low` +* Confidence: `High` + +### Description +Detects the possible usage of a variable before the declaration is stepped over (either because it is later declared, or declared in another scope). + +### Exploit Scenario: + +```solidity +contract C { + function f(uint z) public returns (uint) { + uint y = x + 9 + z; // 'x' is used pre-declaration + uint x = 7; + + if (z % 2 == 0) { + uint max = 5; + // ... + } + + // 'max' was intended to be 5, but it was mistakenly declared in a scope and not assigned (so it is zero). + for (uint i = 0; i < max; i++) { + x += 1; + } + + return x; + } +} +``` +In the case above, the variable `x` is used before its declaration, which may result in unintended consequences. +Additionally, the for-loop uses the variable `max`, which is declared in a previous scope that may not always be reached. This could lead to unintended consequences if the user mistakenly uses a variable prior to any intended declaration assignment. It also may indicate that the user intended to reference a different variable. + +### Recommendation +Move all variable declarations prior to any usage of the variable, and ensure that reaching a variable declaration does not depend on some conditional if it is used unconditionally. + +## Void constructor +### Configuration +* Check: `void-cst` +* Severity: `Low` +* Confidence: `High` + +### Description +Detect the call to a constructor that is not implemented + +### Exploit Scenario: + +```solidity +contract A{} +contract B is A{ + constructor() public A(){} +} +``` +When reading `B`'s constructor definition, we might assume that `A()` initiates the contract, but no code is executed. + +### Recommendation +Remove the constructor call. + +## Calls inside a loop +### Configuration +* Check: `calls-loop` +* Severity: `Low` +* Confidence: `Medium` + +### Description +Calls inside a loop might lead to a denial-of-service attack. + +### Exploit Scenario: + +```solidity +contract CallsInLoop{ + + address[] destinations; + + constructor(address[] newDestinations) public{ + destinations = newDestinations; + } + + function bad() external{ + for (uint i=0; i < destinations.length; i++){ + destinations[i].transfer(i); + } + } + +} +``` +If one of the destinations has a fallback function that reverts, `bad` will always revert. + +### Recommendation +Favor [pull over push](https://github.com/ethereum/wiki/wiki/Safety#favor-pull-over-push-for-external-calls) strategy for external calls. + +## Missing events access control +### Configuration +* Check: `events-access` +* Severity: `Low` +* Confidence: `Medium` + +### Description +Detect missing events for critical access control parameters + +### Exploit Scenario: + +```solidity +contract C { + + modifier onlyAdmin { + if (msg.sender != owner) throw; + _; + } + + function updateOwner(address newOwner) onlyAdmin external { + owner = newOwner; + } +} +``` +`updateOwner()` has no event, so it is difficult to track off-chain owner changes. + + +### Recommendation +Emit an event for critical parameter changes. + +## Missing events arithmetic +### Configuration +* Check: `events-maths` +* Severity: `Low` +* Confidence: `Medium` + +### Description +Detect missing events for critical arithmetic parameters. + +### Exploit Scenario: + +```solidity +contract C { + + modifier onlyOwner { + if (msg.sender != owner) throw; + _; + } + + function setBuyPrice(uint256 newBuyPrice) onlyOwner public { + buyPrice = newBuyPrice; + } + + function buy() external { + ... // buyPrice is used to determine the number of tokens purchased + } +} +``` +`setBuyPrice()` does not emit an event, so it is difficult to track changes in the value of `buyPrice` off-chain. + + +### Recommendation +Emit an event for critical parameter changes. + +## Dangerous unary expressions +### Configuration +* Check: `incorrect-unary` +* Severity: `Low` +* Confidence: `Medium` + +### Description +Unary expressions such as `x=+1` probably typos. + +### Exploit Scenario: + +```Solidity +contract Bug{ + uint public counter; + + function increase() public returns(uint){ + counter=+1; + return counter; + } +} +``` +`increase()` uses `=+` instead of `+=`, so `counter` will never exceed 1. + +### Recommendation +Remove the unary expression. + +## Missing zero address validation +### Configuration +* Check: `missing-zero-check` +* Severity: `Low` +* Confidence: `Medium` + +### Description +Detect missing zero address validation. + +### Exploit Scenario: + +```solidity +contract C { + + modifier onlyAdmin { + if (msg.sender != owner) throw; + _; + } + + function updateOwner(address newOwner) onlyAdmin external { + owner = newOwner; + } +} +``` +Bob calls `updateOwner` without specifying the `newOwner`, so Bob loses ownership of the contract. + + +### Recommendation +Check that the address is not zero. + +## Reentrancy vulnerabilities +### Configuration +* Check: `reentrancy-benign` +* Severity: `Low` +* Confidence: `Medium` + +### Description + +Detection of the [reentrancy bug](https://github.com/trailofbits/not-so-smart-contracts/tree/master/reentrancy). +Only report reentrancy that acts as a double call (see `reentrancy-eth`, `reentrancy-no-eth`). + +### Exploit Scenario: + +```solidity + function callme(){ + if( ! (msg.sender.call()() ) ){ + throw; + } + counter += 1 + } +``` + +`callme` contains a reentrancy. The reentrancy is benign because it's exploitation would have the same effect as two consecutive calls. + +### Recommendation +Apply the [`check-effects-interactions` pattern](http://solidity.readthedocs.io/en/v0.4.21/security-considerations.html#re-entrancy). + +## Reentrancy vulnerabilities +### Configuration +* Check: `reentrancy-events` +* Severity: `Low` +* Confidence: `Medium` + +### Description + +Detects [reentrancies](https://github.com/trailofbits/not-so-smart-contracts/tree/master/reentrancy) that allow manipulation of the order or value of events. + +### Exploit Scenario: + +```solidity +contract ReentrantContract { + function f() external { + if (BugReentrancyEvents(msg.sender).counter() == 1) { + BugReentrancyEvents(msg.sender).count(this); + } + } +} +contract Counter { + uint public counter; + event Counter(uint); + +} +contract BugReentrancyEvents is Counter { + function count(ReentrantContract d) external { + counter += 1; + d.f(); + emit Counter(counter); + } +} +contract NoReentrancyEvents is Counter { + function count(ReentrantContract d) external { + counter += 1; + emit Counter(counter); + d.f(); + } +} +``` + +If the external call `d.f()` re-enters `BugReentrancyEvents`, the `Counter` events will be incorrect (`Counter(2)`, `Counter(2)`) whereas `NoReentrancyEvents` will correctly emit +(`Counter(1)`, `Counter(2)`). This may cause issues for offchain components that rely on the values of events e.g. checking for the amount deposited to a bridge. + +### Recommendation +Apply the [`check-effects-interactions` pattern](https://docs.soliditylang.org/en/latest/security-considerations.html#re-entrancy). + +## Return Bomb +### Configuration +* Check: `return-bomb` +* Severity: `Low` +* Confidence: `Medium` + +### Description +A low level callee may consume all callers gas unexpectedly. + +### Exploit Scenario: + +```solidity +//Modified from https://github.com/nomad-xyz/ExcessivelySafeCall +contract BadGuy { + function youveActivateMyTrapCard() external pure returns (bytes memory) { + assembly{ + revert(0, 1000000) + } + } +} + +contract Mark { + function oops(address badGuy) public{ + bool success; + bytes memory ret; + + // Mark pays a lot of gas for this copy + //(success, ret) = badGuy.call{gas:10000}( + (success, ret) = badGuy.call( + abi.encodeWithSelector( + BadGuy.youveActivateMyTrapCard.selector + ) + ); + + // Mark may OOG here, preventing local state changes + //importantCleanup(); + } +} + +``` +After Mark calls BadGuy bytes are copied from returndata to memory, the memory expansion cost is paid. This means that when using a standard solidity call, the callee can "returnbomb" the caller, imposing an arbitrary gas cost. +Callee unexpectedly makes the caller OOG. + + +### Recommendation +Avoid unlimited implicit decoding of returndata. + +## Block timestamp +### Configuration +* Check: `timestamp` +* Severity: `Low` +* Confidence: `Medium` + +### Description +Dangerous usage of `block.timestamp`. `block.timestamp` can be manipulated by miners. + +### Exploit Scenario: +"Bob's contract relies on `block.timestamp` for its randomness. Eve is a miner and manipulates `block.timestamp` to exploit Bob's contract. + +### Recommendation +Avoid relying on `block.timestamp`. + +## Assembly usage +### Configuration +* Check: `assembly` +* Severity: `Informational` +* Confidence: `High` + +### Description +The use of assembly is error-prone and should be avoided. + +### Recommendation +Do not use `evm` assembly. + +## Assert state change +### Configuration +* Check: `assert-state-change` +* Severity: `Informational` +* Confidence: `High` + +### Description +Incorrect use of `assert()`. See Solidity best [practices](https://solidity.readthedocs.io/en/latest/control-structures.html#id4). + +### Exploit Scenario: + +```solidity +contract A { + + uint s_a; + + function bad() public { + assert((s_a += 1) > 10); + } +} +``` +The assert in `bad()` increments the state variable `s_a` while checking for the condition. + + +### Recommendation +Use `require` for invariants modifying the state. + +## Boolean equality +### Configuration +* Check: `boolean-equal` +* Severity: `Informational` +* Confidence: `High` + +### Description +Detects the comparison to boolean constants. + +### Exploit Scenario: + +```solidity +contract A { + function f(bool x) public { + // ... + if (x == true) { // bad! + // ... + } + // ... + } +} +``` +Boolean constants can be used directly and do not need to be compare to `true` or `false`. + +### Recommendation +Remove the equality to the boolean constant. + +## Cyclomatic complexity +### Configuration +* Check: `cyclomatic-complexity` +* Severity: `Informational` +* Confidence: `High` + +### Description +Detects functions with high (> 11) cyclomatic complexity. + +### Recommendation +Reduce cyclomatic complexity by splitting the function into several smaller subroutines. + +## Deprecated standards +### Configuration +* Check: `deprecated-standards` +* Severity: `Informational` +* Confidence: `High` + +### Description +Detect the usage of deprecated standards. + +### Exploit Scenario: + +```solidity +contract ContractWithDeprecatedReferences { + // Deprecated: Change block.blockhash() -> blockhash() + bytes32 globalBlockHash = block.blockhash(0); + + // Deprecated: Change constant -> view + function functionWithDeprecatedThrow() public constant { + // Deprecated: Change msg.gas -> gasleft() + if(msg.gas == msg.value) { + // Deprecated: Change throw -> revert() + throw; + } + } + + // Deprecated: Change constant -> view + function functionWithDeprecatedReferences() public constant { + // Deprecated: Change sha3() -> keccak256() + bytes32 sha3Result = sha3("test deprecated sha3 usage"); + + // Deprecated: Change callcode() -> delegatecall() + address(this).callcode(); + + // Deprecated: Change suicide() -> selfdestruct() + suicide(address(0)); + } +} +``` + +### Recommendation +Replace all uses of deprecated symbols. + +## Unindexed ERC20 event parameters +### Configuration +* Check: `erc20-indexed` +* Severity: `Informational` +* Confidence: `High` + +### Description +Detects whether events defined by the `ERC20` specification that should have some parameters as `indexed` are missing the `indexed` keyword. + +### Exploit Scenario: + +```solidity +contract ERC20Bad { + // ... + event Transfer(address from, address to, uint value); + event Approval(address owner, address spender, uint value); + + // ... +} +``` +`Transfer` and `Approval` events should have the 'indexed' keyword on their two first parameters, as defined by the `ERC20` specification. +Failure to include these keywords will exclude the parameter data in the transaction/block's bloom filter, so external tooling searching for these parameters may overlook them and fail to index logs from this token contract. + +### Recommendation +Add the `indexed` keyword to event parameters that should include it, according to the `ERC20` specification. + +## Function Initializing State +### Configuration +* Check: `function-init-state` +* Severity: `Informational` +* Confidence: `High` + +### Description +Detects the immediate initialization of state variables through function calls that are not pure/constant, or that use non-constant state variable. + +### Exploit Scenario: + +```solidity +contract StateVarInitFromFunction { + + uint public v = set(); // Initialize from function (sets to 77) + uint public w = 5; + uint public x = set(); // Initialize from function (sets to 88) + address public shouldntBeReported = address(8); + + constructor(){ + // The constructor is run after all state variables are initialized. + } + + function set() public returns(uint) { + // If this function is being used to initialize a state variable declared + // before w, w will be zero. If it is declared after w, w will be set. + if(w == 0) { + return 77; + } + + return 88; + } +} +``` +In this case, users might intend a function to return a value a state variable can initialize with, without realizing the context for the contract is not fully initialized. +In the example above, the same function sets two different values for state variables because it checks a state variable that is not yet initialized in one case, and is initialized in the other. +Special care must be taken when initializing state variables from an immediate function call so as not to incorrectly assume the state is initialized. + + +### Recommendation +Remove any initialization of state variables via non-constant state variables or function calls. If variables must be set upon contract deployment, locate initialization in the constructor instead. + +## Incorrect usage of using-for statement +### Configuration +* Check: `incorrect-using-for` +* Severity: `Informational` +* Confidence: `High` + +### Description +In Solidity, it is possible to use libraries for certain types, by the `using-for` statement (`using for `). However, the Solidity compiler doesn't check whether a given library has at least one function matching a given type. If it doesn't, such a statement has no effect and may be confusing. + +### Exploit Scenario: + + ```solidity + library L { + function f(bool) public pure {} + } + + using L for uint; + ``` + Such a code will compile despite the fact that `L` has no function with `uint` as its first argument. + +### Recommendation +Make sure that the libraries used in `using-for` statements have at least one function matching a type used in these statements. + +## Low-level calls +### Configuration +* Check: `low-level-calls` +* Severity: `Informational` +* Confidence: `High` + +### Description +The use of low-level calls is error-prone. Low-level calls do not check for [code existence](https://solidity.readthedocs.io/en/v0.4.25/control-structures.html#error-handling-assert-require-revert-and-exceptions) or call success. + +### Recommendation +Avoid low-level calls. Check the call success. If the call is meant for a contract, check for code existence. + +## Missing inheritance +### Configuration +* Check: `missing-inheritance` +* Severity: `Informational` +* Confidence: `High` + +### Description +Detect missing inheritance. + +### Exploit Scenario: + +```solidity +interface ISomething { + function f1() external returns(uint); +} + +contract Something { + function f1() external returns(uint){ + return 42; + } +} +``` +`Something` should inherit from `ISomething`. + + +### Recommendation +Inherit from the missing interface or contract. + +## Conformance to Solidity naming conventions +### Configuration +* Check: `naming-convention` +* Severity: `Informational` +* Confidence: `High` + +### Description + +Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.25/style-guide.html#naming-conventions) that should be followed. +#### Rule exceptions +- Allow constant variable name/symbol/decimals to be lowercase (`ERC20`). +- Allow `_` at the beginning of the `mixed_case` match for private variables and unused parameters. + +### Recommendation +Follow the Solidity [naming convention](https://solidity.readthedocs.io/en/v0.4.25/style-guide.html#naming-conventions). + +## Different pragma directives are used +### Configuration +* Check: `pragma` +* Severity: `Informational` +* Confidence: `High` + +### Description +Detect whether different Solidity versions are used. + +### Recommendation +Use one Solidity version. + +## Redundant Statements +### Configuration +* Check: `redundant-statements` +* Severity: `Informational` +* Confidence: `High` + +### Description +Detect the usage of redundant statements that have no effect. + +### Exploit Scenario: + +```solidity +contract RedundantStatementsContract { + + constructor() public { + uint; // Elementary Type Name + bool; // Elementary Type Name + RedundantStatementsContract; // Identifier + } + + function test() public returns (uint) { + uint; // Elementary Type Name + assert; // Identifier + test; // Identifier + return 777; + } +} +``` +Each commented line references types/identifiers, but performs no action with them, so no code will be generated for such statements and they can be removed. + +### Recommendation +Remove redundant statements if they congest code but offer no value. + +## Incorrect versions of Solidity +### Configuration +* Check: `solc-version` +* Severity: `Informational` +* Confidence: `High` + +### Description + +`solc` frequently releases new compiler versions. Using an old version prevents access to new Solidity security checks. +We also recommend avoiding complex `pragma` statement. + +### Recommendation + +Deploy with a recent version of Solidity (at least 0.8.0) with no known severe issues. + +Use a simple pragma version that allows any of these versions. +Consider using the latest version of Solidity for testing. + +## Unimplemented functions +### Configuration +* Check: `unimplemented-functions` +* Severity: `Informational` +* Confidence: `High` + +### Description +Detect functions that are not implemented on derived-most contracts. + +### Exploit Scenario: + +```solidity +interface BaseInterface { + function f1() external returns(uint); + function f2() external returns(uint); +} + +interface BaseInterface2 { + function f3() external returns(uint); +} + +contract DerivedContract is BaseInterface, BaseInterface2 { + function f1() external returns(uint){ + return 42; + } +} +``` +`DerivedContract` does not implement `BaseInterface.f2` or `BaseInterface2.f3`. +As a result, the contract will not properly compile. +All unimplemented functions must be implemented on a contract that is meant to be used. + +### Recommendation +Implement all unimplemented functions in any contract you intend to use directly (not simply inherit from). + +## Unused state variable +### Configuration +* Check: `unused-state` +* Severity: `Informational` +* Confidence: `High` + +### Description +Unused state variable. + +### Recommendation +Remove unused state variables. + +## Costly operations inside a loop +### Configuration +* Check: `costly-loop` +* Severity: `Informational` +* Confidence: `Medium` + +### Description +Costly operations inside a loop might waste gas, so optimizations are justified. + +### Exploit Scenario: + +```solidity +contract CostlyOperationsInLoop{ + + uint loop_count = 100; + uint state_variable=0; + + function bad() external{ + for (uint i=0; i < loop_count; i++){ + state_variable++; + } + } + + function good() external{ + uint local_variable = state_variable; + for (uint i=0; i < loop_count; i++){ + local_variable++; + } + state_variable = local_variable; + } +} +``` +Incrementing `state_variable` in a loop incurs a lot of gas because of expensive `SSTOREs`, which might lead to an `out-of-gas`. + +### Recommendation +Use a local variable to hold the loop computation result. + +## Dead-code +### Configuration +* Check: `dead-code` +* Severity: `Informational` +* Confidence: `Medium` + +### Description +Functions that are not used. + +### Exploit Scenario: + +```solidity +contract Contract{ + function dead_code() internal() {} +} +``` +`dead_code` is not used in the contract, and make the code's review more difficult. + +### Recommendation +Remove unused functions. + +## Reentrancy vulnerabilities +### Configuration +* Check: `reentrancy-unlimited-gas` +* Severity: `Informational` +* Confidence: `Medium` + +### Description + +Detection of the [reentrancy bug](https://github.com/trailofbits/not-so-smart-contracts/tree/master/reentrancy). +Only report reentrancy that is based on `transfer` or `send`. + +### Exploit Scenario: + +```solidity + function callme(){ + msg.sender.transfer(balances[msg.sender]): + balances[msg.sender] = 0; + } +``` + +`send` and `transfer` do not protect from reentrancies in case of gas price changes. + +### Recommendation +Apply the [`check-effects-interactions` pattern](http://solidity.readthedocs.io/en/v0.4.21/security-considerations.html#re-entrancy). + +## Too many digits +### Configuration +* Check: `too-many-digits` +* Severity: `Informational` +* Confidence: `Medium` + +### Description + +Literals with many digits are difficult to read and review. Use scientific notation or suffixes to make the code more readable. + + +### Exploit Scenario: + +```solidity +contract MyContract{ + uint 1_ether = 10000000000000000000; +} +``` + +While `1_ether` looks like `1 ether`, it is `10 ether`. As a result, it's likely to be used incorrectly. + + +### Recommendation + +Use: +- [Ether suffix](https://solidity.readthedocs.io/en/latest/units-and-global-variables.html#ether-units), +- [Time suffix](https://solidity.readthedocs.io/en/latest/units-and-global-variables.html#time-units), or +- [The scientific notation](https://solidity.readthedocs.io/en/latest/types.html#rational-and-integer-literals) + + +## Cache array length +### Configuration +* Check: `cache-array-length` +* Severity: `Optimization` +* Confidence: `High` + +### Description +Detects `for` loops that use `length` member of some storage array in their loop condition and don't modify it. + +### Exploit Scenario: + +```solidity +contract C +{ + uint[] array; + + function f() public + { + for (uint i = 0; i < array.length; i++) + { + // code that does not modify length of `array` + } + } +} +``` +Since the `for` loop in `f` doesn't modify `array.length`, it is more gas efficient to cache it in some local variable and use that variable instead, like in the following example: + +```solidity +contract C +{ + uint[] array; + + function f() public + { + uint array_length = array.length; + for (uint i = 0; i < array_length; i++) + { + // code that does not modify length of `array` + } + } +} +``` + + +### Recommendation +Cache the lengths of storage arrays if they are used and not modified in `for` loops. + +## State variables that could be declared constant +### Configuration +* Check: `constable-states` +* Severity: `Optimization` +* Confidence: `High` + +### Description +State variables that are not updated following deployment should be declared constant to save gas. + +### Recommendation +Add the `constant` attribute to state variables that never change. + +## Public function that could be declared external +### Configuration +* Check: `external-function` +* Severity: `Optimization` +* Confidence: `High` + +### Description +`public` functions that are never called by the contract should be declared `external`, and its immutable parameters should be located in `calldata` to save gas. + +### Recommendation +Use the `external` attribute for functions never called from the contract, and change the location of immutable parameters to `calldata` to save gas. + +## State variables that could be declared immutable +### Configuration +* Check: `immutable-states` +* Severity: `Optimization` +* Confidence: `High` + +### Description +State variables that are not updated following deployment should be declared immutable to save gas. + +### Recommendation +Add the `immutable` attribute to state variables that never change or are set only in the constructor. + +## Public variable read in external context +### Configuration +* Check: `var-read-using-this` +* Severity: `Optimization` +* Confidence: `High` + +### Description +The contract reads its own variable using `this`, adding overhead of an unnecessary STATICCALL. + +### Exploit Scenario: + +```solidity +contract C { + mapping(uint => address) public myMap; + function test(uint x) external returns(address) { + return this.myMap(x); + } +} +``` + + +### Recommendation +Read the variable directly from storage instead of calling the contract. diff --git a/secure_contracts_docs/src/printers/Printer-documentation.md b/secure_contracts_docs/src/printers/Printer-documentation.md new file mode 100644 index 0000000000..df1e1ea244 --- /dev/null +++ b/secure_contracts_docs/src/printers/Printer-documentation.md @@ -0,0 +1,468 @@ +Slither allows printing contracts information through its printers. + +Num | Printer | Description +--- | --- | --- +1 | `call-graph` | [Export the call-graph of the contracts to a dot file](https://github.com/trailofbits/slither/wiki/Printer-documentation#call-graph) +2 | `cfg` | [Export the CFG of each functions](https://github.com/trailofbits/slither/wiki/Printer-documentation#cfg) +3 | `constructor-calls` | [Print the constructors executed](https://github.com/crytic/slither/wiki/Printer-documentation#constructor-calls) +4 | `contract-summary` | [Print a summary of the contracts](https://github.com/trailofbits/slither/wiki/Printer-documentation#contract-summary) +5 | `data-dependency` | [Print the data dependencies of the variables](https://github.com/trailofbits/slither/wiki/Printer-documentation#data-dependencies) +6 | `echidna` | [Export Echidna guiding information](https://github.com/trailofbits/slither/wiki/Printer-documentation#echidna) +7 | `evm` | [Print the evm instructions of nodes in functions](https://github.com/trailofbits/slither/wiki/Printer-documentation#evm) +8 | `function-id` | [Print the keccack256 signature of the functions](https://github.com/trailofbits/slither/wiki/Printer-documentation#function-id) +9 | `function-summary` | [Print a summary of the functions](https://github.com/trailofbits/slither/wiki/Printer-documentation#function-summary) +10 | `human-summary` | [Print a human-readable summary of the contracts](https://github.com/trailofbits/slither/wiki/Printer-documentation#human-summary) +11 | `inheritance` | [Print the inheritance relations between contracts](https://github.com/trailofbits/slither/wiki/Printer-documentation#inheritance) +12 | `inheritance-graph` | [Export the inheritance graph of each contract to a dot file](https://github.com/trailofbits/slither/wiki/Printer-documentation#inheritance-graph) +13 | `modifiers` | [Print the modifiers called by each function](https://github.com/trailofbits/slither/wiki/Printer-documentation#modifiers) +14 | `require` | [Print the require and assert calls of each function](https://github.com/trailofbits/slither/wiki/Printer-documentation#require) +15 | `slithir` | [Print the slithIR representation of the functions](https://github.com/trailofbits/slither/wiki/Printer-documentation#slithir) +16 | `slithir-ssa` | [Print the slithIR representation of the functions](https://github.com/trailofbits/slither/wiki/Printer-documentation#slithir-ssa) +17 | `variable-order` | [Print the storage order of the state variables](https://github.com/trailofbits/slither/wiki/Printer-documentation#variable-order) +18 | `vars-and-auth` | [Print the state variables written and the authorization of the functions](https://github.com/trailofbits/slither/wiki/Printer-documentation#variables-written-and-authorization) + + + + + +Several printers require xdot installed for visualization: +``` +sudo apt install xdot +``` + +## Call Graph +`slither file.sol --print call-graph` + +Export the call-graph of the contracts to a dot file +### Example +``` +$ slither examples/printers/call_graph.sol --print call-graph +``` + + +The output format is [dot](https://www.graphviz.org/). +To vizualize the graph: +``` +$ xdot examples/printers/call_graph.sol.dot +``` +To convert the file to svg: +``` +$ dot examples/printers/call_graph.sol.dot -Tpng -o examples/printers/call_graph.sol.png +``` + + +## CFG +Export the control flow graph of each function + +`slither file.sol --print cfg` + +### Example + +The output format is [dot](https://www.graphviz.org/). +To vizualize the graph: +``` +$ xdot function.sol.dot +``` +To convert the file to svg: +``` +$ dot function.dot -Tsvg -o function.sol.png +``` + + +## Contract Summary + +Output a quick summary of the contract. + +`slither file.sol --print contract-summary` + +### Example +``` +$ slither examples/printers/quick_summary.sol --print contract-summary +``` + + + + +## Data Dependencies + +Print the data dependencies of the variables +`slither file.sol --print data-dependency` + +### Example +``` +$ slither examples/printers/data_dependencies.sol --print data-dependency +``` +``` +Contract MyContract ++----------+----------------------+ +| Variable | Dependencies | ++----------+----------------------+ +| a | ['input_a'] | +| b | ['input_b', 'input'] | +| c | [] | ++----------+----------------------+ + +Function setA(uint256,uint256) ++--------------+--------------+ +| Variable | Dependencies | ++--------------+--------------+ +| input_a | [] | +| input_b | [] | +| MyContract:a | ['input_a'] | +| MyContract:b | [] | +| MyContract:c | [] | ++--------------+--------------+ +Function setB(uint256) ++--------------+----------------------+ +| Variable | Dependencies | ++--------------+----------------------+ +| input | ['input_b'] | +| MyContract:a | [] | +| MyContract:b | ['input_b', 'input'] | +| MyContract:c | [] | ++--------------+----------------------+ +``` + +## Constructor Calls +`slither file.sol --print constructor-calls` + +Print the calling sequence of constructors based on C3 linearization. + +### Example +``` +... +$ slither examples/printers/constructors.sol --print constructor-calls +[..] + +Contact Name: test2 + Constructor Call Sequence: test--> test2 + Constructor Definitions: + + contract name : test2 + constructor()public{ + a=10; + } + + contract name : test + constructor()public{ + a =5; + } + + +Contact Name: test3 + Constructor Call Sequence: test--> test2--> test3 + Constructor Definitions: + + contract name : test3 + constructor(bytes32 _name)public{ + owner = msg.sender; + name = _name; + a=20; + } + + contract name : test2 + constructor()public{ + a=10; + } + + contract name : test + constructor()public{ + a =5; + } +``` + + +## Echidna + +This printer is meant to improve [Echidna](https://github.com/crytic/echidna) code coverage. The printer is a WIP and is not yet used by Echidna. + +## EVM +`slither file.sol --print evm` + +Print the EVM representation of the functions + +### Example +``` +$ slither examples/printers/evm.sol --print evm + +INFO:Printers:Contract Test + Function Test.foo() + Node: ENTRY_POINT None + Source line 5: function foo() public returns (address) { + EVM Instructions: + 0x44: JUMPDEST + 0x45: CALLVALUE + 0x50: POP + 0x51: PUSH1 0x56 + 0x53: PUSH1 0x98 + 0x55: JUMP + 0x56: JUMPDEST + 0x57: PUSH1 0x40 + 0x59: MLOAD + 0x5a: DUP1 + 0x5b: DUP3 + 0x5c: PUSH20 0xffffffffffffffffffffffffffffffffffffffff + 0x71: AND + 0x72: PUSH20 0xffffffffffffffffffffffffffffffffffffffff + 0x87: AND + 0x88: DUP2 + 0x89: MSTORE + 0x8a: PUSH1 0x20 + 0x8c: ADD + 0x8d: SWAP2 + 0x8e: POP + 0x8f: POP + 0x90: PUSH1 0x40 + 0x92: MLOAD + 0x93: DUP1 + 0x94: SWAP2 + 0x95: SUB + 0x96: SWAP1 + 0x97: RETURN + 0x98: JUMPDEST + 0x99: PUSH1 0x0 + 0xa2: POP + 0xa3: SWAP1 + 0xa4: JUMP + Node: NEW VARIABLE from = msg.sender + Source line 6: address from = msg.sender; + EVM Instructions: + 0x9b: DUP1 + 0x9c: CALLER + 0x9d: SWAP1 + 0x9e: POP + Node: RETURN (from) + Source line 7: return(from); + EVM Instructions: + 0x9f: DUP1 + 0xa0: SWAP2 + 0xa1: POP +``` + +## Function id +`slither file.sol --print function-id` +Print the keccack256 signature of the functions + +### Examples +``` +$ slither examples/printers/authorization.sol --print function-id +INFO:Printers: +MyContract: ++---------------+------------+ +| Name | ID | ++---------------+------------+ +| constructor() | 0x90fa17bb | +| mint(uint256) | 0xa0712d68 | ++---------------+------------+ +``` + +## Function Summary +`slither file.sol --print function-summary` + +Output a summary of the contract showing for each function: +- What are the visibility and the modifiers +- What are the state variables read or written +- What are the calls + +### Example +``` +$ slither tests/backdoor.sol --print function-summary +``` +``` +[...] + +Contract C +Contract vars: [] +Inheritances:: [] + ++-----------------+------------+-----------+----------------+-------+---------------------------+----------------+ +| Function | Visibility | Modifiers | Read | Write | Internal Calls | External Calls | ++-----------------+------------+-----------+----------------+-------+---------------------------+----------------+ +| i_am_a_backdoor | public | [] | ['msg.sender'] | [] | ['selfdestruct(address)'] | [] | ++-----------------+------------+-----------+----------------+-------+---------------------------+----------------+ + ++-----------+------------+------+-------+----------------+----------------+ +| Modifiers | Visibility | Read | Write | Internal Calls | External Calls | ++-----------+------------+------+-------+----------------+----------------+ ++-----------+------------+------+-------+----------------+----------------+ +``` + +## Human Summary +`slither file.sol --print human-summary` + +Print a human-readable summary of the contracts + +### Example +``` +$ slither examples/printers/human_printer.sol --print human-summary +``` + + + +## Inheritance +`slither file.sol --print inheritance` +Print the inheritance relations between contracts + +### Example +``` +$ slither examples/printers/inheritances.sol --print inheritance +``` + + + +## Inheritance Graph +`slither file.sol --print inheritance-graph` + +Output a graph showing the inheritance interaction between the contracts. + + + +### Example +``` +$ slither examples/printers/inheritances.sol --print inheritance-graph +[...] +INFO:PrinterInheritance:Inheritance Graph: examples/DAO.sol.dot +``` + +The output format is [dot](https://www.graphviz.org/). +To vizualize the graph: +``` +$ xdot examples/printers/inheritances.sol.dot +``` +To convert the file to svg: +``` +$ dot examples/printers/inheritances.sol.dot -Tsvg -o examples/printers/inheritances.sol.png +``` + + +Indicators: +- If a contract has multiple inheritance, the connecting edges will be labelled in order of declaration. +- Functions highlighted orange override a parent's function. +- Functions which do not override each other directly (but collide due to multiple inheritance) will be emphasized at the bottom of the affected contract node in grey font. +- Variables highlighted red overshadow a parent's variable declaration. +- Variables of type `contract` specify the contract name in parentheses in a blue font. + + +## Modifiers +`slither file.sol --print modifiers` + +Print the modifiers called by each function. + +### Example +``` +$ slither examples/printers/modifier.sol --print modifiers +INFO:Printers: +Contract C ++-------------+-------------+ +| Function | Modifiers | ++-------------+-------------+ +| constructor | [] | +| set | ['isOwner'] | ++-------------+-------------+ +``` + +## Require +`slither file.sol --print require` + +Print the require and assert calls of each function. + +### Example +``` +$ slither examples/printers/require.sol --print require +INFO:Printers: +Contract Lib ++----------+--------------------------------------+ +| Function | require or assert | ++----------+--------------------------------------+ +| set | require(bool)(msg.sender == s.owner) | ++----------+--------------------------------------+ +INFO:Printers: +Contract C ++-------------+--------------------------------------+ +| Function | require or assert | ++-------------+--------------------------------------+ +| constructor | | +| set | require(bool)(msg.sender == s.owner) | ++-------------+--------------------------------------+ +``` + + + +## SlithIR +`slither file.sol --print slithir` + +Print the slithIR representation of the functions + +### Example +``` +$ slither examples/printers/slihtir.sol --print slithir +Contract UnsafeMath + Function add(uint256,uint256) + Expression: a + b + IRs: + TMP_0(uint256) = a + b + RETURN TMP_0 + Function min(uint256,uint256) + Expression: a - b + IRs: + TMP_0(uint256) = a - b + RETURN TMP_0 +Contract MyContract + Function transfer(address,uint256) + Expression: balances[msg.sender] = balances[msg.sender].min(val) + IRs: + REF_3(uint256) -> balances[msg.sender] + REF_1(uint256) -> balances[msg.sender] + TMP_1(uint256) = LIBRARY_CALL, dest:UnsafeMath, function:min, arguments:['REF_1', 'val'] + REF_3 := TMP_1 + Expression: balances[to] = balances[to].add(val) + IRs: + REF_3(uint256) -> balances[to] + REF_1(uint256) -> balances[to] + TMP_1(uint256) = LIBRARY_CALL, dest:UnsafeMath, function:add, arguments:['REF_1', 'val'] + REF_3 := TMP_1 +``` + +## SlithIR-SSA +`slither file.sol --print slithir-ssa` + +Print the slithIR representation of the functions (SSA version) + +## Variable order +`slither file.sol --print variable-order` + +Print the storage order of the state variables + +### Example +``` +$ slither tests/check-upgradability/contractV2_bug.sol --print variable-order +INFO:Printers: +ContractV2: ++-------------+---------+ +| Name | Type | ++-------------+---------+ +| destination | uint256 | +| myFunc | uint256 | ++-------------+---------+ + +``` + + +## Variables written and authorization +`slither file.sol --print vars-and-auth` + +Print the variables written and the check on `msg.sender` of each function. +### Example +``` +... +$ slither examples/printers/authorization.sol --print vars-and-auth +[..] +INFO:Printers: +Contract MyContract ++-------------+-------------------------+----------------------------------------+ +| Function | State variables written | Conditions on msg.sender | ++-------------+-------------------------+----------------------------------------+ +| constructor | ['owner'] | [] | +| mint | ['balances'] | ['require(bool)(msg.sender == owner)'] | ++-------------+-------------------------+----------------------------------------+ +``` \ No newline at end of file diff --git a/secure_contracts_docs/src/tools/Adding-a-new-utility.md b/secure_contracts_docs/src/tools/Adding-a-new-utility.md new file mode 100644 index 0000000000..35474d5d96 --- /dev/null +++ b/secure_contracts_docs/src/tools/Adding-a-new-utility.md @@ -0,0 +1,21 @@ +Slither can be used as a library to create new utilities. +Official utils are present in [tools](https://github.com/crytic/slither/tree/master/slither/tools) + +We recommend following the [developper installation](https://github.com/crytic/slither/wiki/Developer-installation). + +## Skeleton +The skeleton util is present in [tools/demo](https://github.com/crytic/slither/tree/master/slither/tools/demo) + +## Integration + +To enable an util from the command line, update `entry_points` in [setup.py](https://github.com/crytic/slither/blob/master/setup.py). +Installing Slither will then install the util. + +## Guidelines + +- Favor the `logging` module rather than `print` +- Favor raising an exception rather than `sys.exit` +- Add unit-tests (ex: [scripts/travis_test_find_paths.sh](https://github.com/crytic/slither/blob/master/scripts/ci_test_find_paths.sh)) + +## Getting Help +Join our [slack channel](https://empireslacking.herokuapp.com/) to get any help (#ethereum). diff --git a/secure_contracts_docs/src/tools/Code-Similarity-Detector.md b/secure_contracts_docs/src/tools/Code-Similarity-Detector.md new file mode 100644 index 0000000000..3506f95903 --- /dev/null +++ b/secure_contracts_docs/src/tools/Code-Similarity-Detector.md @@ -0,0 +1,151 @@ +`slither-simil` uses state-of-the-art machine learning to detect similar (vulnerable) Solidity functions. We have provided a pretrained model from [etherscan_verified_contracts](https://github.com/thec00n/etherscan_verified_contracts) with 60,000 contracts and more than 850,000 functions to get you started quickly. We included the capability to easily train new models if you have access to larger or different datasets. + +`slither-simil` uses [FastText](https://github.com/facebookresearch/fastText), a vector embedding technique, to generate compact numerical representations of every function. We used FastText because it: + +* implements several state-of-the-art techniques such as nbow and skipgrams, +* has high performance (it is C++ code with Python bindings), +* has MIT license, and +* is well maintained (by Facebook). + +## Requirements + +Install the required packages before using `slither-simil`: + +``` +$ pip3 install pybind11 --user +$ pip3 install https://github.com/facebookresearch/fastText/archive/0.2.0.zip --user +$ pip3 install sklearn matplotlib --user # for plot mode +``` + +Make sure that you are using `pip3.6` or later. If you are running from inside a [virtualenv](https://virtualenv.pypa.io/en/latest/), remove the `--user` parameter. + +## Usage + +Note that these examples will use the following files: + +* [etherscan_verified_contracts.bin](https://drive.google.com/file/d/1oEhbIL4V9582Y5VKp4iiOURGq8qa4cBN/view?usp=sharing) +* [cache.npz](https://drive.google.com/file/d/1vpwusbyzLn1JqqAvlFivHXtLvsEp0VqX/view?usp=sharing) +* [MetaCoin.sol](https://github.com/crytic/slither/wiki/MetacoinExample) + +`slither-simil` has three modes: +- `test` - finds similar functions to your own in a dataset of contracts +- `plot` - provide a visual representation of similarity of multiple sampled functions +- `train` - builds new models of large datasets of contracts +- `info` - inspects the internal information of the pre-trained model or the assessed code + +### Test mode + +This mode transforms a function into a vector and uses it to find similar functions. + +Test mode requires the following parameters: +1. A pre-trained model: this file will be used to transform every function into a vector, you can [train your own](#train-mode) or [use our pre-trained one (etherscan_verified_contracts.bin)](#usage). +2. A contract filename: this file will contain the code that you want to compare, +3. A function name (e.g. `SafeMath.add` or `add`), +4. An input directory or file: this can be either a directory with contracts or a [cache file with a pre-computed list of vectors for every contract (cache.npz)](#usage). + +Use the cache to avoid long processing times to compile and vectorize the input contracts. + +Here's an example that finds functions similar to `sendCoin` in `MetaCoin` (compiled with `solc-0.4.25`). Searching for similar functions among more than 800,000 functions takes only 20 seconds. + +``` +$ slither-simil test etherscan_verified_contracts.bin --filename MetaCoin.sol --fname MetaCoin.sendCoin --input cache.npz --ntop 25 --solc solc-0.4.25 +INFO:Slither-simil:Reviewed 825062 functions, listing the 25 most similar ones: +INFO:Slither-simil:filename contract function score +INFO:Slither-simil:0x954b5de09a55e59755acbda29e1eb74a45d30175_Fluz.sol Fluz transfer 1.0 +INFO:Slither-simil:0x55648de19836338549130b1af587f16bea46f66b_Pebbles.sol Pebbles transfer 1.0 +INFO:Slither-simil:0x3fcee23add6e86dde3c4d395cbce1cae7f16d06d_SnipCoin.sol SnipCoin sendCoin 1.0 +INFO:Slither-simil:0x000000005fbe2cc9b1b684ec445caf176042348e_ProperProposal.sol Vote transfer 1.0 +INFO:Slither-simil:0x000000002bb43c83ece652d161ad0fa862129a2c_AccountRegistry.sol Vote transfer 1.0 +INFO:Slither-simil:0x4e84e9e5fb0a972628cf4568c403167ef1d40431_Fluzcoin.sol Fluzcoin transfer 1.0 +INFO:Slither-simil:0x334eec1482109bd802d9e72a447848de3bcc1063_AirDropToken.sol AirDropToken transfer 1.0 +INFO:Slither-simil:0x28ccdda197d319a241005b9c9f01bac48b90f556_AirDropToken.sol AirDropToken transfer 1.0 +INFO:Slither-simil:0x000000002647e16d9bab9e46604d75591d289277_Vote.sol Vote transfer 1.0 +INFO:Slither-simil:0xc6c4c7826D44ABF22c711E8E86bDC3f5242d2182_token.sol token sendCoin 1.0 +INFO:Slither-simil:0x22033df1d104736ff4c2b23a28affe52863ca9c8_AtmOnlyFluzcoin.sol AtmOnlyFluzcoin transfer 1.0 +INFO:Slither-simil:0xcad796d6a2c0bb1de7f24262819be96fb08c1c3a_Love.sol Love transfer 1.0 +INFO:Slither-simil:0x6cb2b8dc6a508c9a21db9683d1a729715969a6ee_TokenEscrow.sol TokenEscrow transferFromOwner 0.996 +INFO:Slither-simil:0xd75fefe3cdb647281eec3f8fc738e3bc9658f9e4_ProofOfReadToken.sol ProofOfReadToken transfer 0.996 +INFO:Slither-simil:0x7A8Ef7E8c8f16B9D6F39069ce03d752Af23b46d6_OBS_V1.sol MyObs transfer 0.996 +INFO:Slither-simil:0x5ac0197c944c961f58bb02f3d0df58a74fdc15b6_TokenEscrow.sol TokenEscrow transferFromOwner 0.996 +INFO:Slither-simil:0x69719c8c207036bdfc3632ccc24b290fb7240f4a_BitPayToken.sol BitPayToken transfer 0.996 +INFO:Slither-simil:0x3d8a10ce3228cb428cb56baa058d4432464ea25d_TestToken.sol TestToken transfer 0.993 +INFO:Slither-simil:0x69719c8c207036bdfc3632ccc24b290fb7240f4a_BitPayToken.sol BitPayToken transferFrom 0.992 +INFO:Slither-simil:0x486e1f44b2a85150a6dd2de5aab87df375cd8880_CAIRToken.sol StandardToken transfer 0.991 +INFO:Slither-simil:0x2bec16b164725efc192b7ec0296f838c61317514_eda.sol StandardToken transfer 0.991 +INFO:Slither-simil:0xb7cb1c96db6b22b0d3d9536e0108d062bd488f74_WaltonToken.sol StandardToken transfer 0.991 +INFO:Slither-simil:0x346c3be6aebEBaF5Cb766a75aDc9827EfbB7E41A_DelphiToken.sol StandardToken transfer 0.991 +INFO:Slither-simil:0xf5068761511594c82328102f4fde4650ed9ea6c4_WHP.sol WHP transfer 0.991 +INFO:Slither-simil:0x5f9f2ae7150d0beef3bb50ac8d8f4b43e6a6cc57_NABC.sol NABC transfer 0.991 +``` + +### Train mode + +Train mode trains new models used to vectorize functions. You will need a large amount of contracts/functions if you plan to train a new model. + +``` +$ slither-simil train model.bin --input contracts +INFO:Slither-simil:Saving extracted data into last_data_train.txt +INFO:Slither-simil:Starting training +Read 0M words +Number of words: 348 +Number of labels: 0 +Progress: 100.0% words/sec/thread: 53124 lr: 0.000000 loss: 2.066949 ETA: 0h 0m +INFO:Slither-simil:Training complete +INFO:Slither-simil:Saving model +INFO:Slither-simil:Saving cache in cache.npz +INFO:Slither-simil:Done! +``` + +After it runs, the `slither-simil` will output the the trained model in `model.bin`, a cache of every function for use in test mode in `cache.npz`, and the SlithIR of every function for debugging in `last_data_train.txt`. + +### Plot mode + +Plot mode plots sets of functions to visually detect clusters of similar ones. + +Here's an example to plot all the functions named `add` from contracts named `SafeMath` sampling from 500 random contracts: + +``` +$ slither-simil plot etherscan_verified_contracts.bin --fname SafeMath.add --input cache.npz --nsamples 500 +INFO:Slither-simil:Loading data.. +INFO:Slither-simil:Procesing data.. +INFO:Slither-simil:Plotting data.. +INFO:Slither-simil:Saving figure to plot.png.. +``` + +![plot](https://user-images.githubusercontent.com/31542053/57525857-3d794f80-7302-11e9-9677-b4eb3f6a5c20.png) + +This mode performs dimensionality reduction using PCA, so the axes you see here [are **not** associated with any particular unit](https://stats.stackexchange.com/questions/137813/the-meaning-of-units-on-the-axes-of-a-pca-plot). + +It can can be also used to plot sets of functions using only a name from any contract (e.g. `burn`) . + +### Info mode + +This mode has two features. You can inspect the internal information about a pre-trained model. Info mode is typically used for debugging. + +``` +$ slither-simil info etherscan_verified_contracts.bin +INFO:Slither-simil:etherscan_verified_contracts.bin uses the following words: +INFO:Slither-simil: +INFO:Slither-simil:index(uint256) +INFO:Slither-simil:return +INFO:Slither-simil:condition(temporary_variable) +INFO:Slither-simil:member +INFO:Slither-simil:solidity_call(require(bool)) +INFO:Slither-simil:library_call +INFO:Slither-simil:binary(+) +INFO:Slither-simil:event +INFO:Slither-simil:(local_solc_variable(default)):=(temporary_variable) +... +``` + +... or examine the internal representation of function: + +``` +$ slither-simil info etherscan_verified_contracts.bin --filename MetaCoin.sol --fname MetaCoin.sendCoin --solc solc-0.4.25 +INFO:Slither-simil:Function sendCoin in contract MetaCoin is encoded as: +INFO:Slither-simil:index(uint256) binary(<) condition(temporary_variable) return index(uint256) binary(-) index(uint256) binary(+) event return +INFO:Slither-simil:[ 0.00689753 -0.05349572 -0.06854086 -0.01667773 0.1259813 -0.05974023 + 0.06719872 -0.04520541 0.13745852 0.14690697 -0.03721125 0.00579037 + 0.06865194 -0.03804035 0.01224702 -0.1014601 -0.02655532 -0.15334933 +... +``` \ No newline at end of file diff --git a/secure_contracts_docs/src/tools/Contract-Flattening.md b/secure_contracts_docs/src/tools/Contract-Flattening.md new file mode 100644 index 0000000000..b2da1e3be8 --- /dev/null +++ b/secure_contracts_docs/src/tools/Contract-Flattening.md @@ -0,0 +1,33 @@ +`slither-flat` produces a flattened version of the codebase. + +## Features +- Code flattening +- Support multiple [strategies](#strategies) +- Support circular dependency +- Support all the compilation platforms (Truffle, embark, buidler, etherlime, ...). + +## Usage +`slither-flat target` + +- `--contract ContractName`: flatten only one contract (standalone file) +### Strategies +`slither-flat` contains three strategies that can be specified with the `--strategy` flag: +- `MostDerived`: Export all the most derived contracts (every file is standalone) +- `OneFile`: Export all the contracts in one standalone file +- `LocalImport`: Export every contract in one separate file, and include import ".." in their preludes + +Default: `MostDerived` + +### Patching +`slither-flat` can transform the codebase to help some usage (eg. [Echidna](https://github.com/crytic/echidna)) +- `--convert-external`: convert `external` function to `public`. This is meant to facilitate [Echidna](https://github.com/crytic/echidna) usage. +- `--contract name`: To flatten only a target contract +- `--remove-assert`: Remove call to assert(). + +### Export option +- `--dir DirName`: output directory +- `--json file.json`: export the results to a json file (`--json -` output to the standard output +- `--zip file.zip`: export to a zip file +- `--zip-type type`: Zip compression type (default lzma)) + + diff --git a/secure_contracts_docs/src/tools/ERC-Conformance.md b/secure_contracts_docs/src/tools/ERC-Conformance.md new file mode 100644 index 0000000000..fd63b501fb --- /dev/null +++ b/secure_contracts_docs/src/tools/ERC-Conformance.md @@ -0,0 +1,72 @@ +`slither-check-erc` checks for ERC's conformance. + +## Features + +`slither-check-erc` will check that: + +- All the functions are present +- All the events are present +- Functions return the correct type +- Functions that must be view are view +- Events' parameters are correctly indexed +- The functions emit the events +- Derived contracts do not break the conformance + +## Supported ERCs + +- [ERC20](https://eips.ethereum.org/EIPS/eip-20): Token standard +- [ERC223](https://github.com/ethereum/eips/issues/223): Token standard +- [ERC777](https://eips.ethereum.org/EIPS/eip-777): Token standard +- [ERC721](https://eips.ethereum.org/EIPS/eip-721): Non-fungible token standard +- [ERC165](https://eips.ethereum.org/EIPS/eip-165): Standard interface detection +- [ERC1155](https://eips.ethereum.org/EIPS/eip-1155): Multi Token Standard +- [ERC1820](https://eips.ethereum.org/EIPS/eip-1820): Pseudo-introspection registry contract +- [ERC4524](https://eips.ethereum.org/EIPS/eip-4524): Safer ERC-20 +- [ERC1363](https://eips.ethereum.org/EIPS/eip-1363): Payable Token +- [ERC2612](https://eips.ethereum.org/EIPS/eip-2612): Permit Extension for EIP-20 Signed Approvals +- [ERC4626](https://eips.ethereum.org/EIPS/eip-4626): Tokenized Vaults + +## Usage: + +``` +slither-check-erc contract.sol ContractName +``` +For example, on + +```Solidity +contract ERC20{ + + event Transfer(address indexed,address,uint256); + + function transfer(address, uint256) public{ + + emit Transfer(msg.sender,msg.sender,0); + } + +} +``` + +The tool will report: + +``` +# Check ERC20 + +## Check functions +[ ] totalSupply() is missing +[ ] balanceOf(address) is missing +[✓] transfer(address,uint256) is present + [ ] transfer(address,uint256) -> () should return bool + [✓] Transfer(address,address,uint256) is emitted +[ ] transferFrom(address,address,uint256) is missing +[ ] approve(address,uint256) is missing +[ ] allowance(address,address) is missing +[ ] name() is missing (optional) +[ ] symbol() is missing (optional) +[ ] decimals() is missing (optional) + +## Check events +[✓] Transfer(address,address,uint256) is present + [✓] parameter 0 is indexed + [ ] parameter 1 should be indexed +[ ] Approval(address,address,uint256) is missing +``` \ No newline at end of file diff --git a/secure_contracts_docs/src/tools/Path-Finding-Utility.md b/secure_contracts_docs/src/tools/Path-Finding-Utility.md new file mode 100644 index 0000000000..7e41cccb49 --- /dev/null +++ b/secure_contracts_docs/src/tools/Path-Finding-Utility.md @@ -0,0 +1,26 @@ +`slither-find-paths` finds all the paths that reach a given target. + +## Usage +``` +slither-find-paths file.sol [contract.function targets] +``` +- `[contract.function targets]` is either one target, or a list of target + +## Example +Tested on [tests/possible_paths/paths.sol](https://github.com/trailofbits/slither/blob/master/tests/possible_paths/paths.sol) +``` +$ slither-find-paths paths.sol A.destination +Target functions: +- A.destination() + + +The following functions reach the specified targets: +- A.call() +- B.call2(A) + + +The following paths reach the specified targets: +A.call() -> A.destination() + +B.call2(A) -> A.call() -> A.destination() +``` \ No newline at end of file diff --git a/secure_contracts_docs/src/tools/Property-generation.md b/secure_contracts_docs/src/tools/Property-generation.md new file mode 100644 index 0000000000..1b66a7a6a1 --- /dev/null +++ b/secure_contracts_docs/src/tools/Property-generation.md @@ -0,0 +1,158 @@ +`slither-prop` generates code properties (e.g., invariants) that can be tested with unit tests or [Echidna](https://github.com/crytic/echidna/), entirely automatically. Once you have these properties in hand, use [Crytic](https://crytic.io/) to continuously test them with every commit. + +Note: `slither-prop` only supports Truffle for now. We'll be adding support for other frameworks soon! + +## How to Use Slither's Property Generator + +There are four steps: + +1. [Generate the tests](#step-1-generate-the-tests) +1. [Customize the constructor](#step-2-customize-the-constructor) +1. [Run the unit tests](#step-3-run-the-unit-tests-with-truffle) +1. [Run the Echidna tests](#step-4-run-the-property-tests-with-echidna) + +### Step 1. Generate the tests + +``` +slither-prop . --contract ContractName +``` + +`slither-prop` will generate: + +- Solidity files containing the properties +- `echidna_config.yaml` to configure Echidna +- One Truffle migration file +- Two Truffle unit-test files + +For example on [examples/slither-prop](https://github.com/crytic/slither/tree/9623a2781faa4e7759f06d2e8c4adcd45078af69/examples/slither-prop). +``` +Write contracts/crytic/interfaces.sol +Write contracts/crytic/PropertiesERC20BuggyTransferable.sol +Write contracts/crytic/TestERC20BuggyTransferable.sol +Write echidna_config.yaml +Write migrations/1_TestERC20BuggyTransferable.js +Write test/crytic/InitializationTestERC20BuggyTransferable.js +Write test/crytic/TestERC20BuggyTransferable.js +################################################ +Update the constructor in contracts/crytic/TestERC20BuggyTransferable.sol + +To run the unit tests: + truffle test test/crytic/InitializationTestERC20BuggyTransferable.js + truffle test test/crytic/TestERC20BuggyTransferable.js + +To run Echidna: + echidna-test . --contract TestERC20BuggyTransferable --config echidna_config.yaml +``` + +### Step 2. Customize the constructor + +Next, update the constructor in `contracts/crytic/TestX.sol`: + +On [examples/slither-prop/contracts](https://github.com/crytic/slither/tree/9623a2781faa4e7759f06d2e8c4adcd45078af69/examples/slither-prop), update the constructor as follow: + +```solidity + constructor() public{ + + _balanceOf[crytic_user] = 1 ether; + _balanceOf[crytic_owner] = 1 ether; + _balanceOf[crytic_attacker] = 1 ether; + _totalSupply = 3 ether; + + // + // + // Update the following if totalSupply and balanceOf are external functions or state variables: + + initialTotalSupply = totalSupply(); + initialBalance_owner = balanceOf(crytic_owner); + initialBalance_user = balanceOf(crytic_user); + initialBalance_attacker = balanceOf(crytic_attacker); + } +``` + +### Step 3. Run the unit tests with Truffle + +The first unit test file, named `InitializationX.js` will check that the constructor has been correctly initialized: + +``` +$ truffle test test/crytic/InitializationTestERC20BuggyTransferable.js +[..] + Contract: TestERC20BuggyTransferable + ✓ The total supply is correctly initialized. (47ms) + ✓ Owner's balance is correctly initialized. + ✓ User's balance is correctly initialized. + ✓ Attacker's balance is correctly initialized. + ✓ All the users have a positive balance. (69ms) + ✓ The total supply is the user and owner balance. (38ms) + +``` + +If all the unit tests passed, run the property tests: +``` +$ truffle test test/crytic/InitializationTestERC20BuggyTransferable.js + Contract: TestERC20BuggyTransferable + ✓ The address 0x0 should not receive tokens. + ✓ Allowance can be changed. (108ms) + ✓ Balance of one user must be less or equal to the total supply. (70ms) + ✓ Balance of the crytic users must be less or equal to the total supply. + 1) No one should be able to send tokens to the address 0x0 (transfer). + > No events were emitted + 2) No one should be able to send tokens to the address 0x0 (transferFrom). + > No events were emitted + ✓ Self transferFrom works. (115ms) + ✓ transferFrom works. (108ms) + ✓ Self transfer works. (89ms) + ✓ transfer works. (97ms) + 3) Cannot transfer more than the balance. + > No events were emitted + +``` + +As you can see, the unit tests detect some of the bugs. + +### Step 4. Run the property tests with Echidna + +``` +$ echidna-test . --contract TestERC20BuggyTransferable --config echidna_config.yaml +``` + +## Scenarios + +`slither-prop` contains different scenarios that can be specified with the `--scenario NAME` flag. + +Here are the available scenarios: +``` +#################### ERC20 #################### +Transferable - Test the correct tokens transfer +Pausable - Test the pausable functionality +NotMintable - Test that no one can mint tokens +NotMintableNotBurnable - Test that no one can mint or burn tokens +NotBurnable - Test that no one can burn tokens +Burnable - Test the burn of tokens. Require the "burn(address) returns()" function +``` + +## All properties +``` ++-----+-------------------------------------------------------------------------+------------------------+ +| Num | Description | Scenario | ++-----+-------------------------------------------------------------------------+------------------------+ +| 0 | The address 0x0 should not receive tokens. | Transferable | +| 1 | Allowance can be changed. | Transferable | +| 2 | Balance of one user must be less or equal to the total supply. | Transferable | +| 3 | Balance of the crytic users must be less or equal to the total supply. | Transferable | +| 4 | No one should be able to send tokens to the address 0x0 (transfer). | Transferable | +| 5 | No one should be able to send tokens to the address 0x0 (transferFrom). | Transferable | +| 6 | Self transferFrom works. | Transferable | +| 7 | transferFrom works. | Transferable | +| 8 | Self transfer works. | Transferable | +| 9 | transfer works. | Transferable | +| 10 | Cannot transfer more than the balance. | Transferable | +| 11 | Cannot transfer. | Pausable | +| 12 | Cannot execute transferFrom. | Pausable | +| 13 | Cannot change the balance. | Pausable | +| 14 | Cannot change the allowance. | Pausable | +| 15 | The total supply does not increase. | NotMintable | +| 16 | The total supply does not change. | NotMintableNotBurnable | +| 17 | The total supply does not decrease. | NotBurnable | +| 18 | The total supply does not decrease. | Burnable | ++-----+-------------------------------------------------------------------------+------------------------+ +``` \ No newline at end of file diff --git a/secure_contracts_docs/src/tools/Slither-format.md b/secure_contracts_docs/src/tools/Slither-format.md new file mode 100644 index 0000000000..abc9971d7b --- /dev/null +++ b/secure_contracts_docs/src/tools/Slither-format.md @@ -0,0 +1,17 @@ +`slither-format` generates automatically patches. The patches are compatible with `git`. + +Carefully review each patch before applying it. + +## Usage +`slither-format target`. + +The patches will be generated in `crytic-export/patches` + +## Detectors supported +- `unused-state` +- `solc-version` +- `pragma` +- `naming-convention` +- `external-function` +- `constable-states` +- `constant-function` \ No newline at end of file diff --git a/secure_contracts_docs/src/tools/Upgradeability-Checks.md b/secure_contracts_docs/src/tools/Upgradeability-Checks.md new file mode 100644 index 0000000000..49f9eccb8f --- /dev/null +++ b/secure_contracts_docs/src/tools/Upgradeability-Checks.md @@ -0,0 +1,546 @@ +# Upgradeability Checks + +`slither-check-upgradeability` helps review contracts that use the [delegatecall proxy pattern](https://blog.trailofbits.com/2018/09/05/contract-upgrade-anti-patterns/). + +## Checks + + +Num | Check | What it Detects | Impact | Proxy | Contract V2 +--- | --- | --- | --- | --- | --- +1 | `became-constant` | [Variables that should not be constant](https://github.com/crytic/slither/wiki/Upgradeability-Checks#variables-that-should-not-be-constant) | High | | X +2 | `function-id-collision` | [Functions ids collision](https://github.com/crytic/slither/wiki/Upgradeability-Checks#functions-ids-collisions) | High | X | +3 | `function-shadowing` | [Functions shadowing](https://github.com/crytic/slither/wiki/Upgradeability-Checks#functions-shadowing) | High | X | +4 | `missing-calls` | [Missing calls to init functions](https://github.com/crytic/slither/wiki/Upgradeability-Checks#initialize-functions-are-not-called) | High | | +5 | `missing-init-modifier` | [initializer() is not called](https://github.com/crytic/slither/wiki/Upgradeability-Checks#initializer-is-not-called) | High | | +6 | `multiple-calls` | [Init functions called multiple times](https://github.com/crytic/slither/wiki/Upgradeability-Checks#initialize-functions-are-called-multiple-times) | High | | +7 | `order-vars-contracts` | [Incorrect vars order with the v2](https://github.com/crytic/slither/wiki/Upgradeability-Checks#incorrect-variables-with-the-v2) | High | | X +8 | `order-vars-proxy` | [Incorrect vars order with the proxy](https://github.com/crytic/slither/wiki/Upgradeability-Checks#incorrect-variables-with-the-proxy) | High | X | +9 | `variables-initialized` | [State variables with an initial value](https://github.com/crytic/slither/wiki/Upgradeability-Checks#state-variable-initialized) | High | | +10 | `were-constant` | [Variables that should be constant](https://github.com/crytic/slither/wiki/Upgradeability-Checks#variables-that-should-be-constant) | High | | X +11 | `extra-vars-proxy` | [Extra vars in the proxy](https://github.com/crytic/slither/wiki/Upgradeability-Checks#extra-variables-in-the-proxy) | Medium | X | +12 | `missing-variables` | [Variable missing in the v2](https://github.com/crytic/slither/wiki/Upgradeability-Checks#missing-variables) | Medium | | X +13 | `extra-vars-v2` | [Extra vars in the v2](https://github.com/crytic/slither/wiki/Upgradeability-Checks#extra-variables-in-the-v2) | Informational | | X +14 | `init-inherited` | [Initializable is not inherited](https://github.com/crytic/slither/wiki/Upgradeability-Checks#initializable-is-not-inherited) | Informational | | +15 | `init-missing` | [Initializable is missing](https://github.com/crytic/slither/wiki/Upgradeability-Checks#initializable-is-missing) | Informational | | +16 | `initialize-target` | [Initialize function that must be called](https://github.com/crytic/slither/wiki/Upgradeability-Checks#initialize-function) | Informational | | +17 | `initializer-missing` | [initializer() is missing](https://github.com/crytic/slither/wiki/Upgradeability-Checks#initializer-is-missing) | Informational | | + +## Usage + +``` +slither-check-upgradeability project ContractName +``` +- `project` can be a Solidity file, or a platform (truffle/embark/..) directory + +### Contract V2 +If you want to check the contract and its update, use: +- `--new-contract-name ContractName` +- `--new-contract-filename contract_project` + +`--new-contract-filename` is not needed if the new contract is in the same codebase than the original one. + +### Proxy +If you want to check also the proxy, use: +- `--proxy-name ProxyName` +- `--proxy-filename proxy_project` + +`--proxy-filename` is not needed if the proxy is in the same codebase than the targeted contract. + +#### ZOS +If you use zos, you will have the proxy and the contract in different directories. + +Likely, you will use one of the proxy from https://github.com/zeppelinos/zos. Clone the `zos`, and install the dependencies: +``` +git clone https://github.com/zeppelinos/zos +cd zos/packages/lib +npm install +rm contracts/mocks/WithConstructorImplementation.sol +``` +Note: `contracts/mocks/WithConstructorImplementation.sol` must be removed as it contains a [name clash collision](https://github.com/crytic/slither/wiki#keyerror-or-nonetype-error) with `contracts/mocks/Invalid.sol` + +Then from your project directory: +``` +slither-check-upgradeability . ContractName --proxy-filename /path/to/zos/packages/lib/ --proxy-name UpgradeabilityProxy +``` + +According to your setup, you might choose another proxy name than `UpgradeabilityProxy`. + + +## Checks Description + + + +## Variables that should not be constant +### Configuration +* Check: `became-constant` +* Severity: `High` + +### Description + +Detect state variables that should not be `constant̀`. + + +### Exploit Scenario: + +```solidity +contract Contract{ + uint variable1; + uint variable2; + uint variable3; +} + +contract ContractV2{ + uint variable1; + uint constant variable2; + uint variable3; +} +``` +Because `variable2` is now a `constant`, the storage location of `variable3` will be different. +As a result, `ContractV2` will have a corrupted storage layout. + + +### Recommendation + +Do not make an existing state variable `constant`. + + +## Functions ids collisions +### Configuration +* Check: `function-id-collision` +* Severity: `High` + +### Description + +Detect function id collision between the contract and the proxy. + + +### Exploit Scenario: + +```solidity +contract Contract{ + function gsf() public { + // ... + } +} + +contract Proxy{ + function tgeo() public { + // ... + } +} +``` +`Proxy.tgeo()` and `Contract.gsf()` have the same function id (0x67e43e43). +As a result, `Proxy.tgeo()` will shadow Contract.gsf()`. + + +### Recommendation + +Rename the function. Avoid public functions in the proxy. + + +## Functions shadowing +### Configuration +* Check: `function-shadowing` +* Severity: `High` + +### Description + +Detect function shadowing between the contract and the proxy. + + +### Exploit Scenario: + +```solidity +contract Contract{ + function get() public { + // ... + } +} + +contract Proxy{ + function get() public { + // ... + } +} +``` +`Proxy.get` will shadow any call to `get()`. As a result `get()` is never executed in the logic contract and cannot be updated. + + +### Recommendation + +Rename the function. Avoid public functions in the proxy. + + +## Initialize functions are not called +### Configuration +* Check: `missing-calls` +* Severity: `High` + +### Description + +Detect missing calls to initialize functions. + + +### Exploit Scenario: + +```solidity +contract Base{ + function initialize() public{ + /// + } +} +contract Derived is Base{ + function initialize() public{ + /// + } +} + +``` +`Derived.initialize` does not call `Base.initialize` leading the contract to not be correctly initialized. + + +### Recommendation + +Ensure all the initialize functions are reached by the most derived initialize function. + + +## initializer() is not called +### Configuration +* Check: `missing-init-modifier` +* Severity: `High` + +### Description + +Detect if `Initializable.initializer()` is called. + + +### Exploit Scenario: + +```solidity +contract Contract{ + function initialize() public{ + /// + } +} + +``` +`initialize` should have the `initializer` modifier to prevent someone from initializing the contract multiple times. + + +### Recommendation + +Use `Initializable.initializer()`. + + +## Initialize functions are called multiple times +### Configuration +* Check: `multiple-calls` +* Severity: `High` + +### Description + +Detect multiple calls to a initialize function. + + +### Exploit Scenario: + +```solidity +contract Base{ + function initialize(uint) public{ + /// + } +} +contract Derived is Base{ + function initialize(uint a, uint b) public{ + initialize(a); + } +} + +contract DerivedDerived is Derived{ + function initialize() public{ + initialize(0); + initialize(0, 1 ); + } +} + +``` +`Base.initialize(uint)` is called two times in `DerivedDerived.initiliaze` execution, leading to a potential corruption. + + +### Recommendation + +Call only one time every initialize function. + + +## Incorrect variables with the v2 +### Configuration +* Check: `order-vars-contracts` +* Severity: `High` + +### Description + +Detect variables that are different between the original contract and the updated one. + + +### Exploit Scenario: + +```solidity +contract Contract{ + uint variable1; +} + +contract ContractV2{ + address variable1; +} +``` +`Contract` and `ContractV2` do not have the same storage layout. As a result the storage of both contracts can be corrupted. + + +### Recommendation + +Respect the variable order of the original contract in the updated contract. + + +## Incorrect variables with the proxy +### Configuration +* Check: `order-vars-proxy` +* Severity: `High` + +### Description + +Detect variables that are different between the contract and the proxy. + + +### Exploit Scenario: + +```solidity +contract Contract{ + uint variable1; +} + +contract Proxy{ + address variable1; +} +``` +`Contract` and `Proxy` do not have the same storage layout. As a result the storage of both contracts can be corrupted. + + +### Recommendation + +Avoid variables in the proxy. If a variable is in the proxy, ensure it has the same layout than in the contract. + + +## State variable initialized +### Configuration +* Check: `variables-initialized` +* Severity: `High` + +### Description + +Detect state variables that are initialized. + + +### Exploit Scenario: + +```solidity +contract Contract{ + uint variable = 10; +} +``` +Using `Contract` will the delegatecall proxy pattern will lead `variable` to be 0 when called through the proxy. + + +### Recommendation + +Using initialize functions to write initial values in state variables. + + +## Variables that should be constant +### Configuration +* Check: `were-constant` +* Severity: `High` + +### Description + +Detect state variables that should be `constant̀`. + + +### Exploit Scenario: + +```solidity +contract Contract{ + uint variable1; + uint constant variable2; + uint variable3; +} + +contract ContractV2{ + uint variable1; + uint variable2; + uint variable3; +} +``` +Because `variable2` is not anymore a `constant`, the storage location of `variable3` will be different. +As a result, `ContractV2` will have a corrupted storage layout. + + +### Recommendation + +Do not remove `constant` from a state variables during an update. + + +## Extra variables in the proxy +### Configuration +* Check: `extra-vars-proxy` +* Severity: `Medium` + +### Description + +Detect variables that are in the proxy and not in the contract. + + +### Exploit Scenario: + +```solidity +contract Contract{ + uint variable1; +} + +contract Proxy{ + uint variable1; + uint variable2; +} +``` +`Proxy` contains additional variables. A future update of `Contract` is likely to corrupt the proxy. + + +### Recommendation + +Avoid variables in the proxy. If a variable is in the proxy, ensure it has the same layout than in the contract. + + +## Missing variables +### Configuration +* Check: `missing-variables` +* Severity: `Medium` + +### Description + +Detect variables that were present in the original contracts but are not in the updated one. + + +### Exploit Scenario: + +```solidity +contract V1{ + uint variable1; + uint variable2; +} + +contract V2{ + uint variable1; +} +``` +The new version, `V2` does not contain `variable1`. +If a new variable is added in an update of `V2`, this variable will hold the latest value of `variable2` and +will be corrupted. + + +### Recommendation + +Do not change the order of the state variables in the updated contract. + + +## Extra variables in the v2 +### Configuration +* Check: `extra-vars-v2` +* Severity: `Informational` + +### Description + +Show new variables in the updated contract. + +This finding does not have an immediate security impact and is informative. + + +### Exploit Scenario: + +```solidity +contract Contract{ + uint variable1; +} + +contract Proxy{ + uint variable1; + uint variable2; +} +``` +`Proxy` contains additional variables. A future update of `Contract` is likely to corrupt the proxy. + + +### Recommendation + +Ensure that all the new variables are expected. + + +## Initializable is not inherited +### Configuration +* Check: `init-inherited` +* Severity: `Informational` + +### Description + +Detect if `Initializable` is inherited. + + +### Recommendation + +Review manually the contract's initialization. Consider inheriting `Initializable`. + + +## Initializable is missing +### Configuration +* Check: `init-missing` +* Severity: `Informational` + +### Description + +Detect if a contract `Initializable` is present. + + +### Recommendation + +Review manually the contract's initialization.. +Consider using a `Initializable` contract to follow [standard practice](https://docs.openzeppelin.com/upgrades/2.7/writing-upgradeable). + + +## Initialize function +### Configuration +* Check: `initialize-target` +* Severity: `Informational` + +### Description + +Show the function that must be called at deployment. + +This finding does not have an immediate security impact and is informative. + + +### Recommendation + +Ensure that the function is called at deployment. + + +## initializer() is missing +### Configuration +* Check: `initializer-missing` +* Severity: `Informational` + +### Description + +Detect the lack of `Initializable.initializer()` modifier. + + +### Recommendation + +Review manually the contract's initialization. Consider inheriting a `Initializable.initializer()` modifier. diff --git a/secure_contracts_docs/src/tutorials/README.md b/secure_contracts_docs/src/tutorials/README.md new file mode 100644 index 0000000000..5665d91a57 --- /dev/null +++ b/secure_contracts_docs/src/tutorials/README.md @@ -0,0 +1,54 @@ +# Slither + +The objective of this tutorial is to demonstrate how to use Slither to automatically find bugs in smart contracts. + +- [Installation](#installation) +- [Command line usage](#command-line) +- [Introduction to static analysis](./static_analysis.md): A concise introduction to static analysis +- [API](../api/api.md): Python API description + +Once you feel confident with the material in this README, proceed to the exercises: + +- [Exercise 1](./exercise1.md): Function override protection +- [Exercise 2](./exercise2.md): Check for access controls +- [Exercise 3](./exercise3.md): Find variable used in conditional statements + +Watch Slither's [code walkthrough](https://www.youtube.com/watch?v=EUl3UlYSluU), or [API walkthrough](https://www.youtube.com/watch?v=Ijf0pellvgw) to learn about its code structure. + +## Installation + +Slither requires Python >= 3.8. You can install it through pip or by using Docker. + +Installing Slither through pip: + +```bash +pip3 install --user slither-analyzer +``` + +### Docker + +Installing Slither through Docker: + +```bash +docker pull trailofbits/eth-security-toolbox +docker run -it -v "$PWD":/home/trufflecon trailofbits/eth-security-toolbox +``` + +_The last command runs the eth-security-toolbox in a Docker container that has access to your current directory. You can modify the files from your host, and run the tools on the files from the Docker container._ + +Inside the Docker container, run: + +```bash +solc-select 0.5.11 +cd /home/trufflecon/ +``` + +## Command Line + +**Command line vs. user-defined scripts.** Slither comes with a set of pre-defined detectors that can identify many common bugs. Running Slither from the command line will execute all the detectors without requiring detailed knowledge of static analysis: + +```bash +slither project_paths +``` + +Besides detectors, Slither also offers code review capabilities through its [printers](https://github.com/crytic/slither#printers) and [tools](https://github.com/crytic/slither#tools). diff --git a/secure_contracts_docs/src/tutorials/examples/coin.sol b/secure_contracts_docs/src/tutorials/examples/coin.sol new file mode 100644 index 0000000000..210753a968 --- /dev/null +++ b/secure_contracts_docs/src/tutorials/examples/coin.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.0; + +contract Coin { + address owner = msg.sender; + + mapping(address => uint256) balances; + + // _mint must not be overridden + function _mint(address dst, uint256 val) internal virtual { + require(msg.sender == owner); + balances[dst] += val; + } + + function mint(address dst, uint256 val) public { + _mint(dst, val); + } +} + +contract MyCoin is Coin { + event Mint(address, uint256); + + function _mint(address dst, uint256 val) internal override { + balances[dst] += val; + emit Mint(dst, val); + } +} diff --git a/secure_contracts_docs/src/tutorials/examples/expected_results_print_basic_information.txt b/secure_contracts_docs/src/tutorials/examples/expected_results_print_basic_information.txt new file mode 100644 index 0000000000..19a224f931 --- /dev/null +++ b/secure_contracts_docs/src/tutorials/examples/expected_results_print_basic_information.txt @@ -0,0 +1,39 @@ +Contract: Coin + Inherit from[] + _mint(address,uint256): + Visibility: internal + Contract: Coin + Modifier: [] + Is constructor? False + mint(address,uint256): + Visibility: public + Contract: Coin + Modifier: [] + Is constructor? False + slitherConstructorVariables(): + Visibility: internal + Contract: Coin + Modifier: [] + Is constructor? False +Contract: MyCoin + Inherit from['Coin'] + _mint(address,uint256): + Visibility: internal + Contract: MyCoin + Modifier: [] + Is constructor? False + mint(address,uint256): + Visibility: public + Contract: MyCoin + Modifier: [] + Is constructor? False + _mint(address,uint256): + Visibility: internal + Contract: MyCoin + Modifier: [] + Is constructor? False + slitherConstructorVariables(): + Visibility: internal + Contract: MyCoin + Modifier: [] + Is constructor? False diff --git a/secure_contracts_docs/src/tutorials/examples/print_basic_information.py b/secure_contracts_docs/src/tutorials/examples/print_basic_information.py new file mode 100644 index 0000000000..aee217e079 --- /dev/null +++ b/secure_contracts_docs/src/tutorials/examples/print_basic_information.py @@ -0,0 +1,18 @@ +import sys +from slither import Slither + +# Init slither +slither = Slither('coin.sol') + +for contract in slither.contracts: + # Print the contract's name + print(f'Contract: {contract.name}') + # Print the name of the contract inherited + print(f'\tInherit from{[c.name for c in contract.inheritance]}') + for function in contract.functions: + # For each function, print basic information + print(f'\t{function.full_name}:') + print(f'\t\tVisibility: {function.visibility}') + print(f'\t\tContract: {function.contract}') + print(f'\t\tModifier: {[m.name for m in function.modifiers]}') + print(f'\t\tIs constructor? {function.is_constructor}') diff --git a/secure_contracts_docs/src/tutorials/exercise1.md b/secure_contracts_docs/src/tutorials/exercise1.md new file mode 100644 index 0000000000..887ba7b89b --- /dev/null +++ b/secure_contracts_docs/src/tutorials/exercise1.md @@ -0,0 +1,33 @@ +# Exercise 1: Function Overridden Protection + +The goal is to create a script that performs a feature that was not present in previous version of Solidity: function overriding protection. + +[exercises/exercise1/coin.sol](./exercises/exercise1/coin.sol) contains a function that must never be overridden: + +```solidity +_mint(address dst, uint256 val) +``` + +Use Slither to ensure that no contract inheriting Coin overrides this function. + +Use `solc-select install 0.5.0 && solc-select use 0.5.0` to switch to solc 0.5.0 + +## Proposed Algorithm + +``` +Get the Coin contract + For each contract in the project: + If Coin is in the list of inherited contracts: + Get the _mint function + If the contract declaring the _mint function is not Coin: + A bug is found. +``` + +## Tips + +- To get a specific contract, use `slither.get_contract_from_name` (note: it returns a list) +- To get a specific function, use `contract.get_function_from_signature` + +## Solution + +See [exercises/exercise1/solution.py](./exercises/exercise1/solution.py). diff --git a/secure_contracts_docs/src/tutorials/exercise2.md b/secure_contracts_docs/src/tutorials/exercise2.md new file mode 100644 index 0000000000..b6c1a88427 --- /dev/null +++ b/secure_contracts_docs/src/tutorials/exercise2.md @@ -0,0 +1,21 @@ +# Exercise 2: Access Control + +The [exercises/exercise2/coin.sol](./exercises/exercise2/coin.sol) file contains an access control implementation with the `onlyOwner` modifier. A common mistake is forgetting to add the modifier to a crucial function. In this exercise, we will use Slither to implement a conservative access control approach. + +Our goal is to create a script that ensures all public and external functions call `onlyOwner`, except for the functions on the whitelist. + +## Proposed Algorithm + +``` +Create a whitelist of signatures +Explore all the functions + If the function is in the whitelist of signatures: + Skip + If the function is public or external: + If onlyOwner is not in the modifiers: + A bug is found +``` + +## Solution + +Refer to [exercises/exercise2/solution.py](./exercises/exercise2/solution.py) for the solution. diff --git a/secure_contracts_docs/src/tutorials/exercise3.md b/secure_contracts_docs/src/tutorials/exercise3.md new file mode 100644 index 0000000000..c19c99080e --- /dev/null +++ b/secure_contracts_docs/src/tutorials/exercise3.md @@ -0,0 +1,13 @@ +# Exercise 3: Find function that use a given variable in a condition + +The [exercises/exercise3/find.sol](./exercises/exercise3/find.sol) file contains a contract that use `my_variable` variable in multiple locations. + +Our goal is to create a script that list all the functions that use `my_variable` in a conditional or require statement. + +## Proposed Approach + +Explore all the helpers provided by [`Function`](https://github.com/crytic/slither/blob/master/slither/core/declarations/function.py) object to find an easy way to reach the goal + +## Solution + +Refer to [exercises/exercise3/solution.py](./exercises/exercise3/solution.py) for the solution. diff --git a/secure_contracts_docs/src/tutorials/exercises/exercise1/coin.sol b/secure_contracts_docs/src/tutorials/exercises/exercise1/coin.sol new file mode 100644 index 0000000000..7170e3da6e --- /dev/null +++ b/secure_contracts_docs/src/tutorials/exercises/exercise1/coin.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.5.0; + +contract Coin { + address owner = msg.sender; + + mapping(address => uint256) balances; + + // _mint must not be overridden + function _mint(address dst, uint256 val) internal { + require(msg.sender == owner); + balances[dst] += val; + } + + function mint(address dst, uint256 val) public { + _mint(dst, val); + } +} + +contract MyCoin is Coin { + event Mint(address, uint256); + + function _mint(address dst, uint256 val) internal { + balances[dst] += val; + emit Mint(dst, val); + } +} diff --git a/secure_contracts_docs/src/tutorials/exercises/exercise1/expected_results.txt b/secure_contracts_docs/src/tutorials/exercises/exercise1/expected_results.txt new file mode 100644 index 0000000000..8ac9a8c568 --- /dev/null +++ b/secure_contracts_docs/src/tutorials/exercises/exercise1/expected_results.txt @@ -0,0 +1 @@ +Error, MyCoin overrides _mint diff --git a/secure_contracts_docs/src/tutorials/exercises/exercise1/solution.py b/secure_contracts_docs/src/tutorials/exercises/exercise1/solution.py new file mode 100644 index 0000000000..4ad0ce7362 --- /dev/null +++ b/secure_contracts_docs/src/tutorials/exercises/exercise1/solution.py @@ -0,0 +1,15 @@ +from slither.slither import Slither + +slither = Slither('coin.sol') +coin = slither.get_contract_from_name('Coin')[0] + +# Iterate over all the contracts +for contract in slither.contracts: + # If the contract is derived from MyContract + if coin in contract.inheritance: + # Get the function definition + mint = contract.get_function_from_signature('_mint(address,uint256)') + # If the function was not declared by coin, there is a bug ! + # Detect error only for contracts overriding the '_mint' function + if mint.contract_declarer == contract: + print(f'Error, {contract} overrides {mint}') diff --git a/secure_contracts_docs/src/tutorials/exercises/exercise2/coin.sol b/secure_contracts_docs/src/tutorials/exercises/exercise2/coin.sol new file mode 100644 index 0000000000..bec7355687 --- /dev/null +++ b/secure_contracts_docs/src/tutorials/exercises/exercise2/coin.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.0; + +contract Owned { + address public owner; + + constructor() public { + owner = msg.sender; + } + + modifier onlyOwner() { + require(msg.sender == owner); + _; + } +} + +contract Coin is Owned { + uint256 decimals = 18; + + mapping(address => uint256) balances; + + event Mint(address indexed destination, uint256 amount); + + /// @notice Mint tokens + /// @param addr The address holding the new token + /// @param value The amount of token to be minted + /// @dev This function performed no check on the caller. Must stay internal + function _mint(address addr, uint256 value) internal { + balances[addr] += value; + require(balances[addr] >= value); + emit Mint(addr, value); + } + + /// @notice Mint tokens. Callable only by the owner + /// @param addr The address holding the new token + /// @param value The amount of token to be minted + function mint(address addr, uint256 value) public onlyOwner { + _mint(addr, value); + } + + /// @notice Mint tokens. Used by the owner to mint directly tokens to himself. Callable only by the owner + /// @param value The amount of token to be minted + function mint(uint256 value) public { + _mint(msg.sender, value); + } + + /// @notice Return the user's balance + /// @param dst User address + function balanceOf(address dst) public view returns (uint256) { + return balances[dst]; + } +} diff --git a/secure_contracts_docs/src/tutorials/exercises/exercise2/expected_results.txt b/secure_contracts_docs/src/tutorials/exercises/exercise2/expected_results.txt new file mode 100644 index 0000000000..eebacba3d6 --- /dev/null +++ b/secure_contracts_docs/src/tutorials/exercises/exercise2/expected_results.txt @@ -0,0 +1 @@ +mint(uint256) is unprotected! diff --git a/secure_contracts_docs/src/tutorials/exercises/exercise2/solution.py b/secure_contracts_docs/src/tutorials/exercises/exercise2/solution.py new file mode 100644 index 0000000000..f693a97080 --- /dev/null +++ b/secure_contracts_docs/src/tutorials/exercises/exercise2/solution.py @@ -0,0 +1,15 @@ +from slither import Slither + +slither = Slither('coin.sol') + +whitelist = ['balanceOf(address)'] + +for contract in slither.contracts: + for function in contract.functions: + if function.full_name in whitelist: + continue + if function.is_constructor: + continue + if function.visibility in ['public', 'external']: + if not 'onlyOwner()' in [m.full_name for m in function.modifiers]: + print(f'{function.full_name} is unprotected!') diff --git a/secure_contracts_docs/src/tutorials/exercises/exercise3/expected_results.txt b/secure_contracts_docs/src/tutorials/exercises/exercise3/expected_results.txt new file mode 100644 index 0000000000..f06033223f --- /dev/null +++ b/secure_contracts_docs/src/tutorials/exercises/exercise3/expected_results.txt @@ -0,0 +1 @@ +The function using "a" in condition are ['condition', 'call_require'] diff --git a/secure_contracts_docs/src/tutorials/exercises/exercise3/find.sol b/secure_contracts_docs/src/tutorials/exercises/exercise3/find.sol new file mode 100644 index 0000000000..0e30388ee7 --- /dev/null +++ b/secure_contracts_docs/src/tutorials/exercises/exercise3/find.sol @@ -0,0 +1,15 @@ +contract Find { + uint my_variable; + + function condition() public { + if (my_variable == 0) {} + } + + function call_require() public { + require(my_variable == 0); + } + + function read_and_write() public { + my_variable = my_variable + 1; + } +} diff --git a/secure_contracts_docs/src/tutorials/exercises/exercise3/solution.py b/secure_contracts_docs/src/tutorials/exercises/exercise3/solution.py new file mode 100644 index 0000000000..410194caaf --- /dev/null +++ b/secure_contracts_docs/src/tutorials/exercises/exercise3/solution.py @@ -0,0 +1,20 @@ +from slither.slither import Slither + +slither = Slither('find.sol') +find = slither.get_contract_from_name('Find')[0] + +assert find + +# Get the variable +my_variable = find.get_state_variable_from_name("my_variable") +assert my_variable + + +function_using_a_as_condition = [ + f + for f in find.functions + if f.is_reading_in_conditional_node(my_variable) or f.is_reading_in_require_or_assert(my_variable) +] + +# Print the result +print(f'The function using "a" in condition are {[f.name for f in function_using_a_as_condition]}') \ No newline at end of file diff --git a/secure_contracts_docs/src/tutorials/images/ast.png b/secure_contracts_docs/src/tutorials/images/ast.png new file mode 100644 index 0000000000000000000000000000000000000000..b19e32779ec4785b61972e0235d180d1992840cc GIT binary patch literal 20508 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 literal 0 HcmV?d00001 diff --git a/secure_contracts_docs/src/tutorials/scripts/gh_action_test.sh b/secure_contracts_docs/src/tutorials/scripts/gh_action_test.sh new file mode 100644 index 0000000000..d2bc01e494 --- /dev/null +++ b/secure_contracts_docs/src/tutorials/scripts/gh_action_test.sh @@ -0,0 +1,73 @@ +#!/usr/bin/env bash + +test_examples(){ + cd examples + + python print_basic_information.py > results.txt + if [ $? -ne 0 ] + then + exit -1 + fi + + DIFF=$(diff results.txt expected_results_print_basic_information.txt) + + if [ "$DIFF" != "" ] + then + echo "print_basic_information.py failed" + cat results.txt + echo "" + cat expected_results_print_basic_information.txt + echo "" + echo "$DIFF" + exit -1 + fi + + echo "print_basic_information.py passed" + cd .. +} + +test_exercise(){ + cd "exercises/exercise$1" + + python solution.py > results.txt + if [ $? -ne 0 ] + then + exit -1 + fi + + DIFF=$(diff results.txt expected_results.txt) + + if [ "$DIFF" != "" ] + then + echo "exercise $1 failed" + cat results.txt + echo "" + cat expected_results.txt + echo "" + echo "$DIFF" + exit -1 + fi + + echo "exercise $1 passed" + cd ../.. +} + + +cd program-analysis/slither +pip install slither-analyzer +solc-select install 0.8.20 +solc-select use 0.8.20 + +test_examples + +solc-select install 0.5.11 +solc-select use 0.5.11 +test_exercise 1 + +solc-select use 0.8.20 +test_exercise 2 + +test_exercise 3 + +echo "Slither tests passed" + From 823897efd1e719bbabcfb0dad41d11c8f76209c0 Mon Sep 17 00:00:00 2001 From: Feist Josselin Date: Tue, 25 Feb 2025 12:10:45 +0100 Subject: [PATCH 09/30] Improvements --- secure_contracts_docs/src/README.md | 1 - secure_contracts_docs/src/Usage.md | 3 +- .../src/api/Developer-installation.md | 0 secure_contracts_docs/src/api/SlithIR-SSA.md | 2 +- secure_contracts_docs/src/api/api.md | 2 +- .../src/{tutorials => api}/examples/coin.sol | 0 ...pected_results_print_basic_information.txt | 0 .../examples/print_basic_information.py | 0 .../src/{tutorials => api}/images/ast.png | Bin .../src/{tutorials => api}/images/cfg.png | Bin .../src/api/static_analysis.md | 12 ++--- .../src/printers/Printer-documentation.md | 47 ++++++++++-------- .../src/tools/Adding-a-new-utility.md | 2 - .../src/tools/Upgradeability-Checks.md | 34 ++++++------- 14 files changed, 53 insertions(+), 50 deletions(-) delete mode 100644 secure_contracts_docs/src/api/Developer-installation.md rename secure_contracts_docs/src/{tutorials => api}/examples/coin.sol (100%) rename secure_contracts_docs/src/{tutorials => api}/examples/expected_results_print_basic_information.txt (100%) rename secure_contracts_docs/src/{tutorials => api}/examples/print_basic_information.py (100%) rename secure_contracts_docs/src/{tutorials => api}/images/ast.png (100%) rename secure_contracts_docs/src/{tutorials => api}/images/cfg.png (100%) diff --git a/secure_contracts_docs/src/README.md b/secure_contracts_docs/src/README.md index 7b4c6a126b..67c7276fa3 100644 --- a/secure_contracts_docs/src/README.md +++ b/secure_contracts_docs/src/README.md @@ -13,7 +13,6 @@ If you are looking to leverage Slither inbuilt features: - [Tools](./tools): Custom tools If you are looking to learn how to extend Slither's capabilities: -- [Developper-installation](./Developer-installation.md): How to install slither in a dev mode - [API](./api): Introduction to static analysis & Slither's API - [Tutorial](./tutorials/): Hands-on exercises diff --git a/secure_contracts_docs/src/Usage.md b/secure_contracts_docs/src/Usage.md index 87f3134176..42b11cbf94 100644 --- a/secure_contracts_docs/src/Usage.md +++ b/secure_contracts_docs/src/Usage.md @@ -1,8 +1,7 @@ ## Usage - [How to run Slither](#how-to-run-slither) - - [Truffle/Dapp/Etherlime](#truffledappetherlime) - - [Embark](#embark) + - [Foundry/Hardhat](#foundryhardhat) - [solc](#solc) - [Etherscan](#etherscan) - [AST input](#ast-file) diff --git a/secure_contracts_docs/src/api/Developer-installation.md b/secure_contracts_docs/src/api/Developer-installation.md deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/secure_contracts_docs/src/api/SlithIR-SSA.md b/secure_contracts_docs/src/api/SlithIR-SSA.md index 155361fad5..7956e16816 100644 --- a/secure_contracts_docs/src/api/SlithIR-SSA.md +++ b/secure_contracts_docs/src/api/SlithIR-SSA.md @@ -2,5 +2,5 @@ Slither possess a Static Single Assignment (SSA) form representation of SlithIR. SSA is a commonly used representation in compilation and static analysis in general. It requires that each variable is assigned at least one time. SSA is a key component for building an efficient data-dependency analysis. -The [SSA printer](https://github.com/trailofbits/slither/wiki/Printer-documentation#slithir-ssa) allows to visualize this representation. +The [SSA printer](../printers/Printer-documentation.md#slithir-ssa) allows to visualize this representation. diff --git a/secure_contracts_docs/src/api/api.md b/secure_contracts_docs/src/api/api.md index d347d7903c..53c7b81a4f 100644 --- a/secure_contracts_docs/src/api/api.md +++ b/secure_contracts_docs/src/api/api.md @@ -205,4 +205,4 @@ External call found HIGH_LEVEL_CALL, […] (...TetherToken.sol#339) ### Example: Print Basic Information -[print_basic_information.py](https://github.com/crytic/building-secure-contracts/tree/master/program-analysis/slither/examples/print_basic_information.py) demonstrates how to print basic information about a project. +[print_basic_information.py](./examples/print_basic_information.py) demonstrates how to print basic information about a project. diff --git a/secure_contracts_docs/src/tutorials/examples/coin.sol b/secure_contracts_docs/src/api/examples/coin.sol similarity index 100% rename from secure_contracts_docs/src/tutorials/examples/coin.sol rename to secure_contracts_docs/src/api/examples/coin.sol diff --git a/secure_contracts_docs/src/tutorials/examples/expected_results_print_basic_information.txt b/secure_contracts_docs/src/api/examples/expected_results_print_basic_information.txt similarity index 100% rename from secure_contracts_docs/src/tutorials/examples/expected_results_print_basic_information.txt rename to secure_contracts_docs/src/api/examples/expected_results_print_basic_information.txt diff --git a/secure_contracts_docs/src/tutorials/examples/print_basic_information.py b/secure_contracts_docs/src/api/examples/print_basic_information.py similarity index 100% rename from secure_contracts_docs/src/tutorials/examples/print_basic_information.py rename to secure_contracts_docs/src/api/examples/print_basic_information.py diff --git a/secure_contracts_docs/src/tutorials/images/ast.png b/secure_contracts_docs/src/api/images/ast.png similarity index 100% rename from secure_contracts_docs/src/tutorials/images/ast.png rename to secure_contracts_docs/src/api/images/ast.png diff --git a/secure_contracts_docs/src/tutorials/images/cfg.png b/secure_contracts_docs/src/api/images/cfg.png similarity index 100% rename from secure_contracts_docs/src/tutorials/images/cfg.png rename to secure_contracts_docs/src/api/images/cfg.png diff --git a/secure_contracts_docs/src/api/static_analysis.md b/secure_contracts_docs/src/api/static_analysis.md index 85cf4b2814..6c8f86b668 100644 --- a/secure_contracts_docs/src/api/static_analysis.md +++ b/secure_contracts_docs/src/api/static_analysis.md @@ -76,9 +76,9 @@ Slither can navigate through the different components of the code and their repr For example, the following detectors look for syntax-related issues: -- [State variable shadowing](https://github.com/crytic/slither/wiki/Detector-Documentation#state-variable-shadowing): iterates over all state variables and checks if any shadow a variable from an inherited contract ([state.py#L51-L62](https://github.com/crytic/slither/blob/0441338e055ab7151b30ca69258561a5a793f8ba/slither/detectors/shadowing/state.py#L51-L62)) +- [State variable shadowing](../detectors/Detector-Documentation.md#state-variable-shadowing): iterates over all state variables and checks if any shadow a variable from an inherited contract ([state.py#L51-L62](https://github.com/crytic/slither/blob/0441338e055ab7151b30ca69258561a5a793f8ba/slither/detectors/shadowing/state.py#L51-L62)) -- [Incorrect ERC20 interface](https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-erc20-interface): searches for incorrect ERC20 function signatures ([incorrect_erc20_interface.py#L34-L55](https://github.com/crytic/slither/blob/0441338e055ab7151b30ca69258561a5a793f8ba/slither/detectors/erc/incorrect_erc20_interface.py#L34-L55)) +- [Incorrect ERC20 interface](./detectors/Detector-Documentation.md#incorrect-erc20-interface): searches for incorrect ERC20 function signatures ([incorrect_erc20_interface.py#L34-L55](https://github.com/crytic/slither/blob/0441338e055ab7151b30ca69258561a5a793f8ba/slither/detectors/erc/incorrect_erc20_interface.py#L34-L55)) ### Semantic analysis @@ -97,9 +97,9 @@ In the following code, `variable_a` is dependent on `variable_b`: variable_a = variable_b + 1; ``` -Slither comes with built-in [data dependency](https://github.com/crytic/slither/wiki/data-dependency) capabilities, thanks to its intermediate representation (discussed later). +Slither comes with built-in [data dependency](./Data-dependency.md) capabilities, thanks to its intermediate representation (discussed later). -An example of data dependency usage can be found in the [dangerous strict equality detector](https://github.com/crytic/slither/wiki/Detector-Documentation#dangerous-strict-equalities). Slither looks for strict equality comparisons to dangerous values ([incorrect_strict_equality.py#L86-L87](https://github.com/crytic/slither/blob/6d86220a53603476f9567c3358524ea4db07fb25/slither/detectors/statements/incorrect_strict_equality.py#L86-L87)) and informs the user that they should use `>=` or `<=` instead of `==` to prevent attackers from trapping the contract. Among other things, the detector considers the return value of a call to `balanceOf(address)` to be dangerous ([incorrect_strict_equality.py#L63-L64](https://github.com/crytic/slither/blob/6d86220a53603476f9567c3358524ea4db07fb25/slither/detectors/statements/incorrect_strict_equality.py#L63-L64)) and uses the data dependency engine to track its usage. +An example of data dependency usage can be found in the [dangerous strict equality detector](./detectors/Detector-Documentation.md#dangerous-strict-equalities). Slither looks for strict equality comparisons to dangerous values ([incorrect_strict_equality.py#L86-L87](https://github.com/crytic/slither/blob/6d86220a53603476f9567c3358524ea4db07fb25/slither/detectors/statements/incorrect_strict_equality.py#L86-L87)) and informs the user that they should use `>=` or `<=` instead of `==` to prevent attackers from trapping the contract. Among other things, the detector considers the return value of a call to `balanceOf(address)` to be dangerous ([incorrect_strict_equality.py#L63-L64](https://github.com/crytic/slither/blob/6d86220a53603476f9567c3358524ea4db07fb25/slither/detectors/statements/incorrect_strict_equality.py#L63-L64)) and uses the data dependency engine to track its usage. #### Fixed-point computation @@ -119,6 +119,6 @@ Writing analyses using efficient fixed-point computation requires a good underst ### Intermediate representation -An intermediate representation (IR) is a language designed to be more amenable to static analysis than the original one. Slither translates Solidity to its own IR: [SlithIR](https://github.com/crytic/slither/wiki/SlithIR). +An intermediate representation (IR) is a language designed to be more amenable to static analysis than the original one. Slither translates Solidity to its own IR: [SlithIR](./SlithIR.md). -Understanding SlithIR is not necessary if you only want to write basic checks. However, it becomes essential if you plan to write advanced semantic analyses. The [SlithIR](https://github.com/crytic/slither/wiki/Printer-documentation#slithir) and [SSA](https://github.com/crytic/slither/wiki/Printer-documentation#slithir-ssa) printers can help you understand how the code is translated. +Understanding SlithIR is not necessary if you only want to write basic checks. However, it becomes essential if you plan to write advanced semantic analyses. The [SlithIR](../printers/Printer-documentation.md#slithir) and [SSA](../printers/Printer-documentation.md#slithir-ssa) printers can help you understand how the code is translated. diff --git a/secure_contracts_docs/src/printers/Printer-documentation.md b/secure_contracts_docs/src/printers/Printer-documentation.md index df1e1ea244..a5763793f2 100644 --- a/secure_contracts_docs/src/printers/Printer-documentation.md +++ b/secure_contracts_docs/src/printers/Printer-documentation.md @@ -2,26 +2,33 @@ Slither allows printing contracts information through its printers. Num | Printer | Description --- | --- | --- -1 | `call-graph` | [Export the call-graph of the contracts to a dot file](https://github.com/trailofbits/slither/wiki/Printer-documentation#call-graph) -2 | `cfg` | [Export the CFG of each functions](https://github.com/trailofbits/slither/wiki/Printer-documentation#cfg) -3 | `constructor-calls` | [Print the constructors executed](https://github.com/crytic/slither/wiki/Printer-documentation#constructor-calls) -4 | `contract-summary` | [Print a summary of the contracts](https://github.com/trailofbits/slither/wiki/Printer-documentation#contract-summary) -5 | `data-dependency` | [Print the data dependencies of the variables](https://github.com/trailofbits/slither/wiki/Printer-documentation#data-dependencies) -6 | `echidna` | [Export Echidna guiding information](https://github.com/trailofbits/slither/wiki/Printer-documentation#echidna) -7 | `evm` | [Print the evm instructions of nodes in functions](https://github.com/trailofbits/slither/wiki/Printer-documentation#evm) -8 | `function-id` | [Print the keccack256 signature of the functions](https://github.com/trailofbits/slither/wiki/Printer-documentation#function-id) -9 | `function-summary` | [Print a summary of the functions](https://github.com/trailofbits/slither/wiki/Printer-documentation#function-summary) -10 | `human-summary` | [Print a human-readable summary of the contracts](https://github.com/trailofbits/slither/wiki/Printer-documentation#human-summary) -11 | `inheritance` | [Print the inheritance relations between contracts](https://github.com/trailofbits/slither/wiki/Printer-documentation#inheritance) -12 | `inheritance-graph` | [Export the inheritance graph of each contract to a dot file](https://github.com/trailofbits/slither/wiki/Printer-documentation#inheritance-graph) -13 | `modifiers` | [Print the modifiers called by each function](https://github.com/trailofbits/slither/wiki/Printer-documentation#modifiers) -14 | `require` | [Print the require and assert calls of each function](https://github.com/trailofbits/slither/wiki/Printer-documentation#require) -15 | `slithir` | [Print the slithIR representation of the functions](https://github.com/trailofbits/slither/wiki/Printer-documentation#slithir) -16 | `slithir-ssa` | [Print the slithIR representation of the functions](https://github.com/trailofbits/slither/wiki/Printer-documentation#slithir-ssa) -17 | `variable-order` | [Print the storage order of the state variables](https://github.com/trailofbits/slither/wiki/Printer-documentation#variable-order) -18 | `vars-and-auth` | [Print the state variables written and the authorization of the functions](https://github.com/trailofbits/slither/wiki/Printer-documentation#variables-written-and-authorization) - - +1 | `call-graph` | [Export the call-graph of the contracts to a dot file](#call-graph) +2 | `cfg` | [Export the CFG of each functions](#cfg) +3 | `cheatcode` | Print the usage of (Foundry) cheatcodes in the code. +4 | `ck` | Chidamber and Kemerer (CK) complexity metrics and related function attributes +5 | `constructor-calls` | [Print the constructors executed](#constructor-calls) +6 | `contract-summary` | [Print a summary of the contracts](#contract-summary) +7 | `data-dependency` | [Print the data dependencies of the variables](#data-dependencies) +8 | `declaration` | Prototype showing the source code declaration, implementation and references of the contracts objects +9 | `dominator` | Export the dominator tree of each functions +10 | `echidna` | Export Echidna guiding information +11 | `entry-points` | Print all the state-changing entry point functions of the contracts +12 | `evm` | [Print the evm instructions of nodes in functions](#evm) +13 | `function-id` | [Print the keccak256 signature of the functions](#function-id) +14 | `function-summary` | [Print a summary of the functions](#function-summary) +15 | `halstead` | Computes the Halstead complexity metrics for each contract +16 | `human-summary` | [Print a human-readable summary of the contracts](#human-summary) +17 | `inheritance` | [Print the inheritance relations between contracts](#inheritance) +18 | `inheritance-graph` | [Export the inheritance graph of each contract to a dot file](#inheritance-graph) +19 | `loc` | Count the total number lines of code (LOC), source lines of code (SLOC) +20 | `martin` | Martin agile software metrics (Ca, Ce, I, A, D) +21 | `modifiers` | Print the modifiers called by each function +22 | `not-pausable` | Print functions that do not use whenNotPaused +23 | `require` | [Print the require and assert calls of each function](#require) +24 | `slithir` | [Print the slithIR representation of the functions](#slithir) +25 | `slithir-ssa` | [Print the slithIR representation of the functions](#slithir-ssa) +26 | `variable-order` | [Print the storage order of the state variables](#variable-order) +27 | `vars-and-auth` | [Print the state variables written and the authorization of the functions](#variables-written-and-authorization) diff --git a/secure_contracts_docs/src/tools/Adding-a-new-utility.md b/secure_contracts_docs/src/tools/Adding-a-new-utility.md index 35474d5d96..8008964707 100644 --- a/secure_contracts_docs/src/tools/Adding-a-new-utility.md +++ b/secure_contracts_docs/src/tools/Adding-a-new-utility.md @@ -1,8 +1,6 @@ Slither can be used as a library to create new utilities. Official utils are present in [tools](https://github.com/crytic/slither/tree/master/slither/tools) -We recommend following the [developper installation](https://github.com/crytic/slither/wiki/Developer-installation). - ## Skeleton The skeleton util is present in [tools/demo](https://github.com/crytic/slither/tree/master/slither/tools/demo) diff --git a/secure_contracts_docs/src/tools/Upgradeability-Checks.md b/secure_contracts_docs/src/tools/Upgradeability-Checks.md index 49f9eccb8f..9e87d1594a 100644 --- a/secure_contracts_docs/src/tools/Upgradeability-Checks.md +++ b/secure_contracts_docs/src/tools/Upgradeability-Checks.md @@ -7,23 +7,23 @@ Num | Check | What it Detects | Impact | Proxy | Contract V2 --- | --- | --- | --- | --- | --- -1 | `became-constant` | [Variables that should not be constant](https://github.com/crytic/slither/wiki/Upgradeability-Checks#variables-that-should-not-be-constant) | High | | X -2 | `function-id-collision` | [Functions ids collision](https://github.com/crytic/slither/wiki/Upgradeability-Checks#functions-ids-collisions) | High | X | -3 | `function-shadowing` | [Functions shadowing](https://github.com/crytic/slither/wiki/Upgradeability-Checks#functions-shadowing) | High | X | -4 | `missing-calls` | [Missing calls to init functions](https://github.com/crytic/slither/wiki/Upgradeability-Checks#initialize-functions-are-not-called) | High | | -5 | `missing-init-modifier` | [initializer() is not called](https://github.com/crytic/slither/wiki/Upgradeability-Checks#initializer-is-not-called) | High | | -6 | `multiple-calls` | [Init functions called multiple times](https://github.com/crytic/slither/wiki/Upgradeability-Checks#initialize-functions-are-called-multiple-times) | High | | -7 | `order-vars-contracts` | [Incorrect vars order with the v2](https://github.com/crytic/slither/wiki/Upgradeability-Checks#incorrect-variables-with-the-v2) | High | | X -8 | `order-vars-proxy` | [Incorrect vars order with the proxy](https://github.com/crytic/slither/wiki/Upgradeability-Checks#incorrect-variables-with-the-proxy) | High | X | -9 | `variables-initialized` | [State variables with an initial value](https://github.com/crytic/slither/wiki/Upgradeability-Checks#state-variable-initialized) | High | | -10 | `were-constant` | [Variables that should be constant](https://github.com/crytic/slither/wiki/Upgradeability-Checks#variables-that-should-be-constant) | High | | X -11 | `extra-vars-proxy` | [Extra vars in the proxy](https://github.com/crytic/slither/wiki/Upgradeability-Checks#extra-variables-in-the-proxy) | Medium | X | -12 | `missing-variables` | [Variable missing in the v2](https://github.com/crytic/slither/wiki/Upgradeability-Checks#missing-variables) | Medium | | X -13 | `extra-vars-v2` | [Extra vars in the v2](https://github.com/crytic/slither/wiki/Upgradeability-Checks#extra-variables-in-the-v2) | Informational | | X -14 | `init-inherited` | [Initializable is not inherited](https://github.com/crytic/slither/wiki/Upgradeability-Checks#initializable-is-not-inherited) | Informational | | -15 | `init-missing` | [Initializable is missing](https://github.com/crytic/slither/wiki/Upgradeability-Checks#initializable-is-missing) | Informational | | -16 | `initialize-target` | [Initialize function that must be called](https://github.com/crytic/slither/wiki/Upgradeability-Checks#initialize-function) | Informational | | -17 | `initializer-missing` | [initializer() is missing](https://github.com/crytic/slither/wiki/Upgradeability-Checks#initializer-is-missing) | Informational | | +1 | `became-constant` | [Variables that should not be constant](#variables-that-should-not-be-constant) | High | | X +2 | `function-id-collision` | [Functions ids collision](#functions-ids-collisions) | High | X | +3 | `function-shadowing` | [Functions shadowing](#functions-shadowing) | High | X | +4 | `missing-calls` | [Missing calls to init functions](#initialize-functions-are-not-called) | High | | +5 | `missing-init-modifier` | [initializer() is not called](#initializer-is-not-called) | High | | +6 | `multiple-calls` | [Init functions called multiple times](#initialize-functions-are-called-multiple-times) | High | | +7 | `order-vars-contracts` | [Incorrect vars order with the v2](#incorrect-variables-with-the-v2) | High | | X +8 | `order-vars-proxy` | [Incorrect vars order with the proxy](#incorrect-variables-with-the-proxy) | High | X | +9 | `variables-initialized` | [State variables with an initial value](#state-variable-initialized) | High | | +10 | `were-constant` | [Variables that should be constant](#variables-that-should-be-constant) | High | | X +11 | `extra-vars-proxy` | [Extra vars in the proxy](#extra-variables-in-the-proxy) | Medium | X | +12 | `missing-variables` | [Variable missing in the v2](#missing-variables) | Medium | | X +13 | `extra-vars-v2` | [Extra vars in the v2](#extra-variables-in-the-v2) | Informational | | X +14 | `init-inherited` | [Initializable is not inherited](#initializable-is-not-inherited) | Informational | | +15 | `init-missing` | [Initializable is missing](#initializable-is-missing) | Informational | | +16 | `initialize-target` | [Initialize function that must be called](#initialize-function) | Informational | | +17 | `initializer-missing` | [initializer() is missing](#initializer-is-missing) | Informational | | ## Usage From add8f56a05d5f8bd2666ff03e76725418324dd7b Mon Sep 17 00:00:00 2001 From: Feist Josselin Date: Fri, 7 Mar 2025 11:50:06 +0100 Subject: [PATCH 10/30] Improvements --- README.md | 17 ++++++++--------- {secure_contracts_docs => docs}/src/README.md | 0 {secure_contracts_docs => docs}/src/SUMMARY.md | 9 +++++++-- {secure_contracts_docs => docs}/src/Usage.md | 0 .../src/api/Data-dependency.md | 0 .../src/api/JSON-output.md | 0 .../src/api/README.md | 0 .../src/api/SlithIR-SSA.md | 0 .../src/api/SlithIR.md | 0 {secure_contracts_docs => docs}/src/api/api.md | 0 .../src/api/examples/coin.sol | 0 ...xpected_results_print_basic_information.txt | 0 .../api/examples/print_basic_information.py | 0 .../src/api/images/ast.png | Bin .../src/api/images/cfg.png | Bin .../src/api/static_analysis.md | 0 .../src/detectors/Adding-a-new-detector.md | 0 .../src/detectors/Detector-Documentation.md | 0 .../src/printers/Printer-documentation.md | 0 .../src/tools/Adding-a-new-utility.md | 0 .../src/tools/Code-Similarity-Detector.md | 0 .../src/tools/Contract-Flattening.md | 0 .../README.md => docs/src/tools/Doctor.md | 0 .../src/tools/Documentation.md | 0 .../src/tools/ERC-Conformance.md | 0 .../README.md => docs/src/tools/Interface.md | 0 .../README.md => docs/src/tools/Mutator.md | 0 .../src/tools/Path-Finding-Utility.md | 0 .../src/tools/Property-generation.md | 0 .../README.md => docs/src/tools/ReadStorage.md | 0 .../src/tools/Slither-format.md | 0 .../src/tools/Upgradeability-Checks.md | 0 .../src/tutorials/README.md | 0 .../src/tutorials/exercise1.md | 0 .../src/tutorials/exercise2.md | 0 .../src/tutorials/exercise3.md | 0 .../src/tutorials/exercises/exercise1/coin.sol | 0 .../exercises/exercise1/expected_results.txt | 0 .../tutorials/exercises/exercise1/solution.py | 0 .../src/tutorials/exercises/exercise2/coin.sol | 0 .../exercises/exercise2/expected_results.txt | 0 .../tutorials/exercises/exercise2/solution.py | 0 .../exercises/exercise3/expected_results.txt | 0 .../src/tutorials/exercises/exercise3/find.sol | 0 .../tutorials/exercises/exercise3/solution.py | 0 .../src/tutorials/scripts/gh_action_test.sh | 0 46 files changed, 15 insertions(+), 11 deletions(-) rename {secure_contracts_docs => docs}/src/README.md (100%) rename {secure_contracts_docs => docs}/src/SUMMARY.md (82%) rename {secure_contracts_docs => docs}/src/Usage.md (100%) rename {secure_contracts_docs => docs}/src/api/Data-dependency.md (100%) rename {secure_contracts_docs => docs}/src/api/JSON-output.md (100%) rename {secure_contracts_docs => docs}/src/api/README.md (100%) rename {secure_contracts_docs => docs}/src/api/SlithIR-SSA.md (100%) rename {secure_contracts_docs => docs}/src/api/SlithIR.md (100%) rename {secure_contracts_docs => docs}/src/api/api.md (100%) rename {secure_contracts_docs => docs}/src/api/examples/coin.sol (100%) rename {secure_contracts_docs => docs}/src/api/examples/expected_results_print_basic_information.txt (100%) rename {secure_contracts_docs => docs}/src/api/examples/print_basic_information.py (100%) rename {secure_contracts_docs => docs}/src/api/images/ast.png (100%) rename {secure_contracts_docs => docs}/src/api/images/cfg.png (100%) rename {secure_contracts_docs => docs}/src/api/static_analysis.md (100%) rename {secure_contracts_docs => docs}/src/detectors/Adding-a-new-detector.md (100%) rename {secure_contracts_docs => docs}/src/detectors/Detector-Documentation.md (100%) rename {secure_contracts_docs => docs}/src/printers/Printer-documentation.md (100%) rename {secure_contracts_docs => docs}/src/tools/Adding-a-new-utility.md (100%) rename {secure_contracts_docs => docs}/src/tools/Code-Similarity-Detector.md (100%) rename {secure_contracts_docs => docs}/src/tools/Contract-Flattening.md (100%) rename slither/tools/doctor/README.md => docs/src/tools/Doctor.md (100%) rename slither/tools/documentation/README.md => docs/src/tools/Documentation.md (100%) rename {secure_contracts_docs => docs}/src/tools/ERC-Conformance.md (100%) rename slither/tools/interface/README.md => docs/src/tools/Interface.md (100%) rename slither/tools/mutator/README.md => docs/src/tools/Mutator.md (100%) rename {secure_contracts_docs => docs}/src/tools/Path-Finding-Utility.md (100%) rename {secure_contracts_docs => docs}/src/tools/Property-generation.md (100%) rename slither/tools/read_storage/README.md => docs/src/tools/ReadStorage.md (100%) rename {secure_contracts_docs => docs}/src/tools/Slither-format.md (100%) rename {secure_contracts_docs => docs}/src/tools/Upgradeability-Checks.md (100%) rename {secure_contracts_docs => docs}/src/tutorials/README.md (100%) rename {secure_contracts_docs => docs}/src/tutorials/exercise1.md (100%) rename {secure_contracts_docs => docs}/src/tutorials/exercise2.md (100%) rename {secure_contracts_docs => docs}/src/tutorials/exercise3.md (100%) rename {secure_contracts_docs => docs}/src/tutorials/exercises/exercise1/coin.sol (100%) rename {secure_contracts_docs => docs}/src/tutorials/exercises/exercise1/expected_results.txt (100%) rename {secure_contracts_docs => docs}/src/tutorials/exercises/exercise1/solution.py (100%) rename {secure_contracts_docs => docs}/src/tutorials/exercises/exercise2/coin.sol (100%) rename {secure_contracts_docs => docs}/src/tutorials/exercises/exercise2/expected_results.txt (100%) rename {secure_contracts_docs => docs}/src/tutorials/exercises/exercise2/solution.py (100%) rename {secure_contracts_docs => docs}/src/tutorials/exercises/exercise3/expected_results.txt (100%) rename {secure_contracts_docs => docs}/src/tutorials/exercises/exercise3/find.sol (100%) rename {secure_contracts_docs => docs}/src/tutorials/exercises/exercise3/solution.py (100%) rename {secure_contracts_docs => docs}/src/tutorials/scripts/gh_action_test.sh (100%) diff --git a/README.md b/README.md index c75c76a2f9..b76cffdd2a 100644 --- a/README.md +++ b/README.md @@ -258,15 +258,14 @@ See the [Printer documentation](https://github.com/crytic/slither/wiki/Printer-d ## Tools -* `slither-check-upgradeability`: [Review `delegatecall`-based upgradeability](https://github.com/crytic/slither/wiki/Upgradeability-Checks) -* `slither-prop`: [Automatic unit test and property generation](https://github.com/crytic/slither/wiki/Property-generation) -* `slither-flat`: [Flatten a codebase](https://github.com/crytic/slither/wiki/Contract-Flattening) -* `slither-check-erc`: [Check the ERC's conformance](https://github.com/crytic/slither/wiki/ERC-Conformance) -* `slither-format`: [Automatic patch generation](https://github.com/crytic/slither/wiki/Slither-format) -* `slither-read-storage`: [Read storage values from contracts](./slither/tools/read_storage/README.md) -* `slither-interface`: [Generate an interface for a contract](./slither/tools/interface/README.md) - -See the [Tool documentation](https://github.com/crytic/slither/wiki/Tool-Documentation) for additional tools. +* `slither-check-upgradeability`: [Review `delegatecall`-based upgradeability](./docs/src/tools/Upgradeability-Checks.md) +* `slither-prop`: [Automatic unit test and property generation](./docs/src/tools/Property-generation.md) +* `slither-flat`: [Flatten a codebase](./docs/src/tools/Contract-Flattening.md) +* `slither-check-erc`: [Check the ERC's conformance](./docs/src/tools/ERC-Conformance.md) +* `slither-read-storage`: [Read storage values from contracts](./docs/src/tools/ReadStorage.md) +* `slither-interface`: [Generate an interface for a contract](./docs/src/tools/Interface.md) + +See the [Tool documentation](./docs/src/tools/README.md) for additional tools. [Contact us](https://www.trailofbits.com/contact/) to get help on building custom tools. diff --git a/secure_contracts_docs/src/README.md b/docs/src/README.md similarity index 100% rename from secure_contracts_docs/src/README.md rename to docs/src/README.md diff --git a/secure_contracts_docs/src/SUMMARY.md b/docs/src/SUMMARY.md similarity index 82% rename from secure_contracts_docs/src/SUMMARY.md rename to docs/src/SUMMARY.md index e86e2dd5c0..7987857617 100644 --- a/secure_contracts_docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -13,12 +13,17 @@ - [Printers](./printers/Printer-documentation.md) - [Tools](./tools/Adding-a-new-utility.md) - [Addning a new tool](./tools/Adding-a-new-utility.md) - - [Path Finding Utility](./tools/Path-Finding-Utility.md) - [Code Similarity](./tools/Code-Similarity-detector.md) - [Contract Flattening](./tools/Contract-Flattening.md) - - [Format](./tools/Slither-format.md) + - [Documentation](./tools/Documentation.md) + - [Doctor](./tools/Doctor.md) - [ERC Conformance](./tools/ERC-Conformance.md) + - [Interface](./tools/Interface.md) + - [Mutator](./tools/Mutator.md) + - [Path Finding Utility](./tools/Path-Finding-Utility.md) - [Property Generation](./tools/Property-generation.md) + - [Read Storage](./tools/ReadStorage.md) + - [Format](./tools/Slither-format.md) - [Upgradeability checks](./tools/Upgradeability-Checks.md) - [Tutorials](./tutorials/README.md) - [Exercise 1](./tutorials/exercise1.md) diff --git a/secure_contracts_docs/src/Usage.md b/docs/src/Usage.md similarity index 100% rename from secure_contracts_docs/src/Usage.md rename to docs/src/Usage.md diff --git a/secure_contracts_docs/src/api/Data-dependency.md b/docs/src/api/Data-dependency.md similarity index 100% rename from secure_contracts_docs/src/api/Data-dependency.md rename to docs/src/api/Data-dependency.md diff --git a/secure_contracts_docs/src/api/JSON-output.md b/docs/src/api/JSON-output.md similarity index 100% rename from secure_contracts_docs/src/api/JSON-output.md rename to docs/src/api/JSON-output.md diff --git a/secure_contracts_docs/src/api/README.md b/docs/src/api/README.md similarity index 100% rename from secure_contracts_docs/src/api/README.md rename to docs/src/api/README.md diff --git a/secure_contracts_docs/src/api/SlithIR-SSA.md b/docs/src/api/SlithIR-SSA.md similarity index 100% rename from secure_contracts_docs/src/api/SlithIR-SSA.md rename to docs/src/api/SlithIR-SSA.md diff --git a/secure_contracts_docs/src/api/SlithIR.md b/docs/src/api/SlithIR.md similarity index 100% rename from secure_contracts_docs/src/api/SlithIR.md rename to docs/src/api/SlithIR.md diff --git a/secure_contracts_docs/src/api/api.md b/docs/src/api/api.md similarity index 100% rename from secure_contracts_docs/src/api/api.md rename to docs/src/api/api.md diff --git a/secure_contracts_docs/src/api/examples/coin.sol b/docs/src/api/examples/coin.sol similarity index 100% rename from secure_contracts_docs/src/api/examples/coin.sol rename to docs/src/api/examples/coin.sol diff --git a/secure_contracts_docs/src/api/examples/expected_results_print_basic_information.txt b/docs/src/api/examples/expected_results_print_basic_information.txt similarity index 100% rename from secure_contracts_docs/src/api/examples/expected_results_print_basic_information.txt rename to docs/src/api/examples/expected_results_print_basic_information.txt diff --git a/secure_contracts_docs/src/api/examples/print_basic_information.py b/docs/src/api/examples/print_basic_information.py similarity index 100% rename from secure_contracts_docs/src/api/examples/print_basic_information.py rename to docs/src/api/examples/print_basic_information.py diff --git a/secure_contracts_docs/src/api/images/ast.png b/docs/src/api/images/ast.png similarity index 100% rename from secure_contracts_docs/src/api/images/ast.png rename to docs/src/api/images/ast.png diff --git a/secure_contracts_docs/src/api/images/cfg.png b/docs/src/api/images/cfg.png similarity index 100% rename from secure_contracts_docs/src/api/images/cfg.png rename to docs/src/api/images/cfg.png diff --git a/secure_contracts_docs/src/api/static_analysis.md b/docs/src/api/static_analysis.md similarity index 100% rename from secure_contracts_docs/src/api/static_analysis.md rename to docs/src/api/static_analysis.md diff --git a/secure_contracts_docs/src/detectors/Adding-a-new-detector.md b/docs/src/detectors/Adding-a-new-detector.md similarity index 100% rename from secure_contracts_docs/src/detectors/Adding-a-new-detector.md rename to docs/src/detectors/Adding-a-new-detector.md diff --git a/secure_contracts_docs/src/detectors/Detector-Documentation.md b/docs/src/detectors/Detector-Documentation.md similarity index 100% rename from secure_contracts_docs/src/detectors/Detector-Documentation.md rename to docs/src/detectors/Detector-Documentation.md diff --git a/secure_contracts_docs/src/printers/Printer-documentation.md b/docs/src/printers/Printer-documentation.md similarity index 100% rename from secure_contracts_docs/src/printers/Printer-documentation.md rename to docs/src/printers/Printer-documentation.md diff --git a/secure_contracts_docs/src/tools/Adding-a-new-utility.md b/docs/src/tools/Adding-a-new-utility.md similarity index 100% rename from secure_contracts_docs/src/tools/Adding-a-new-utility.md rename to docs/src/tools/Adding-a-new-utility.md diff --git a/secure_contracts_docs/src/tools/Code-Similarity-Detector.md b/docs/src/tools/Code-Similarity-Detector.md similarity index 100% rename from secure_contracts_docs/src/tools/Code-Similarity-Detector.md rename to docs/src/tools/Code-Similarity-Detector.md diff --git a/secure_contracts_docs/src/tools/Contract-Flattening.md b/docs/src/tools/Contract-Flattening.md similarity index 100% rename from secure_contracts_docs/src/tools/Contract-Flattening.md rename to docs/src/tools/Contract-Flattening.md diff --git a/slither/tools/doctor/README.md b/docs/src/tools/Doctor.md similarity index 100% rename from slither/tools/doctor/README.md rename to docs/src/tools/Doctor.md diff --git a/slither/tools/documentation/README.md b/docs/src/tools/Documentation.md similarity index 100% rename from slither/tools/documentation/README.md rename to docs/src/tools/Documentation.md diff --git a/secure_contracts_docs/src/tools/ERC-Conformance.md b/docs/src/tools/ERC-Conformance.md similarity index 100% rename from secure_contracts_docs/src/tools/ERC-Conformance.md rename to docs/src/tools/ERC-Conformance.md diff --git a/slither/tools/interface/README.md b/docs/src/tools/Interface.md similarity index 100% rename from slither/tools/interface/README.md rename to docs/src/tools/Interface.md diff --git a/slither/tools/mutator/README.md b/docs/src/tools/Mutator.md similarity index 100% rename from slither/tools/mutator/README.md rename to docs/src/tools/Mutator.md diff --git a/secure_contracts_docs/src/tools/Path-Finding-Utility.md b/docs/src/tools/Path-Finding-Utility.md similarity index 100% rename from secure_contracts_docs/src/tools/Path-Finding-Utility.md rename to docs/src/tools/Path-Finding-Utility.md diff --git a/secure_contracts_docs/src/tools/Property-generation.md b/docs/src/tools/Property-generation.md similarity index 100% rename from secure_contracts_docs/src/tools/Property-generation.md rename to docs/src/tools/Property-generation.md diff --git a/slither/tools/read_storage/README.md b/docs/src/tools/ReadStorage.md similarity index 100% rename from slither/tools/read_storage/README.md rename to docs/src/tools/ReadStorage.md diff --git a/secure_contracts_docs/src/tools/Slither-format.md b/docs/src/tools/Slither-format.md similarity index 100% rename from secure_contracts_docs/src/tools/Slither-format.md rename to docs/src/tools/Slither-format.md diff --git a/secure_contracts_docs/src/tools/Upgradeability-Checks.md b/docs/src/tools/Upgradeability-Checks.md similarity index 100% rename from secure_contracts_docs/src/tools/Upgradeability-Checks.md rename to docs/src/tools/Upgradeability-Checks.md diff --git a/secure_contracts_docs/src/tutorials/README.md b/docs/src/tutorials/README.md similarity index 100% rename from secure_contracts_docs/src/tutorials/README.md rename to docs/src/tutorials/README.md diff --git a/secure_contracts_docs/src/tutorials/exercise1.md b/docs/src/tutorials/exercise1.md similarity index 100% rename from secure_contracts_docs/src/tutorials/exercise1.md rename to docs/src/tutorials/exercise1.md diff --git a/secure_contracts_docs/src/tutorials/exercise2.md b/docs/src/tutorials/exercise2.md similarity index 100% rename from secure_contracts_docs/src/tutorials/exercise2.md rename to docs/src/tutorials/exercise2.md diff --git a/secure_contracts_docs/src/tutorials/exercise3.md b/docs/src/tutorials/exercise3.md similarity index 100% rename from secure_contracts_docs/src/tutorials/exercise3.md rename to docs/src/tutorials/exercise3.md diff --git a/secure_contracts_docs/src/tutorials/exercises/exercise1/coin.sol b/docs/src/tutorials/exercises/exercise1/coin.sol similarity index 100% rename from secure_contracts_docs/src/tutorials/exercises/exercise1/coin.sol rename to docs/src/tutorials/exercises/exercise1/coin.sol diff --git a/secure_contracts_docs/src/tutorials/exercises/exercise1/expected_results.txt b/docs/src/tutorials/exercises/exercise1/expected_results.txt similarity index 100% rename from secure_contracts_docs/src/tutorials/exercises/exercise1/expected_results.txt rename to docs/src/tutorials/exercises/exercise1/expected_results.txt diff --git a/secure_contracts_docs/src/tutorials/exercises/exercise1/solution.py b/docs/src/tutorials/exercises/exercise1/solution.py similarity index 100% rename from secure_contracts_docs/src/tutorials/exercises/exercise1/solution.py rename to docs/src/tutorials/exercises/exercise1/solution.py diff --git a/secure_contracts_docs/src/tutorials/exercises/exercise2/coin.sol b/docs/src/tutorials/exercises/exercise2/coin.sol similarity index 100% rename from secure_contracts_docs/src/tutorials/exercises/exercise2/coin.sol rename to docs/src/tutorials/exercises/exercise2/coin.sol diff --git a/secure_contracts_docs/src/tutorials/exercises/exercise2/expected_results.txt b/docs/src/tutorials/exercises/exercise2/expected_results.txt similarity index 100% rename from secure_contracts_docs/src/tutorials/exercises/exercise2/expected_results.txt rename to docs/src/tutorials/exercises/exercise2/expected_results.txt diff --git a/secure_contracts_docs/src/tutorials/exercises/exercise2/solution.py b/docs/src/tutorials/exercises/exercise2/solution.py similarity index 100% rename from secure_contracts_docs/src/tutorials/exercises/exercise2/solution.py rename to docs/src/tutorials/exercises/exercise2/solution.py diff --git a/secure_contracts_docs/src/tutorials/exercises/exercise3/expected_results.txt b/docs/src/tutorials/exercises/exercise3/expected_results.txt similarity index 100% rename from secure_contracts_docs/src/tutorials/exercises/exercise3/expected_results.txt rename to docs/src/tutorials/exercises/exercise3/expected_results.txt diff --git a/secure_contracts_docs/src/tutorials/exercises/exercise3/find.sol b/docs/src/tutorials/exercises/exercise3/find.sol similarity index 100% rename from secure_contracts_docs/src/tutorials/exercises/exercise3/find.sol rename to docs/src/tutorials/exercises/exercise3/find.sol diff --git a/secure_contracts_docs/src/tutorials/exercises/exercise3/solution.py b/docs/src/tutorials/exercises/exercise3/solution.py similarity index 100% rename from secure_contracts_docs/src/tutorials/exercises/exercise3/solution.py rename to docs/src/tutorials/exercises/exercise3/solution.py diff --git a/secure_contracts_docs/src/tutorials/scripts/gh_action_test.sh b/docs/src/tutorials/scripts/gh_action_test.sh similarity index 100% rename from secure_contracts_docs/src/tutorials/scripts/gh_action_test.sh rename to docs/src/tutorials/scripts/gh_action_test.sh From 12c49ffa4248310e3f2d32c16a8ac40481fb3709 Mon Sep 17 00:00:00 2001 From: Feist Josselin Date: Fri, 7 Mar 2025 12:20:42 +0100 Subject: [PATCH 11/30] minor --- docs/src/SUMMARY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 7987857617..b1180f1013 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -11,7 +11,7 @@ - [Detectors](./detectors/Detector-Documentation.md) - [Adding a detector](./detectors/Adding-a-new-detector.md) - [Printers](./printers/Printer-documentation.md) - - [Tools](./tools/Adding-a-new-utility.md) + - [Tools](./tools/README.md) - [Addning a new tool](./tools/Adding-a-new-utility.md) - [Code Similarity](./tools/Code-Similarity-detector.md) - [Contract Flattening](./tools/Contract-Flattening.md) From 22896d1db01335dc9e6803dba724772fbcb755f3 Mon Sep 17 00:00:00 2001 From: Feist Josselin Date: Fri, 7 Mar 2025 12:22:58 +0100 Subject: [PATCH 12/30] Update gitignore --- docs/src/tools/README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 docs/src/tools/README.md diff --git a/docs/src/tools/README.md b/docs/src/tools/README.md new file mode 100644 index 0000000000..9e5d4528d0 --- /dev/null +++ b/docs/src/tools/README.md @@ -0,0 +1,17 @@ +Slither comes with inbuilt tools + +| Name | Command Line | What it Does | +|------|-------------|--------------| +| [Code Similarity](./Code-Similarity-detector.md) | `slither-simil` | Detects similar Solidity functions/contracts using code similarity analysis. Useful for finding duplicated code, similar vulnerabilities, or analyzing large codebases. | +| [Contract Flattening](./Contract-Flattening.md) | `slither-flat` | Flattens a Solidity codebase by inlining all imports into a single file. Useful for contract verification on Etherscan or debugging. | +| [Documentation](./Documentation.md) | `slither-doc` | Automatically generates documentation for Solidity contracts, including inheritance information, functions, modifiers, and more. | +| [Doctor](./Doctor.md) | `slither-doctor` | Helps diagnose and fix common issues in your environment that might prevent Slither from working correctly. | +| [ERC Conformance](./ERC-Conformance.md) | `slither-check-erc` | Validates whether a contract correctly implements various ERC standards (ERC20, ERC721, etc.) by checking required functions and their signatures. | +| [Interface](./Interface.md) | `slither-interface` | Generates Solidity interfaces from contract implementations, useful for creating minimal interfaces for contract interactions. | +| [Mutator](./Mutator.md) | `slither-mutate` | Performs mutation testing on Solidity contracts by automatically generating variants with small modifications to test suite effectiveness. | +| [Path Finding](./Path-Finding-Utility.md) | `slither-prop` | Analyzes call paths between functions in smart contracts to understand control and data flow. | +| [Property Generation](./Property-generation.md) | `slither-prop` | Automatically generates security properties and unit tests for smart contracts based on their behavior. | +| [Read Storage](./ReadStorage.md) | `slither-read-storage` | Reads contract storage values directly from the blockchain, helping debug deployed contracts. | +| [Format](./Slither-format.md) | `slither-format` | Automatically patch bugs. | +| [Upgradeability Checks](./Upgradeability-Checks.md) | `slither-check-upgradeability` | Analyzes upgradeable contracts for common issues and vulnerabilities in proxy patterns. | + From 8155ba8c5452405b95be0ea03dac2e35e019a34c Mon Sep 17 00:00:00 2001 From: Feist Josselin Date: Fri, 7 Mar 2025 12:30:25 +0100 Subject: [PATCH 13/30] Minor --- .gitignore | 3 --- 1 file changed, 3 deletions(-) diff --git a/.gitignore b/.gitignore index a895c22ee1..083ddebe27 100644 --- a/.gitignore +++ b/.gitignore @@ -113,8 +113,5 @@ test_artifacts/ # crytic export crytic-export/ -# Auto-generated Github pages docs -docs/ - # slither.db.json slither.db.json From 812e704c11be8acc521ba5a79bc1562edfd36758 Mon Sep 17 00:00:00 2001 From: Josselin Feist Date: Fri, 7 Mar 2025 13:05:53 +0100 Subject: [PATCH 14/30] Update docs/src/api/examples/print_basic_information.py Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- docs/src/api/examples/print_basic_information.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/api/examples/print_basic_information.py b/docs/src/api/examples/print_basic_information.py index aee217e079..bfb9e6c2e1 100644 --- a/docs/src/api/examples/print_basic_information.py +++ b/docs/src/api/examples/print_basic_information.py @@ -2,7 +2,7 @@ from slither import Slither # Init slither -slither = Slither('coin.sol') +slither = Slither("coin.sol") for contract in slither.contracts: # Print the contract's name From 133db130c10f5f5bc50d7743117a7584227b6af9 Mon Sep 17 00:00:00 2001 From: Josselin Feist Date: Fri, 7 Mar 2025 13:05:59 +0100 Subject: [PATCH 15/30] Update docs/src/api/examples/print_basic_information.py Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- docs/src/api/examples/print_basic_information.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/api/examples/print_basic_information.py b/docs/src/api/examples/print_basic_information.py index bfb9e6c2e1..58d2520d4c 100644 --- a/docs/src/api/examples/print_basic_information.py +++ b/docs/src/api/examples/print_basic_information.py @@ -6,7 +6,7 @@ for contract in slither.contracts: # Print the contract's name - print(f'Contract: {contract.name}') + print(f"Contract: {contract.name}") # Print the name of the contract inherited print(f'\tInherit from{[c.name for c in contract.inheritance]}') for function in contract.functions: From 2801f44db7fc6ae5e14c8b34807268c9c75fe1c2 Mon Sep 17 00:00:00 2001 From: Josselin Feist Date: Fri, 7 Mar 2025 13:06:05 +0100 Subject: [PATCH 16/30] Update docs/src/api/examples/print_basic_information.py Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- docs/src/api/examples/print_basic_information.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/api/examples/print_basic_information.py b/docs/src/api/examples/print_basic_information.py index 58d2520d4c..5c2778236d 100644 --- a/docs/src/api/examples/print_basic_information.py +++ b/docs/src/api/examples/print_basic_information.py @@ -8,7 +8,7 @@ # Print the contract's name print(f"Contract: {contract.name}") # Print the name of the contract inherited - print(f'\tInherit from{[c.name for c in contract.inheritance]}') + print(f"\tInherit from{[c.name for c in contract.inheritance]}") for function in contract.functions: # For each function, print basic information print(f'\t{function.full_name}:') From 46ec01d78f34c94c74e283d48a2a0973e5ac55b3 Mon Sep 17 00:00:00 2001 From: Josselin Feist Date: Fri, 7 Mar 2025 13:06:11 +0100 Subject: [PATCH 17/30] Update docs/src/api/examples/print_basic_information.py Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- docs/src/api/examples/print_basic_information.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/src/api/examples/print_basic_information.py b/docs/src/api/examples/print_basic_information.py index 5c2778236d..6a960d0aba 100644 --- a/docs/src/api/examples/print_basic_information.py +++ b/docs/src/api/examples/print_basic_information.py @@ -11,8 +11,8 @@ print(f"\tInherit from{[c.name for c in contract.inheritance]}") for function in contract.functions: # For each function, print basic information - print(f'\t{function.full_name}:') - print(f'\t\tVisibility: {function.visibility}') - print(f'\t\tContract: {function.contract}') - print(f'\t\tModifier: {[m.name for m in function.modifiers]}') - print(f'\t\tIs constructor? {function.is_constructor}') + print(f"\t{function.full_name}:") + print(f"\t\tVisibility: {function.visibility}") + print(f"\t\tContract: {function.contract}") + print(f"\t\tModifier: {[m.name for m in function.modifiers]}") + print(f"\t\tIs constructor? {function.is_constructor}") From 107d3c10fba655feea60bd605abcf2f85222f642 Mon Sep 17 00:00:00 2001 From: Josselin Feist Date: Fri, 7 Mar 2025 13:06:20 +0100 Subject: [PATCH 18/30] Update docs/src/tutorials/exercises/exercise1/solution.py Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- docs/src/tutorials/exercises/exercise1/solution.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/tutorials/exercises/exercise1/solution.py b/docs/src/tutorials/exercises/exercise1/solution.py index 4ad0ce7362..3e2857be4d 100644 --- a/docs/src/tutorials/exercises/exercise1/solution.py +++ b/docs/src/tutorials/exercises/exercise1/solution.py @@ -1,7 +1,7 @@ from slither.slither import Slither -slither = Slither('coin.sol') -coin = slither.get_contract_from_name('Coin')[0] +slither = Slither("coin.sol") +coin = slither.get_contract_from_name("Coin")[0] # Iterate over all the contracts for contract in slither.contracts: From 13e7451ad86333466d2066eede32b0844679cf0c Mon Sep 17 00:00:00 2001 From: Josselin Feist Date: Fri, 7 Mar 2025 13:06:27 +0100 Subject: [PATCH 19/30] Update docs/src/tutorials/exercises/exercise3/solution.py Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- docs/src/tutorials/exercises/exercise3/solution.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/tutorials/exercises/exercise3/solution.py b/docs/src/tutorials/exercises/exercise3/solution.py index 410194caaf..e177276de7 100644 --- a/docs/src/tutorials/exercises/exercise3/solution.py +++ b/docs/src/tutorials/exercises/exercise3/solution.py @@ -1,7 +1,7 @@ from slither.slither import Slither -slither = Slither('find.sol') -find = slither.get_contract_from_name('Find')[0] +slither = Slither("find.sol") +find = slither.get_contract_from_name("Find")[0] assert find From f7b72ef38d0c15c3734a28fac9fdd91c1c1b32bf Mon Sep 17 00:00:00 2001 From: Josselin Feist Date: Fri, 7 Mar 2025 13:06:38 +0100 Subject: [PATCH 20/30] Update docs/src/tutorials/exercises/exercise1/solution.py Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../tutorials/exercises/exercise1/solution.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/src/tutorials/exercises/exercise1/solution.py b/docs/src/tutorials/exercises/exercise1/solution.py index 3e2857be4d..3e7668a063 100644 --- a/docs/src/tutorials/exercises/exercise1/solution.py +++ b/docs/src/tutorials/exercises/exercise1/solution.py @@ -5,11 +5,11 @@ # Iterate over all the contracts for contract in slither.contracts: - # If the contract is derived from MyContract - if coin in contract.inheritance: - # Get the function definition - mint = contract.get_function_from_signature('_mint(address,uint256)') - # If the function was not declared by coin, there is a bug ! - # Detect error only for contracts overriding the '_mint' function - if mint.contract_declarer == contract: - print(f'Error, {contract} overrides {mint}') + # If the contract is derived from MyContract + if coin in contract.inheritance: + # Get the function definition + mint = contract.get_function_from_signature("_mint(address,uint256)") + # If the function was not declared by coin, there is a bug ! + # Detect error only for contracts overriding the '_mint' function + if mint.contract_declarer == contract: + print(f"Error, {contract} overrides {mint}") From ad24fb824bcf91b90d6a74d08782776b507e76e3 Mon Sep 17 00:00:00 2001 From: Josselin Feist Date: Fri, 7 Mar 2025 13:06:44 +0100 Subject: [PATCH 21/30] Update docs/src/tutorials/exercises/exercise2/solution.py Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- docs/src/tutorials/exercises/exercise2/solution.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/src/tutorials/exercises/exercise2/solution.py b/docs/src/tutorials/exercises/exercise2/solution.py index f693a97080..a756486474 100644 --- a/docs/src/tutorials/exercises/exercise2/solution.py +++ b/docs/src/tutorials/exercises/exercise2/solution.py @@ -10,6 +10,6 @@ continue if function.is_constructor: continue - if function.visibility in ['public', 'external']: - if not 'onlyOwner()' in [m.full_name for m in function.modifiers]: - print(f'{function.full_name} is unprotected!') + if function.visibility in ["public", "external"]: + if not "onlyOwner()" in [m.full_name for m in function.modifiers]: + print(f"{function.full_name} is unprotected!") From 3e513bbeccfc891a6d70281c9c016a310c59edca Mon Sep 17 00:00:00 2001 From: Josselin Feist Date: Fri, 7 Mar 2025 13:06:51 +0100 Subject: [PATCH 22/30] Update docs/src/tutorials/exercises/exercise3/solution.py Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- docs/src/tutorials/exercises/exercise3/solution.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/tutorials/exercises/exercise3/solution.py b/docs/src/tutorials/exercises/exercise3/solution.py index e177276de7..ddc2850e64 100644 --- a/docs/src/tutorials/exercises/exercise3/solution.py +++ b/docs/src/tutorials/exercises/exercise3/solution.py @@ -11,7 +11,7 @@ function_using_a_as_condition = [ - f + f for f in find.functions if f.is_reading_in_conditional_node(my_variable) or f.is_reading_in_require_or_assert(my_variable) ] From 980a422770b6f0dc7f17250943ebd8c3bad1e6aa Mon Sep 17 00:00:00 2001 From: Josselin Feist Date: Fri, 7 Mar 2025 13:06:59 +0100 Subject: [PATCH 23/30] Update docs/src/tutorials/exercises/exercise3/solution.py Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- docs/src/tutorials/exercises/exercise3/solution.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/src/tutorials/exercises/exercise3/solution.py b/docs/src/tutorials/exercises/exercise3/solution.py index ddc2850e64..b783294bc1 100644 --- a/docs/src/tutorials/exercises/exercise3/solution.py +++ b/docs/src/tutorials/exercises/exercise3/solution.py @@ -13,7 +13,8 @@ function_using_a_as_condition = [ f for f in find.functions - if f.is_reading_in_conditional_node(my_variable) or f.is_reading_in_require_or_assert(my_variable) + if f.is_reading_in_conditional_node(my_variable) + or f.is_reading_in_require_or_assert(my_variable) ] # Print the result From 129d78c19d5b047d72ec65db8d60b8eb415ac3af Mon Sep 17 00:00:00 2001 From: Josselin Feist Date: Fri, 7 Mar 2025 13:07:07 +0100 Subject: [PATCH 24/30] Update docs/src/tutorials/exercises/exercise2/solution.py Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- docs/src/tutorials/exercises/exercise2/solution.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/tutorials/exercises/exercise2/solution.py b/docs/src/tutorials/exercises/exercise2/solution.py index a756486474..54a72745ff 100644 --- a/docs/src/tutorials/exercises/exercise2/solution.py +++ b/docs/src/tutorials/exercises/exercise2/solution.py @@ -1,6 +1,6 @@ from slither import Slither -slither = Slither('coin.sol') +slither = Slither("coin.sol") whitelist = ['balanceOf(address)'] From bb47e01a4579da134478caf3fb6654bbad2517ba Mon Sep 17 00:00:00 2001 From: Josselin Feist Date: Fri, 7 Mar 2025 13:07:15 +0100 Subject: [PATCH 25/30] Update docs/src/tutorials/exercises/exercise2/solution.py Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- docs/src/tutorials/exercises/exercise2/solution.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/tutorials/exercises/exercise2/solution.py b/docs/src/tutorials/exercises/exercise2/solution.py index 54a72745ff..1a3f5861d8 100644 --- a/docs/src/tutorials/exercises/exercise2/solution.py +++ b/docs/src/tutorials/exercises/exercise2/solution.py @@ -2,7 +2,7 @@ slither = Slither("coin.sol") -whitelist = ['balanceOf(address)'] +whitelist = ["balanceOf(address)"] for contract in slither.contracts: for function in contract.functions: From b217fbd63fa373023cccc211c2f4edde631ba7e2 Mon Sep 17 00:00:00 2001 From: Feist Josselin Date: Fri, 7 Mar 2025 14:55:27 +0100 Subject: [PATCH 26/30] minor --- docs/src/SUMMARY.md | 4 ++-- docs/src/tools/README.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index b1180f1013..edad533680 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -12,8 +12,8 @@ - [Adding a detector](./detectors/Adding-a-new-detector.md) - [Printers](./printers/Printer-documentation.md) - [Tools](./tools/README.md) - - [Addning a new tool](./tools/Adding-a-new-utility.md) - - [Code Similarity](./tools/Code-Similarity-detector.md) + - [Adding a new tool](./tools/Adding-a-new-utility.md) + - [Code Similarity](./tools/Code-Similarity-Detector.md) - [Contract Flattening](./tools/Contract-Flattening.md) - [Documentation](./tools/Documentation.md) - [Doctor](./tools/Doctor.md) diff --git a/docs/src/tools/README.md b/docs/src/tools/README.md index 9e5d4528d0..d6afa8685e 100644 --- a/docs/src/tools/README.md +++ b/docs/src/tools/README.md @@ -2,7 +2,7 @@ Slither comes with inbuilt tools | Name | Command Line | What it Does | |------|-------------|--------------| -| [Code Similarity](./Code-Similarity-detector.md) | `slither-simil` | Detects similar Solidity functions/contracts using code similarity analysis. Useful for finding duplicated code, similar vulnerabilities, or analyzing large codebases. | +| [Code Similarity](./Code-Similarity-Detector.md) | `slither-simil` | Detects similar Solidity functions/contracts using code similarity analysis. Useful for finding duplicated code, similar vulnerabilities, or analyzing large codebases. | | [Contract Flattening](./Contract-Flattening.md) | `slither-flat` | Flattens a Solidity codebase by inlining all imports into a single file. Useful for contract verification on Etherscan or debugging. | | [Documentation](./Documentation.md) | `slither-doc` | Automatically generates documentation for Solidity contracts, including inheritance information, functions, modifiers, and more. | | [Doctor](./Doctor.md) | `slither-doctor` | Helps diagnose and fix common issues in your environment that might prevent Slither from working correctly. | From 3148bd959e0eb04071afa7ebfd07993afb2c187d Mon Sep 17 00:00:00 2001 From: Feist Josselin Date: Fri, 7 Mar 2025 15:14:49 +0100 Subject: [PATCH 27/30] Improvements --- docs/src/README.md | 6 +- docs/src/SUMMARY.md | 60 +- docs/src/Usage.md | 16 +- docs/src/api/Data-dependency.md | 10 +- docs/src/api/JSON-output.md | 90 +- docs/src/api/README.md | 3 +- docs/src/api/SlithIR-SSA.md | 1 - docs/src/api/SlithIR.md | 35 +- .../api/examples/print_basic_information.py | 1 - docs/src/detectors/Adding-a-new-detector.md | 11 +- docs/src/detectors/Detector-Documentation.md | 1185 +++++++++++------ docs/src/printers/Printer-documentation.md | 128 +- docs/src/tools/Adding-a-new-utility.md | 2 + docs/src/tools/Code-Similarity-Detector.md | 84 +- docs/src/tools/Contract-Flattening.md | 16 +- docs/src/tools/Doctor.md | 2 +- docs/src/tools/ERC-Conformance.md | 13 +- docs/src/tools/Interface.md | 3 +- docs/src/tools/Path-Finding-Utility.md | 6 +- docs/src/tools/Property-generation.md | 12 +- docs/src/tools/README.md | 29 +- docs/src/tools/Slither-format.md | 4 +- docs/src/tools/Upgradeability-Checks.md | 230 ++-- docs/src/tutorials/README.md | 4 +- .../tutorials/exercises/exercise3/solution.py | 2 +- docs/src/tutorials/scripts/gh_action_test.sh | 73 - .../detectors/shadowing/builtin_symbols.py | 4 +- 27 files changed, 1244 insertions(+), 786 deletions(-) delete mode 100644 docs/src/tutorials/scripts/gh_action_test.sh diff --git a/docs/src/README.md b/docs/src/README.md index 67c7276fa3..0c5613d82c 100644 --- a/docs/src/README.md +++ b/docs/src/README.md @@ -5,14 +5,16 @@ [**Slither**](https://github.com/crytic/slither) is a Solidity & Vyper static analysis framework written in Python3. It runs a suite of vulnerability detectors, prints visual information about contract details, and provides an API to easily write custom analyses. Slither enables developers to find vulnerabilities, enhance their code comprehension, and quickly prototype custom analyses. If you are looking to use Slither's cli: + - [Usage](./Usage.md) the most common flags If you are looking to leverage Slither inbuilt features: + - [Detectors](./detectors/): Vulnerabilities detectors - [Printers](./printers): Printers (code vizualiation) - [Tools](./tools): Custom tools If you are looking to learn how to extend Slither's capabilities: -- [API](./api): Introduction to static analysis & Slither's API -- [Tutorial](./tutorials/): Hands-on exercises +- [API](./api): Introduction to static analysis & Slither's API +- [Tutorial](./tutorials/): Hands-on exercises diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index edad533680..44e7023976 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -1,31 +1,31 @@ - - [Introduction](./README.md) - - [Usage](./Usage.md) - - [API](./api/README.md) - - [Static Analysis](./api/static_analysis.md) - - [API](./api/api.md) - - [SlithIR](./api/SlithIR.md) - - [SSA](./api/SlithIR-SSA.md) - - [Data dependency](./api/Data-dependency.md) - - [JSON output](./api/JSON-output.md) +- [Introduction](./README.md) +- [Usage](./Usage.md) +- [API](./api/README.md) + - [Static Analysis](./api/static_analysis.md) + - [API](./api/api.md) + - [SlithIR](./api/SlithIR.md) + - [SSA](./api/SlithIR-SSA.md) + - [Data dependency](./api/Data-dependency.md) + - [JSON output](./api/JSON-output.md) +- [Detectors](./detectors/Detector-Documentation.md) - [Detectors](./detectors/Detector-Documentation.md) - - [Detectors](./detectors/Detector-Documentation.md) - - [Adding a detector](./detectors/Adding-a-new-detector.md) - - [Printers](./printers/Printer-documentation.md) - - [Tools](./tools/README.md) - - [Adding a new tool](./tools/Adding-a-new-utility.md) - - [Code Similarity](./tools/Code-Similarity-Detector.md) - - [Contract Flattening](./tools/Contract-Flattening.md) - - [Documentation](./tools/Documentation.md) - - [Doctor](./tools/Doctor.md) - - [ERC Conformance](./tools/ERC-Conformance.md) - - [Interface](./tools/Interface.md) - - [Mutator](./tools/Mutator.md) - - [Path Finding Utility](./tools/Path-Finding-Utility.md) - - [Property Generation](./tools/Property-generation.md) - - [Read Storage](./tools/ReadStorage.md) - - [Format](./tools/Slither-format.md) - - [Upgradeability checks](./tools/Upgradeability-Checks.md) - - [Tutorials](./tutorials/README.md) - - [Exercise 1](./tutorials/exercise1.md) - - [Exercise 2](./tutorials/exercise2.md) - - [Exercise 3](./tutorials/exercise3.md) + - [Adding a detector](./detectors/Adding-a-new-detector.md) +- [Printers](./printers/Printer-documentation.md) +- [Tools](./tools/README.md) + - [Adding a new tool](./tools/Adding-a-new-utility.md) + - [Code Similarity](./tools/Code-Similarity-Detector.md) + - [Contract Flattening](./tools/Contract-Flattening.md) + - [Documentation](./tools/Documentation.md) + - [Doctor](./tools/Doctor.md) + - [ERC Conformance](./tools/ERC-Conformance.md) + - [Interface](./tools/Interface.md) + - [Mutator](./tools/Mutator.md) + - [Path Finding Utility](./tools/Path-Finding-Utility.md) + - [Property Generation](./tools/Property-generation.md) + - [Read Storage](./tools/ReadStorage.md) + - [Format](./tools/Slither-format.md) + - [Upgradeability checks](./tools/Upgradeability-Checks.md) +- [Tutorials](./tutorials/README.md) + - [Exercise 1](./tutorials/exercise1.md) + - [Exercise 2](./tutorials/exercise2.md) + - [Exercise 3](./tutorials/exercise3.md) diff --git a/docs/src/Usage.md b/docs/src/Usage.md index 42b11cbf94..8b08099f16 100644 --- a/docs/src/Usage.md +++ b/docs/src/Usage.md @@ -20,6 +20,7 @@ All the [`crytic-compile`](https://github.com/crytic/crytic-compile/wiki/Configu ### Foundry/hardhat To run Slither on a Foundry/hardhat directory: + ``` slither . ``` @@ -42,17 +43,18 @@ slither 0x7F37f78cBD74481E593F9C737776F7113d76B315 We recommend installing [solc-select](https://github.com/crytic/solc-select/) so Slither can switch to the expected solc version automatically. - ### Detector selection Slither runs all its detectors by default. To run only selected detectors, use `--detect detector1,detector2`. For example: + ``` slither file.sol --detect arbitrary-send,pragma ``` To exclude detectors, use `--exclude detector1,detector2`. For example: + ``` slither file.sol --exclude naming-convention,unused-state,suicidal ``` @@ -66,6 +68,7 @@ To exclude detectors with an informational or low severity, use `--exclude-infor By default, no printers are run. To run selected printers, use `--print printer1,printer2`. For example: + ``` slither file.sol --print inheritance-graph ``` @@ -77,19 +80,23 @@ slither file.sol --print inheritance-graph `--filter-paths path1` will exclude all the results that are only related to `path1`. The path specified can be a path directory or a filename. Direct string comparison and [Python regular expression](https://docs.python.org/3/library/re.html) are used. Examples: + ``` slither . --filter-paths "openzepellin" ``` + Filter all the results only related to openzepellin. + ``` slither . --filter-paths "Migrations.sol|ConvertLib.sol" ``` -Filter all the results only related to the file `SafeMath.sol` or `ConvertLib.sol`. +Filter all the results only related to the file `SafeMath.sol` or `ConvertLib.sol`. ### Triage mode Slither offers two ways to remove results: + - By adding `//slither-disable-next-line DETECTOR_NAME` before the issue - By adding `// slither-disable-start [detector] ... // slither-disable-end [detector]` around the code to disable the detector on a large section - By adding `@custom:security non-reentrant` before the variable declaration will indicate to Slither that the external calls from this variable are non-reentrant @@ -100,6 +107,7 @@ Slither offers two ways to remove results: `--triage-mode` runs Slither in its triage mode. For every finding, Slither will ask if the result should be shown for the next run. Results are saved in `slither.db.json`. Examples: + ``` slither . --triage-mode [...] @@ -116,7 +124,7 @@ To show the hidden results again, delete `slither.db.json`. ### Configuration File -Some options can be set through a json configuration file. By default, `slither.config.json` is used if present (it can be changed through `--config-file file.config.json`). +Some options can be set through a json configuration file. By default, `slither.config.json` is used if present (it can be changed through `--config-file file.config.json`). Options passed via the CLI have priority over options set in the configuration file. @@ -159,4 +167,4 @@ The following flags are supported: } ``` -For flags related to the compilation, see the [`crytic-compile` configuration](https://github.com/crytic/crytic-compile/blob/master/crytic_compile/cryticparser/defaults.py) \ No newline at end of file +For flags related to the compilation, see the [`crytic-compile` configuration](https://github.com/crytic/crytic-compile/blob/master/crytic_compile/cryticparser/defaults.py) diff --git a/docs/src/api/Data-dependency.md b/docs/src/api/Data-dependency.md index 2caa72bba4..6eb0a35c87 100644 --- a/docs/src/api/Data-dependency.md +++ b/docs/src/api/Data-dependency.md @@ -3,6 +3,7 @@ Data dependency allows knowing if the value of a given variable is influenced by another variable's value. Because smart contracts have a state machine based architecture, the results of the data dependency depend on the context (function/contract) of the analysis. Consider the following example: + ```solidity contract MyContract{ uint a = 0; @@ -20,18 +21,22 @@ contract MyContract{ ``` In this example, if we consider only `setA`, we have the following dependency: -- `a` is dependent on `input_a` + +- `a` is dependent on `input_a` If we consider only `setB`, we have: + - `b` is dependent on `a` If we consider the contract entirely (with all the functions), we have: -- `a` is dependent on `input_a` + +- `a` is dependent on `input_a` - `b` is dependent on `a` and `input_a` (by transitivity) `slither.analyses.is_dependent(variable, variable_source, context)` allows to know if `variable` is dependent on `variable_source` on the given context. As a result, in our previous example, `is_dependent(b, a, funcA)` will return `False`, while `is_dependent(b, a, myContract)` will return `True`: + ``` from slither import Slither from slither.analyses import is_dependent @@ -48,4 +53,3 @@ b = myContract.get_state_variable_from_name('b') print(f'{b.name} is dependant from {input_a.name}?: {is_dependent(b, a, funcA)}') print(f'{b.name} is dependant from {input_a.name}?: {is_dependent(b, a, myContract)}') ``` - diff --git a/docs/src/api/JSON-output.md b/docs/src/api/JSON-output.md index d781a76cfd..bee991ba47 100644 --- a/docs/src/api/JSON-output.md +++ b/docs/src/api/JSON-output.md @@ -3,30 +3,37 @@ - [Upgradeability output](https://github.com/crytic/slither/wiki/JSON-output#slither-check-upgradeability) ## Top-level Command Output + At the top level, the JSON output provided by slither will appear in the following format: + ```json -{ - "success": true, - "error": null, - "results": {} +{ + "success": true, + "error": null, + "results": {} } ``` + - `success` (boolean): `true` if `results` were output successfully, `false` if an `error` occurred. - `error` (string | null): If `success` is `false`, this will be a string with relevant error information. Otherwise, it will be `null`. - `results` (command-results, see below): If `success` is `true`, this will be an object populated with different types of results, depending on the JSON arguments specified. ## Command Results + The underlying `results` item above will appear in the following format: + ```json -{ - "detectors": [], - "upgradeability-check": {} -} +{ + "detectors": [], + "upgradeability-check": {} +} ``` + - `detectors` (OPTIONAL, vulnerability-results, see below): The results of any detector analysis. - `upgradeability-check` (OPTIONAL, upgradeability-results, see below): The results of `slither-check-upgradeability`. ## Detector Results + A detector result found in the `detectors` array above will be of the following format: ``` @@ -38,6 +45,7 @@ A detector result found in the `detectors` array above will be of the following "elements": [] } ``` + - `check` (string): The detector identifier (see the [list of detectors](https://github.com/trailofbits/slither#detectors)) - `impact` (string): representation of the impact (`High`/ `Medium`/ `Low`/ `Informational`) - `confidence` (string): representation of the confidence (`High`/ `Medium`/ `Low`) @@ -47,38 +55,43 @@ A detector result found in the `detectors` array above will be of the following - `additional_fields`: (OPTIONAL, any): Offers additional detector-specific information, does not always exist. ## Detector Result Elements + Each element found in `elements` above is of the form: + ```json { - "type": "...", - "name": "...", - "source_mapping": {}, - "type_specific_fields": {}, - "additional_fields": {} + "type": "...", + "name": "...", + "source_mapping": {}, + "type_specific_fields": {}, + "additional_fields": {} } ``` + - `type` (string): Refers to the type of element, this can be either: (`contract`, `function`, `variable`, `node`, `pragma`, `enum`, `struct`, `event`). -- `name` (string): Refers to the name of the element. - - For `contract`/`function`/`variable`/`enum`/`struct`/`event` types, this refers to the definition name. +- `name` (string): Refers to the name of the element. + - For `contract`/`function`/`variable`/`enum`/`struct`/`event` types, this refers to the definition name. - For `node` types, this refers to a string representation of any underlying expression. A blank string is used if there is no underlying expression. - For `pragma` types, this refers to a string representation of the `version` portion of the pragma (ie: `^0.5.0`). - `source_mapping` (source mapping, see below): Refers to a source mapping object which defines the source range which represents this element. -- `type_specific_fields` (OPTIONAL, any): +- `type_specific_fields` (OPTIONAL, any): - For `function`/`event` type elements: - `parent` (result-element): Refers to the parent contract of this definition. - `signature` (string): Refers to the full signature of this function - For `enum`/`struct` type elements: - `parent` (result-element): Refers to the parent contract of this definition. - - For `variable` type elements: + - For `variable` type elements: - `parent` (result-element): Refers to the parent contract if this variable is a state variable. Refers to the parent function if this variable is a local variable. - For `node` type elements: - `parent` (result-element): Refers to the parent function of this node. - - For `pragma` type elements: + - For `pragma` type elements: - `directive` (string array): Fully serialized pragma directive (ie: `["solidity", "^", "0.4", ".9"]`) - `additional_fields` (OPTIONAL, any): Offers additional detector-specific element information, does not always exist. ## Source Mapping + Each `source_mapping` object is used to map an element to some portion of source. It is of the form: + ``` "source_mapping": { "start": 45 @@ -96,6 +109,7 @@ Each `source_mapping` object is used to map an element to some portion of source "ending_column": 24, } ``` + - `start` (integer): Refers to the starting byte position of the mapped source. - `length` (integer): Refers to the byte-length of the mapped source. - `filename_relative` (string): A relative file path from the analysis directory. @@ -107,10 +121,13 @@ Each `source_mapping` object is used to map an element to some portion of source - `ending_column` (integer): The ending column/character position for the last mapped source line. Begins from 1. ## Detector-specific additional fields + Some detectors have custom elements output via the `additional_fields` field of their result, or result elements. Annotations here will specify _result_ or _result-element_ to specify the location of the additional fields. -- `constant-function`: + +- `constant-function`: - `contain_assembly` (result, boolean): Specifies if the result is due to the function containing assembly. -- `naming-convention`: +- `naming-convention`: + - `convention` (result-element, string): Used to denote the convention used to find the result element/issue. Valid conventions are: - `CapWords` - `mixedCase` @@ -127,34 +144,37 @@ Some detectors have custom elements output via the `additional_fields` field of - `enum` - `modifier` -- `reentrancy` (all variants): +- `reentrancy` (all variants): - `underlying_type` (result-element, string): Specifies the type of result element. Is one of `external_calls`, `external_calls_sending_eth`, or `variables_written`. - -## Slither Check Upgradeability +## Slither Check Upgradeability The `slither-check-upgradeability` tool also produces JSON output (with the use of the `--json` option). At the top level, this JSON output will appear in the format similar to that of Slither above: + ```json -{ - "success": true, - "error": null, - "results": { - "upgradeability-check": {} - } +{ + "success": true, + "error": null, + "results": { + "upgradeability-check": {} + } } ``` + - `success` (boolean): `true` if `results` were output successfully, `false` if an `error` occurred. - `error` (string | null): If `success` is `false`, this will be a string with relevant error information. Otherwise, it will be `null`. - `results` (upgradeability-check-results, see below): If `success` is `true`, this will contain an `upgradeability-check` object populated with the different upgradeability checks. If `success` is `false`, `upgradeability-check` object will be empty. ## Command Results + The underlying `upgradeability-check` item above will appear in the following format: + ```json -{ - "check-initialization": {}, - "check-initialization-v2": {}, - "compare-function-ids": {}, - "compare-variables-order-proxy": {}, - "compare-variables-order-implementation": {} +{ + "check-initialization": {}, + "check-initialization-v2": {}, + "compare-function-ids": {}, + "compare-variables-order-proxy": {}, + "compare-variables-order-implementation": {} } ``` diff --git a/docs/src/api/README.md b/docs/src/api/README.md index 4e16d63551..62e67fc49c 100644 --- a/docs/src/api/README.md +++ b/docs/src/api/README.md @@ -1,7 +1,8 @@ Slither is fully customizable: + - [Static Analysis](./static_analysis.md): learn about the basics of static analysis - [API](./api.md) - [SlithIR](./SlithIR.md) - [SSA](./SlithIR-SSA.md) - [Data dependency](./Data-dependency.md) -- [JSON output](./JSON-output.md) \ No newline at end of file +- [JSON output](./JSON-output.md) diff --git a/docs/src/api/SlithIR-SSA.md b/docs/src/api/SlithIR-SSA.md index 7956e16816..50f3f4b4ed 100644 --- a/docs/src/api/SlithIR-SSA.md +++ b/docs/src/api/SlithIR-SSA.md @@ -3,4 +3,3 @@ Slither possess a Static Single Assignment (SSA) form representation of SlithIR. SSA is a commonly used representation in compilation and static analysis in general. It requires that each variable is assigned at least one time. SSA is a key component for building an efficient data-dependency analysis. The [SSA printer](../printers/Printer-documentation.md#slithir-ssa) allows to visualize this representation. - diff --git a/docs/src/api/SlithIR.md b/docs/src/api/SlithIR.md index bb5d1aa957..e345ff082f 100644 --- a/docs/src/api/SlithIR.md +++ b/docs/src/api/SlithIR.md @@ -1,4 +1,5 @@ # SlithIR + Slither translates Solidity an intermediate representation, SlithIR, to enable high-precision analysis via a simple API. It supports taint and value tracking to enable detection of complex patterns. SlithIR is a work in progress, although it is usable today. New developments in SlithIR are driven by needs identified by new detector modules. Please help us bugtest and enhance SlithIR! @@ -16,6 +17,7 @@ Solidity is a quirky language with a number of edge cases, both in terms of synt Additionally, Slither can include non-trivial variable tracking by default by translating to an IR. This can build richer representations of contracts and allow for deeper analysis of potential vulnerabilities. For example, answering the question “can a user control a variable” is central to uncovering more complex vulnerabilities from a static position. Slither will propagate information from function parameters to program state in an iterative fashion, which captures the control flow of information across potentially multiple transactions. In this way, Slither can enrich information and statically provide a large amount of assurance to contracts that standard vulnerabilities exist and are reachable under certain conditions. ## Example + `$ slither file.sol --print slithir` will output the IR for every function. ``` @@ -37,19 +39,20 @@ Contract MyContract IRs: REF_3(uint256) -> balances[msg.sender] REF_1(uint256) -> balances[msg.sender] - TMP_1(uint256) = LIBRARY_CALL, dest:UnsafeMath, function:min, arguments:['REF_1', 'val'] + TMP_1(uint256) = LIBRARY_CALL, dest:UnsafeMath, function:min, arguments:['REF_1', 'val'] REF_3 := TMP_1 Expression: balances[to] = balances[to].add(val) IRs: REF_3(uint256) -> balances[to] REF_1(uint256) -> balances[to] - TMP_1(uint256) = LIBRARY_CALL, dest:UnsafeMath, function:add, arguments:['REF_1', 'val'] + TMP_1(uint256) = LIBRARY_CALL, dest:UnsafeMath, function:add, arguments:['REF_1', 'val'] REF_3 := TMP_1 ``` -# SlithIR Specification +# SlithIR Specification ## Variables + - `StateVariable` - `LocalVariable` - `Constant` (`string` or `int`) @@ -59,8 +62,9 @@ Contract MyContract - `ReferenceVariable` (variables added by SlithIR, for mapping/index accesses) In the following we use: + - `LVALUE` can be: `StateVariable`, `LocalVariable`, `TemporaryVariable`, `ReferenceVariable` or `TupleVariable` -- `RVALUE` can be: `StateVariable`, `LocalVariable`, `Constant`, `SolidityVariable`, `TemporaryVariable` or `ReferenceVariable` +- `RVALUE` can be: `StateVariable`, `LocalVariable`, `Constant`, `SolidityVariable`, `TemporaryVariable` or `ReferenceVariable` ## Operators @@ -96,15 +100,18 @@ In the following we use: - `LVALUE = RVALUE -- RVALUE` ### Unary Operation + - `LVALUE = ! RVALUE` - `LVALUE = ~ RVALUE` ### Index + - `REFERENCE -> LVALUE [ RVALUE ]` Note: The reference points to the memory location ### Member + - `REFERENCE -> LVALUE . RVALUE` - `REFERENCE -> CONTRACT . RVALUE` - `REFERENCE -> ENUM . RVALUE` @@ -112,6 +119,7 @@ Note: The reference points to the memory location Note: The reference points to the memory location ### New Operators + - `LVALUE = NEW_ARRAY ARRAY_TYPE DEPTH(:int)` `ARRAY_TYPE` is a [solidity_types](https://github.com/crytic/slither/tree/master/slither/core/solidity_types) @@ -125,21 +133,26 @@ Note: The reference points to the memory location `ELEMENTARY_TYPE` is defined in [slither/core/solidity_types/elementary_type.py](https://github.com/crytic/slither/blob/master/slither/core/solidity_types/elementary_type.py) ### Push Operator + - `PUSH LVALUE RVALUE` - `PUSH LVALUE Function` (for dynamic function) ### Delete Operator + - `DELETE LVALUE` ### Conversion + - `CONVERT LVALUE RVALUE TYPE` TYPE is a [solidity_types](https://github.com/crytic/slither/tree/master/slither/core/solidity_types) ### Unpack + - `LVALUE = UNPACK TUPLEVARIABLE INDEX(:int)` ### Array Initialization + - `LVALUE = INIT_VALUES` `INIT_VALUES` is a list of `RVALUE`, or a list of lists in case of a multidimensional array. @@ -153,12 +166,12 @@ In the following, `ARG` is a variable as defined in [SlithIR#variables](https:// `FUNCTION_NAME` can only be `call`/`delegatecall`/`codecall` -- `LVALUE = SOLIDITY_CALL SOLIDITY_FUNCTION [ARG, ..]` +- `LVALUE = SOLIDITY_CALL SOLIDITY_FUNCTION [ARG, ..]` `SOLIDITY_FUNCTION` is defined in [slither/core/declarations/solidity_variables.py](https://github.com/crytic/slither/blob/master/slither/core/declarations/solidity_variables.py) -- `LVALUE = INTERNAL_CALL FUNCTION [ARG, ..]` -- `LVALUE = INTERNAL_DYNAMIC_CALL FUNCTION_TYPE [ARG, ..]` +- `LVALUE = INTERNAL_CALL FUNCTION [ARG, ..]` +- `LVALUE = INTERNAL_DYNAMIC_CALL FUNCTION_TYPE [ARG, ..]` `INTERNAL_DYNAMIC_CALL` represents the pointer of function. @@ -169,10 +182,12 @@ In the following, `ARG` is a variable as defined in [SlithIR#variables](https:// - `LVALUE = SEND DESTINATION VALUE` - `TRANSFER DESTINATION VALUE` -Optional arguments: -- `GAS` and `VALUE` for `HIGH_LEVEL_CALL` / `LOW_LEVEL_CALL`. +Optional arguments: + +- `GAS` and `VALUE` for `HIGH_LEVEL_CALL` / `LOW_LEVEL_CALL`. ### Return + - `RETURN RVALUE` - `RETURN TUPLE` - `RETURN None` @@ -180,7 +195,7 @@ Optional arguments: `Return None` represents an empty return statement. ### Condition + - `CONDITION RVALUE` `CONDITION` holds the condition in a conditional node. - diff --git a/docs/src/api/examples/print_basic_information.py b/docs/src/api/examples/print_basic_information.py index 6a960d0aba..0df6120b71 100644 --- a/docs/src/api/examples/print_basic_information.py +++ b/docs/src/api/examples/print_basic_information.py @@ -1,4 +1,3 @@ -import sys from slither import Slither # Init slither diff --git a/docs/src/detectors/Adding-a-new-detector.md b/docs/src/detectors/Adding-a-new-detector.md index c905a604d6..abc35fc047 100644 --- a/docs/src/detectors/Adding-a-new-detector.md +++ b/docs/src/detectors/Adding-a-new-detector.md @@ -1,4 +1,4 @@ -Slither's plugin architecture lets you integrate new detectors that run from the command line. +Slither's plugin architecture lets you integrate new detectors that run from the command-line. ## Detector Skeleton @@ -32,8 +32,8 @@ class Skeleton(AbstractDetector): return [res] ``` -- `ARGUMENT` lets you run the detector from the command line -- `HELP` is the information printed from the command line +- `ARGUMENT` lets you run the detector from the command-line +- `HELP` is the information printed from the command-line - `IMPACT` indicates the impact of the issue. Allowed values are: - `DetectorClassification.OPTIMIZATION`: printed in green - `DetectorClassification.INFORMATIONAL`: printed in green @@ -46,18 +46,21 @@ class Skeleton(AbstractDetector): - `DetectorClassification.HIGH` - `WIKI` constants are used to generate automatically the documentation. -`_detect()` needs to return a list of findings. A finding is an element generated with `self.generate_result(info)`, where `info` is a list of text or contract's object (contract, function, node, ...) +`_detect()` needs to return a list of findings. A finding is an element generated with `self.generate_result(info)`, where `info` is a list of text or contract's object (contract, function, node, ...) An `AbstractDetector` object has the `slither` attribute, which returns the current `Slither` object. ## Integration You can integrate your detector into Slither by: + - Adding it in [slither/detectors/all_detectors.py](https://github.com/trailofbits/slither/blob/ae7c410938b616d993e6c27678f6e48d9a4d7dd6/slither/detectors/all_detectors.py) - or, by creating a plugin package (see the [skeleton example](https://github.com/trailofbits/slither/tree/ae7c410938b616d993e6c27678f6e48d9a4d7dd6/plugin_example)). ### Test the detector + See [CONTRIBUTING.md#development-environment](https://github.com/crytic/slither/blob/master/CONTRIBUTING.md#development-environment) ## Example + [backdoor.py](https://github.com/crytic/slither/blob/ae7c410938b616d993e6c27678f6e48d9a4d7dd6/slither/detectors/examples/backdoor.py) will detect any function with `backdoor` in its name. diff --git a/docs/src/detectors/Detector-Documentation.md b/docs/src/detectors/Detector-Documentation.md index f17109f338..41990962f8 100644 --- a/docs/src/detectors/Detector-Documentation.md +++ b/docs/src/detectors/Detector-Documentation.md @@ -1,18 +1,17 @@ - # Public Detectors List of public detectors - - - ## Storage ABIEncoderV2 Array + ### Configuration -* Check: `abiencoderv2-array` -* Severity: `High` -* Confidence: `High` + +- Check: `abiencoderv2-array` +- Severity: `High` +- Confidence: `High` ### Description + `solc` versions `0.4.7`-`0.5.9` contain a [compiler bug](https://blog.ethereum.org/2019/06/25/solidity-storage-array-bugs) leading to incorrect ABI encoder usage. ### Exploit Scenario: @@ -20,26 +19,30 @@ List of public detectors ```solidity contract A { uint[2][3] bad_arr = [[1, 2], [3, 4], [5, 6]]; - + /* Array of arrays passed to abi.encode is vulnerable */ - function bad() public { + function bad() public { bytes memory b = abi.encode(bad_arr); } } ``` -`abi.encode(bad_arr)` in a call to `bad()` will incorrectly encode the array as `[[1, 2], [2, 3], [3, 4]]` and lead to unintended behavior. +`abi.encode(bad_arr)` in a call to `bad()` will incorrectly encode the array as `[[1, 2], [2, 3], [3, 4]]` and lead to unintended behavior. ### Recommendation + Use a compiler >= `0.5.10`. ## Arbitrary `from` in transferFrom + ### Configuration -* Check: `arbitrary-send-erc20` -* Severity: `High` -* Confidence: `High` + +- Check: `arbitrary-send-erc20` +- Severity: `High` +- Confidence: `High` ### Description + Detect when `msg.sender` is not used as `from` in transferFrom. ### Exploit Scenario: @@ -49,20 +52,23 @@ Detect when `msg.sender` is not used as `from` in transferFrom. erc20.transferFrom(from, to, am); } ``` + Alice approves this contract to spend her ERC20 tokens. Bob can call `a` and specify Alice's address as the `from` parameter in `transferFrom`, allowing him to transfer Alice's tokens to himself. ### Recommendation Use `msg.sender` as `from` in transferFrom. - ## Modifying storage array by value + ### Configuration -* Check: `array-by-reference` -* Severity: `High` -* Confidence: `High` + +- Check: `array-by-reference` +- Severity: `High` +- Confidence: `High` ### Description + Detect arrays passed to a function that expects reference to a storage array ### Exploit Scenario: @@ -90,15 +96,19 @@ Bob calls `f()`. Bob assumes that at the end of the call `x[0]` is 2, but it is As a result, Bob's usage of the contract is incorrect. ### Recommendation + Ensure the correct usage of `memory` and `storage` in the function parameters. Make all the locations explicit. ## ABI encodePacked Collision + ### Configuration -* Check: `encode-packed-collision` -* Severity: `High` -* Confidence: `High` + +- Check: `encode-packed-collision` +- Severity: `High` +- Confidence: `High` ### Description + Detect collision due to dynamic type usages in `abi.encodePacked` ### Exploit Scenario: @@ -110,22 +120,26 @@ contract Sign { } } ``` + Bob calls `get_hash_for_signature` with (`bob`, `This is the content`). The hash returned is used as an ID. Eve creates a collision with the ID using (`bo`, `bThis is the content`) and compromises the system. - ### Recommendation + Do not use more than one dynamic type in `abi.encodePacked()` -(see the [Solidity documentation](https://solidity.readthedocs.io/en/v0.5.10/abi-spec.html?highlight=abi.encodePacked#non-standard-packed-modeDynamic)). +(see the [Solidity documentation](https://solidity.readthedocs.io/en/v0.5.10/abi-spec.html?highlight=abi.encodePacked#non-standard-packed-modeDynamic)). Use `abi.encode()`, preferably. ## Incorrect shift in assembly. + ### Configuration -* Check: `incorrect-shift` -* Severity: `High` -* Confidence: `High` + +- Check: `incorrect-shift` +- Severity: `High` +- Confidence: `High` ### Description + Detect if the values in a shift operation are reversed ### Exploit Scenario: @@ -139,18 +153,23 @@ contract C { } } ``` + The shift statement will right-shift the constant 8 by `a` bits ### Recommendation + Swap the order of parameters. ## Multiple constructor schemes + ### Configuration -* Check: `multiple-constructors` -* Severity: `High` -* Confidence: `High` + +- Check: `multiple-constructors` +- Severity: `High` +- Confidence: `High` ### Description + Detect multiple constructor definitions in the same contract (using new and old schemes). ### Exploit Scenario: @@ -164,24 +183,29 @@ contract A { function A() public { x = 1; } - + function test() public returns(uint) { return x; } } ``` + In Solidity [0.4.22](https://github.com/ethereum/solidity/releases/tag/v0.4.23), a contract with both constructor schemes will compile. The first constructor will take precedence over the second, which may be unintended. ### Recommendation + Only declare one constructor, preferably using the new scheme `constructor(...)` instead of `function (...)`. ## Name reused + ### Configuration -* Check: `name-reused` -* Severity: `High` -* Confidence: `High` + +- Check: `name-reused` +- Severity: `High` +- Confidence: `High` ### Description + If a codebase has two contracts the similar names, the compilation artifacts will not contain one of the contracts with the duplicate name. @@ -191,17 +215,20 @@ Bob's `truffle` codebase has two contracts named `ERC20`. When `truffle compile` runs, only one of the two contracts will generate artifacts in `build/contracts`. As a result, the second contract cannot be analyzed. - ### Recommendation + Rename the contract. ## Protected Variables + ### Configuration -* Check: `protected-vars` -* Severity: `High` -* Confidence: `High` + +- Check: `protected-vars` +- Severity: `High` +- Confidence: `High` ### Description + Detect unprotected variable that are marked protected ### Exploit Scenario: @@ -219,36 +246,45 @@ contract Buggy{ function set_not_protected() public{ owner = msg.sender; } -} +} ``` -`owner` must be always written by function using `onlyOwner` (`write-protection="onlyOwner()"`), however anyone can call `set_not_protected`. +`owner` must be always written by function using `onlyOwner` (`write-protection="onlyOwner()"`), however anyone can call `set_not_protected`. ### Recommendation + Add access controls to the vulnerable function ## Public mappings with nested variables + ### Configuration -* Check: `public-mappings-nested` -* Severity: `High` -* Confidence: `High` + +- Check: `public-mappings-nested` +- Severity: `High` +- Confidence: `High` ### Description + Prior to Solidity 0.5, a public mapping with nested structures returned [incorrect values](https://github.com/ethereum/solidity/issues/5520). ### Exploit Scenario: + Bob interacts with a contract that has a public mapping with nested structures. The values returned by the mapping are incorrect, breaking Bob's usage ### Recommendation + Do not use public mapping with nested structures. ## Right-to-Left-Override character + ### Configuration -* Check: `rtlo` -* Severity: `High` -* Confidence: `High` + +- Check: `rtlo` +- Severity: `High` +- Confidence: `High` ### Description + An attacker can manipulate the logic of the contract by using a right-to-left-override character (`U+202E)`. ### Exploit Scenario: @@ -279,18 +315,20 @@ contract Token `Token` uses the right-to-left-override character when calling `_withdraw`. As a result, the fee is incorrectly sent to `msg.sender`, and the token balance is sent to the owner. - - ### Recommendation + Special control characters must not be allowed. ## State variable shadowing + ### Configuration -* Check: `shadowing-state` -* Severity: `High` -* Confidence: `High` + +- Check: `shadowing-state` +- Severity: `High` +- Confidence: `High` ### Description + Detection of state variables shadowed. ### Exploit Scenario: @@ -318,18 +356,23 @@ contract DerivedContract is BaseContract{ } } ``` + `owner` of `BaseContract` is never assigned and the modifier `isOwner` does not work. ### Recommendation + Remove the state variable shadowing. ## Suicidal + ### Configuration -* Check: `suicidal` -* Severity: `High` -* Confidence: `High` + +- Check: `suicidal` +- Severity: `High` +- Confidence: `High` ### Description + Unprotected call to a function executing `selfdestruct`/`suicide`. ### Exploit Scenario: @@ -341,18 +384,23 @@ contract Suicidal{ } } ``` + Bob calls `kill` and destructs the contract. ### Recommendation + Protect access to all sensitive functions. ## Uninitialized state variables + ### Configuration -* Check: `uninitialized-state` -* Severity: `High` -* Confidence: `High` + +- Check: `uninitialized-state` +- Severity: `High` +- Confidence: `High` ### Description + Uninitialized state variables. ### Exploit Scenario: @@ -366,21 +414,23 @@ contract Uninitialized{ } } ``` -Bob calls `transfer`. As a result, the Ether are sent to the address `0x0` and are lost. +Bob calls `transfer`. As a result, the Ether are sent to the address `0x0` and are lost. ### Recommendation Initialize all the variables. If a variable is meant to be initialized to zero, explicitly set it to zero to improve code readability. - ## Uninitialized storage variables + ### Configuration -* Check: `uninitialized-storage` -* Severity: `High` -* Confidence: `High` + +- Check: `uninitialized-storage` +- Severity: `High` +- Confidence: `High` ### Description + An uninitialized storage variable will act as a reference to the first state variable, and can override a critical variable. ### Exploit Scenario: @@ -399,19 +449,23 @@ contract Uninitialized{ } } ``` -Bob calls `func`. As a result, `owner` is overridden to `0`. +Bob calls `func`. As a result, `owner` is overridden to `0`. ### Recommendation + Initialize all storage variables. ## Unprotected upgradeable contract + ### Configuration -* Check: `unprotected-upgrade` -* Severity: `High` -* Confidence: `High` + +- Check: `unprotected-upgrade` +- Severity: `High` +- Confidence: `High` ### Description + Detects logic contract that can be destructed. ### Exploit Scenario: @@ -430,19 +484,23 @@ contract Buggy is Initializable{ } } ``` -Buggy is an upgradeable contract. Anyone can call initialize on the logic contract, and destruct the contract. +Buggy is an upgradeable contract. Anyone can call initialize on the logic contract, and destruct the contract. ### Recommendation + Add a constructor to ensure `initialize` cannot be called on the logic contract. ## Arbitrary `from` in transferFrom used with permit + ### Configuration -* Check: `arbitrary-send-erc20-permit` -* Severity: `High` -* Confidence: `Medium` + +- Check: `arbitrary-send-erc20-permit` +- Severity: `High` +- Confidence: `Medium` ### Description + Detect when `msg.sender` is not used as `from` in transferFrom and permit is used. ### Exploit Scenario: @@ -453,20 +511,23 @@ Detect when `msg.sender` is not used as `from` in transferFrom and permit is use erc20.transferFrom(from, to, value); } ``` + If an ERC20 token does not implement permit and has a fallback function e.g. WETH, transferFrom allows an attacker to transfer all tokens approved for this contract. ### Recommendation Ensure that the underlying ERC20 token correctly implements a permit function. - ## Functions that send Ether to arbitrary destinations + ### Configuration -* Check: `arbitrary-send-eth` -* Severity: `High` -* Confidence: `Medium` + +- Check: `arbitrary-send-eth` +- Severity: `High` +- Confidence: `Medium` ### Description + Unprotected call to a function sending Ether to an arbitrary address. ### Exploit Scenario: @@ -483,18 +544,23 @@ contract ArbitrarySendEth{ } } ``` + Bob calls `setDestination` and `withdraw`. As a result he withdraws the contract's balance. ### Recommendation + Ensure that an arbitrary user cannot withdraw unauthorized funds. ## Array Length Assignment + ### Configuration -* Check: `controlled-array-length` -* Severity: `High` -* Confidence: `Medium` + +- Check: `controlled-array-length` +- Severity: `High` +- Confidence: `Medium` ### Description + Detects the direct assignment of an array's length. ### Exploit Scenario: @@ -516,22 +582,27 @@ contract A { } } ``` + Contract storage/state-variables are indexed by a 256-bit integer. -The user can set the array length to `2**256-1` in order to index all storage slots. -In the example above, one could call the function `f` to set the array length, then call the function `g` to control any storage slot desired. +The user can set the array length to `2**256-1` in order to index all storage slots. +In the example above, one could call the function `f` to set the array length, then call the function `g` to control any storage slot desired. Note that storage slots here are indexed via a hash of the indexers; nonetheless, all storage will still be accessible and could be controlled by the attacker. ### Recommendation + Do not allow array lengths to be set directly set; instead, opt to add values as needed. Otherwise, thoroughly review the contract to ensure a user-controlled variable cannot reach an array length assignment. ## Controlled Delegatecall + ### Configuration -* Check: `controlled-delegatecall` -* Severity: `High` -* Confidence: `Medium` + +- Check: `controlled-delegatecall` +- Severity: `High` +- Confidence: `Medium` ### Description + `Delegatecall` or `callcode` to an address controlled by the user. ### Exploit Scenario: @@ -543,18 +614,23 @@ contract Delegatecall{ } } ``` + Bob calls `delegate` and delegates the execution to his malicious contract. As a result, Bob withdraws the funds of the contract and destructs it. ### Recommendation + Avoid using `delegatecall`. Use only trusted destinations. ## Payable functions using `delegatecall` inside a loop + ### Configuration -* Check: `delegatecall-loop` -* Severity: `High` -* Confidence: `Medium` + +- Check: `delegatecall-loop` +- Severity: `High` +- Confidence: `Medium` ### Description + Detect the use of `delegatecall` inside a loop in a payable function. ### Exploit Scenario: @@ -572,24 +648,27 @@ contract DelegatecallInLoop{ function addBalance(address a) public payable { balances[a] += msg.value; - } + } } ``` + When calling `bad` the same `msg.value` amount will be accredited multiple times. ### Recommendation Carefully check that the function called by `delegatecall` is not payable/doesn't use `msg.value`. - ## Incorrect exponentiation + ### Configuration -* Check: `incorrect-exp` -* Severity: `High` -* Confidence: `Medium` + +- Check: `incorrect-exp` +- Severity: `High` +- Confidence: `Medium` ### Description + Detect use of bitwise `xor ^` instead of exponential `**` ### Exploit Scenario: @@ -600,18 +679,23 @@ contract Bug{ ... } ``` + Alice deploys a contract in which `UINT_MAX` incorrectly uses `^` operator instead of `**` for exponentiation ### Recommendation + Use the correct operator `**` for exponentiation. ## Incorrect return in assembly + ### Configuration -* Check: `incorrect-return` -* Severity: `High` -* Confidence: `Medium` + +- Check: `incorrect-return` +- Severity: `High` +- Confidence: `Medium` ### Description + Detect if `return` in an assembly block halts unexpectedly the execution. ### Exploit Scenario: @@ -630,19 +714,24 @@ contract C { } } ``` + The return statement in `f` will cause execution in `g` to halt. The function will return 6 bytes starting from offset 5, instead of returning a boolean. ### Recommendation + Use the `leave` statement. ## `msg.value` inside a loop + ### Configuration -* Check: `msg-value-loop` -* Severity: `High` -* Confidence: `Medium` + +- Check: `msg-value-loop` +- Severity: `High` +- Confidence: `Medium` ### Description + Detect the use of `msg.value` inside a loop. ### Exploit Scenario: @@ -661,17 +750,17 @@ contract MsgValueInLoop{ } ``` - ### Recommendation Provide an explicit array of amounts alongside the receivers array, and check that the sum of all amounts matches `msg.value`. - ## Reentrancy vulnerabilities + ### Configuration -* Check: `reentrancy-eth` -* Severity: `High` -* Confidence: `Medium` + +- Check: `reentrancy-eth` +- Severity: `High` +- Confidence: `Medium` ### Description @@ -694,15 +783,19 @@ Do not report reentrancies that don't involve Ether (see `reentrancy-no-eth`) Bob uses the re-entrancy bug to call `withdrawBalance` two times, and withdraw more than its initial deposit to the contract. ### Recommendation + Apply the [`check-effects-interactions pattern`](http://solidity.readthedocs.io/en/v0.4.21/security-considerations.html#re-entrancy). ## Return instead of leave in assembly + ### Configuration -* Check: `return-leave` -* Severity: `High` -* Confidence: `Medium` + +- Check: `return-leave` +- Severity: `High` +- Confidence: `Medium` ### Description + Detect if a `return` is used where a `leave` should be used. ### Exploit Scenario: @@ -717,18 +810,23 @@ contract C { } ``` + The function will halt the execution, instead of returning a two uint. ### Recommendation + Use the `leave` statement. ## Storage Signed Integer Array + ### Configuration -* Check: `storage-array` -* Severity: `High` -* Confidence: `Medium` + +- Check: `storage-array` +- Severity: `High` +- Confidence: `Medium` ### Description + `solc` versions `0.4.7`-`0.5.9` contain [a compiler bug](https://blog.ethereum.org/2019/06/25/solidity-storage-array-bugs) leading to incorrect values in signed integer arrays. @@ -744,20 +842,24 @@ contract A { } } ``` + `bad0()` uses a (storage-allocated) signed integer array state variable to store the ether balances of three accounts. `-1` is supposed to indicate uninitialized values but the Solidity bug makes these as `1`, which could be exploited by the accounts. - ### Recommendation + Use a compiler version >= `0.5.10`. ## Unchecked transfer + ### Configuration -* Check: `unchecked-transfer` -* Severity: `High` -* Confidence: `Medium` + +- Check: `unchecked-transfer` +- Severity: `High` +- Confidence: `Medium` ### Description + The return value of an external transfer/transferFrom call is not checked ### Exploit Scenario: @@ -766,7 +868,7 @@ The return value of an external transfer/transferFrom call is not checked contract Token { function transferFrom(address _from, address _to, uint256 _value) public returns (bool success); } -contract MyBank{ +contract MyBank{ mapping(address => uint) balances; Token token; function deposit(uint amount) public{ @@ -775,18 +877,23 @@ contract MyBank{ } } ``` + Several tokens do not revert in case of failure and return false. If one of these tokens is used in `MyBank`, `deposit` will not revert if the transfer fails, and an attacker can call `deposit` for free.. ### Recommendation + Use `SafeERC20`, or ensure that the transfer/transferFrom return value is checked. ## Weak PRNG + ### Configuration -* Check: `weak-prng` -* Severity: `High` -* Confidence: `Medium` + +- Check: `weak-prng` +- Severity: `High` +- Confidence: `Medium` ### Description + Weak PRNG due to a modulo on `block.timestamp`, `now` or `blockhash`. These can be influenced by miners to some extent so they should be avoided. ### Exploit Scenario: @@ -801,34 +908,44 @@ contract Game { } } ``` -Eve is a miner. Eve calls `guessing` and re-orders the block containing the transaction. + +Eve is a miner. Eve calls `guessing` and re-orders the block containing the transaction. As a result, Eve wins the game. ### Recommendation + Do not use `block.timestamp`, `now` or `blockhash` as a source of randomness ## Codex + ### Configuration -* Check: `codex` -* Severity: `High` -* Confidence: `Low` + +- Check: `codex` +- Severity: `High` +- Confidence: `Low` ### Description + Use [codex](https://openai.com/blog/openai-codex/) to find vulnerabilities ### Exploit Scenario: + N/A ### Recommendation + Review codex's message. ## Domain separator collision + ### Configuration -* Check: `domain-separator-collision` -* Severity: `Medium` -* Confidence: `High` + +- Check: `domain-separator-collision` +- Severity: `Medium` +- Confidence: `High` ### Description + An ERC20 token has a function whose signature collides with EIP-2612's DOMAIN_SEPARATOR(), causing unanticipated behavior for contracts using `permit` functionality. ### Exploit Scenario: @@ -838,18 +955,23 @@ contract Contract{ function some_collisions() external() {} } ``` + `some_collision` clashes with EIP-2612's DOMAIN_SEPARATOR() and will interfere with contract's using `permit`. ### Recommendation + Remove or rename the function that collides with DOMAIN_SEPARATOR(). ## Dangerous enum conversion + ### Configuration -* Check: `enum-conversion` -* Severity: `Medium` -* Confidence: `High` + +- Check: `enum-conversion` +- Severity: `Medium` +- Confidence: `High` ### Description + Detect out-of-range `enum` conversion (`solc` < `0.4.5`). ### Exploit Scenario: @@ -865,18 +987,23 @@ Detect out-of-range `enum` conversion (`solc` < `0.4.5`). } } ``` + Attackers can trigger unexpected behaviour by calling `bug(1)`. ### Recommendation + Use a recent compiler version. If `solc` <`0.4.5` is required, check the `enum` conversion range. ## Incorrect erc20 interface + ### Configuration -* Check: `erc20-interface` -* Severity: `Medium` -* Confidence: `High` + +- Check: `erc20-interface` +- Severity: `Medium` +- Confidence: `High` ### Description + Incorrect return values for `ERC20` functions. A contract compiled with Solidity > 0.4.22 interacting with these functions will fail to execute them, as the return value is missing. ### Exploit Scenario: @@ -887,18 +1014,23 @@ contract Token{ //... } ``` + `Token.transfer` does not return a boolean. Bob deploys the token. Alice creates a contract that interacts with it but assumes a correct `ERC20` interface implementation. Alice's contract is unable to interact with Bob's contract. ### Recommendation + Set the appropriate return values and types for the defined `ERC20` functions. ## Incorrect erc721 interface + ### Configuration -* Check: `erc721-interface` -* Severity: `Medium` -* Confidence: `High` + +- Check: `erc721-interface` +- Severity: `Medium` +- Confidence: `High` ### Description + Incorrect return values for `ERC721` functions. A contract compiled with solidity > 0.4.22 interacting with these functions will fail to execute them, as the return value is missing. ### Exploit Scenario: @@ -909,18 +1041,23 @@ contract Token{ //... } ``` + `Token.ownerOf` does not return an address like `ERC721` expects. Bob deploys the token. Alice creates a contract that interacts with it but assumes a correct `ERC721` interface implementation. Alice's contract is unable to interact with Bob's contract. ### Recommendation + Set the appropriate return values and vtypes for the defined `ERC721` functions. ## Dangerous strict equalities + ### Configuration -* Check: `incorrect-equality` -* Severity: `Medium` -* Confidence: `High` + +- Check: `incorrect-equality` +- Severity: `Medium` +- Confidence: `High` ### Description + Use of strict equalities that can be easily manipulated by an attacker. ### Exploit Scenario: @@ -931,19 +1068,24 @@ contract Crowdsale{ return this.balance == 100 ether; } ``` + `Crowdsale` relies on `fund_reached` to know when to stop the sale of tokens. `Crowdsale` reaches 100 Ether. Bob sends 0.1 Ether. As a result, `fund_reached` is always false and the `crowdsale` never ends. ### Recommendation + Don't use strict equality to determine if an account has enough Ether or tokens. ## Contracts that lock Ether + ### Configuration -* Check: `locked-ether` -* Severity: `Medium` -* Confidence: `High` + +- Check: `locked-ether` +- Severity: `Medium` +- Confidence: `High` ### Description + Contract with a `payable` function, but without a withdrawal capacity. ### Exploit Scenario: @@ -955,18 +1097,23 @@ contract Locked{ } } ``` + Every Ether sent to `Locked` will be lost. ### Recommendation + Remove the payable attribute or add a withdraw function. ## Deletion on mapping containing a structure + ### Configuration -* Check: `mapping-deletion` -* Severity: `Medium` -* Confidence: `High` + +- Check: `mapping-deletion` +- Severity: `Medium` +- Confidence: `High` ### Description + A deletion in a structure containing a mapping will not delete the mapping (see the [Solidity documentation](https://solidity.readthedocs.io/en/latest/types.html##delete)). The remaining data may be used to compromise the contract. ### Exploit Scenario: @@ -982,19 +1129,24 @@ A deletion in a structure containing a mapping will not delete the mapping (see delete stackBalance[msg.sender]; } ``` + `remove` deletes an item of `stackBalance`. The mapping `balances` is never deleted, so `remove` does not work as intended. ### Recommendation + Use a lock mechanism instead of a deletion to disable structure containing a mapping. ## Pyth deprecated functions + ### Configuration -* Check: `pyth-deprecated-functions` -* Severity: `Medium` -* Confidence: `High` + +- Check: `pyth-deprecated-functions` +- Severity: `Medium` +- Confidence: `High` ### Description + Detect when a Pyth deprecated function is used ### Exploit Scenario: @@ -1006,30 +1158,34 @@ import "@pythnetwork/pyth-sdk-solidity/PythStructs.sol"; contract C { IPyth pyth; - + constructor(IPyth _pyth) { pyth = _pyth; } - + function A(bytes32 priceId) public { PythStructs.Price memory price = pyth.getPrice(priceId); ... } -} +} ``` -The function `A` uses the deprecated `getPrice` Pyth function. +The function `A` uses the deprecated `getPrice` Pyth function. ### Recommendation + Do not use deprecated Pyth functions. Visit https://api-reference.pyth.network/. ## Pyth unchecked confidence level + ### Configuration -* Check: `pyth-unchecked-confidence` -* Severity: `Medium` -* Confidence: `High` + +- Check: `pyth-unchecked-confidence` +- Severity: `Medium` +- Confidence: `High` ### Description + Detect when the confidence level of a Pyth price is not checked ### Exploit Scenario: @@ -1049,21 +1205,25 @@ contract C { PythStructs.Price memory price = pyth.getEmaPriceNoOlderThan(id, age); // Use price } -} +} ``` -The function `A` uses the price without checking its confidence level. +The function `A` uses the price without checking its confidence level. ### Recommendation + Check the confidence level of a Pyth price. Visit https://docs.pyth.network/price-feeds/best-practices#confidence-intervals for more information. ## Pyth unchecked publishTime + ### Configuration -* Check: `pyth-unchecked-publishtime` -* Severity: `Medium` -* Confidence: `High` + +- Check: `pyth-unchecked-publishtime` +- Severity: `Medium` +- Confidence: `High` ### Description + Detect when the publishTime of a Pyth price is not checked ### Exploit Scenario: @@ -1083,21 +1243,25 @@ contract C { PythStructs.Price memory price = pyth.getEmaPriceUnsafe(id); // Use price } -} +} ``` -The function `A` uses the price without checking its `publishTime` coming from the `getEmaPriceUnsafe` function. +The function `A` uses the price without checking its `publishTime` coming from the `getEmaPriceUnsafe` function. ### Recommendation + Check the publishTime of a Pyth price. ## State variable shadowing from abstract contracts + ### Configuration -* Check: `shadowing-abstract` -* Severity: `Medium` -* Confidence: `High` + +- Check: `shadowing-abstract` +- Severity: `Medium` +- Confidence: `High` ### Description + Detection of state variables shadowed from abstract contracts. ### Exploit Scenario: @@ -1111,18 +1275,23 @@ contract DerivedContract is BaseContract{ address owner; } ``` + `owner` of `BaseContract` is shadowed in `DerivedContract`. ### Recommendation + Remove the state variable shadowing. ## Tautological compare + ### Configuration -* Check: `tautological-compare` -* Severity: `Medium` -* Confidence: `High` + +- Check: `tautological-compare` +- Severity: `Medium` +- Confidence: `High` ### Description + A variable compared to itself is probably an error as it will always return `true` for `==`, `>=`, `<=` and always `false` for `<`, `>` and `!=`. ### Exploit Scenario: @@ -1132,18 +1301,23 @@ A variable compared to itself is probably an error as it will always return `tru return (a >= a); } ``` + `check` always return true. ### Recommendation + Remove comparison or compare to different value. ## Tautology or contradiction + ### Configuration -* Check: `tautology` -* Severity: `Medium` -* Confidence: `High` + +- Check: `tautology` +- Severity: `Medium` +- Confidence: `High` ### Description + Detects expressions that are tautologies or contradictions. ### Exploit Scenario: @@ -1165,20 +1339,24 @@ contract A { } } ``` -`x` is a `uint256`, so `x >= 0` will be always true. -`y` is a `uint8`, so `y <512` will be always true. +`x` is a `uint256`, so `x >= 0` will be always true. +`y` is a `uint8`, so `y <512` will be always true. ### Recommendation + Fix the incorrect comparison by changing the value type or the comparison. ## Write after write + ### Configuration -* Check: `write-after-write` -* Severity: `Medium` -* Confidence: `High` + +- Check: `write-after-write` +- Severity: `Medium` +- Confidence: `High` ### Description + Detects variables that are written but never read and written again. ### Exploit Scenario: @@ -1196,15 +1374,19 @@ Detects variables that are written but never read and written again. `a` is first asigned to `b`, and then to `c`. As a result the first write does nothing. ### Recommendation + Fix or remove the writes. ## Misuse of a Boolean constant + ### Configuration -* Check: `boolean-cst` -* Severity: `Medium` -* Confidence: `Medium` + +- Check: `boolean-cst` +- Severity: `Medium` +- Confidence: `Medium` ### Description + Detects the misuse of a Boolean constant. ### Exploit Scenario: @@ -1226,19 +1408,24 @@ contract A { } } ``` -Boolean constants in code have only a few legitimate uses. + +Boolean constants in code have only a few legitimate uses. Other uses (in complex expressions, as conditionals) indicate either an error or, most likely, the persistence of faulty code. ### Recommendation + Verify and simplify the condition. ## Chronicle unchecked price + ### Configuration -* Check: `chronicle-unchecked-price` -* Severity: `Medium` -* Confidence: `Medium` + +- Check: `chronicle-unchecked-price` +- Severity: `Medium` +- Confidence: `Medium` ### Description + Chronicle oracle is used and the price returned is not checked to be valid. For more information https://docs.chroniclelabs.org/Resources/FAQ/Oracles#how-do-i-check-if-an-oracle-becomes-inactive-gets-deprecated. ### Exploit Scenario: @@ -1255,16 +1442,20 @@ contract C { uint256 price = chronicle.read(); } ``` + The `bad` function gets the price from Chronicle by calling the read function however it does not check if the price is valid. ### Recommendation + Validate that the price returned by the oracle is valid. ## Constant functions using assembly code + ### Configuration -* Check: `constant-function-asm` -* Severity: `Medium` -* Confidence: `Medium` + +- Check: `constant-function-asm` +- Severity: `Medium` +- Confidence: `Medium` ### Description @@ -1286,17 +1477,21 @@ contract Constant{ } } ``` -`Constant` was deployed with Solidity 0.4.25. Bob writes a smart contract that interacts with `Constant` in Solidity 0.5.0. + +`Constant` was deployed with Solidity 0.4.25. Bob writes a smart contract that interacts with `Constant` in Solidity 0.5.0. All the calls to `get` revert, breaking Bob's smart contract execution. ### Recommendation + Ensure the attributes of contracts compiled prior to Solidity 0.5.0 are correct. ## Constant functions changing the state + ### Configuration -* Check: `constant-function-state` -* Severity: `Medium` -* Confidence: `Medium` + +- Check: `constant-function-state` +- Severity: `Medium` +- Confidence: `Medium` ### Description @@ -1318,19 +1513,24 @@ contract Constant{ } } ``` -`Constant` was deployed with Solidity 0.4.25. Bob writes a smart contract that interacts with `Constant` in Solidity 0.5.0. + +`Constant` was deployed with Solidity 0.4.25. Bob writes a smart contract that interacts with `Constant` in Solidity 0.5.0. All the calls to `get` revert, breaking Bob's smart contract execution. ### Recommendation + Ensure that attributes of contracts compiled prior to Solidity 0.5.0 are correct. ## Divide before multiply + ### Configuration -* Check: `divide-before-multiply` -* Severity: `Medium` -* Confidence: `Medium` + +- Check: `divide-before-multiply` +- Severity: `Medium` +- Confidence: `Medium` ### Description + Solidity's integer division truncates. Thus, performing division before multiplication can lead to precision loss. ### Exploit Scenario: @@ -1342,20 +1542,25 @@ contract A { } } ``` + If `n` is greater than `oldSupply`, `coins` will be zero. For example, with `oldSupply = 5; n = 10, interest = 2`, coins will be zero. -If `(oldSupply * interest / n)` was used, `coins` would have been `1`. +If `(oldSupply * interest / n)` was used, `coins` would have been `1`. In general, it's usually a good idea to re-arrange arithmetic to perform multiplication before division, unless the limit of a smaller type makes this dangerous. ### Recommendation + Consider ordering multiplication before division. ## Gelato unprotected randomness + ### Configuration -* Check: `gelato-unprotected-randomness` -* Severity: `Medium` -* Confidence: `Medium` + +- Check: `gelato-unprotected-randomness` +- Severity: `Medium` +- Confidence: `Medium` ### Description + Detect calls to `_requestRandomness` within an unprotected function. ### Exploit Scenario: @@ -1375,18 +1580,23 @@ contract C is GelatoVRFConsumerBase { } } ``` + The function `bad` is uprotected and requests randomness. ### Recommendation + Function that request randomness should be allowed only to authorized users. ## Out-of-order retryable transactions + ### Configuration -* Check: `out-of-order-retryable` -* Severity: `Medium` -* Confidence: `Medium` + +- Check: `out-of-order-retryable` +- Severity: `Medium` +- Confidence: `Medium` ### Description + Out-of-order retryable transactions ### Exploit Scenario: @@ -1435,16 +1645,20 @@ contract L2 { } } ``` + Bob calls `doStuffOnL2` but the first retryable ticket calling `claim_rewards` fails. The second retryable ticket calling `unstake` is executed successfully. As a result, Bob loses his rewards. ### Recommendation + Do not rely on the order or successful execution of retryable tickets. ## Reentrancy vulnerabilities + ### Configuration -* Check: `reentrancy-no-eth` -* Severity: `Medium` -* Confidence: `Medium` + +- Check: `reentrancy-no-eth` +- Severity: `Medium` +- Confidence: `Medium` ### Description @@ -1460,20 +1674,23 @@ Do not report reentrancies that involve Ether (see `reentrancy-eth`). throw; } not_called = False; - } + } ``` - ### Recommendation + Apply the [`check-effects-interactions` pattern](http://solidity.readthedocs.io/en/v0.4.21/security-considerations.html#re-entrancy). ## Reused base constructors + ### Configuration -* Check: `reused-constructor` -* Severity: `Medium` -* Confidence: `Medium` + +- Check: `reused-constructor` +- Severity: `Medium` +- Confidence: `Medium` ### Description + Detects if the same base constructor is called with arguments from two different locations in the same inheritance hierarchy. ### Exploit Scenario: @@ -1504,21 +1721,27 @@ contract E is B { constructor() A(1) public { /* ... */ } } ``` + The constructor of `A` is called multiple times in `D` and `E`: + - `D` inherits from `B` and `C`, both of which construct `A`. - `E` only inherits from `B`, but `B` and `E` construct `A`. -. + . ### Recommendation + Remove the duplicate constructor call. ## Dangerous usage of `tx.origin` + ### Configuration -* Check: `tx-origin` -* Severity: `Medium` -* Confidence: `Medium` + +- Check: `tx-origin` +- Severity: `Medium` +- Confidence: `Medium` ### Description + `tx.origin`-based protection can be abused by a malicious contract if a legitimate user interacts with the malicious contract. ### Exploit Scenario: @@ -1531,18 +1754,23 @@ contract TxOrigin { require(tx.origin == owner); } ``` + Bob is the owner of `TxOrigin`. Bob calls Eve's contract. Eve's contract calls `TxOrigin` and bypasses the `tx.origin` protection. ### Recommendation + Do not use `tx.origin` for authorization. ## Unchecked low-level calls + ### Configuration -* Check: `unchecked-lowlevel` -* Severity: `Medium` -* Confidence: `Medium` + +- Check: `unchecked-lowlevel` +- Severity: `Medium` +- Confidence: `Medium` ### Description + The return value of a low-level call is not checked. ### Exploit Scenario: @@ -1554,20 +1782,24 @@ contract MyConc{ } } ``` + The return value of the low-level call is not checked, so if the call fails, the Ether will be locked in the contract. If the low level is used to prevent blocking operations, consider logging failed calls. - ### Recommendation + Ensure that the return value of a low-level call is checked or logged. ## Unchecked Send + ### Configuration -* Check: `unchecked-send` -* Severity: `Medium` -* Confidence: `Medium` + +- Check: `unchecked-send` +- Severity: `Medium` +- Confidence: `Medium` ### Description + The return value of a `send` is not checked. ### Exploit Scenario: @@ -1579,20 +1811,24 @@ contract MyConc{ } } ``` + The return value of `send` is not checked, so if the send fails, the Ether will be locked in the contract. If `send` is used to prevent blocking operations, consider logging the failed `send`. - ### Recommendation + Ensure that the return value of `send` is checked or logged. ## Uninitialized local variables + ### Configuration -* Check: `uninitialized-local` -* Severity: `Medium` -* Confidence: `Medium` + +- Check: `uninitialized-local` +- Severity: `Medium` +- Confidence: `Medium` ### Description + Uninitialized local variables. ### Exploit Scenario: @@ -1605,42 +1841,52 @@ contract Uninitialized is Owner{ } } ``` + Bob calls `transfer`. As a result, all Ether is sent to the address `0x0` and is lost. ### Recommendation + Initialize all the variables. If a variable is meant to be initialized to zero, explicitly set it to zero to improve code readability. ## Unused return + ### Configuration -* Check: `unused-return` -* Severity: `Medium` -* Confidence: `Medium` + +- Check: `unused-return` +- Severity: `Medium` +- Confidence: `Medium` ### Description + The return value of an external call is not stored in a local or state variable. ### Exploit Scenario: ```solidity contract MyConc{ - using SafeMath for uint; + using SafeMath for uint; function my_func(uint a, uint b) public{ a.add(b); } } ``` + `MyConc` calls `add` of `SafeMath`, but does not store the result in `a`. As a result, the computation has no effect. ### Recommendation + Ensure that all the return values of the function calls are used. ## Chainlink Feed Registry usage + ### Configuration -* Check: `chainlink-feed-registry` -* Severity: `Low` -* Confidence: `High` + +- Check: `chainlink-feed-registry` +- Severity: `Low` +- Confidence: `High` ### Description + Detect when Chainlink Feed Registry is used. At the moment is only available on Ethereum Mainnet. ### Exploit Scenario: @@ -1660,21 +1906,25 @@ contract A { // Do price validation return uint256(price); } -} +} ``` -If the contract is deployed on a different chain than Ethereum Mainnet the `getPrice` function will revert. +If the contract is deployed on a different chain than Ethereum Mainnet the `getPrice` function will revert. ### Recommendation + Do not use Chainlink Feed Registry outside of Ethereum Mainnet. ## Incorrect modifier + ### Configuration -* Check: `incorrect-modifier` -* Severity: `Low` -* Confidence: `High` + +- Check: `incorrect-modifier` +- Severity: `Low` +- Confidence: `High` ### Description + If a modifier does not execute `_` or revert, the execution of the function will return the default value, which can be misleading for the caller. ### Exploit Scenario: @@ -1689,18 +1939,23 @@ If a modifier does not execute `_` or revert, the execution of the function will } ``` + If the condition in `myModif` is false, the execution of `get()` will return 0. ### Recommendation + All the paths in a modifier must execute `_` or revert. ## Optimism deprecated predeploy or function + ### Configuration -* Check: `optimism-deprecation` -* Severity: `Low` -* Confidence: `High` + +- Check: `optimism-deprecation` +- Severity: `Low` +- Confidence: `High` ### Description + Detect when deprecated Optimism predeploy or function is used. ### Exploit Scenario: @@ -1714,23 +1969,27 @@ contract Test { GasPriceOracle constant OPT_GAS = GasPriceOracle(0x420000000000000000000000000000000000000F); function a() public { - OPT_GAS.scalar(); + OPT_GAS.scalar(); } } ``` -The call to the `scalar` function of the Optimism GasPriceOracle predeploy always revert. +The call to the `scalar` function of the Optimism GasPriceOracle predeploy always revert. ### Recommendation + Do not use the deprecated components. -## Builtin Symbol Shadowing +## Built-in Symbol Shadowing + ### Configuration -* Check: `shadowing-builtin` -* Severity: `Low` -* Confidence: `High` + +- Check: `shadowing-builtin` +- Severity: `Low` +- Confidence: `High` ### Description + Detection of shadowing built-in symbols using local variables, state variables, functions, modifiers, or events. ### Exploit Scenario: @@ -1750,18 +2009,23 @@ contract Bug { } } ``` + `now` is defined as a state variable, and shadows with the built-in symbol `now`. The function `assert` overshadows the built-in `assert` function. Any use of either of these built-in symbols may lead to unexpected results. ### Recommendation -Rename the local variables, state variables, functions, modifiers, and events that shadow a builtin symbol. + +Rename the local variables, state variables, functions, modifiers, and events that shadow a Built-in symbol. ## Local variable shadowing + ### Configuration -* Check: `shadowing-local` -* Severity: `Low` -* Confidence: `High` + +- Check: `shadowing-local` +- Severity: `Low` +- Confidence: `High` ### Description + Detection of shadowing using local variables. ### Exploit Scenario: @@ -1784,18 +2048,23 @@ contract Bug { } } ``` + `sensitive_function.owner` shadows `Bug.owner`. As a result, the use of `owner` in `sensitive_function` might be incorrect. ### Recommendation + Rename the local variables that shadow another component. ## Uninitialized function pointers in constructors + ### Configuration -* Check: `uninitialized-fptr-cst` -* Severity: `Low` -* Confidence: `High` + +- Check: `uninitialized-fptr-cst` +- Severity: `Low` +- Confidence: `High` ### Description + solc versions `0.4.5`-`0.4.26` and `0.5.0`-`0.5.8` contain a compiler bug leading to unexpected behavior when calling uninitialized function pointers in constructors. ### Exploit Scenario: @@ -1811,18 +2080,23 @@ contract bad0 { } ``` + The call to `a(10)` will lead to unexpected behavior because function pointer `a` is not initialized in the constructor. ### Recommendation + Initialize function pointers before calling. Avoid function pointers if possible. ## Pre-declaration usage of local variables + ### Configuration -* Check: `variable-scope` -* Severity: `Low` -* Confidence: `High` + +- Check: `variable-scope` +- Severity: `Low` +- Confidence: `High` ### Description + Detects the possible usage of a variable before the declaration is stepped over (either because it is later declared, or declared in another scope). ### Exploit Scenario: @@ -1847,19 +2121,24 @@ contract C { } } ``` -In the case above, the variable `x` is used before its declaration, which may result in unintended consequences. + +In the case above, the variable `x` is used before its declaration, which may result in unintended consequences. Additionally, the for-loop uses the variable `max`, which is declared in a previous scope that may not always be reached. This could lead to unintended consequences if the user mistakenly uses a variable prior to any intended declaration assignment. It also may indicate that the user intended to reference a different variable. ### Recommendation + Move all variable declarations prior to any usage of the variable, and ensure that reaching a variable declaration does not depend on some conditional if it is used unconditionally. ## Void constructor + ### Configuration -* Check: `void-cst` -* Severity: `Low` -* Confidence: `High` + +- Check: `void-cst` +- Severity: `Low` +- Confidence: `High` ### Description + Detect the call to a constructor that is not implemented ### Exploit Scenario: @@ -1870,18 +2149,23 @@ contract B is A{ constructor() public A(){} } ``` + When reading `B`'s constructor definition, we might assume that `A()` initiates the contract, but no code is executed. ### Recommendation + Remove the constructor call. ## Calls inside a loop + ### Configuration -* Check: `calls-loop` -* Severity: `Low` -* Confidence: `Medium` + +- Check: `calls-loop` +- Severity: `Low` +- Confidence: `Medium` ### Description + Calls inside a loop might lead to a denial-of-service attack. ### Exploit Scenario: @@ -1903,18 +2187,23 @@ contract CallsInLoop{ } ``` + If one of the destinations has a fallback function that reverts, `bad` will always revert. ### Recommendation + Favor [pull over push](https://github.com/ethereum/wiki/wiki/Safety#favor-pull-over-push-for-external-calls) strategy for external calls. ## Missing events access control + ### Configuration -* Check: `events-access` -* Severity: `Low` -* Confidence: `Medium` + +- Check: `events-access` +- Severity: `Low` +- Confidence: `Medium` ### Description + Detect missing events for critical access control parameters ### Exploit Scenario: @@ -1932,19 +2221,23 @@ contract C { } } ``` -`updateOwner()` has no event, so it is difficult to track off-chain owner changes. +`updateOwner()` has no event, so it is difficult to track off-chain owner changes. ### Recommendation + Emit an event for critical parameter changes. ## Missing events arithmetic + ### Configuration -* Check: `events-maths` -* Severity: `Low` -* Confidence: `Medium` + +- Check: `events-maths` +- Severity: `Low` +- Confidence: `Medium` ### Description + Detect missing events for critical arithmetic parameters. ### Exploit Scenario: @@ -1963,27 +2256,31 @@ contract C { function buy() external { ... // buyPrice is used to determine the number of tokens purchased - } + } } ``` -`setBuyPrice()` does not emit an event, so it is difficult to track changes in the value of `buyPrice` off-chain. +`setBuyPrice()` does not emit an event, so it is difficult to track changes in the value of `buyPrice` off-chain. ### Recommendation + Emit an event for critical parameter changes. ## Dangerous unary expressions + ### Configuration -* Check: `incorrect-unary` -* Severity: `Low` -* Confidence: `Medium` + +- Check: `incorrect-unary` +- Severity: `Low` +- Confidence: `Medium` ### Description + Unary expressions such as `x=+1` probably typos. ### Exploit Scenario: -```Solidity +```Solidity contract Bug{ uint public counter; @@ -1993,18 +2290,23 @@ contract Bug{ } } ``` + `increase()` uses `=+` instead of `+=`, so `counter` will never exceed 1. ### Recommendation + Remove the unary expression. ## Missing zero address validation + ### Configuration -* Check: `missing-zero-check` -* Severity: `Low` -* Confidence: `Medium` + +- Check: `missing-zero-check` +- Severity: `Low` +- Confidence: `Medium` ### Description + Detect missing zero address validation. ### Exploit Scenario: @@ -2022,17 +2324,20 @@ contract C { } } ``` -Bob calls `updateOwner` without specifying the `newOwner`, so Bob loses ownership of the contract. +Bob calls `updateOwner` without specifying the `newOwner`, so Bob loses ownership of the contract. ### Recommendation + Check that the address is not zero. ## Reentrancy vulnerabilities + ### Configuration -* Check: `reentrancy-benign` -* Severity: `Low` -* Confidence: `Medium` + +- Check: `reentrancy-benign` +- Severity: `Low` +- Confidence: `Medium` ### Description @@ -2047,19 +2352,22 @@ Only report reentrancy that acts as a double call (see `reentrancy-eth`, `reentr throw; } counter += 1 - } + } ``` `callme` contains a reentrancy. The reentrancy is benign because it's exploitation would have the same effect as two consecutive calls. ### Recommendation + Apply the [`check-effects-interactions` pattern](http://solidity.readthedocs.io/en/v0.4.21/security-considerations.html#re-entrancy). ## Reentrancy vulnerabilities + ### Configuration -* Check: `reentrancy-events` -* Severity: `Low` -* Confidence: `Medium` + +- Check: `reentrancy-events` +- Severity: `Low` +- Confidence: `Medium` ### Description @@ -2096,19 +2404,23 @@ contract NoReentrancyEvents is Counter { } ``` -If the external call `d.f()` re-enters `BugReentrancyEvents`, the `Counter` events will be incorrect (`Counter(2)`, `Counter(2)`) whereas `NoReentrancyEvents` will correctly emit +If the external call `d.f()` re-enters `BugReentrancyEvents`, the `Counter` events will be incorrect (`Counter(2)`, `Counter(2)`) whereas `NoReentrancyEvents` will correctly emit (`Counter(1)`, `Counter(2)`). This may cause issues for offchain components that rely on the values of events e.g. checking for the amount deposited to a bridge. ### Recommendation + Apply the [`check-effects-interactions` pattern](https://docs.soliditylang.org/en/latest/security-considerations.html#re-entrancy). ## Return Bomb + ### Configuration -* Check: `return-bomb` -* Severity: `Low` -* Confidence: `Medium` + +- Check: `return-bomb` +- Severity: `Low` +- Confidence: `Medium` ### Description + A low level callee may consume all callers gas unexpectedly. ### Exploit Scenario: @@ -2142,47 +2454,60 @@ contract Mark { } ``` -After Mark calls BadGuy bytes are copied from returndata to memory, the memory expansion cost is paid. This means that when using a standard solidity call, the callee can "returnbomb" the caller, imposing an arbitrary gas cost. -Callee unexpectedly makes the caller OOG. +After Mark calls BadGuy bytes are copied from returndata to memory, the memory expansion cost is paid. This means that when using a standard solidity call, the callee can "returnbomb" the caller, imposing an arbitrary gas cost. +Callee unexpectedly makes the caller OOG. ### Recommendation + Avoid unlimited implicit decoding of returndata. ## Block timestamp + ### Configuration -* Check: `timestamp` -* Severity: `Low` -* Confidence: `Medium` + +- Check: `timestamp` +- Severity: `Low` +- Confidence: `Medium` ### Description + Dangerous usage of `block.timestamp`. `block.timestamp` can be manipulated by miners. ### Exploit Scenario: + "Bob's contract relies on `block.timestamp` for its randomness. Eve is a miner and manipulates `block.timestamp` to exploit Bob's contract. ### Recommendation + Avoid relying on `block.timestamp`. ## Assembly usage + ### Configuration -* Check: `assembly` -* Severity: `Informational` -* Confidence: `High` + +- Check: `assembly` +- Severity: `Informational` +- Confidence: `High` ### Description + The use of assembly is error-prone and should be avoided. ### Recommendation + Do not use `evm` assembly. ## Assert state change + ### Configuration -* Check: `assert-state-change` -* Severity: `Informational` -* Confidence: `High` + +- Check: `assert-state-change` +- Severity: `Informational` +- Confidence: `High` ### Description + Incorrect use of `assert()`. See Solidity best [practices](https://solidity.readthedocs.io/en/latest/control-structures.html#id4). ### Exploit Scenario: @@ -2197,19 +2522,23 @@ contract A { } } ``` -The assert in `bad()` increments the state variable `s_a` while checking for the condition. +The assert in `bad()` increments the state variable `s_a` while checking for the condition. ### Recommendation + Use `require` for invariants modifying the state. ## Boolean equality + ### Configuration -* Check: `boolean-equal` -* Severity: `Informational` -* Confidence: `High` + +- Check: `boolean-equal` +- Severity: `Informational` +- Confidence: `High` ### Description + Detects the comparison to boolean constants. ### Exploit Scenario: @@ -2225,30 +2554,39 @@ contract A { } } ``` + Boolean constants can be used directly and do not need to be compare to `true` or `false`. ### Recommendation + Remove the equality to the boolean constant. ## Cyclomatic complexity + ### Configuration -* Check: `cyclomatic-complexity` -* Severity: `Informational` -* Confidence: `High` + +- Check: `cyclomatic-complexity` +- Severity: `Informational` +- Confidence: `High` ### Description + Detects functions with high (> 11) cyclomatic complexity. ### Recommendation + Reduce cyclomatic complexity by splitting the function into several smaller subroutines. ## Deprecated standards + ### Configuration -* Check: `deprecated-standards` -* Severity: `Informational` -* Confidence: `High` + +- Check: `deprecated-standards` +- Severity: `Informational` +- Confidence: `High` ### Description + Detect the usage of deprecated standards. ### Exploit Scenario: @@ -2282,15 +2620,19 @@ contract ContractWithDeprecatedReferences { ``` ### Recommendation + Replace all uses of deprecated symbols. ## Unindexed ERC20 event parameters + ### Configuration -* Check: `erc20-indexed` -* Severity: `Informational` -* Confidence: `High` + +- Check: `erc20-indexed` +- Severity: `Informational` +- Confidence: `High` ### Description + Detects whether events defined by the `ERC20` specification that should have some parameters as `indexed` are missing the `indexed` keyword. ### Exploit Scenario: @@ -2304,19 +2646,24 @@ contract ERC20Bad { // ... } ``` + `Transfer` and `Approval` events should have the 'indexed' keyword on their two first parameters, as defined by the `ERC20` specification. Failure to include these keywords will exclude the parameter data in the transaction/block's bloom filter, so external tooling searching for these parameters may overlook them and fail to index logs from this token contract. ### Recommendation + Add the `indexed` keyword to event parameters that should include it, according to the `ERC20` specification. ## Function Initializing State + ### Configuration -* Check: `function-init-state` -* Severity: `Informational` -* Confidence: `High` + +- Check: `function-init-state` +- Severity: `Informational` +- Confidence: `High` ### Description + Detects the immediate initialization of state variables through function calls that are not pure/constant, or that use non-constant state variable. ### Exploit Scenario: @@ -2344,22 +2691,26 @@ contract StateVarInitFromFunction { } } ``` -In this case, users might intend a function to return a value a state variable can initialize with, without realizing the context for the contract is not fully initialized. -In the example above, the same function sets two different values for state variables because it checks a state variable that is not yet initialized in one case, and is initialized in the other. -Special care must be taken when initializing state variables from an immediate function call so as not to incorrectly assume the state is initialized. +In this case, users might intend a function to return a value a state variable can initialize with, without realizing the context for the contract is not fully initialized. +In the example above, the same function sets two different values for state variables because it checks a state variable that is not yet initialized in one case, and is initialized in the other. +Special care must be taken when initializing state variables from an immediate function call so as not to incorrectly assume the state is initialized. ### Recommendation + Remove any initialization of state variables via non-constant state variables or function calls. If variables must be set upon contract deployment, locate initialization in the constructor instead. ## Incorrect usage of using-for statement + ### Configuration -* Check: `incorrect-using-for` -* Severity: `Informational` -* Confidence: `High` + +- Check: `incorrect-using-for` +- Severity: `Informational` +- Confidence: `High` ### Description -In Solidity, it is possible to use libraries for certain types, by the `using-for` statement (`using for `). However, the Solidity compiler doesn't check whether a given library has at least one function matching a given type. If it doesn't, such a statement has no effect and may be confusing. + +In Solidity, it is possible to use libraries for certain types, by the `using-for` statement (`using for `). However, the Solidity compiler doesn't check whether a given library has at least one function matching a given type. If it doesn't, such a statement has no effect and may be confusing. ### Exploit Scenario: @@ -2367,33 +2718,41 @@ In Solidity, it is possible to use libraries for certain types, by the `using-fo library L { function f(bool) public pure {} } - + using L for uint; ``` Such a code will compile despite the fact that `L` has no function with `uint` as its first argument. ### Recommendation -Make sure that the libraries used in `using-for` statements have at least one function matching a type used in these statements. + +Make sure that the libraries used in `using-for` statements have at least one function matching a type used in these statements. ## Low-level calls + ### Configuration -* Check: `low-level-calls` -* Severity: `Informational` -* Confidence: `High` + +- Check: `low-level-calls` +- Severity: `Informational` +- Confidence: `High` ### Description + The use of low-level calls is error-prone. Low-level calls do not check for [code existence](https://solidity.readthedocs.io/en/v0.4.25/control-structures.html#error-handling-assert-require-revert-and-exceptions) or call success. ### Recommendation + Avoid low-level calls. Check the call success. If the call is meant for a contract, check for code existence. ## Missing inheritance + ### Configuration -* Check: `missing-inheritance` -* Severity: `Informational` -* Confidence: `High` + +- Check: `missing-inheritance` +- Severity: `Informational` +- Confidence: `High` ### Description + Detect missing inheritance. ### Exploit Scenario: @@ -2409,47 +2768,60 @@ contract Something { } } ``` -`Something` should inherit from `ISomething`. +`Something` should inherit from `ISomething`. ### Recommendation + Inherit from the missing interface or contract. ## Conformance to Solidity naming conventions + ### Configuration -* Check: `naming-convention` -* Severity: `Informational` -* Confidence: `High` + +- Check: `naming-convention` +- Severity: `Informational` +- Confidence: `High` ### Description Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.25/style-guide.html#naming-conventions) that should be followed. + #### Rule exceptions + - Allow constant variable name/symbol/decimals to be lowercase (`ERC20`). - Allow `_` at the beginning of the `mixed_case` match for private variables and unused parameters. ### Recommendation + Follow the Solidity [naming convention](https://solidity.readthedocs.io/en/v0.4.25/style-guide.html#naming-conventions). ## Different pragma directives are used + ### Configuration -* Check: `pragma` -* Severity: `Informational` -* Confidence: `High` + +- Check: `pragma` +- Severity: `Informational` +- Confidence: `High` ### Description + Detect whether different Solidity versions are used. ### Recommendation + Use one Solidity version. ## Redundant Statements + ### Configuration -* Check: `redundant-statements` -* Severity: `Informational` -* Confidence: `High` + +- Check: `redundant-statements` +- Severity: `Informational` +- Confidence: `High` ### Description + Detect the usage of redundant statements that have no effect. ### Exploit Scenario: @@ -2471,16 +2843,20 @@ contract RedundantStatementsContract { } } ``` + Each commented line references types/identifiers, but performs no action with them, so no code will be generated for such statements and they can be removed. ### Recommendation + Remove redundant statements if they congest code but offer no value. ## Incorrect versions of Solidity + ### Configuration -* Check: `solc-version` -* Severity: `Informational` -* Confidence: `High` + +- Check: `solc-version` +- Severity: `Informational` +- Confidence: `High` ### Description @@ -2495,12 +2871,15 @@ Use a simple pragma version that allows any of these versions. Consider using the latest version of Solidity for testing. ## Unimplemented functions + ### Configuration -* Check: `unimplemented-functions` -* Severity: `Informational` -* Confidence: `High` + +- Check: `unimplemented-functions` +- Severity: `Informational` +- Confidence: `High` ### Description + Detect functions that are not implemented on derived-most contracts. ### Exploit Scenario: @@ -2521,32 +2900,41 @@ contract DerivedContract is BaseInterface, BaseInterface2 { } } ``` + `DerivedContract` does not implement `BaseInterface.f2` or `BaseInterface2.f3`. -As a result, the contract will not properly compile. +As a result, the contract will not properly compile. All unimplemented functions must be implemented on a contract that is meant to be used. ### Recommendation + Implement all unimplemented functions in any contract you intend to use directly (not simply inherit from). ## Unused state variable + ### Configuration -* Check: `unused-state` -* Severity: `Informational` -* Confidence: `High` + +- Check: `unused-state` +- Severity: `Informational` +- Confidence: `High` ### Description + Unused state variable. ### Recommendation + Remove unused state variables. ## Costly operations inside a loop + ### Configuration -* Check: `costly-loop` -* Severity: `Informational` -* Confidence: `Medium` + +- Check: `costly-loop` +- Severity: `Informational` +- Confidence: `Medium` ### Description + Costly operations inside a loop might waste gas, so optimizations are justified. ### Exploit Scenario: @@ -2572,18 +2960,23 @@ contract CostlyOperationsInLoop{ } } ``` + Incrementing `state_variable` in a loop incurs a lot of gas because of expensive `SSTOREs`, which might lead to an `out-of-gas`. ### Recommendation + Use a local variable to hold the loop computation result. ## Dead-code + ### Configuration -* Check: `dead-code` -* Severity: `Informational` -* Confidence: `Medium` + +- Check: `dead-code` +- Severity: `Informational` +- Confidence: `Medium` ### Description + Functions that are not used. ### Exploit Scenario: @@ -2593,16 +2986,20 @@ contract Contract{ function dead_code() internal() {} } ``` + `dead_code` is not used in the contract, and make the code's review more difficult. ### Recommendation + Remove unused functions. ## Reentrancy vulnerabilities + ### Configuration -* Check: `reentrancy-unlimited-gas` -* Severity: `Informational` -* Confidence: `Medium` + +- Check: `reentrancy-unlimited-gas` +- Severity: `Informational` +- Confidence: `Medium` ### Description @@ -2615,52 +3012,56 @@ Only report reentrancy that is based on `transfer` or `send`. function callme(){ msg.sender.transfer(balances[msg.sender]): balances[msg.sender] = 0; - } + } ``` `send` and `transfer` do not protect from reentrancies in case of gas price changes. ### Recommendation + Apply the [`check-effects-interactions` pattern](http://solidity.readthedocs.io/en/v0.4.21/security-considerations.html#re-entrancy). ## Too many digits + ### Configuration -* Check: `too-many-digits` -* Severity: `Informational` -* Confidence: `Medium` + +- Check: `too-many-digits` +- Severity: `Informational` +- Confidence: `Medium` ### Description Literals with many digits are difficult to read and review. Use scientific notation or suffixes to make the code more readable. - ### Exploit Scenario: ```solidity contract MyContract{ - uint 1_ether = 10000000000000000000; + uint 1_ether = 10000000000000000000; } ``` While `1_ether` looks like `1 ether`, it is `10 ether`. As a result, it's likely to be used incorrectly. - ### Recommendation Use: + - [Ether suffix](https://solidity.readthedocs.io/en/latest/units-and-global-variables.html#ether-units), - [Time suffix](https://solidity.readthedocs.io/en/latest/units-and-global-variables.html#time-units), or - [The scientific notation](https://solidity.readthedocs.io/en/latest/types.html#rational-and-integer-literals) - ## Cache array length + ### Configuration -* Check: `cache-array-length` -* Severity: `Optimization` -* Confidence: `High` + +- Check: `cache-array-length` +- Severity: `Optimization` +- Confidence: `High` ### Description -Detects `for` loops that use `length` member of some storage array in their loop condition and don't modify it. + +Detects `for` loops that use `length` member of some storage array in their loop condition and don't modify it. ### Exploit Scenario: @@ -2668,8 +3069,8 @@ Detects `for` loops that use `length` member of some storage array in their loop contract C { uint[] array; - - function f() public + + function f() public { for (uint i = 0; i < array.length; i++) { @@ -2678,14 +3079,15 @@ contract C } } ``` + Since the `for` loop in `f` doesn't modify `array.length`, it is more gas efficient to cache it in some local variable and use that variable instead, like in the following example: ```solidity contract C { uint[] array; - - function f() public + + function f() public { uint array_length = array.length; for (uint i = 0; i < array_length; i++) @@ -2695,54 +3097,69 @@ contract C } } ``` - ### Recommendation + Cache the lengths of storage arrays if they are used and not modified in `for` loops. ## State variables that could be declared constant + ### Configuration -* Check: `constable-states` -* Severity: `Optimization` -* Confidence: `High` + +- Check: `constable-states` +- Severity: `Optimization` +- Confidence: `High` ### Description + State variables that are not updated following deployment should be declared constant to save gas. ### Recommendation + Add the `constant` attribute to state variables that never change. ## Public function that could be declared external + ### Configuration -* Check: `external-function` -* Severity: `Optimization` -* Confidence: `High` + +- Check: `external-function` +- Severity: `Optimization` +- Confidence: `High` ### Description + `public` functions that are never called by the contract should be declared `external`, and its immutable parameters should be located in `calldata` to save gas. ### Recommendation + Use the `external` attribute for functions never called from the contract, and change the location of immutable parameters to `calldata` to save gas. ## State variables that could be declared immutable + ### Configuration -* Check: `immutable-states` -* Severity: `Optimization` -* Confidence: `High` + +- Check: `immutable-states` +- Severity: `Optimization` +- Confidence: `High` ### Description + State variables that are not updated following deployment should be declared immutable to save gas. ### Recommendation + Add the `immutable` attribute to state variables that never change or are set only in the constructor. ## Public variable read in external context + ### Configuration -* Check: `var-read-using-this` -* Severity: `Optimization` -* Confidence: `High` + +- Check: `var-read-using-this` +- Severity: `Optimization` +- Confidence: `High` ### Description + The contract reads its own variable using `this`, adding overhead of an unnecessary STATICCALL. ### Exploit Scenario: @@ -2756,6 +3173,6 @@ contract C { } ``` - ### Recommendation + Read the variable directly from storage instead of calling the contract. diff --git a/docs/src/printers/Printer-documentation.md b/docs/src/printers/Printer-documentation.md index a5763793f2..b2b59d5ab2 100644 --- a/docs/src/printers/Printer-documentation.md +++ b/docs/src/printers/Printer-documentation.md @@ -1,64 +1,70 @@ Slither allows printing contracts information through its printers. -Num | Printer | Description ---- | --- | --- -1 | `call-graph` | [Export the call-graph of the contracts to a dot file](#call-graph) -2 | `cfg` | [Export the CFG of each functions](#cfg) -3 | `cheatcode` | Print the usage of (Foundry) cheatcodes in the code. -4 | `ck` | Chidamber and Kemerer (CK) complexity metrics and related function attributes -5 | `constructor-calls` | [Print the constructors executed](#constructor-calls) -6 | `contract-summary` | [Print a summary of the contracts](#contract-summary) -7 | `data-dependency` | [Print the data dependencies of the variables](#data-dependencies) -8 | `declaration` | Prototype showing the source code declaration, implementation and references of the contracts objects -9 | `dominator` | Export the dominator tree of each functions -10 | `echidna` | Export Echidna guiding information -11 | `entry-points` | Print all the state-changing entry point functions of the contracts -12 | `evm` | [Print the evm instructions of nodes in functions](#evm) -13 | `function-id` | [Print the keccak256 signature of the functions](#function-id) -14 | `function-summary` | [Print a summary of the functions](#function-summary) -15 | `halstead` | Computes the Halstead complexity metrics for each contract -16 | `human-summary` | [Print a human-readable summary of the contracts](#human-summary) -17 | `inheritance` | [Print the inheritance relations between contracts](#inheritance) -18 | `inheritance-graph` | [Export the inheritance graph of each contract to a dot file](#inheritance-graph) -19 | `loc` | Count the total number lines of code (LOC), source lines of code (SLOC) -20 | `martin` | Martin agile software metrics (Ca, Ce, I, A, D) -21 | `modifiers` | Print the modifiers called by each function -22 | `not-pausable` | Print functions that do not use whenNotPaused -23 | `require` | [Print the require and assert calls of each function](#require) -24 | `slithir` | [Print the slithIR representation of the functions](#slithir) -25 | `slithir-ssa` | [Print the slithIR representation of the functions](#slithir-ssa) -26 | `variable-order` | [Print the storage order of the state variables](#variable-order) -27 | `vars-and-auth` | [Print the state variables written and the authorization of the functions](#variables-written-and-authorization) - - +| Num | Printer | Description | +| --- | ------------------- | ---------------------------------------------------------------------------------------------------------------- | +| 1 | `call-graph` | [Export the call-graph of the contracts to a dot file](#call-graph) | +| 2 | `cfg` | [Export the CFG of each functions](#cfg) | +| 3 | `cheatcode` | Print the usage of (Foundry) cheatcodes in the code. | +| 4 | `ck` | Chidamber and Kemerer (CK) complexity metrics and related function attributes | +| 5 | `constructor-calls` | [Print the constructors executed](#constructor-calls) | +| 6 | `contract-summary` | [Print a summary of the contracts](#contract-summary) | +| 7 | `data-dependency` | [Print the data dependencies of the variables](#data-dependencies) | +| 8 | `declaration` | Prototype showing the source code declaration, implementation and references of the contracts objects | +| 9 | `dominator` | Export the dominator tree of each functions | +| 10 | `echidna` | Export Echidna guiding information | +| 11 | `entry-points` | Print all the state-changing entry point functions of the contracts | +| 12 | `evm` | [Print the evm instructions of nodes in functions](#evm) | +| 13 | `function-id` | [Print the keccak256 signature of the functions](#function-id) | +| 14 | `function-summary` | [Print a summary of the functions](#function-summary) | +| 15 | `halstead` | Computes the Halstead complexity metrics for each contract | +| 16 | `human-summary` | [Print a human-readable summary of the contracts](#human-summary) | +| 17 | `inheritance` | [Print the inheritance relations between contracts](#inheritance) | +| 18 | `inheritance-graph` | [Export the inheritance graph of each contract to a dot file](#inheritance-graph) | +| 19 | `loc` | Count the total number lines of code (LOC), source lines of code (SLOC) | +| 20 | `martin` | Martin agile software metrics (Ca, Ce, I, A, D) | +| 21 | `modifiers` | Print the modifiers called by each function | +| 22 | `not-pausable` | Print functions that do not use whenNotPaused | +| 23 | `require` | [Print the require and assert calls of each function](#require) | +| 24 | `slithir` | [Print the slithIR representation of the functions](#slithir) | +| 25 | `slithir-ssa` | [Print the slithIR representation of the functions](#slithir-ssa) | +| 26 | `variable-order` | [Print the storage order of the state variables](#variable-order) | +| 27 | `vars-and-auth` | [Print the state variables written and the authorization of the functions](#variables-written-and-authorization) | Several printers require xdot installed for visualization: + ``` sudo apt install xdot ``` ## Call Graph + `slither file.sol --print call-graph` Export the call-graph of the contracts to a dot file + ### Example + ``` $ slither examples/printers/call_graph.sol --print call-graph ``` + The output format is [dot](https://www.graphviz.org/). To vizualize the graph: + ``` $ xdot examples/printers/call_graph.sol.dot ``` + To convert the file to svg: + ``` $ dot examples/printers/call_graph.sol.dot -Tpng -o examples/printers/call_graph.sol.png ``` - ## CFG + Export the control flow graph of each function `slither file.sol --print cfg` @@ -67,15 +73,17 @@ Export the control flow graph of each function The output format is [dot](https://www.graphviz.org/). To vizualize the graph: + ``` $ xdot function.sol.dot ``` + To convert the file to svg: + ``` $ dot function.dot -Tsvg -o function.sol.png ``` - ## Contract Summary Output a quick summary of the contract. @@ -83,22 +91,24 @@ Output a quick summary of the contract. `slither file.sol --print contract-summary` ### Example + ``` $ slither examples/printers/quick_summary.sol --print contract-summary ``` - ## Data Dependencies Print the data dependencies of the variables `slither file.sol --print data-dependency` ### Example + ``` $ slither examples/printers/data_dependencies.sol --print data-dependency ``` + ``` Contract MyContract +----------+----------------------+ @@ -131,11 +141,13 @@ Function setB(uint256) ``` ## Constructor Calls + `slither file.sol --print constructor-calls` Print the calling sequence of constructors based on C3 linearization. ### Example + ``` ... $ slither examples/printers/constructors.sol --print constructor-calls @@ -178,17 +190,18 @@ Contact Name: test3 } ``` - ## Echidna This printer is meant to improve [Echidna](https://github.com/crytic/echidna) code coverage. The printer is a WIP and is not yet used by Echidna. ## EVM + `slither file.sol --print evm` Print the EVM representation of the functions ### Example + ``` $ slither examples/printers/evm.sol --print evm @@ -247,10 +260,12 @@ INFO:Printers:Contract Test ``` ## Function id + `slither file.sol --print function-id` Print the keccack256 signature of the functions ### Examples + ``` $ slither examples/printers/authorization.sol --print function-id INFO:Printers: @@ -264,24 +279,28 @@ MyContract: ``` ## Function Summary + `slither file.sol --print function-summary` Output a summary of the contract showing for each function: -- What are the visibility and the modifiers + +- What are the visibility and the modifiers - What are the state variables read or written - What are the calls ### Example + ``` $ slither tests/backdoor.sol --print function-summary ``` + ``` [...] Contract C Contract vars: [] Inheritances:: [] - + +-----------------+------------+-----------+----------------+-------+---------------------------+----------------+ | Function | Visibility | Modifiers | Read | Write | Internal Calls | External Calls | +-----------------+------------+-----------+----------------+-------+---------------------------+----------------+ @@ -295,11 +314,13 @@ Inheritances:: [] ``` ## Human Summary + `slither file.sol --print human-summary` Print a human-readable summary of the contracts ### Example + ``` $ slither examples/printers/human_printer.sol --print human-summary ``` @@ -307,10 +328,12 @@ $ slither examples/printers/human_printer.sol --print human-summary ## Inheritance + `slither file.sol --print inheritance` Print the inheritance relations between contracts ### Example + ``` $ slither examples/printers/inheritances.sol --print inheritance ``` @@ -318,13 +341,13 @@ $ slither examples/printers/inheritances.sol --print inheritance ## Inheritance Graph + `slither file.sol --print inheritance-graph` Output a graph showing the inheritance interaction between the contracts. - - ### Example + ``` $ slither examples/printers/inheritances.sol --print inheritance-graph [...] @@ -333,29 +356,35 @@ INFO:PrinterInheritance:Inheritance Graph: examples/DAO.sol.dot The output format is [dot](https://www.graphviz.org/). To vizualize the graph: + ``` $ xdot examples/printers/inheritances.sol.dot ``` + To convert the file to svg: + ``` $ dot examples/printers/inheritances.sol.dot -Tsvg -o examples/printers/inheritances.sol.png ``` + Indicators: + - If a contract has multiple inheritance, the connecting edges will be labelled in order of declaration. - Functions highlighted orange override a parent's function. - Functions which do not override each other directly (but collide due to multiple inheritance) will be emphasized at the bottom of the affected contract node in grey font. - Variables highlighted red overshadow a parent's variable declaration. - Variables of type `contract` specify the contract name in parentheses in a blue font. - ## Modifiers + `slither file.sol --print modifiers` Print the modifiers called by each function. ### Example + ``` $ slither examples/printers/modifier.sol --print modifiers INFO:Printers: @@ -369,11 +398,13 @@ Contract C ``` ## Require + `slither file.sol --print require` Print the require and assert calls of each function. ### Example + ``` $ slither examples/printers/require.sol --print require INFO:Printers: @@ -393,14 +424,14 @@ Contract C +-------------+--------------------------------------+ ``` - - ## SlithIR + `slither file.sol --print slithir` Print the slithIR representation of the functions ### Example + ``` $ slither examples/printers/slihtir.sol --print slithir Contract UnsafeMath @@ -420,27 +451,30 @@ Contract MyContract IRs: REF_3(uint256) -> balances[msg.sender] REF_1(uint256) -> balances[msg.sender] - TMP_1(uint256) = LIBRARY_CALL, dest:UnsafeMath, function:min, arguments:['REF_1', 'val'] + TMP_1(uint256) = LIBRARY_CALL, dest:UnsafeMath, function:min, arguments:['REF_1', 'val'] REF_3 := TMP_1 Expression: balances[to] = balances[to].add(val) IRs: REF_3(uint256) -> balances[to] REF_1(uint256) -> balances[to] - TMP_1(uint256) = LIBRARY_CALL, dest:UnsafeMath, function:add, arguments:['REF_1', 'val'] + TMP_1(uint256) = LIBRARY_CALL, dest:UnsafeMath, function:add, arguments:['REF_1', 'val'] REF_3 := TMP_1 ``` ## SlithIR-SSA + `slither file.sol --print slithir-ssa` Print the slithIR representation of the functions (SSA version) ## Variable order + `slither file.sol --print variable-order` Print the storage order of the state variables ### Example + ``` $ slither tests/check-upgradability/contractV2_bug.sol --print variable-order INFO:Printers: @@ -454,12 +488,14 @@ ContractV2: ``` - ## Variables written and authorization + `slither file.sol --print vars-and-auth` Print the variables written and the check on `msg.sender` of each function. + ### Example + ``` ... $ slither examples/printers/authorization.sol --print vars-and-auth @@ -472,4 +508,4 @@ Contract MyContract | constructor | ['owner'] | [] | | mint | ['balances'] | ['require(bool)(msg.sender == owner)'] | +-------------+-------------------------+----------------------------------------+ -``` \ No newline at end of file +``` diff --git a/docs/src/tools/Adding-a-new-utility.md b/docs/src/tools/Adding-a-new-utility.md index 8008964707..08a1ccfa08 100644 --- a/docs/src/tools/Adding-a-new-utility.md +++ b/docs/src/tools/Adding-a-new-utility.md @@ -2,6 +2,7 @@ Slither can be used as a library to create new utilities. Official utils are present in [tools](https://github.com/crytic/slither/tree/master/slither/tools) ## Skeleton + The skeleton util is present in [tools/demo](https://github.com/crytic/slither/tree/master/slither/tools/demo) ## Integration @@ -16,4 +17,5 @@ Installing Slither will then install the util. - Add unit-tests (ex: [scripts/travis_test_find_paths.sh](https://github.com/crytic/slither/blob/master/scripts/ci_test_find_paths.sh)) ## Getting Help + Join our [slack channel](https://empireslacking.herokuapp.com/) to get any help (#ethereum). diff --git a/docs/src/tools/Code-Similarity-Detector.md b/docs/src/tools/Code-Similarity-Detector.md index 3506f95903..7446118cf0 100644 --- a/docs/src/tools/Code-Similarity-Detector.md +++ b/docs/src/tools/Code-Similarity-Detector.md @@ -2,11 +2,11 @@ `slither-simil` uses [FastText](https://github.com/facebookresearch/fastText), a vector embedding technique, to generate compact numerical representations of every function. We used FastText because it: -* implements several state-of-the-art techniques such as nbow and skipgrams, -* has high performance (it is C++ code with Python bindings), -* has MIT license, and -* is well maintained (by Facebook). - +- implements several state-of-the-art techniques such as nbow and skipgrams, +- has high performance (it is C++ code with Python bindings), +- has MIT license, and +- is well maintained (by Facebook). + ## Requirements Install the required packages before using `slither-simil`: @@ -23,11 +23,12 @@ Make sure that you are using `pip3.6` or later. If you are running from inside a Note that these examples will use the following files: -* [etherscan_verified_contracts.bin](https://drive.google.com/file/d/1oEhbIL4V9582Y5VKp4iiOURGq8qa4cBN/view?usp=sharing) -* [cache.npz](https://drive.google.com/file/d/1vpwusbyzLn1JqqAvlFivHXtLvsEp0VqX/view?usp=sharing) -* [MetaCoin.sol](https://github.com/crytic/slither/wiki/MetacoinExample) +- [etherscan_verified_contracts.bin](https://drive.google.com/file/d/1oEhbIL4V9582Y5VKp4iiOURGq8qa4cBN/view?usp=sharing) +- [cache.npz](https://drive.google.com/file/d/1vpwusbyzLn1JqqAvlFivHXtLvsEp0VqX/view?usp=sharing) +- [MetaCoin.sol](https://github.com/crytic/slither/wiki/MetacoinExample) `slither-simil` has three modes: + - `test` - finds similar functions to your own in a dataset of contracts - `plot` - provide a visual representation of similarity of multiple sampled functions - `train` - builds new models of large datasets of contracts @@ -37,45 +38,46 @@ Note that these examples will use the following files: This mode transforms a function into a vector and uses it to find similar functions. -Test mode requires the following parameters: +Test mode requires the following parameters: + 1. A pre-trained model: this file will be used to transform every function into a vector, you can [train your own](#train-mode) or [use our pre-trained one (etherscan_verified_contracts.bin)](#usage). 2. A contract filename: this file will contain the code that you want to compare, -3. A function name (e.g. `SafeMath.add` or `add`), +3. A function name (e.g. `SafeMath.add` or `add`), 4. An input directory or file: this can be either a directory with contracts or a [cache file with a pre-computed list of vectors for every contract (cache.npz)](#usage). -Use the cache to avoid long processing times to compile and vectorize the input contracts. +Use the cache to avoid long processing times to compile and vectorize the input contracts. Here's an example that finds functions similar to `sendCoin` in `MetaCoin` (compiled with `solc-0.4.25`). Searching for similar functions among more than 800,000 functions takes only 20 seconds. ``` $ slither-simil test etherscan_verified_contracts.bin --filename MetaCoin.sol --fname MetaCoin.sendCoin --input cache.npz --ntop 25 --solc solc-0.4.25 INFO:Slither-simil:Reviewed 825062 functions, listing the 25 most similar ones: -INFO:Slither-simil:filename contract function score -INFO:Slither-simil:0x954b5de09a55e59755acbda29e1eb74a45d30175_Fluz.sol Fluz transfer 1.0 -INFO:Slither-simil:0x55648de19836338549130b1af587f16bea46f66b_Pebbles.sol Pebbles transfer 1.0 -INFO:Slither-simil:0x3fcee23add6e86dde3c4d395cbce1cae7f16d06d_SnipCoin.sol SnipCoin sendCoin 1.0 -INFO:Slither-simil:0x000000005fbe2cc9b1b684ec445caf176042348e_ProperProposal.sol Vote transfer 1.0 -INFO:Slither-simil:0x000000002bb43c83ece652d161ad0fa862129a2c_AccountRegistry.sol Vote transfer 1.0 -INFO:Slither-simil:0x4e84e9e5fb0a972628cf4568c403167ef1d40431_Fluzcoin.sol Fluzcoin transfer 1.0 -INFO:Slither-simil:0x334eec1482109bd802d9e72a447848de3bcc1063_AirDropToken.sol AirDropToken transfer 1.0 -INFO:Slither-simil:0x28ccdda197d319a241005b9c9f01bac48b90f556_AirDropToken.sol AirDropToken transfer 1.0 -INFO:Slither-simil:0x000000002647e16d9bab9e46604d75591d289277_Vote.sol Vote transfer 1.0 -INFO:Slither-simil:0xc6c4c7826D44ABF22c711E8E86bDC3f5242d2182_token.sol token sendCoin 1.0 -INFO:Slither-simil:0x22033df1d104736ff4c2b23a28affe52863ca9c8_AtmOnlyFluzcoin.sol AtmOnlyFluzcoin transfer 1.0 -INFO:Slither-simil:0xcad796d6a2c0bb1de7f24262819be96fb08c1c3a_Love.sol Love transfer 1.0 -INFO:Slither-simil:0x6cb2b8dc6a508c9a21db9683d1a729715969a6ee_TokenEscrow.sol TokenEscrow transferFromOwner 0.996 -INFO:Slither-simil:0xd75fefe3cdb647281eec3f8fc738e3bc9658f9e4_ProofOfReadToken.sol ProofOfReadToken transfer 0.996 -INFO:Slither-simil:0x7A8Ef7E8c8f16B9D6F39069ce03d752Af23b46d6_OBS_V1.sol MyObs transfer 0.996 -INFO:Slither-simil:0x5ac0197c944c961f58bb02f3d0df58a74fdc15b6_TokenEscrow.sol TokenEscrow transferFromOwner 0.996 -INFO:Slither-simil:0x69719c8c207036bdfc3632ccc24b290fb7240f4a_BitPayToken.sol BitPayToken transfer 0.996 -INFO:Slither-simil:0x3d8a10ce3228cb428cb56baa058d4432464ea25d_TestToken.sol TestToken transfer 0.993 -INFO:Slither-simil:0x69719c8c207036bdfc3632ccc24b290fb7240f4a_BitPayToken.sol BitPayToken transferFrom 0.992 -INFO:Slither-simil:0x486e1f44b2a85150a6dd2de5aab87df375cd8880_CAIRToken.sol StandardToken transfer 0.991 -INFO:Slither-simil:0x2bec16b164725efc192b7ec0296f838c61317514_eda.sol StandardToken transfer 0.991 -INFO:Slither-simil:0xb7cb1c96db6b22b0d3d9536e0108d062bd488f74_WaltonToken.sol StandardToken transfer 0.991 -INFO:Slither-simil:0x346c3be6aebEBaF5Cb766a75aDc9827EfbB7E41A_DelphiToken.sol StandardToken transfer 0.991 -INFO:Slither-simil:0xf5068761511594c82328102f4fde4650ed9ea6c4_WHP.sol WHP transfer 0.991 -INFO:Slither-simil:0x5f9f2ae7150d0beef3bb50ac8d8f4b43e6a6cc57_NABC.sol NABC transfer 0.991 +INFO:Slither-simil:filename contract function score +INFO:Slither-simil:0x954b5de09a55e59755acbda29e1eb74a45d30175_Fluz.sol Fluz transfer 1.0 +INFO:Slither-simil:0x55648de19836338549130b1af587f16bea46f66b_Pebbles.sol Pebbles transfer 1.0 +INFO:Slither-simil:0x3fcee23add6e86dde3c4d395cbce1cae7f16d06d_SnipCoin.sol SnipCoin sendCoin 1.0 +INFO:Slither-simil:0x000000005fbe2cc9b1b684ec445caf176042348e_ProperProposal.sol Vote transfer 1.0 +INFO:Slither-simil:0x000000002bb43c83ece652d161ad0fa862129a2c_AccountRegistry.sol Vote transfer 1.0 +INFO:Slither-simil:0x4e84e9e5fb0a972628cf4568c403167ef1d40431_Fluzcoin.sol Fluzcoin transfer 1.0 +INFO:Slither-simil:0x334eec1482109bd802d9e72a447848de3bcc1063_AirDropToken.sol AirDropToken transfer 1.0 +INFO:Slither-simil:0x28ccdda197d319a241005b9c9f01bac48b90f556_AirDropToken.sol AirDropToken transfer 1.0 +INFO:Slither-simil:0x000000002647e16d9bab9e46604d75591d289277_Vote.sol Vote transfer 1.0 +INFO:Slither-simil:0xc6c4c7826D44ABF22c711E8E86bDC3f5242d2182_token.sol token sendCoin 1.0 +INFO:Slither-simil:0x22033df1d104736ff4c2b23a28affe52863ca9c8_AtmOnlyFluzcoin.sol AtmOnlyFluzcoin transfer 1.0 +INFO:Slither-simil:0xcad796d6a2c0bb1de7f24262819be96fb08c1c3a_Love.sol Love transfer 1.0 +INFO:Slither-simil:0x6cb2b8dc6a508c9a21db9683d1a729715969a6ee_TokenEscrow.sol TokenEscrow transferFromOwner 0.996 +INFO:Slither-simil:0xd75fefe3cdb647281eec3f8fc738e3bc9658f9e4_ProofOfReadToken.sol ProofOfReadToken transfer 0.996 +INFO:Slither-simil:0x7A8Ef7E8c8f16B9D6F39069ce03d752Af23b46d6_OBS_V1.sol MyObs transfer 0.996 +INFO:Slither-simil:0x5ac0197c944c961f58bb02f3d0df58a74fdc15b6_TokenEscrow.sol TokenEscrow transferFromOwner 0.996 +INFO:Slither-simil:0x69719c8c207036bdfc3632ccc24b290fb7240f4a_BitPayToken.sol BitPayToken transfer 0.996 +INFO:Slither-simil:0x3d8a10ce3228cb428cb56baa058d4432464ea25d_TestToken.sol TestToken transfer 0.993 +INFO:Slither-simil:0x69719c8c207036bdfc3632ccc24b290fb7240f4a_BitPayToken.sol BitPayToken transferFrom 0.992 +INFO:Slither-simil:0x486e1f44b2a85150a6dd2de5aab87df375cd8880_CAIRToken.sol StandardToken transfer 0.991 +INFO:Slither-simil:0x2bec16b164725efc192b7ec0296f838c61317514_eda.sol StandardToken transfer 0.991 +INFO:Slither-simil:0xb7cb1c96db6b22b0d3d9536e0108d062bd488f74_WaltonToken.sol StandardToken transfer 0.991 +INFO:Slither-simil:0x346c3be6aebEBaF5Cb766a75aDc9827EfbB7E41A_DelphiToken.sol StandardToken transfer 0.991 +INFO:Slither-simil:0xf5068761511594c82328102f4fde4650ed9ea6c4_WHP.sol WHP transfer 0.991 +INFO:Slither-simil:0x5f9f2ae7150d0beef3bb50ac8d8f4b43e6a6cc57_NABC.sol NABC transfer 0.991 ``` ### Train mode @@ -105,7 +107,7 @@ Plot mode plots sets of functions to visually detect clusters of similar ones. Here's an example to plot all the functions named `add` from contracts named `SafeMath` sampling from 500 random contracts: ``` -$ slither-simil plot etherscan_verified_contracts.bin --fname SafeMath.add --input cache.npz --nsamples 500 +$ slither-simil plot etherscan_verified_contracts.bin --fname SafeMath.add --input cache.npz --nsamples 500 INFO:Slither-simil:Loading data.. INFO:Slither-simil:Procesing data.. INFO:Slither-simil:Plotting data.. @@ -114,7 +116,7 @@ INFO:Slither-simil:Saving figure to plot.png.. ![plot](https://user-images.githubusercontent.com/31542053/57525857-3d794f80-7302-11e9-9677-b4eb3f6a5c20.png) -This mode performs dimensionality reduction using PCA, so the axes you see here [are **not** associated with any particular unit](https://stats.stackexchange.com/questions/137813/the-meaning-of-units-on-the-axes-of-a-pca-plot). +This mode performs dimensionality reduction using PCA, so the axes you see here [are **not** associated with any particular unit](https://stats.stackexchange.com/questions/137813/the-meaning-of-units-on-the-axes-of-a-pca-plot). It can can be also used to plot sets of functions using only a name from any contract (e.g. `burn`) . @@ -123,7 +125,7 @@ It can can be also used to plot sets of functions using only a name from any con This mode has two features. You can inspect the internal information about a pre-trained model. Info mode is typically used for debugging. ``` -$ slither-simil info etherscan_verified_contracts.bin +$ slither-simil info etherscan_verified_contracts.bin INFO:Slither-simil:etherscan_verified_contracts.bin uses the following words: INFO:Slither-simil: INFO:Slither-simil:index(uint256) @@ -148,4 +150,4 @@ INFO:Slither-simil:[ 0.00689753 -0.05349572 -0.06854086 -0.01667773 0.1259813 0.06719872 -0.04520541 0.13745852 0.14690697 -0.03721125 0.00579037 0.06865194 -0.03804035 0.01224702 -0.1014601 -0.02655532 -0.15334933 ... -``` \ No newline at end of file +``` diff --git a/docs/src/tools/Contract-Flattening.md b/docs/src/tools/Contract-Flattening.md index b2da1e3be8..80447a5b56 100644 --- a/docs/src/tools/Contract-Flattening.md +++ b/docs/src/tools/Contract-Flattening.md @@ -1,17 +1,22 @@ `slither-flat` produces a flattened version of the codebase. ## Features + - Code flattening - Support multiple [strategies](#strategies) - Support circular dependency - Support all the compilation platforms (Truffle, embark, buidler, etherlime, ...). ## Usage + `slither-flat target` - `--contract ContractName`: flatten only one contract (standalone file) + ### Strategies + `slither-flat` contains three strategies that can be specified with the `--strategy` flag: + - `MostDerived`: Export all the most derived contracts (every file is standalone) - `OneFile`: Export all the contracts in one standalone file - `LocalImport`: Export every contract in one separate file, and include import ".." in their preludes @@ -19,15 +24,16 @@ Default: `MostDerived` ### Patching + `slither-flat` can transform the codebase to help some usage (eg. [Echidna](https://github.com/crytic/echidna)) + - `--convert-external`: convert `external` function to `public`. This is meant to facilitate [Echidna](https://github.com/crytic/echidna) usage. -- `--contract name`: To flatten only a target contract +- `--contract name`: To flatten only a target contract - `--remove-assert`: Remove call to assert(). ### Export option + - `--dir DirName`: output directory - `--json file.json`: export the results to a json file (`--json -` output to the standard output -- `--zip file.zip`: export to a zip file -- `--zip-type type`: Zip compression type (default lzma)) - - +- `--zip file.zip`: export to a ZIP file +- `--zip-type type`: ZIP compression type (default lzma)) diff --git a/docs/src/tools/Doctor.md b/docs/src/tools/Doctor.md index ce724f1094..2b9360e33b 100644 --- a/docs/src/tools/Doctor.md +++ b/docs/src/tools/Doctor.md @@ -1,3 +1,3 @@ # Slither doctor -Slither doctor is a tool designed to troubleshoot running Slither on a project. \ No newline at end of file +Slither doctor is a tool designed to troubleshoot running Slither on a project. diff --git a/docs/src/tools/ERC-Conformance.md b/docs/src/tools/ERC-Conformance.md index fd63b501fb..9dbdadbc6e 100644 --- a/docs/src/tools/ERC-Conformance.md +++ b/docs/src/tools/ERC-Conformance.md @@ -31,6 +31,7 @@ ``` slither-check-erc contract.sol ContractName ``` + For example, on ```Solidity @@ -52,14 +53,14 @@ The tool will report: # Check ERC20 ## Check functions -[ ] totalSupply() is missing -[ ] balanceOf(address) is missing +[ ] totalSupply() is missing +[ ] balanceOf(address) is missing [✓] transfer(address,uint256) is present [ ] transfer(address,uint256) -> () should return bool [✓] Transfer(address,address,uint256) is emitted -[ ] transferFrom(address,address,uint256) is missing -[ ] approve(address,uint256) is missing -[ ] allowance(address,address) is missing +[ ] transferFrom(address,address,uint256) is missing +[ ] approve(address,uint256) is missing +[ ] allowance(address,address) is missing [ ] name() is missing (optional) [ ] symbol() is missing (optional) [ ] decimals() is missing (optional) @@ -69,4 +70,4 @@ The tool will report: [✓] parameter 0 is indexed [ ] parameter 1 should be indexed [ ] Approval(address,address,uint256) is missing -``` \ No newline at end of file +``` diff --git a/docs/src/tools/Interface.md b/docs/src/tools/Interface.md index a77e780b0a..fca1135de5 100644 --- a/docs/src/tools/Interface.md +++ b/docs/src/tools/Interface.md @@ -7,6 +7,7 @@ Generates code for a Solidity interface from contract Run `slither-interface `. ## CLI Interface + ```shell positional arguments: contract_source The name of the contract (case sensitive) followed by the deployed contract address if verified on etherscan or project directory/filename for local contracts. @@ -18,4 +19,4 @@ optional arguments: --exclude-errors Excludes custom error signatures in the interface --exclude-enums Excludes enum definitions in the interface --exclude-structs Exclude struct definitions in the interface -``` \ No newline at end of file +``` diff --git a/docs/src/tools/Path-Finding-Utility.md b/docs/src/tools/Path-Finding-Utility.md index 7e41cccb49..b7618a8b29 100644 --- a/docs/src/tools/Path-Finding-Utility.md +++ b/docs/src/tools/Path-Finding-Utility.md @@ -1,13 +1,17 @@ `slither-find-paths` finds all the paths that reach a given target. ## Usage + ``` slither-find-paths file.sol [contract.function targets] ``` + - `[contract.function targets]` is either one target, or a list of target ## Example + Tested on [tests/possible_paths/paths.sol](https://github.com/trailofbits/slither/blob/master/tests/possible_paths/paths.sol) + ``` $ slither-find-paths paths.sol A.destination Target functions: @@ -23,4 +27,4 @@ The following paths reach the specified targets: A.call() -> A.destination() B.call2(A) -> A.call() -> A.destination() -``` \ No newline at end of file +``` diff --git a/docs/src/tools/Property-generation.md b/docs/src/tools/Property-generation.md index 1b66a7a6a1..5ceabef0dd 100644 --- a/docs/src/tools/Property-generation.md +++ b/docs/src/tools/Property-generation.md @@ -25,6 +25,7 @@ slither-prop . --contract ContractName - Two Truffle unit-test files For example on [examples/slither-prop](https://github.com/crytic/slither/tree/9623a2781faa4e7759f06d2e8c4adcd45078af69/examples/slither-prop). + ``` Write contracts/crytic/interfaces.sol Write contracts/crytic/PropertiesERC20BuggyTransferable.sol @@ -58,8 +59,8 @@ On [examples/slither-prop/contracts](https://github.com/crytic/slither/tree/9623 _balanceOf[crytic_attacker] = 1 ether; _totalSupply = 3 ether; - // - // + // + // // Update the following if totalSupply and balanceOf are external functions or state variables: initialTotalSupply = totalSupply(); @@ -87,6 +88,7 @@ $ truffle test test/crytic/InitializationTestERC20BuggyTransferable.js ``` If all the unit tests passed, run the property tests: + ``` $ truffle test test/crytic/InitializationTestERC20BuggyTransferable.js Contract: TestERC20BuggyTransferable @@ -120,6 +122,7 @@ $ echidna-test . --contract TestERC20BuggyTransferable --config echidna_config.y `slither-prop` contains different scenarios that can be specified with the `--scenario NAME` flag. Here are the available scenarios: + ``` #################### ERC20 #################### Transferable - Test the correct tokens transfer @@ -128,9 +131,10 @@ NotMintable - Test that no one can mint tokens NotMintableNotBurnable - Test that no one can mint or burn tokens NotBurnable - Test that no one can burn tokens Burnable - Test the burn of tokens. Require the "burn(address) returns()" function -``` +``` ## All properties + ``` +-----+-------------------------------------------------------------------------+------------------------+ | Num | Description | Scenario | @@ -155,4 +159,4 @@ Burnable - Test the burn of tokens. Require the "burn(address) returns()" functi | 17 | The total supply does not decrease. | NotBurnable | | 18 | The total supply does not decrease. | Burnable | +-----+-------------------------------------------------------------------------+------------------------+ -``` \ No newline at end of file +``` diff --git a/docs/src/tools/README.md b/docs/src/tools/README.md index d6afa8685e..f17053e89c 100644 --- a/docs/src/tools/README.md +++ b/docs/src/tools/README.md @@ -1,17 +1,16 @@ Slither comes with inbuilt tools -| Name | Command Line | What it Does | -|------|-------------|--------------| -| [Code Similarity](./Code-Similarity-Detector.md) | `slither-simil` | Detects similar Solidity functions/contracts using code similarity analysis. Useful for finding duplicated code, similar vulnerabilities, or analyzing large codebases. | -| [Contract Flattening](./Contract-Flattening.md) | `slither-flat` | Flattens a Solidity codebase by inlining all imports into a single file. Useful for contract verification on Etherscan or debugging. | -| [Documentation](./Documentation.md) | `slither-doc` | Automatically generates documentation for Solidity contracts, including inheritance information, functions, modifiers, and more. | -| [Doctor](./Doctor.md) | `slither-doctor` | Helps diagnose and fix common issues in your environment that might prevent Slither from working correctly. | -| [ERC Conformance](./ERC-Conformance.md) | `slither-check-erc` | Validates whether a contract correctly implements various ERC standards (ERC20, ERC721, etc.) by checking required functions and their signatures. | -| [Interface](./Interface.md) | `slither-interface` | Generates Solidity interfaces from contract implementations, useful for creating minimal interfaces for contract interactions. | -| [Mutator](./Mutator.md) | `slither-mutate` | Performs mutation testing on Solidity contracts by automatically generating variants with small modifications to test suite effectiveness. | -| [Path Finding](./Path-Finding-Utility.md) | `slither-prop` | Analyzes call paths between functions in smart contracts to understand control and data flow. | -| [Property Generation](./Property-generation.md) | `slither-prop` | Automatically generates security properties and unit tests for smart contracts based on their behavior. | -| [Read Storage](./ReadStorage.md) | `slither-read-storage` | Reads contract storage values directly from the blockchain, helping debug deployed contracts. | -| [Format](./Slither-format.md) | `slither-format` | Automatically patch bugs. | -| [Upgradeability Checks](./Upgradeability-Checks.md) | `slither-check-upgradeability` | Analyzes upgradeable contracts for common issues and vulnerabilities in proxy patterns. | - +| Name | Command-Line | What it Does | +| --------------------------------------------------- | ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [Code Similarity](./Code-Similarity-Detector.md) | `slither-simil` | Detects similar Solidity functions/contracts using code similarity analysis. Useful for finding duplicated code, similar vulnerabilities, or analyzing large codebases. | +| [Contract Flattening](./Contract-Flattening.md) | `slither-flat` | Flattens a Solidity codebase by inlining all imports into a single file. Useful for contract verification on Etherscan or debugging. | +| [Documentation](./Documentation.md) | `slither-doc` | Automatically generates documentation for Solidity contracts, including inheritance information, functions, modifiers, and more. | +| [Doctor](./Doctor.md) | `slither-doctor` | Helps diagnose and fix common issues in your environment that might prevent Slither from working correctly. | +| [ERC Conformance](./ERC-Conformance.md) | `slither-check-erc` | Validates whether a contract correctly implements various ERC standards (ERC20, ERC721, etc.) by checking required functions and their signatures. | +| [Interface](./Interface.md) | `slither-interface` | Generates Solidity interfaces from contract implementations, useful for creating minimal interfaces for contract interactions. | +| [Mutator](./Mutator.md) | `slither-mutate` | Performs mutation testing on Solidity contracts by automatically generating variants with small modifications to test suite effectiveness. | +| [Path Finding](./Path-Finding-Utility.md) | `slither-prop` | Analyzes call paths between functions in smart contracts to understand control and data flow. | +| [Property Generation](./Property-generation.md) | `slither-prop` | Automatically generates security properties and unit tests for smart contracts based on their behavior. | +| [Read Storage](./ReadStorage.md) | `slither-read-storage` | Reads contract storage values directly from the blockchain, helping debug deployed contracts. | +| [Format](./Slither-format.md) | `slither-format` | Automatically patch bugs. | +| [Upgradeability Checks](./Upgradeability-Checks.md) | `slither-check-upgradeability` | Analyzes upgradeable contracts for common issues and vulnerabilities in proxy patterns. | diff --git a/docs/src/tools/Slither-format.md b/docs/src/tools/Slither-format.md index abc9971d7b..5f155160ce 100644 --- a/docs/src/tools/Slither-format.md +++ b/docs/src/tools/Slither-format.md @@ -3,15 +3,17 @@ Carefully review each patch before applying it. ## Usage + `slither-format target`. The patches will be generated in `crytic-export/patches` ## Detectors supported + - `unused-state` - `solc-version` - `pragma` - `naming-convention` - `external-function` - `constable-states` -- `constant-function` \ No newline at end of file +- `constant-function` diff --git a/docs/src/tools/Upgradeability-Checks.md b/docs/src/tools/Upgradeability-Checks.md index 9e87d1594a..d2cf7a7a15 100644 --- a/docs/src/tools/Upgradeability-Checks.md +++ b/docs/src/tools/Upgradeability-Checks.md @@ -2,84 +2,90 @@ `slither-check-upgradeability` helps review contracts that use the [delegatecall proxy pattern](https://blog.trailofbits.com/2018/09/05/contract-upgrade-anti-patterns/). -## Checks - - -Num | Check | What it Detects | Impact | Proxy | Contract V2 ---- | --- | --- | --- | --- | --- -1 | `became-constant` | [Variables that should not be constant](#variables-that-should-not-be-constant) | High | | X -2 | `function-id-collision` | [Functions ids collision](#functions-ids-collisions) | High | X | -3 | `function-shadowing` | [Functions shadowing](#functions-shadowing) | High | X | -4 | `missing-calls` | [Missing calls to init functions](#initialize-functions-are-not-called) | High | | -5 | `missing-init-modifier` | [initializer() is not called](#initializer-is-not-called) | High | | -6 | `multiple-calls` | [Init functions called multiple times](#initialize-functions-are-called-multiple-times) | High | | -7 | `order-vars-contracts` | [Incorrect vars order with the v2](#incorrect-variables-with-the-v2) | High | | X -8 | `order-vars-proxy` | [Incorrect vars order with the proxy](#incorrect-variables-with-the-proxy) | High | X | -9 | `variables-initialized` | [State variables with an initial value](#state-variable-initialized) | High | | -10 | `were-constant` | [Variables that should be constant](#variables-that-should-be-constant) | High | | X -11 | `extra-vars-proxy` | [Extra vars in the proxy](#extra-variables-in-the-proxy) | Medium | X | -12 | `missing-variables` | [Variable missing in the v2](#missing-variables) | Medium | | X -13 | `extra-vars-v2` | [Extra vars in the v2](#extra-variables-in-the-v2) | Informational | | X -14 | `init-inherited` | [Initializable is not inherited](#initializable-is-not-inherited) | Informational | | -15 | `init-missing` | [Initializable is missing](#initializable-is-missing) | Informational | | -16 | `initialize-target` | [Initialize function that must be called](#initialize-function) | Informational | | -17 | `initializer-missing` | [initializer() is missing](#initializer-is-missing) | Informational | | +## Checks + +| Num | Check | What it Detects | Impact | Proxy | Contract V2 | +| --- | ----------------------- | --------------------------------------------------------------------------------------- | ------------- | ----- | ----------- | +| 1 | `became-constant` | [Variables that should not be constant](#variables-that-should-not-be-constant) | High | | X | +| 2 | `function-id-collision` | [Functions IDs collision](#functions-ids-collisions) | High | X | +| 3 | `function-shadowing` | [Functions shadowing](#functions-shadowing) | High | X | +| 4 | `missing-calls` | [Missing calls to init functions](#initialize-functions-are-not-called) | High | | +| 5 | `missing-init-modifier` | [initializer() is not called](#initializer-is-not-called) | High | | +| 6 | `multiple-calls` | [Init functions called multiple times](#initialize-functions-are-called-multiple-times) | High | | +| 7 | `order-vars-contracts` | [Incorrect vars order with the v2](#incorrect-variables-with-the-v2) | High | | X | +| 8 | `order-vars-proxy` | [Incorrect vars order with the proxy](#incorrect-variables-with-the-proxy) | High | X | +| 9 | `variables-initialized` | [State variables with an initial value](#state-variable-initialized) | High | | +| 10 | `were-constant` | [Variables that should be constant](#variables-that-should-be-constant) | High | | X | +| 11 | `extra-vars-proxy` | [Extra vars in the proxy](#extra-variables-in-the-proxy) | Medium | X | +| 12 | `missing-variables` | [Variable missing in the v2](#missing-variables) | Medium | | X | +| 13 | `extra-vars-v2` | [Extra vars in the v2](#extra-variables-in-the-v2) | Informational | | X | +| 14 | `init-inherited` | [Initializable is not inherited](#initializable-is-not-inherited) | Informational | | +| 15 | `init-missing` | [Initializable is missing](#initializable-is-missing) | Informational | | +| 16 | `initialize-target` | [Initialize function that must be called](#initialize-function) | Informational | | +| 17 | `initializer-missing` | [initializer() is missing](#initializer-is-missing) | Informational | | ## Usage ``` slither-check-upgradeability project ContractName ``` + - `project` can be a Solidity file, or a platform (truffle/embark/..) directory ### Contract V2 + If you want to check the contract and its update, use: + - `--new-contract-name ContractName` - `--new-contract-filename contract_project` `--new-contract-filename` is not needed if the new contract is in the same codebase than the original one. ### Proxy + If you want to check also the proxy, use: + - `--proxy-name ProxyName` - `--proxy-filename proxy_project` `--proxy-filename` is not needed if the proxy is in the same codebase than the targeted contract. #### ZOS + If you use zos, you will have the proxy and the contract in different directories. Likely, you will use one of the proxy from https://github.com/zeppelinos/zos. Clone the `zos`, and install the dependencies: + ``` git clone https://github.com/zeppelinos/zos cd zos/packages/lib npm install rm contracts/mocks/WithConstructorImplementation.sol ``` -Note: `contracts/mocks/WithConstructorImplementation.sol` must be removed as it contains a [name clash collision](https://github.com/crytic/slither/wiki#keyerror-or-nonetype-error) with `contracts/mocks/Invalid.sol` + +Note: `contracts/mocks/WithConstructorImplementation.sol` must be removed as it contains a [name clash collision](https://github.com/crytic/slither/wiki#keyerror-or-nonetype-error) with `contracts/mocks/Invalid.sol` Then from your project directory: + ``` -slither-check-upgradeability . ContractName --proxy-filename /path/to/zos/packages/lib/ --proxy-name UpgradeabilityProxy +slither-check-upgradeability . ContractName --proxy-filename /path/to/zos/packages/lib/ --proxy-name UpgradeabilityProxy ``` According to your setup, you might choose another proxy name than `UpgradeabilityProxy`. - ## Checks Description - - ## Variables that should not be constant + ### Configuration -* Check: `became-constant` -* Severity: `High` + +- Check: `became-constant` +- Severity: `High` ### Description Detect state variables that should not be `constant̀`. - ### Exploit Scenario: ```solidity @@ -95,24 +101,24 @@ contract ContractV2{ uint variable3; } ``` + Because `variable2` is now a `constant`, the storage location of `variable3` will be different. As a result, `ContractV2` will have a corrupted storage layout. - ### Recommendation Do not make an existing state variable `constant`. +## Functions IDs collisions -## Functions ids collisions ### Configuration -* Check: `function-id-collision` -* Severity: `High` -### Description +- Check: `function-id-collision` +- Severity: `High` -Detect function id collision between the contract and the proxy. +### Description +Detect function ID collision between the contract and the proxy. ### Exploit Scenario: @@ -129,25 +135,25 @@ contract Proxy{ } } ``` -`Proxy.tgeo()` and `Contract.gsf()` have the same function id (0x67e43e43). -As a result, `Proxy.tgeo()` will shadow Contract.gsf()`. +`Proxy.tgeo()` and `Contract.gsf()` have the same function ID (0x67e43e43). +As a result, `Proxy.tgeo()` will shadow Contract.gsf()`. ### Recommendation Rename the function. Avoid public functions in the proxy. - ## Functions shadowing + ### Configuration -* Check: `function-shadowing` -* Severity: `High` + +- Check: `function-shadowing` +- Severity: `High` ### Description Detect function shadowing between the contract and the proxy. - ### Exploit Scenario: ```solidity @@ -163,24 +169,24 @@ contract Proxy{ } } ``` -`Proxy.get` will shadow any call to `get()`. As a result `get()` is never executed in the logic contract and cannot be updated. +`Proxy.get` will shadow any call to `get()`. As a result `get()` is never executed in the logic contract and cannot be updated. ### Recommendation Rename the function. Avoid public functions in the proxy. - ## Initialize functions are not called + ### Configuration -* Check: `missing-calls` -* Severity: `High` + +- Check: `missing-calls` +- Severity: `High` ### Description Detect missing calls to initialize functions. - ### Exploit Scenario: ```solidity @@ -196,24 +202,24 @@ contract Derived is Base{ } ``` -`Derived.initialize` does not call `Base.initialize` leading the contract to not be correctly initialized. +`Derived.initialize` does not call `Base.initialize` leading the contract to not be correctly initialized. ### Recommendation Ensure all the initialize functions are reached by the most derived initialize function. - ## initializer() is not called + ### Configuration -* Check: `missing-init-modifier` -* Severity: `High` + +- Check: `missing-init-modifier` +- Severity: `High` ### Description Detect if `Initializable.initializer()` is called. - ### Exploit Scenario: ```solidity @@ -224,24 +230,24 @@ contract Contract{ } ``` -`initialize` should have the `initializer` modifier to prevent someone from initializing the contract multiple times. +`initialize` should have the `initializer` modifier to prevent someone from initializing the contract multiple times. ### Recommendation Use `Initializable.initializer()`. - ## Initialize functions are called multiple times + ### Configuration -* Check: `multiple-calls` -* Severity: `High` + +- Check: `multiple-calls` +- Severity: `High` ### Description Detect multiple calls to a initialize function. - ### Exploit Scenario: ```solidity @@ -264,24 +270,24 @@ contract DerivedDerived is Derived{ } ``` -`Base.initialize(uint)` is called two times in `DerivedDerived.initiliaze` execution, leading to a potential corruption. +`Base.initialize(uint)` is called two times in `DerivedDerived.initiliaze` execution, leading to a potential corruption. ### Recommendation Call only one time every initialize function. - ## Incorrect variables with the v2 + ### Configuration -* Check: `order-vars-contracts` -* Severity: `High` + +- Check: `order-vars-contracts` +- Severity: `High` ### Description Detect variables that are different between the original contract and the updated one. - ### Exploit Scenario: ```solidity @@ -293,24 +299,24 @@ contract ContractV2{ address variable1; } ``` -`Contract` and `ContractV2` do not have the same storage layout. As a result the storage of both contracts can be corrupted. +`Contract` and `ContractV2` do not have the same storage layout. As a result the storage of both contracts can be corrupted. ### Recommendation Respect the variable order of the original contract in the updated contract. - ## Incorrect variables with the proxy + ### Configuration -* Check: `order-vars-proxy` -* Severity: `High` + +- Check: `order-vars-proxy` +- Severity: `High` ### Description Detect variables that are different between the contract and the proxy. - ### Exploit Scenario: ```solidity @@ -322,24 +328,24 @@ contract Proxy{ address variable1; } ``` -`Contract` and `Proxy` do not have the same storage layout. As a result the storage of both contracts can be corrupted. +`Contract` and `Proxy` do not have the same storage layout. As a result the storage of both contracts can be corrupted. ### Recommendation Avoid variables in the proxy. If a variable is in the proxy, ensure it has the same layout than in the contract. - ## State variable initialized + ### Configuration -* Check: `variables-initialized` -* Severity: `High` + +- Check: `variables-initialized` +- Severity: `High` ### Description Detect state variables that are initialized. - ### Exploit Scenario: ```solidity @@ -347,24 +353,24 @@ contract Contract{ uint variable = 10; } ``` -Using `Contract` will the delegatecall proxy pattern will lead `variable` to be 0 when called through the proxy. +Using `Contract` will the delegatecall proxy pattern will lead `variable` to be 0 when called through the proxy. ### Recommendation Using initialize functions to write initial values in state variables. - ## Variables that should be constant + ### Configuration -* Check: `were-constant` -* Severity: `High` + +- Check: `were-constant` +- Severity: `High` ### Description Detect state variables that should be `constant̀`. - ### Exploit Scenario: ```solidity @@ -380,25 +386,25 @@ contract ContractV2{ uint variable3; } ``` + Because `variable2` is not anymore a `constant`, the storage location of `variable3` will be different. As a result, `ContractV2` will have a corrupted storage layout. - ### Recommendation Do not remove `constant` from a state variables during an update. - ## Extra variables in the proxy + ### Configuration -* Check: `extra-vars-proxy` -* Severity: `Medium` + +- Check: `extra-vars-proxy` +- Severity: `Medium` ### Description Detect variables that are in the proxy and not in the contract. - ### Exploit Scenario: ```solidity @@ -411,24 +417,24 @@ contract Proxy{ uint variable2; } ``` -`Proxy` contains additional variables. A future update of `Contract` is likely to corrupt the proxy. +`Proxy` contains additional variables. A future update of `Contract` is likely to corrupt the proxy. ### Recommendation Avoid variables in the proxy. If a variable is in the proxy, ensure it has the same layout than in the contract. - ## Missing variables + ### Configuration -* Check: `missing-variables` -* Severity: `Medium` + +- Check: `missing-variables` +- Severity: `Medium` ### Description Detect variables that were present in the original contracts but are not in the updated one. - ### Exploit Scenario: ```solidity @@ -441,28 +447,28 @@ contract V2{ uint variable1; } ``` -The new version, `V2` does not contain `variable1`. + +The new version, `V2` does not contain `variable1`. If a new variable is added in an update of `V2`, this variable will hold the latest value of `variable2` and will be corrupted. - ### Recommendation Do not change the order of the state variables in the updated contract. - ## Extra variables in the v2 + ### Configuration -* Check: `extra-vars-v2` -* Severity: `Informational` + +- Check: `extra-vars-v2` +- Severity: `Informational` ### Description -Show new variables in the updated contract. +Show new variables in the updated contract. This finding does not have an immediate security impact and is informative. - ### Exploit Scenario: ```solidity @@ -475,72 +481,72 @@ contract Proxy{ uint variable2; } ``` -`Proxy` contains additional variables. A future update of `Contract` is likely to corrupt the proxy. +`Proxy` contains additional variables. A future update of `Contract` is likely to corrupt the proxy. ### Recommendation Ensure that all the new variables are expected. - ## Initializable is not inherited + ### Configuration -* Check: `init-inherited` -* Severity: `Informational` + +- Check: `init-inherited` +- Severity: `Informational` ### Description Detect if `Initializable` is inherited. - ### Recommendation Review manually the contract's initialization. Consider inheriting `Initializable`. - ## Initializable is missing + ### Configuration -* Check: `init-missing` -* Severity: `Informational` + +- Check: `init-missing` +- Severity: `Informational` ### Description Detect if a contract `Initializable` is present. - ### Recommendation Review manually the contract's initialization.. Consider using a `Initializable` contract to follow [standard practice](https://docs.openzeppelin.com/upgrades/2.7/writing-upgradeable). - ## Initialize function + ### Configuration -* Check: `initialize-target` -* Severity: `Informational` + +- Check: `initialize-target` +- Severity: `Informational` ### Description -Show the function that must be called at deployment. +Show the function that must be called at deployment. This finding does not have an immediate security impact and is informative. - ### Recommendation Ensure that the function is called at deployment. - ## initializer() is missing + ### Configuration -* Check: `initializer-missing` -* Severity: `Informational` + +- Check: `initializer-missing` +- Severity: `Informational` ### Description Detect the lack of `Initializable.initializer()` modifier. - ### Recommendation Review manually the contract's initialization. Consider inheriting a `Initializable.initializer()` modifier. diff --git a/docs/src/tutorials/README.md b/docs/src/tutorials/README.md index 5665d91a57..8c93e5646d 100644 --- a/docs/src/tutorials/README.md +++ b/docs/src/tutorials/README.md @@ -3,7 +3,7 @@ The objective of this tutorial is to demonstrate how to use Slither to automatically find bugs in smart contracts. - [Installation](#installation) -- [Command line usage](#command-line) +- [Command-line usage](#command-line) - [Introduction to static analysis](./static_analysis.md): A concise introduction to static analysis - [API](../api/api.md): Python API description @@ -45,7 +45,7 @@ cd /home/trufflecon/ ## Command Line -**Command line vs. user-defined scripts.** Slither comes with a set of pre-defined detectors that can identify many common bugs. Running Slither from the command line will execute all the detectors without requiring detailed knowledge of static analysis: +**Command-line vs. user-defined scripts.** Slither comes with a set of predefined detectors that can identify many common bugs. Running Slither from the command line will execute all the detectors without requiring detailed knowledge of static analysis: ```bash slither project_paths diff --git a/docs/src/tutorials/exercises/exercise3/solution.py b/docs/src/tutorials/exercises/exercise3/solution.py index b783294bc1..ff582da5c3 100644 --- a/docs/src/tutorials/exercises/exercise3/solution.py +++ b/docs/src/tutorials/exercises/exercise3/solution.py @@ -18,4 +18,4 @@ ] # Print the result -print(f'The function using "a" in condition are {[f.name for f in function_using_a_as_condition]}') \ No newline at end of file +print(f'The function using "a" in condition are {[f.name for f in function_using_a_as_condition]}') diff --git a/docs/src/tutorials/scripts/gh_action_test.sh b/docs/src/tutorials/scripts/gh_action_test.sh deleted file mode 100644 index d2bc01e494..0000000000 --- a/docs/src/tutorials/scripts/gh_action_test.sh +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env bash - -test_examples(){ - cd examples - - python print_basic_information.py > results.txt - if [ $? -ne 0 ] - then - exit -1 - fi - - DIFF=$(diff results.txt expected_results_print_basic_information.txt) - - if [ "$DIFF" != "" ] - then - echo "print_basic_information.py failed" - cat results.txt - echo "" - cat expected_results_print_basic_information.txt - echo "" - echo "$DIFF" - exit -1 - fi - - echo "print_basic_information.py passed" - cd .. -} - -test_exercise(){ - cd "exercises/exercise$1" - - python solution.py > results.txt - if [ $? -ne 0 ] - then - exit -1 - fi - - DIFF=$(diff results.txt expected_results.txt) - - if [ "$DIFF" != "" ] - then - echo "exercise $1 failed" - cat results.txt - echo "" - cat expected_results.txt - echo "" - echo "$DIFF" - exit -1 - fi - - echo "exercise $1 passed" - cd ../.. -} - - -cd program-analysis/slither -pip install slither-analyzer -solc-select install 0.8.20 -solc-select use 0.8.20 - -test_examples - -solc-select install 0.5.11 -solc-select use 0.5.11 -test_exercise 1 - -solc-select use 0.8.20 -test_exercise 2 - -test_exercise 3 - -echo "Slither tests passed" - diff --git a/slither/detectors/shadowing/builtin_symbols.py b/slither/detectors/shadowing/builtin_symbols.py index ab54861053..9d87764ae4 100644 --- a/slither/detectors/shadowing/builtin_symbols.py +++ b/slither/detectors/shadowing/builtin_symbols.py @@ -29,7 +29,7 @@ class BuiltinSymbolShadowing(AbstractDetector): WIKI = "https://github.com/crytic/slither/wiki/Detector-Documentation#builtin-symbol-shadowing" - WIKI_TITLE = "Builtin Symbol Shadowing" + WIKI_TITLE = "Built-in Symbol Shadowing" WIKI_DESCRIPTION = "Detection of shadowing built-in symbols using local variables, state variables, functions, modifiers, or events." # region wiki_exploit_scenario @@ -52,7 +52,7 @@ class BuiltinSymbolShadowing(AbstractDetector): `now` is defined as a state variable, and shadows with the built-in symbol `now`. The function `assert` overshadows the built-in `assert` function. Any use of either of these built-in symbols may lead to unexpected results.""" # endregion wiki_exploit_scenario - WIKI_RECOMMENDATION = "Rename the local variables, state variables, functions, modifiers, and events that shadow a builtin symbol." + WIKI_RECOMMENDATION = "Rename the local variables, state variables, functions, modifiers, and events that shadow a built-in symbol." SHADOWING_FUNCTION = "function" SHADOWING_MODIFIER = "modifier" From 27bfca9d7670b607df01f27a8fbb83ab7ce3e4b2 Mon Sep 17 00:00:00 2001 From: Feist Josselin Date: Fri, 7 Mar 2025 15:32:20 +0100 Subject: [PATCH 28/30] Update --- docs/src/printers/Printer-documentation.md | 2 +- docs/src/tools/Adding-a-new-utility.md | 2 +- docs/src/tutorials/README.md | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/src/printers/Printer-documentation.md b/docs/src/printers/Printer-documentation.md index b2b59d5ab2..389c9659b7 100644 --- a/docs/src/printers/Printer-documentation.md +++ b/docs/src/printers/Printer-documentation.md @@ -259,7 +259,7 @@ INFO:Printers:Contract Test 0xa1: POP ``` -## Function id +## Function ID `slither file.sol --print function-id` Print the keccack256 signature of the functions diff --git a/docs/src/tools/Adding-a-new-utility.md b/docs/src/tools/Adding-a-new-utility.md index 08a1ccfa08..54d1449d36 100644 --- a/docs/src/tools/Adding-a-new-utility.md +++ b/docs/src/tools/Adding-a-new-utility.md @@ -7,7 +7,7 @@ The skeleton util is present in [tools/demo](https://github.com/crytic/slither/t ## Integration -To enable an util from the command line, update `entry_points` in [setup.py](https://github.com/crytic/slither/blob/master/setup.py). +To enable an util from the command-line, update `entry_points` in [setup.py](https://github.com/crytic/slither/blob/master/setup.py). Installing Slither will then install the util. ## Guidelines diff --git a/docs/src/tutorials/README.md b/docs/src/tutorials/README.md index 8c93e5646d..77947425bf 100644 --- a/docs/src/tutorials/README.md +++ b/docs/src/tutorials/README.md @@ -45,10 +45,10 @@ cd /home/trufflecon/ ## Command Line -**Command-line vs. user-defined scripts.** Slither comes with a set of predefined detectors that can identify many common bugs. Running Slither from the command line will execute all the detectors without requiring detailed knowledge of static analysis: +**Command-line vs. user-defined scripts.** Slither comes with a set of predefined detectors that can identify many common bugs. Running Slither from the command-line will execute all the detectors without requiring detailed knowledge of static analysis: ```bash slither project_paths ``` -Besides detectors, Slither also offers code review capabilities through its [printers](https://github.com/crytic/slither#printers) and [tools](https://github.com/crytic/slither#tools). +Besides detectors, Slither also offers code review capabilities through its [printers](https://github.com/crytic/slither#printers) and [tools](https://github.com/crytic/slither#tools). \ No newline at end of file From 167e841a03718cfb9aada669f3b703813c1dc66e Mon Sep 17 00:00:00 2001 From: Feist Josselin Date: Fri, 7 Mar 2025 16:05:38 +0100 Subject: [PATCH 29/30] Remove docs from markdownlint --- .github/workflows/linter.yml | 4 ++++ docs/src/tutorials/README.md | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index c49f9c09c5..30960bd8d0 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -44,6 +44,10 @@ jobs: run: | echo "::add-matcher::.github/workflows/matchers/yamllint.json" + - name: Remove part of the doc + run: | + rm docs/src/ + - name: Lint everything else uses: super-linter/super-linter/slim@v6.1.1 if: always() diff --git a/docs/src/tutorials/README.md b/docs/src/tutorials/README.md index 77947425bf..e06ada95f3 100644 --- a/docs/src/tutorials/README.md +++ b/docs/src/tutorials/README.md @@ -51,4 +51,4 @@ cd /home/trufflecon/ slither project_paths ``` -Besides detectors, Slither also offers code review capabilities through its [printers](https://github.com/crytic/slither#printers) and [tools](https://github.com/crytic/slither#tools). \ No newline at end of file +Besides detectors, Slither also offers code review capabilities through its [printers](https://github.com/crytic/slither#printers) and [tools](https://github.com/crytic/slither#tools). From 72043c4fc723d818e099d1f5a38f63b462826f43 Mon Sep 17 00:00:00 2001 From: Feist Josselin Date: Fri, 7 Mar 2025 16:11:12 +0100 Subject: [PATCH 30/30] Minor --- .github/workflows/linter.yml | 2 +- docs/src/tutorials/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 30960bd8d0..f2db214f82 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -46,7 +46,7 @@ jobs: - name: Remove part of the doc run: | - rm docs/src/ + rm -rf docs/src/ - name: Lint everything else uses: super-linter/super-linter/slim@v6.1.1 diff --git a/docs/src/tutorials/README.md b/docs/src/tutorials/README.md index e06ada95f3..d5bff9ab2a 100644 --- a/docs/src/tutorials/README.md +++ b/docs/src/tutorials/README.md @@ -43,7 +43,7 @@ solc-select 0.5.11 cd /home/trufflecon/ ``` -## Command Line +## Command-line **Command-line vs. user-defined scripts.** Slither comes with a set of predefined detectors that can identify many common bugs. Running Slither from the command-line will execute all the detectors without requiring detailed knowledge of static analysis: