Skip to content

Commit

Permalink
Merge pull request from GHSA-ph9x-4vc9-m39g
Browse files Browse the repository at this point in the history
the routine for aligning call-site posargs and kwargs in
`vyper.codegen.context.lookup_internal_function` was incorrect in cases
where the internal function had more than one default argument - it
consumed default args at the call site from the end instead of the
beginning of the defaults list. this commit fixes and adds some tests
for the alignment routine.
  • Loading branch information
charles-cooper committed May 11, 2023
1 parent 3de1415 commit c3e68c3
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 3 deletions.
62 changes: 62 additions & 0 deletions tests/parser/features/test_internal_call.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import string
from decimal import Decimal

import hypothesis.strategies as st
import pytest
from hypothesis import given, settings

from vyper.compiler import compile_code
from vyper.exceptions import ArgumentException, CallViolation
Expand Down Expand Up @@ -642,3 +645,62 @@ def bar() -> String[6]:
c = get_contract_with_gas_estimation(contract)

assert c.bar() == "hello"


# TODO probably want to refactor these into general test utils
st_uint256 = st.integers(min_value=0, max_value=2**256 - 1)
st_string65 = st.text(max_size=65, alphabet=string.printable)
st_bytes65 = st.binary(max_size=65)
st_sarray3 = st.lists(st_uint256, min_size=3, max_size=3)
st_darray3 = st.lists(st_uint256, max_size=3)

internal_call_kwargs_cases = [
("uint256", st_uint256),
("String[65]", st_string65),
("Bytes[65]", st_bytes65),
("uint256[3]", st_sarray3),
("DynArray[uint256, 3]", st_darray3),
]


@pytest.mark.parametrize("typ1,strategy1", internal_call_kwargs_cases)
@pytest.mark.parametrize("typ2,strategy2", internal_call_kwargs_cases)
def test_internal_call_kwargs(get_contract, typ1, strategy1, typ2, strategy2):
# GHSA-ph9x-4vc9-m39g

@given(kwarg1=strategy1, default1=strategy1, kwarg2=strategy2, default2=strategy2)
@settings(deadline=None, max_examples=5) # len(cases) * len(cases) * 5 * 5
def fuzz(kwarg1, kwarg2, default1, default2):
code = f"""
@internal
def foo(a: {typ1} = {repr(default1)}, b: {typ2} = {repr(default2)}) -> ({typ1}, {typ2}):
return a, b
@external
def test0() -> ({typ1}, {typ2}):
return self.foo()
@external
def test1() -> ({typ1}, {typ2}):
return self.foo({repr(kwarg1)})
@external
def test2() -> ({typ1}, {typ2}):
return self.foo({repr(kwarg1)}, {repr(kwarg2)})
@external
def test3(x1: {typ1}) -> ({typ1}, {typ2}):
return self.foo(x1)
@external
def test4(x1: {typ1}, x2: {typ2}) -> ({typ1}, {typ2}):
return self.foo(x1, x2)
"""
c = get_contract(code)
assert c.test0() == [default1, default2]
assert c.test1() == [kwarg1, default2]
assert c.test2() == [kwarg1, kwarg2]
assert c.test3(kwarg1) == [kwarg1, default2]
assert c.test4(kwarg1, kwarg2) == [kwarg1, kwarg2]

fuzz()
4 changes: 1 addition & 3 deletions vyper/codegen/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,10 +267,8 @@ def _check(cond, s="Unreachable"):
# _check(all(l.typ == r.typ for (l, r) in zip(args_ir, sig.args))

num_provided_kwargs = len(args_ir) - len(sig.base_args)
num_kwargs = len(sig.default_args)
kwargs_needed = num_kwargs - num_provided_kwargs

kw_vals = list(sig.default_values.values())[:kwargs_needed]
kw_vals = list(sig.default_values.values())[num_provided_kwargs:]

return sig, kw_vals

Expand Down

0 comments on commit c3e68c3

Please sign in to comment.