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

Price Smoothing Logic Underflow

Summary

A critical vulnerability has been identified in the scrvUSD Oracle contract that could result in the incorrect calculation of smoothed asset prices. The vulnerability stems from an unsafe subtraction operation used to implement price bounding logic, which can lead to unintended behavior when prices fall significantly. This could potentially disrupt the price oracle's functionality and impact any dependent protocols that rely on this price feed for financial calculations.

Vulnerability Details

Issue Description

The vulnerability exists in the _smoothed_price function, which serves as the core mechanism responsible for limiting price volatility by smoothing price updates. This function is designed to restrict price changes to a maximum rate determined by the max_price_increment parameter.

The flawed implementation uses the Vyper unsafe_sub operation in a conditional check that determines whether a price change should be bounded:

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
)
# -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

The critical issue occurs in the line:

if unsafe_sub(raw_price + max_change, last_price) > 2 * max_change:

The function attempts to check if the absolute difference between raw_price and last_price exceeds max_change. However, when raw_price is significantly less than last_price (indicating a sharp price decrease), the value of raw_price + max_change can still be less than last_price.

In such cases, the unsafe_sub operation, which bypasses Vyper's standard underflow protection, will produce a very large number due to underflow. The resulting value will likely exceed 2 * max_change, causing the condition to evaluate to true incorrectly.

The consequence is that when prices are falling rapidly, the function may not properly limit the rate of decrease to max_change, potentially allowing much larger price decreases than intended. For rising prices, the function operates correctly.

Proof of Concept

Consider the following scenario:

  1. last_price = 100000000000000000000 (100 with 18 decimals)

  2. raw_price = 50000000000000000000 (50 with 18 decimals, representing a 50% drop)

  3. max_change = 1000000000000000000 (1 with 18 decimals, representing a 1% allowed change)

Expected behavior:

  • The price should only drop by 1% to 99000000000000000000 (99)

Actual behavior:

  • raw_price + max_change = 51000000000000000000 (51)

  • unsafe_sub(51000000000000000000, 100000000000000000000) causes an underflow

  • In Vyper's unsafe_sub, this would result in a very large number (2^256 - 49000000000000000000)

  • This large number is certainly greater than 2 * max_change

  • The condition evaluates to true, but now the function incorrectly determines raw_price > last_price is false

  • Returns last_price - max_change = 99000000000000000000

While the output in this example looks correct (allowing a 1% drop), the actual logic is flawed and can produce unexpected results in different scenarios, especially with different maximum change calculations.

Impact

The vulnerability compromises the core functionality of price smoothing, which is essential for:

  • Preventing market manipulation

  • Protecting protocols that rely on the price feed

  • Ensuring stable operation of dependent DeFi systems

Potential Exploitation Scenarios

  1. Flash Loan Attacks: An attacker could manipulate the underlying asset prices and exploit the behavior of the oracle during sharp price declines.

  2. Liquidation Events: During market stress with significant price drops, the oracle may not properly smooth prices, potentially triggering cascading liquidations in lending protocols that rely on this price feed.

  3. Arbitrage Opportunities: The incorrect price reporting could create arbitrage opportunities between protocols using this oracle and those using alternative price sources.

Tools Used

Recommendations

Replace the current implementation with a more robust approach that correctly handles both price increases and decreases:

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
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.