From 4595938734d9988f8e46e8df38049ae0559abedb Mon Sep 17 00:00:00 2001 From: Charles Cooper Date: Tue, 26 Mar 2024 17:21:18 -0400 Subject: [PATCH] fix[ux]: fix compiler hang for large exponentiations (#3893) this commit fixes a UX bug where the compiler would hang while trying to constant-fold large exponentiations. it does a loose check to see if the output would be vastly out of bounds and fails fast if so. --- .../codegen/types/numbers/test_exponents.py | 12 ++++++++++++ vyper/ast/nodes.py | 10 ++++++++++ 2 files changed, 22 insertions(+) diff --git a/tests/functional/codegen/types/numbers/test_exponents.py b/tests/functional/codegen/types/numbers/test_exponents.py index e958436efb..2975f839e5 100644 --- a/tests/functional/codegen/types/numbers/test_exponents.py +++ b/tests/functional/codegen/types/numbers/test_exponents.py @@ -3,6 +3,18 @@ from hypothesis import strategies as st from vyper.codegen.arithmetic import calculate_largest_base, calculate_largest_power +from vyper.compiler import compile_code +from vyper.exceptions import InvalidLiteral + + +def test_compiler_hang(): + code = """ +@external +def f0(): + lv0: uint256 = max_value(int128) ** max_value(int128) + """ + with pytest.raises(InvalidLiteral): + compile_code(code) @pytest.mark.fuzzing diff --git a/vyper/ast/nodes.py b/vyper/ast/nodes.py index 5079303cd3..4ba2d1a593 100644 --- a/vyper/ast/nodes.py +++ b/vyper/ast/nodes.py @@ -3,6 +3,7 @@ import copy import decimal import functools +import math import operator import pickle import sys @@ -1080,6 +1081,15 @@ def _op(self, left, right): raise TypeMismatch("Cannot perform exponentiation on decimal values.", self._parent) if right < 0: raise InvalidOperation("Cannot calculate a negative power", self._parent) + # prevent a compiler hang. we are ok with false positives at this + # stage since we are just trying to filter out inputs which can cause + # the compiler to hang. the others will get caught during constant + # folding or codegen. + # l**r > 2**256 + # r * ln(l) > ln(2 ** 256) + # r > ln(2 ** 256) / ln(l) + if right > math.log(decimal.Decimal(2**257)) / math.log(decimal.Decimal(left)): + raise InvalidLiteral("Out of bounds", self) return int(left**right)