#38292 [SC-Medium] Incorrect Sqrt Calculation Result
Was this helpful?
Was this helpful?
Submitted on Dec 30th 2024 at 12:38:02 UTC by @anatomist for
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
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.
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.
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.
https://github.com/vyperlang/vyper/blob/e20c36376e8566184b63b7ed340e4587bfb3735b/vyper/builtins/functions.py#L2133
Already shown in Vulnerability Details section.