Platform: Code4rena
Start Date: 16/02/2023
Pot Size: $144,750 USDC
Total HM: 17
Participants: 154
Period: 19 days
Judge: Trust
Total Solo HM: 5
Id: 216
League: ETH
Rank: 49/154
Findings: 1
Award: $142.85
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: GalloDaSballo
Also found by: 0xBeirao, 0xRobocop, AkshaySrivastav, KingNFT, MiloTruck, PaludoX0, bin2chen, hansfriese, imare, kaden
142.8544 USDC - $142.85
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/ActivePool.sol#L248-L251
In the ActivePool
contract, the _rebalance()
function subtracts the amount of collateral currently in the vault (vars.sharesToAssets
) from the amount originally allocated (vars.currentAllocated
) to calculate its profit. This can be seen in ActivePool.sol#L248-L251
:
vars.sharesToAssets = vars.yieldGenerator.convertToAssets(vars.ownedShares); // if we have profit that's more than the threshold, record it for withdrawal and redistribution vars.profit = vars.sharesToAssets.sub(vars.currentAllocated);
As seen from above, the sub()
function from SafeMath
is used to do the calculation. Hence, whenever the vault is at a loss, _rebalance()
will always revert as vars.currentAllocated
is less than vars.sharesToAssets
, leading to an arithmetic underflow.
This affects the following functions as they call the _rebalance()
function:
BorrowOperations.sol
openTrove
closeTrove
addColl
withdrawColl
withdrawLUSD
repayLUSD
adjustTrove
TroveManager.sol
redeemCollateral
liquidate
batchLiquidateTroves
liquidateTroves
Whenever a vault containing one type of collateral is at a loss, all the functions listed above will revert when called with the same type of collateral, massively affecting the protocol's functionality.
Moreover, since all the functions used to withdraw/transfer collateral are affected, all the borrowers who opened troves with the same type of collateral will have their collateral stuck in the protocol until the vault has a non-negative profit.
The test in this gist demonstrates how a vault at a loss causes all the functions listed above to revert.
To run the test:
MockBorrowerOperations.sol
into Ethos-Core/contracts/TestContracts
.RebalanceDOSTest.js
into Ethos-Core/test
.npx hardhat test ./test/RebalanceDOSTest.js
Consider declaring vars.profit
as a int256
, and changing ActivePool.sol#L250-L254
to the following:
// if we have profit that's more than the threshold, record it for withdrawal and redistribution if (vars.sharesToAssets < vars.currentAllocated && vars.currentAllocated - vars.sharesToAssets >= yieldClaimThreshold[_collateral]) { vars.profit = -int256(vars.currentAllocated - vars.sharesToAssets); } else if (vars.sharesToAssets > vars.currentAllocated && vars.sharesToAssets - vars.currentAllocated >= yieldClaimThreshold[_collateral]) { vars.profit = int256(vars.sharesToAssets - vars.currentAllocated); }
#0 - c4-judge
2023-03-08T15:14:06Z
trust1995 marked the issue as unsatisfactory: Invalid
#1 - c4-judge
2023-03-22T10:23:24Z
trust1995 marked the issue as satisfactory
#2 - c4-judge
2023-03-22T10:23:35Z
trust1995 marked the issue as duplicate of #632