Algo Ssstablecoinsss

AI First Flight #2
Beginner FriendlyDeFi
EXP
View results
Submission Details
Impact: medium
Likelihood: medium
Invalid

Missing Zero and Negative Price Validation in `oracle_lib.vy` Triggers Division by Zero and Protocol Denial of Service

[M-01] Missing Zero and Negative Price Validation in oracle_lib.vy Triggers Division by Zero and Protocol Denial of Service

Summary

The _stale_check_latest_round_data function in oracle_lib.vy validates that updated_at != 0 and checks staleness against a timeout, but omits validating whether price > 0. If a Chainlink aggregator returns zero or negative price, callers converting the price to uint256 trigger division by zero in _get_token_amount_from_usd, resulting in an unhandled denial of service for liquidations.

Vulnerability Details

In src/oracle_lib.vy:

# Lines 27-44
@internal
@view
def _stale_check_latest_round_data(
chainlink_feed: AggregatorV3Interface,
) -> int256:
round_id: uint80 = 0
price: int256 = 0
started_at: uint256 = 0
updated_at: uint256 = 0
answered_in_round: uint80 = 0
(
round_id,
price,
started_at,
updated_at,
answered_in_round,
) = staticcall chainlink_feed.latestRoundData()
if updated_at == 0 or block.timestamp - updated_at > TIMEOUT:
raise "OracleLib__StalePrice"
return price

Notice that price is returned as int256 without verifying price > 0.
In src/dsc_engine.vy, _get_token_amount_from_usd uses the price as a divisor:

# Lines 301-309
@internal
@view
def _get_token_amount_from_usd(token: address, usd_amount_in_wei: uint256) -> uint256:
price_feed: AggregatorV3Interface = AggregatorV3Interface(self.s_price_feeds[token])
price: int256 = price_feed.stale_check_latest_round_data()
return (usd_amount_in_wei * PRECISION) // (
(convert(price, uint256)) * ADDITIONAL_FEED_PRECISION
)

If Chainlink returns 0 (such as during a market flash crash, sequencer halt, or aggregator misconfiguration), convert(0, uint256) * ADDITIONAL_FEED_PRECISION evaluates to 0. The integer division // 0 causes an immediate transaction revert.
If Chainlink returns a negative value, converting int256 to uint256 produces a two's-complement astronomical value, completely corrupting calculations.

Impact

  • Denial of Service: When the oracle returns zero, liquidators cannot liquidate any positions collateralized by the affected asset.

  • Corrupted Accounting: Negative prices cast to uint256 cause massive collateral undervaluation or unexpected reverts.

Proof of Concept

Verified in tests/unit/test_poc_audit.py::test_poc_oracle_zero_price_causes_failure:

def test_poc_oracle_zero_price_causes_failure():
_, dsce, weth, _, eth_usd, _, _, _ = setup_audit_fixture()
eth_usd.updateAnswer(0)
with boa.reverts():
dsce.get_token_amount_from_usd(weth.address, to_wei(100, "ether"))

Tools Used

  • Moccasin v0.4.4

  • Titanoboa v0.2.8

  • Pytest

Recommended Mitigation

Add explicit validation for price > 0 within _stale_check_latest_round_data:

if updated_at == 0 or block.timestamp - updated_at > TIMEOUT:
raise "OracleLib__StalePrice"
+ if price <= 0:
+ raise "OracleLib__InvalidPrice"
return price
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 2 hours ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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

Give us feedback!