crytic / crytic/slither

[Bug]: Inconsistent behavior when setting the type of the return value of abi.decode()

Open
#1,794 0 comments 1 reaction 0 assignees View on GitHub
bug ir
Dominant language
Python
Stars
6.4k
Forks
1.1k
PR merge metrics
No merged PRs in 30d

Description

### Describe the issue:

First case: The type of return variable of `SOLIDITY_CALL abi.decode()` is None when the decoded data type is a single user-defined type, e.g., `(L.E x) = abi.decode(data, (L.E));`.

I am aware of #566, which leaves type None as a temporary solution.

However, I see (case 2) when the `lvalue` is `TupleVariable`, e.g., `(L.E y, uint z) = abi.decode(data, (L.E, uint));`, the reference variable refers to the user-defined type is used as the type of return values.
This is inconsistent with the first case where None is set as type.

Keeping the behavior of type inference consistent would benefit the downstream analyzers a lot.
I hope this can be improved, i.e., also set the type as the reference variable in the first case.

BTW, are you guys still planning to enhance `ReferenceVariable` as mentioned as one of the solutions in #566 in branch `dev-0.7`?

### Code example to reproduce the issue:

```solidity
library L {
enum E { A, B }
}
contract A {
function foo(bytes memory data) public {
(L.E x) = abi.decode(data, (L.E)); // case 1
(L.E y, uint z) = abi.decode(data, (L.E, unit)); // case 2
(uint m) = abi.decode(data, (uint));
}
}
```

### Version:

0.9.3

### Relevant log output:

```shell
Contract A
Function A.foo(bytes) (*)
Expression: x = abi.decode(data,(L.E)) # case 1
IRs:
REF_1(None) -> L.E
TMP_0(None) = SOLIDITY_CALL abi.decode()(data,REF_1)
x(L.E) := TMP_0(None)
Expression: (y,z) = abi.decode(data,(L.E,uint256)) # case 2
IRs:
REF_3(None) -> L.E
TUPLE_0(REF_3,uint256) = SOLIDITY_CALL abi.decode()(data(REF_3,uint256))
y(L.E)= UNPACK TUPLE_0 index: 0
z(uint256)= UNPACK TUPLE_0 index: 1
Expression: m = abi.decode(data,(uint256))
IRs:
TMP_1(uint256) = SOLIDITY_CALL abi.decode()(data,uint256)
m(uint256) := TMP_1(uint256)
```

Contributor guide

Open the contributing guide

Research direction

Reproduce the two abi.decode() examples from the issue and compare their emitted IR, especially the SOLIDITY_CALL and ReferenceVariable handling. Trace the code path for single-value and tuple return types, then verify that both forms assign consistent user-defined types and that the existing uint case remains unchanged.

Written by the indexing model from the issue text.

Assessment

Tech stack
python, solidity
Domain
devtools
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.