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

DoS via Zero Division in Exponentiation (Pow) Handling in vyper compiler #4395

Open
eternalsakura13 opened this issue Dec 13, 2024 · 0 comments
Labels
needs triage needs triage

Comments

@eternalsakura13
Copy link

Brief/Intro

A potential bug exists in the Vyper compiler's constant folding logic, specifically within the Pow operator implementation in vyper/ast/nodes.py. When the compiler attempts to evaluate certain constant expressions at compile-time, it uses logarithmic checks to ensure exponentiation results remain within safe bounds. However, this logic is susceptible to a division by zero error under certain conditions.

Details

https://github.com/vyperlang/vyper/blob/master/vyper/ast/nodes.py#L1123

In the Pow class, there is a check to prevent generating out-of-bounds exponentiation results. This check involves computing:

// vyper/vyper/ast/nodes.py
class Pow(Operator):
    __slots__ = ()
    _description = "exponentiation"
    _pretty = "**"

    def _op(self, left, right):
        if isinstance(left, decimal.Decimal):
            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)): // ------------->[0]
            raise InvalidLiteral("Out of bounds", self)

        return int(left**right)

This calculation poses a problem: If left == 1, then math.log(decimal.Decimal(1)) equals 0, resulting in a division by zero.

In the case, a carefully crafted input causing the compiler to evaluate 1 ** number, could trigger these exceptions. The compiler might then crash unexpectedly during constant folding.

Impcat

The Vyper compiler should normally output 1, since 1 raised to any power is always 1. However, it reports a division-by-zero error in this case, which is clearly unexpected.

PoC

@external
def foo(b: int256) -> int256:
    return 1**10

test

@eternalsakura13 eternalsakura13 added the needs triage needs triage label Dec 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs triage needs triage
Projects
None yet
Development

No branches or pull requests

1 participant