Skip to content

Tricky Navy Bird - Complete Market DoS via Division by Zero in utilizationRate() Function When Reserves Exceed or Equal Available Cash #698 #1478

@Sahil2389

Description

@Sahil2389

Tricky Navy Bird

High

Complete Market DoS via Division by Zero in utilizationRate() Function When Reserves Exceed or Equal Available Cash

Summary

The utilizationRate() function contains a critical mathematical flaw that allows attackers to permanently disable entire lending markets. When reserves equal or exceed the sum of cash and borrows, the function either reverts due to arithmetic underflow or division by zero, making all market operations impossible.

Root Cause

Two Failure Scenarios:
Arithmetic Underflow (when reserves > cash + borrows)

Denominator becomes negative: (cash + borrows - reserves) < 0
Solidity 0.8+ reverts on underflow
Division by Zero (when reserves = cash + borrows)

Denominator becomes zero: (cash + borrows - reserves) = 0
Division by zero causes revert
function utilizationRate(uint256 cash, uint256 borrows, uint256 reserves) public pure override returns (uint256) {
if (borrows == 0) {
return 0;
}
return borrows * 1e18 / (cash + borrows - reserves); // ← VULNERABLE LINE
}
code snippet -https://github.com/sherlock-audit/2025-07-malda/blob/798d00b879b8412ca4049ba09dba5ae42464cfe7/malda-lending/src/interest/JumpRateModelV4.sol#L96-L109

Internal Pre-conditions

Market is empty (cash=0, borrows=0, reserves=0).

External Pre-conditions

No one mint any token on that market.
Attack Path
Simple Attack (Empty Market):
Step 1: Market starts empty (cash=0, borrows=0, reserves=0)

Step 2: Attacker donates X tokens via _addReserves()
→ cash = X, borrows = 0, reserves = X

Step 3: Attacker supplies collateral in another market

Step 4: Attacker borrows small amount B from target market
→ cash = X-B, borrows = B, reserves = X
→ Denominator = (X-B) + B - X = 0

Where B is as small as possible [1] . Because interest will acquire over the time. Attacker will borrow small amount to DoS for long period of time.

Step 5: Market is now DoSed.

Mathematical Example:

Initial: cash = 0, borrows = 0, reserves = 0
After Donate: cash = 1000, borrows = 0, reserves = 1000
After Borrow: cash = 999, borrows = 1, reserves = 1000

Calculation: (999 + 1 - 1000) = 0
Result: Division by zero → REVERT { borrows * 1e18 / (totalSupply - reserves)}

Impact

No new lending - All operations frozen
Admin helpless - Even emergency functions fail

PoC

NA

Mitigation

Add proper validation to prevent the DoS condition:

function utilizationRate(uint256 cash, uint256 borrows, uint256 reserves)
public pure override returns (uint256) {
if (borrows == 0) {
return 0;
}

uint256 totalSupply = cash + borrows;

// Prevent DoS: ensure denominator is positive
require(totalSupply > reserves, "Insufficient available cash");

return borrows * 1e18 / (totalSupply - reserves);

}

Lead judge reply --

That is not epxloitable in any way practically, thanks to the borrow risk control parameters.

Author Reply --

I disagree with your comment "That is not epxloitable in any way practically, thanks to the borrow risk control parameters." This is a Duplicate Report of #1409 [https://audits.sherlock.xyz/contests/1029/voting/1409]

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions