crytic / crytic/crytic-compile

crytic_compile.py | get_line_from_offset() pulling from incorrect file

Open
#285 1 comment 0 reactions 0 assignees View on GitHub
high-priority
Dominant language
Python
Stars
197
Forks
97
PR merge metrics
No merged PRs in 30d

Description

Ran into issues involving `KeyError XXX` similar to https://github.com/crytic/slither/issues/1324

Did some basic debugging and noticed this is due to sourceMap iteration going out of bounds for particular contracts in the initialization stages of crytic_compile.

I added in some basic print statements to the `get_line_from_offset()` fn and noticed that changing unrelated contracts would adjust the KeyError OoB value for another unrelated contract.


## Set Up
Two contract files:
```js
// ./src/A.sol
pragma solidity ^0.8.4;

contract BaseContract{
uint256 one;
uint256[50] private __gap;
uint256 two;
}

contract DerivedContract is BaseContract{
uint256 three;
uint256[50] private __gap;
uint256 four;
}
```

```js
// ./src/complex_func.sol
pragma solidity ^0.4.24;
// pragma solidity ^0.8.12;

contract Complex {
uint i0 = 0;
uint i1 = 0;
uint i2 = 0;
uint i3 = 0;
uint i4 = 0;
uint i5 = 0;
uint i6 = 0;

function complexStateVars() external {
i0 = 1;
}
}
```

Command:
`slither .`
As I'm in a foundry project, crytic_compile runs command
`forge build --extra-output abi --extra-output userdoc --extra-output devdoc --extra-output evm.methodIdentifiers --force`
under the hood

## Debugging Output
FIrst, I added a print statement to show the file `file offset`, `line`, and `char offset` in the `get_line_from_offset()` fn

https://github.com/crytic/crytic-compile/blob/6bb6e7281b5918789d4045903a7210ecd00e21db/crytic_compile/crytic_compile.py#L273-L292

```python
def get_line_from_offset(self, filename: Union[Filename, str], offset: int) -> Tuple[int, int]:
### snip ###

lines_delimiters = self._cached_offset_to_line[file]

# added this print:
print("File offset: " + str(offset) + ", line: " + str(lines_delimiters[offset][0]) + ", char: " + str(lines_delimiters[offset][1]) + " from file: " + str(file.short))

return lines_delimiters[offset]
```

Which provides this type of output:
```
File offset: 0, line: 1, char: 1 from file: src/complex_func.sol
File offset: 24, line: 1, char: 25 from file: src/complex_func.sol
File offset: 54, line: 4, char: 1 from file: src/complex_func.sol
File offset: 259, line: 16, char: 2 from file: src/complex_func.sol
File offset: 0, line: 1, char: 1 from file: src/A.sol
File offset: 23, line: 1, char: 24 from file: src/A.sol
File offset: 25, line: 3, char: 1 from file: src/A.sol
File offset: 114, line: 7, char: 2 from file: src/A.sol
File offset: 116, line: 9, char: 1 from file: src/A.sol
File offset: 227, line: 14, char: 0 from file: src/A.sol
```

## Where two paths diverge
Running `complex_func.sol` as `solidity ^0.4.24` causes a different output from running it as `solidity ^0.8.12`

Under 0.8.12:
```
File offset: 28, line: 2, char: 1 from file: src/complex_func.sol
File offset: 52, line: 2, char: 25 from file: src/complex_func.sol
File offset: 54, line: 4, char: 1 from file: src/complex_func.sol
File offset: 259, line: 16, char: 2 from file: src/complex_func.sol
File offset: 0, line: 1, char: 1 from file: src/A.sol
File offset: 23, line: 1, char: 24 from file: src/A.sol
File offset: 25, line: 3, char: 1 from file: src/A.sol
File offset: 114, line: 7, char: 2 from file: src/A.sol
File offset: 116, line: 9, char: 1 from file: src/A.sol
File offset: 227, line: 14, char: 0 from file: src/A.sol
File offset: 77, line: 5, char: 5 from file: src/complex_func.sol ##Note: Scope has changed to complex_func for inner level contract nodes!
File offset: 88, line: 5, char: 16 from file: src/complex_func.sol
File offset: 94, line: 6, char: 5 from file: src/complex_func.sol
File offset: 105, line: 6, char: 16 from file: src/complex_func.sol
File offset: 111, line: 7, char: 5 from file: src/complex_func.sol
File offset: 122, line: 7, char: 16 from file: src/complex_func.sol
File offset: 128, line: 8, char: 5 from file: src/complex_func.sol
File offset: 139, line: 8, char: 16 from file: src/complex_func.sol
File offset: 145, line: 9, char: 5 from file: src/complex_func.sol
File offset: 156, line: 9, char: 16 from file: src/complex_func.sol
File offset: 162, line: 10, char: 5 from file: src/complex_func.sol
File offset: 173, line: 10, char: 16 from file: src/complex_func.sol
File offset: 179, line: 11, char: 5 from file: src/complex_func.sol
File offset: 190, line: 11, char: 16 from file: src/complex_func.sol
File offset: 197, line: 13, char: 5 from file: src/complex_func.sol
File offset: 257, line: 15, char: 6 from file: src/complex_func.sol
```

- Compilation succeeds
Seems like sourceMapping goes from:
complex_func - file level
A - file level
complex_func - contract level
A - contract level
...

Under 0.4.24:

```
File offset: 0, line: 1, char: 1 from file: src/complex_func.sol
File offset: 24, line: 1, char: 25 from file: src/complex_func.sol
File offset: 54, line: 4, char: 1 from file: src/complex_func.sol
File offset: 259, line: 16, char: 2 from file: src/complex_func.sol
File offset: 0, line: 1, char: 1 from file: src/A.sol
File offset: 23, line: 1, char: 24 from file: src/A.sol
File offset: 25, line: 3, char: 1 from file: src/A.sol
File offset: 114, line: 7, char: 2 from file: src/A.sol
File offset: 116, line: 9, char: 1 from file: src/A.sol
File offset: 227, line: 14, char: 0 from file: src/A.sol
File offset: 77, line: 5, char: 13 from file: src/A.sol # Note: Why does this stay in file A.sol? And why is it referring to complex_func's offset 77? This is incorrect
File offset: 88, line: 5, char: 24 from file: src/A.sol
File offset: 94, line: 5, char: 30 from file: src/A.sol
File offset: 105, line: 6, char: 10 from file: src/A.sol
File offset: 111, line: 6, char: 16 from file: src/A.sol
File offset: 122, line: 9, char: 7 from file: src/A.sol
File offset: 128, line: 9, char: 13 from file: src/A.sol
File offset: 139, line: 9, char: 24 from file: src/A.sol
File offset: 145, line: 9, char: 30 from file: src/A.sol
File offset: 156, line: 9, char: 41 from file: src/A.sol
File offset: 162, line: 10, char: 5 from file: src/A.sol
File offset: 173, line: 10, char: 16 from file: src/A.sol
File offset: 179, line: 11, char: 3 from file: src/A.sol
File offset: 190, line: 11, char: 14 from file: src/A.sol
File offset: 197, line: 11, char: 21 from file: src/A.sol
```
- Compilation fails
- Appears that after switching to context of A.sol, it remains there and attempts to map complex_func.sol's source to it

Eventually output continues to a traceback saying `KeyError 257`, which is OoB in A.sol source.
Offset 257 happens to coincide with complex_func.sol's ending curly brace of fn `complexStateVars()`

Adding lines/comments to complex_func will relate to a new KeyError offset within A.sol.

I'm not sure where the context switching logic is within crytic_compile, but it seems to be working incorrectly for mismatched solidity versions coming from foundry/hardhat repos.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.