The original code for function repay and liquidate compares the input shares with the current debtShares and will revert if these 2 values are not equal:
function _repay(uint256 tokenId, uint256 amount, bool isShare, bytes memory permitData) internal {
// fails if too much repayed
if (shares > currentShares) {
revert RepayExceedsDebt();
}
}
function liquidate(LiquidateParams calldata params) external override returns (uint256 amount0, uint256 amount1) {
if (debtShares != params.debtShares) {
revert DebtChanged();
}
}
This allows a loan owner to repeatedly repay a small amount of loan and make others' attempts to repay/liquidate the position fail.
Mitigation
PR #14, PR #30
For function _repay, if input shares > debtShares, then shares = debtShares:
// if too much repayed - just set to max
if (shares > currentShares) {
shares = currentShares;
assets = _convertToAssets(shares, newDebtExchangeRateX96, Math.Rounding.Up);
}
For function liquidate, the shares now is taken directly from loans data instead of taking it from liquidator input:
Lines of code
Vulnerability details
C4 issue
M-16: Repayments and liquidations can be forced to revert by an attacker that repays miniscule amount of shares
Comment
The original code for function
repay
andliquidate
compares the input shares with the current debtShares and will revert if these 2 values are not equal:This allows a loan owner to repeatedly repay a small amount of loan and make others' attempts to repay/liquidate the position fail.
Mitigation
PR #14, PR #30 For function
_repay
, if input shares >debtShares
, thenshares = debtShares
:For function
liquidate
, the shares now is taken directly fromloans
data instead of taking it from liquidator input:The mitigation solved the original issue.
Conclusion
LGTM