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

Division by Zero in _obtain_price_params: Oracle Paralysis from Zero Period

Summary

The scrvUSD oracle system contains a critical vulnerability in the calculation logic. If profit_max_unlock_time is set to zero, it would cause a division by zero error in the _obtain_price_params function when calculating number_of_periods. This occurs in the operation:

`number_of_periods = (parameters_ts - params.last_profit_update) // period`

Since period would be zero, this division operation would revert, causing a permanent denial of service for all oracle price calculations. This would effectively paralyze the entire oracle system, potentially affecting any dependent financial operations.

The vulnerability has high severity due to:

  • System-wide impact on core oracle functionality

  • Potential financial implications for dependent contracts

  • Persistent failure state requiring contract updates

  • Complete denial of service for price queries

Adding a simple validation check to ensure profit_max_unlock_time is always greater than zero would effectively mitigate this vulnerability.

Vulnerability Details

Division by Zero in _obtain_price_params

Description

The scrvUSD oracle implementation has a critical vulnerability that can cause a division by zero error. This occurs in the _obtain_price_params function where the code calculates the number of periods using the following division operation:

`number_of_periods: uint256 = min( (parameters_ts - params.last_profit_update) // period, self.max_v2_duration, )`

In this calculation, period is set to the value of self.profit_max_unlock_time. If profit_max_unlock_time is set to zero (which is possible through the update_profit_max_unlock_time function), this would cause a division by zero error, reverting all transactions that execute this code path.

Root Cause

The vulnerability stems from the absence of validation checks in the update_profit_max_unlock_time function:

`@external def update_profit_max_unlock_time(_profit_max_unlock_time: uint256, _block_number: uint256) -> bool: access_control._check_role(UNLOCK_TIME_VERIFIER, msg.sender) assert self.last_block_number <= _block_number, "Outdated" self.last_block_number = _block_number prev_value: uint256 = self.profit_max_unlock_time self.profit_max_unlock_time = _profit_max_unlock_time return prev_value != _profit_max_unlock_time`

This function accepts any value for _profit_max_unlock_time, including zero, without performing boundary validation.

Impact

The impact of this vulnerability is severe for several reasons:

  1. Complete Oracle Failure:

    • The _obtain_price_params function is called by all price calculation methods (price_v1, price_v2, raw_price).

    • A zero period would make all these functions revert, causing a total denial of service for the oracle.

  2. Permanent System Breakdown:

    • Once profit_max_unlock_time is set to zero, the oracle becomes unusable until a contract upgrade or migration is performed.

    • This would require governance intervention and could lead to significant downtime.

  3. Financial Implications:

    • As an oracle for scrvUSD share price, this component likely serves critical financial infrastructure.

    • Any systems relying on this oracle for pricing or valuation would be affected.

    • This could potentially freeze assets or break dependent protocols.

  4. Difficult Recovery:

    • Recovering from this state would require a privileged transaction to reset the value.

    • If the issue is discovered during an attack, there could be a race condition between attackers and defenders.

Reproduction Steps

  1. An address with the UNLOCK_TIME_VERIFIER role calls update_profit_max_unlock_time(0, current_block_number).

  2. The profit_max_unlock_time is set to 0.

  3. Any subsequent call to price_v1, price_v2, or raw_price that triggers the _obtain_price_params function will revert due to division by zero.

Tools Used

manual review

Recommendations

`

@external
def update_profit_max_unlock_time(_profit_max_unlock_time: uint256, _block_number: uint256) -> bool:
"""
@notice Update price using `_parameters`
@param _profit_max_unlock_time New `profit_max_unlock_time` value
@param _block_number Block number of parameters to linearize updates
@return Boolean whether value changed
"""
access_control._check_role(UNLOCK_TIME_VERIFIER, msg.sender)
# Allowing same block updates for fixing bad blockhash provided (if possible)
assert self.last_block_number <= _block_number, "Outdated"
self.last_block_number = _block_number
# Validación para asegurar que _profit_max_unlock_time sea mayor que cero
assert _profit_max_unlock_time > 0, "profit_max_unlock_time debe ser mayor que cero"
prev_value: uint256 = self.profit_max_unlock_time
self.profit_max_unlock_time = _profit_max_unlock_time
return prev_value != _profit_max_unlock_time

`

Updates

Lead Judging Commences

0xnevi Lead Judge
6 months ago
0xnevi Lead Judge 5 months ago
Submission Judgement Published
Invalidated
Reason: Out of scope

Support

FAQs

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