#38292 [SC-Medium] Incorrect Sqrt Calculation Result
Submitted on Dec 30th 2024 at 12:38:02 UTC by @anatomist for Attackathon | Ethereum Protocol
Report ID: #38292
Report Type: Smart Contract
Report severity: Medium
Target: https://github.com/vyperlang/vyper
Impacts:
(Compiler) Incorrect bytecode generation leading to incorrect behavior
(Compiler) Unexpected behavior
Description
Brief/Intro
Vyper sqrt
builtin uses the babylonian method to calculate square roots of decimals. Unfortunately, improper handling of the oscillating final states may lead to sqrt
incorrectly returning rounded up results.
Vulnerability Details
Vyper injects the following code to handle calculation of decimal sqrt
. x
is the input provided by user.
Notably, the terminal condition of the algorithm is either z_cur == z_prev
or the algorithm runs for 256 rounds. The expectation here is that the babylonian method should converge within 256 iterations.
However, if we look carefully at the babylonian algorithm, there's a problem with our usage. It does not guarantee to converge to a single value. For certain inputs, the z
might actually oscillate between N
and N + epsilon
, where N ** 2 <= x < (N + epsilon) ** 2
. To explain this better, let's look at the following analysis.
When we have z_cur = N
, the possible z_next
are
When we have z_cur = N + epsilon
, the range of the next possible z_next
are
Now we've shown that it is theoretically possible to have oscillating outputs. To further support this analysis, we provide a concrete example where such oscillation happens.
The snippet here returns 0.9999999999
, the rounded up result for sqrt(0.9999999998)
. This is due to the oscillation ending in N + epsilon
instead of the correct N
.
The most intuitive fix is to compare y
and z
after breaking out of the loop, and selecting the smaller of the two. This is the approach used in isqrt
. It's probably also useful to formally prove convergence (at least up to the oscillating state) within 256 iterations if that's not yet done.
Impact Details
Precision mishandling and rounding in incorrect directions have been a notorious bug class, and also the root cause of several high profile attacks. These kind of issues have been especially devastating to AMMs that relies on precise tracking of values boundaries (e.g. tick boundaries in uni-v3). So while the sqrt
miscalculation only happens in a limited set of input, we still believe it is a serious issue due to its potential to introduce subtle bugs in critical business logic.
References
https://github.com/vyperlang/vyper/blob/e20c36376e8566184b63b7ed340e4587bfb3735b/vyper/builtins/functions.py#L2133
Proof of Concept
Proof of Concept
Already shown in Vulnerability Details section.
Was this helpful?