DeFiLayer 1Layer 2
14,723 OP
View results
Submission Details
Severity: medium
Invalid

Integer Underflow/Overflow in _smoothed_price (ScrvusdOracleV2.vy)

Summary:

Hi,

I have found out a potential bug in contract 'ScrvusdOracleV2.vy' in which the function _smoothed_price uses unsafe_sub for calculation of raw_price + max_change - last_price which can create unreleable results and ultimately cause integer overflow/underflow issue.

Vulnerability Details:

The key details of this potential vulnerability can be given as follows:

In the function _smoothed_price, it uses unsafe_sub to calculate max_change in which bypassing of vyper's underflow/overflow checks is possible (although it's possible to get negative results in investment value yet it's important to maintain security norms and checks).

@view
def _smoothed_price(last_price: uint256, raw_price: uint256) -> uint256:
# Ideally should be (max_price_increment / 10**18) ** (block.timestamp - self.last_update)
# Using linear approximation to simplify calculations
max_change: uint256 = (
self.max_price_increment * (block.timestamp - self.last_update) * last_price // 10**18
)
# -max_change <= (raw_price - last_price) <= max_change
@> if unsafe_sub(raw_price + max_change, last_price) > 2 * max_change:
return last_price + max_change if raw_price > last_price else last_price - max_change
return raw_price

Impact:

If last_price is too large, subtraction wraps around and mechanism behaves incorrectly which allows the attacker to manipulate prices and bypassing smoothing limits.

**Reference: **https://security.snyk.io/vuln/SNYK-PYTHON-VYPER-5880665

Tools Used:

Manual Code Analysis

Recommendations:

It's good to do some changes in code and get checks for each scenario for price change:

if raw_price + max_change > last_price:
check_price: uint256 = raw_price + max_change - last_price
if check_price > 2 * max_change:
return last_price + max_change if raw_price > last_price else last_price - max_change
return raw_price
else:
check_price: uint256 = last_price - (raw_price + max_change)
if check_price > 2 * max_change:
return last_price
def _smoothed_price(last_price: uint256, raw_price: uint256) -> uint256:
max_change: uint256 = (
self.max_price_increment * (block.timestamp - self.last_update) * last_price // 10**18
)
if raw_price > last_price:
if raw_price - last_price > max_change:
return last_price + max_change
return raw_price
else:
if last_price - raw_price > max_change:
return last_price - max_change
return raw_price
Updates

Lead Judging Commences

0xnevi Lead Judge 5 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
Assigned finding tags:

[invalid] finding-unsafe-sub-underflow

If underflow occurs, it must have meant that `raw_price` has deviated from `last_price` by more than `max_change`, meaning it is correct to restrict the `last_price` increment to `max_change`

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.