Skip to content
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

fix[codegen]: add back in returndatasize check #4144

Merged
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions tests/functional/builtins/codegen/test_abi_decode.py
Original file line number Diff line number Diff line change
Expand Up @@ -1421,3 +1421,23 @@ def foo(a:Bytes[1000]):

with tx_failed():
c.foo(_abi_payload_from_tuple(payload))


# returndatasize check for uint256
def test_returndatasize_check(get_contract, tx_failed):
code = """
@external
def bar():
pass

interface A:
def bar() -> uint256: nonpayable

@external
def run() -> uint256:
return extcall A(self).bar()
"""
c = get_contract(code)

with tx_failed():
c.run()
25 changes: 0 additions & 25 deletions vyper/abi_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,6 @@ def embedded_dynamic_size_bound(self):
return 0
return self.size_bound()

def embedded_min_dynamic_size(self):
if not self.is_dynamic():
return 0
return self.min_size()

# size (in bytes) of the static section
def static_size(self):
raise NotImplementedError("ABIType.static_size")
Expand All @@ -42,14 +37,6 @@ def dynamic_size_bound(self):
def size_bound(self):
return self.static_size() + self.dynamic_size_bound()

def min_size(self):
return self.static_size() + self.min_dynamic_size()

def min_dynamic_size(self):
if not self.is_dynamic():
return 0
raise NotImplementedError("ABIType.min_dynamic_size")

# The canonical name of the type for calculating the function selector
def selector_name(self):
raise NotImplementedError("ABIType.selector_name")
Expand Down Expand Up @@ -158,9 +145,6 @@ def static_size(self):
def dynamic_size_bound(self):
return self.m_elems * self.subtyp.embedded_dynamic_size_bound()

def min_dynamic_size(self):
return self.m_elems * self.subtyp.embedded_min_dynamic_size()

def selector_name(self):
return f"{self.subtyp.selector_name()}[{self.m_elems}]"

Expand All @@ -187,9 +171,6 @@ def dynamic_size_bound(self):
# length word + data
return 32 + ceil32(self.bytes_bound)

def min_dynamic_size(self):
return 32

def selector_name(self):
return "bytes"

Expand Down Expand Up @@ -222,9 +203,6 @@ def dynamic_size_bound(self):
# length + size of embedded children
return 32 + subtyp_size * self.elems_bound

def min_dynamic_size(self):
return 32

def selector_name(self):
return f"{self.subtyp.selector_name()}[]"

Expand All @@ -245,9 +223,6 @@ def static_size(self):
def dynamic_size_bound(self):
return sum([t.embedded_dynamic_size_bound() for t in self.subtyps])

def min_dynamic_size(self):
return sum([t.embedded_min_dynamic_size() for t in self.subtyps])

def is_complex_type(self):
return True

Expand Down
13 changes: 12 additions & 1 deletion vyper/codegen/external_call.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,9 @@ def _unpack_returndata(buf, fn_type, call_kwargs, contract_address, context, exp

abi_return_t = wrapped_return_t.abi_type

min_return_size = abi_return_t.static_size()
max_return_size = abi_return_t.size_bound()
assert 0 <= max_return_size
assert 0 < min_return_size <= max_return_size

ret_ofst = buf
ret_len = max_return_size
Expand Down Expand Up @@ -118,6 +119,16 @@ def _unpack_returndata(buf, fn_type, call_kwargs, contract_address, context, exp
b1.resolve(make_setter(return_buf, buf, hi=add_ofst(buf, payload_bound)))
)
else:
# revert when returndatasize is not in bounds
# (except when return_override is provided.)
# make_setter checks returndatasize, except when needs_clamp is False.
cyberthirst marked this conversation as resolved.
Show resolved Hide resolved
if not call_kwargs.skip_contract_check:
assertion = IRnode.from_list(
["assert", ["ge", "returndatasize", min_return_size]],
error_msg="returndatasize too small",
)
unpacker.append(assertion)

return_buf = buf

if call_kwargs.default_return_value is not None:
Expand Down
Loading