Summary
The getOpenInterest function contains divisions that could result in division by zero if perpMarket.skew or perpMarket.openInterest equals zero.
Vulnerability Details
Code Reference:
function getOpenInterest(uint128 marketId)
external
view
returns (UD60x18 longsOpenInterest, UD60x18 shortsOpenInterest, UD60x18 totalOpenInterest)
{
PerpMarket.Data storage perpMarket = PerpMarket.load(marketId);
SD59x18 halfSkew = sd59x18(perpMarket.skew).div(sd59x18(2e18));
SD59x18 currentOpenInterest = ud60x18(perpMarket.openInterest).intoSD59x18();
SD59x18 halfOpenInterest = currentOpenInterest.div(sd59x18(2e18));
longsOpenInterest = halfOpenInterest.add(halfSkew).lt(SD59x18_ZERO)
? UD60x18_ZERO
: halfOpenInterest.add(halfSkew).intoUD60x18();
shortsOpenInterest = unary(halfOpenInterest).add(halfSkew).abs().intoUD60x18();
totalOpenInterest = longsOpenInterest.add(shortsOpenInterest);
}
Proof of Concept:
To demonstrate the risk of division by zero, consider the following steps:
Deployment and Initialization:
uint128 marketId = 1;
(UD60x18 longsOpenInterest, UD60x18 shortsOpenInterest, UD60x18 totalOpenInterest) = perpMarketBranchInstance.getOpenInterest(marketId);
Scenario 2 - Zero Open Interest:
uint128 marketId = 1;
(UD60x18 longsOpenInterest, UD60x18 shortsOpenInterest, UD60x18 totalOpenInterest) = perpMarketBranchInstance.getOpenInterest(marketId);
-
Expected Behavior:
Return calculated open interests without errors.
-
Actual Behavior:
Encounters a division by zero error, causing runtime exceptions.
Impact
Low.
Division by zero can cause critical runtime errors, making open interest calculations unreliable and potentially halting market operations.
Tools Used
Manual code review
Recommendations
Implement a check to ensure indexPrice is greater than zero:
function getOpenInterest(uint128 marketId)
external
view
returns (UD60x18 longsOpenInterest, UD60x18 shortsOpenInterest, UD60x18 totalOpenInterest)
{
PerpMarket.Data storage perpMarket = PerpMarket.load(marketId);
+ if (perpMarket.skew == 0 || perpMarket.openInterest == 0) {
+ return (UD60x18_ZERO, UD60x18_ZERO, UD60x18_ZERO);
}
SD59x18 halfSkew = sd59x18(perpMarket.skew).div(sd59x18(2e18));
SD59x18 currentOpenInterest = ud60x18(perpMarket.openInterest).intoSD59x18();
SD59x18 halfOpenInterest = currentOpenInterest.div(sd59x18(2e18));
longsOpenInterest = halfOpenInterest.add(halfSkew).lt(SD59x18_ZERO)
? UD60x18_ZERO
: halfOpenInterest.add(halfSkew).intoUD60x18();
shortsOpenInterest = unary(halfOpenInterest).add(halfSkew).abs().intoUD60x18();
totalOpenInterest = longsOpenInterest.add(shortsOpenInterest);
}