From 98f333d9d9f08d279052d80b0484724ea8a3eea2 Mon Sep 17 00:00:00 2001 From: Charles Cooper Date: Thu, 16 May 2024 07:46:37 -0400 Subject: [PATCH 1/6] fix[venom]: fix some sccp evaluations some sccp operations were not wrapped in `wrap_*op`, so they could panic on input which is out of range. this commit wraps all the remaining sccp evaluators for well-formedness. --- vyper/venom/passes/sccp/eval.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/vyper/venom/passes/sccp/eval.py b/vyper/venom/passes/sccp/eval.py index cd4243e998..aa9565523a 100644 --- a/vyper/venom/passes/sccp/eval.py +++ b/vyper/venom/passes/sccp/eval.py @@ -46,6 +46,16 @@ def wrapper(ops: list[IROperand]) -> int: return wrapper +def _wrap_unop(operation): + def wrapper(ops: list[IROperand]) -> int: + value = _signed_to_unsigned(ops[0].value) + ret = operation(value) + assert isinstance(ret, int) + return ret & SizeLimits.MAX_UINT256 + + return wrapper + + def _evm_signextend(ops: list[IROperand]) -> int: value = ops[0].value nbytes = ops[1].value @@ -121,11 +131,11 @@ def _evm_not(ops: list[IROperand]) -> int: "or": _wrap_binop(operator.or_), "and": _wrap_binop(operator.and_), "xor": _wrap_binop(operator.xor), - "not": _evm_not, - "signextend": _evm_signextend, - "iszero": _evm_iszero, - "shr": _evm_shr, - "shl": _evm_shl, - "sar": _evm_sar, + "not": _wrap_unop(_evm_not), + "signextend": _wrap_binop(_evm_signextend), + "iszero": _wrap_unop(_evm_iszero), + "shr": _wrap_binop(_evm_shr), + "shl": _wrap_binop(_evm_shl), + "sar": _wrap_signed_binop(_evm_sar), "store": lambda ops: ops[0].value, } From 17d3142fec051e56e7602633a1ef0836c22ad20d Mon Sep 17 00:00:00 2001 From: Charles Cooper Date: Thu, 16 May 2024 12:36:30 -0400 Subject: [PATCH 2/6] fix wrapped functions --- vyper/venom/passes/sccp/eval.py | 44 ++++++++++++++------------------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/vyper/venom/passes/sccp/eval.py b/vyper/venom/passes/sccp/eval.py index aa9565523a..7b76bc232e 100644 --- a/vyper/venom/passes/sccp/eval.py +++ b/vyper/venom/passes/sccp/eval.py @@ -13,34 +13,31 @@ def _unsigned_to_signed(value: int) -> int: - if value <= SizeLimits.MAX_INT256: - return value # fast exit - else: - return unsigned_to_signed(value, 256) + assert isinstance(value, int) + return unsigned_to_signed(value, 256) def _signed_to_unsigned(value: int) -> int: - if value >= 0: - return value # fast exit - else: - return signed_to_unsigned(value, 256) + assert isinstance(value, int) + return signed_to_unsigned(value, 256) def _wrap_signed_binop(operation): def wrapper(ops: list[IROperand]) -> int: + assert len(ops) == 2 first = _unsigned_to_signed(ops[1].value) second = _unsigned_to_signed(ops[0].value) - return _signed_to_unsigned(int(operation(first, second))) + return _signed_to_unsigned(operation(first, second)) return wrapper def _wrap_binop(operation): def wrapper(ops: list[IROperand]) -> int: + assert len(ops) == 2 first = _signed_to_unsigned(ops[1].value) second = _signed_to_unsigned(ops[0].value) ret = operation(first, second) - assert isinstance(ret, int) return ret & SizeLimits.MAX_UINT256 return wrapper @@ -48,23 +45,22 @@ def wrapper(ops: list[IROperand]) -> int: def _wrap_unop(operation): def wrapper(ops: list[IROperand]) -> int: + assert len(ops) == 1 value = _signed_to_unsigned(ops[0].value) ret = operation(value) - assert isinstance(ret, int) return ret & SizeLimits.MAX_UINT256 return wrapper -def _evm_signextend(ops: list[IROperand]) -> int: - value = ops[0].value - nbytes = ops[1].value - +def _evm_signextend(value, nbytes) -> int: assert 0 <= value <= SizeLimits.MAX_UINT256, "Value out of bounds" if nbytes > 31: return value + assert nbytes >= 0 + sign_bit = 1 << (nbytes * 8 + 7) if value & sign_bit: value |= SizeLimits.CEILING_UINT256 - sign_bit @@ -80,31 +76,27 @@ def _evm_iszero(ops: list[IROperand]) -> int: return int(value == 0) # 1 if True else 0 -def _evm_shr(ops: list[IROperand]) -> int: - value = ops[0].value - shift_len = ops[1].value +def _evm_shr(value: int, shift_len: int) -> int: assert 0 <= value <= SizeLimits.MAX_UINT256, "Value out of bounds" + assert shift_len >= 0 return value >> shift_len -def _evm_shl(ops: list[IROperand]) -> int: - value = ops[0].value - shift_len = ops[1].value +def _evm_shl(value: int, shift_len: int) -> int: assert 0 <= value <= SizeLimits.MAX_UINT256, "Value out of bounds" if shift_len >= 256: return 0 + assert shift_len >= 0 return (value << shift_len) & SizeLimits.MAX_UINT256 -def _evm_sar(ops: list[IROperand]) -> int: - value = _unsigned_to_signed(ops[0].value) +def _evm_sar(value: int, shift_len: int) -> int: assert SizeLimits.MIN_INT256 <= value <= SizeLimits.MAX_INT256, "Value out of bounds" - shift_len = ops[1].value + assert shift_len >= 0 return value >> shift_len -def _evm_not(ops: list[IROperand]) -> int: - value = ops[0].value +def _evm_not(value: int) -> int: assert 0 <= value <= SizeLimits.MAX_UINT256, "Value out of bounds" return SizeLimits.MAX_UINT256 ^ value From fe699f17af9d694e04b6f6018019458b2b34781e Mon Sep 17 00:00:00 2001 From: Charles Cooper Date: Thu, 16 May 2024 16:41:19 +0000 Subject: [PATCH 3/6] fix another signature --- vyper/venom/passes/sccp/eval.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/vyper/venom/passes/sccp/eval.py b/vyper/venom/passes/sccp/eval.py index 7b76bc232e..09240abcb2 100644 --- a/vyper/venom/passes/sccp/eval.py +++ b/vyper/venom/passes/sccp/eval.py @@ -70,8 +70,7 @@ def _evm_signextend(value, nbytes) -> int: return value -def _evm_iszero(ops: list[IROperand]) -> int: - value = ops[0].value +def _evm_iszero(value: int) -> int: assert SizeLimits.MIN_INT256 <= value <= SizeLimits.MAX_UINT256, "Value out of bounds" return int(value == 0) # 1 if True else 0 From c6eef21612c9b9287b6dbae1eb90ef2dcd09a11f Mon Sep 17 00:00:00 2001 From: Charles Cooper Date: Thu, 16 May 2024 16:48:10 +0000 Subject: [PATCH 4/6] fix a static assertion --- tests/functional/codegen/types/numbers/test_signed_ints.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/functional/codegen/types/numbers/test_signed_ints.py b/tests/functional/codegen/types/numbers/test_signed_ints.py index 608783000a..42224fb106 100644 --- a/tests/functional/codegen/types/numbers/test_signed_ints.py +++ b/tests/functional/codegen/types/numbers/test_signed_ints.py @@ -163,15 +163,14 @@ def negative_three() -> {typ}: return -(1+2) @external -def negative_four() -> {typ}: - a: {typ} = 2 +def negative_four(a: {typ}) -> {typ}: return -(a+2) """ c = get_contract(negative_nums_code) assert c.negative_one() == -1 assert c.negative_three() == -3 - assert c.negative_four() == -4 + assert c.negative_four(2) == -4 @pytest.mark.parametrize("typ", types) From 3274789bc412d1c357d5f3f195bc03fac7faa112 Mon Sep 17 00:00:00 2001 From: Charles Cooper Date: Thu, 16 May 2024 12:54:42 -0400 Subject: [PATCH 5/6] Revert "fix a static assertion" This reverts commit c6eef21612c9b9287b6dbae1eb90ef2dcd09a11f. --- tests/functional/codegen/types/numbers/test_signed_ints.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/functional/codegen/types/numbers/test_signed_ints.py b/tests/functional/codegen/types/numbers/test_signed_ints.py index 42224fb106..608783000a 100644 --- a/tests/functional/codegen/types/numbers/test_signed_ints.py +++ b/tests/functional/codegen/types/numbers/test_signed_ints.py @@ -163,14 +163,15 @@ def negative_three() -> {typ}: return -(1+2) @external -def negative_four(a: {typ}) -> {typ}: +def negative_four() -> {typ}: + a: {typ} = 2 return -(a+2) """ c = get_contract(negative_nums_code) assert c.negative_one() == -1 assert c.negative_three() == -3 - assert c.negative_four(2) == -4 + assert c.negative_four() == -4 @pytest.mark.parametrize("typ", types) From b94bcb059ef80f8449b3a158961a75815c30e1c9 Mon Sep 17 00:00:00 2001 From: Charles Cooper Date: Thu, 16 May 2024 12:56:25 -0400 Subject: [PATCH 6/6] fix argument order --- vyper/venom/passes/sccp/eval.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/vyper/venom/passes/sccp/eval.py b/vyper/venom/passes/sccp/eval.py index 09240abcb2..b5786bb304 100644 --- a/vyper/venom/passes/sccp/eval.py +++ b/vyper/venom/passes/sccp/eval.py @@ -53,7 +53,7 @@ def wrapper(ops: list[IROperand]) -> int: return wrapper -def _evm_signextend(value, nbytes) -> int: +def _evm_signextend(nbytes, value) -> int: assert 0 <= value <= SizeLimits.MAX_UINT256, "Value out of bounds" if nbytes > 31: @@ -75,13 +75,13 @@ def _evm_iszero(value: int) -> int: return int(value == 0) # 1 if True else 0 -def _evm_shr(value: int, shift_len: int) -> int: +def _evm_shr(shift_len: int, value: int) -> int: assert 0 <= value <= SizeLimits.MAX_UINT256, "Value out of bounds" assert shift_len >= 0 return value >> shift_len -def _evm_shl(value: int, shift_len: int) -> int: +def _evm_shl(shift_len: int, value: int) -> int: assert 0 <= value <= SizeLimits.MAX_UINT256, "Value out of bounds" if shift_len >= 256: return 0 @@ -89,7 +89,7 @@ def _evm_shl(value: int, shift_len: int) -> int: return (value << shift_len) & SizeLimits.MAX_UINT256 -def _evm_sar(value: int, shift_len: int) -> int: +def _evm_sar(shift_len: int, value: int) -> int: assert SizeLimits.MIN_INT256 <= value <= SizeLimits.MAX_INT256, "Value out of bounds" assert shift_len >= 0 return value >> shift_len