vyperlang / vyper

Pythonic Smart Contract Language for the EVM
https://vyperlang.org
Other
4.85k stars 791 forks source link

Multiple Evaluations of the Target of Byte and Dynamic Array Assignments #4071

Open ritzdorf opened 5 months ago

ritzdorf commented 5 months ago

Version Information

Multiple evaluations of the target of byte and dynamic array assignments have been described in issue 3514. However, this issue was incomplete since it would only show the case where the complex expression being evaluated twice is caught by the compiler's "non-unique symbol" sanity check. However, it is possible to have a complex expression evaluated twice without the compiler crashing by using builtins like raw_call or the create_ class or .pop() on a non-storage array.

Note that this issue has been partially mitigated by 3835 given that builtins like raw_call and the create_ class are now emitting unique symbols, however, this is not yet the case for .pop() when the array is not in storage.

POC

The following code will compile successfully, and x.pop() will be performed twice:

d:DynArray[DynArray[uint256,1],1]
addr:address
@external
def foo() -> DynArray[uint256, 2]:
    self.d = [[0]]
    x:DynArray[uint256, 2] = [0,0]
    self.d[x.pop()] = [1]
    return x # return [] instead of [0]
ritzdorf commented 5 months ago

We also found an example using a HashMap now:

#@version 0.4.0.rc3
m: HashMap[uint256, String[33]]

@external
def foo(a: address):
    self.m[len(raw_call(a, msg.data, max_outsize=32))] = "Hello world"

fails to compile with

AssertionError: non-unique symbols {'raw_call_builtin102'}
charles-cooper commented 4 months ago

addressed in https://github.com/vyperlang/vyper/pull/4030