Missing data dependencies involving assembly with solc <0.6.0
- Dominant language
- Python
- Stars
- 6.4k
- Forks
- 1.1k
- PR merge metrics
- No merged PRs in 30d
Description
### Describe the issue:
With solc versions < 0.6.0, assembly is not parsed very well in Slither, which has caused me trouble before. All we get is a single assembly node with `node.inline_asm` containing all of the assembly code as one long string.
My problem right now is that `slither.analyses.data_dependency.get_dependencies()` is missing several dependencies when the data flow involves some assembly and solc is < 0.6.0. In the code example provided, I am trying to trace the `implementation` parameter in `_delegate(address implementation)` back to its source, `IMPLEMENTATION_SLOT`. The data dependency should be as follows:
- `implementation` gets its value from a temporary variable being passed into a call to `_delegate` in the function `_fallback`.
- The temporary variable is the return value from a call to `_implementation()`, i.e. `_delegate(_implementation())`.
- The return variable is declared in the function signature of `_implementation` as `address impl`.
- `impl` is assigned a value _in assembly_: `impl := sload(slot)`
- `slot` is assigned the value of `IMPLEMENTATION_SLOT` before the assembly.
When I do call `get_dependencies(implementation, contract)`, the only dependency currently being returned is the temporary variable.
The new `test_upgradeability_util.py` included in #1757 fails for this reason [at line 73](https://github.com/crytic/slither/blob/bff30a3481f61babbd55323b3ab6a42eb3be6118/tests/test_upgradeability_util.py#L73).
### Code example to reproduce the issue:
```solidity
pragma solidity ^0.5.0;
contract ZosProxy {
function () payable external {
_fallback();
}
function _implementation() internal view returns (address);
function _delegate(address implementation) internal {
assembly {
calldatacopy(0, 0, calldatasize)
let result := delegatecall(gas, implementation, 0, calldatasize, 0, 0)
returndatacopy(0, 0, returndatasize)
switch result
case 0 { revert(0, returndatasize) }
default { return(0, returndatasize) }
}
}
function _willFallback() internal {
}
function _fallback() internal {
_willFallback();
_delegate(_implementation());
}
}
library AddressUtils {
function isContract(address addr) internal view returns (bool) {
uint256 size;
assembly { size := extcodesize(addr) }
return size > 0;
}
}
contract UpgradeabilityProxy is ZosProxy {
event Upgraded(address indexed implementation);
bytes32 private constant IMPLEMENTATION_SLOT = 0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3;
constructor(address _implementation) public payable {
assert(IMPLEMENTATION_SLOT == keccak256("org.zeppelinos.proxy.implementation"));
_setImplementation(_implementation);
}
function _implementation() internal view returns (address impl) {
bytes32 slot = IMPLEMENTATION_SLOT;
assembly {
impl := sload(slot)
}
}
function _upgradeTo(address newImplementation) internal {
_setImplementation(newImplementation);
emit Upgraded(newImplementation);
}
function _setImplementation(address newImplementation) private {
require(AddressUtils.isContract(newImplementation), "Cannot set a proxy implementation to a non-contract address");
bytes32 slot = IMPLEMENTATION_SLOT;
assembly {
sstore(slot, newImplementation)
}
}
}
```
### Version:
0.9.2
### Relevant log output:
```shell
dependencies = {set: 1} {}
{TemporaryVariable} TMP_11
```
Contributor guide
Assessment
This issue has not been assessed yet.