-
Notifications
You must be signed in to change notification settings - Fork 969
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
slither-read-storage: Handle unstructured storage #1752
slither-read-storage: Handle unstructured storage #1752
Conversation
in `coerce_type(solidity_type, value)`
i.e., all constant bytes32 variables
before calling `get_storage_layout`
Can we add a test with an address that uses this format? |
The address I used is 0x8315177aB297bA92A06054cE80a67Ed4DBd7ed3a, which is the Arbitrum Bridge's Transparent Upgradeable Proxy. Where would you recommend adding the test? |
@webthethird You would deploy a contract to ganache and run slither-read-storage against the local network as is done here https://github.com/crytic/slither/blob/master/tests/test_read_storage.py |
@0xalpharush Would it make sense to add it to the existing test? Since this PR applies to slither-read-storage itself, just adding to what would print if the contract used any constant storage slots. |
using slither-read-storage
@montyly @0xalpharush I added a new test, I believe the failing CI test is related to #1710 |
ret = func.return_type[0] | ||
size, _ = ret.storage_size | ||
return str(ret), size * 8 | ||
for node in func.all_nodes(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You may be able to use https://crytic.github.io/slither/slither/core/declarations/contract.html#Contract.get_functions_reading_from_variable to make this more general and not have to rely on matching these specific function names. If it only returns one function and that function has one return value that's less than or equal to 32 bytes, assume the return type is that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it only returns one function and that function has one return value that's less than or equal to 32 bytes, assume the return type is that.
For the most part I am doing this on lines 275-278. Though I could streamline it by using Contract.get_functions_reading_from_variable
.
to make this more general and not have to rely on matching these specific function names.
The reason I resorted to checking for the function names from the StorageSlot library is because there may be some slot (i.e., _ROLLBACK_SLOT
in the standard ERC1967Upgrade.sol) which do not have any setter or getter functions. With the _ROLLBACK_SLOT
example, the only function which reads from that variable is _upgradeToAndCallUUPS
which contains the line if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value)
.
check function return type sizes, and add comments in `find_constant_slot_storage_type`
instead of string casting
state_var_slots = [ | ||
self.get_variable_info(contract, var)[0] | ||
for contract, var in self.target_variables | ||
] | ||
if int_value in state_var_slots: | ||
return None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One thing to be aware of is that the storage layout is only calculated for contracts_derived
and not contracts
so there's a potential that a KeyError
happens if a variable is found in an abstract contract. It's not clear to me this can happen in practice, but the error is handled by get_storage_slot
for this reason.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible for a variable in an abstract contract to end up in self.target_variables
?
…ge-unstructured # Conflicts: # tests/conftest.py
@0xalpharush After merging from dev I'm getting some odd test errors. Any idea why? Also please let me know if there's anything else to do on this PR to get it ready to merge. This one has probably been around too long! |
Now I'm getting an error when trying to run Black
|
With the Black issue resolved, all checks are passing |
I think we can squash and merge |
@webthethird Can you resolve merge conflicts? |
@0xalpharush merge conflicts resolved, and tests passing |
@@ -63,25 +64,35 @@ def result(self) -> "Literal": | |||
# emulate 256-bit wrapping | |||
if str(self._type).startswith("uint"): | |||
value = value & (2**256 - 1) | |||
if str(self._type).startswith("byte"): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mhh would this cause problem for bytes1
/ bytes2
/ ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@0xalpharush what do you think? This is part of the constant folding change you made I think.
@0xalpharush I just merged dev into this branch again, and suddenly we're getting a bunch of test failures. Any idea what the cause might be? I had already merged from dev yesterday, so it must be something recent. |
This approach scans for constant bytes32 variables and uses a couple heuristics to determine (a) that it is a storage slot, and (b) the type of value stored in the slot.
For example, running the following command yields the table below: