Platform: Code4rena
Start Date: 02/10/2023
Pot Size: $1,100,000 USDC
Total HM: 28
Participants: 64
Period: 21 days
Judge: GalloDaSballo
Total Solo HM: 13
Id: 292
League: ETH
Rank: 50/64
Findings: 1
Award: $273.57
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: HE1M
Also found by: 0xsomeone, AkshaySrivastav, Aymen0909, J4de, Koolex, Mohandes, bin2chen, brgltd, cccz, hals, ladboy233, max10afternoon, peanuts, rvierdiiev, shealtielanz, tsvetanovv, zzzitron
273.5673 USDC - $273.57
In L1ERC20Bridge
when user calls claimFailedDeposit
, it can fail due to underflow in _verifyDepositLimit
in certain conditions, which can mean user's funds being locked in the contract forever. This is caused by the ability to turn on an off deposit limitation
for the token.
The issue lies in Line 345 of L1ERC20Bridge contract, in _verifyDepositLimit
function:
totalDepositedAmountPerUser[_l1Token][_depositor] -= _amount;
Here is the full code for '_verifyDepositLimit':
function _verifyDepositLimit(address _l1Token, address _depositor, uint256 _amount, bool _claiming) internal { IAllowList.Deposit memory limitData = IAllowList(allowList).getTokenDepositLimitData(_l1Token); if (!limitData.depositLimitation) return; // no deposit limitation is placed for this token if (_claiming) { totalDepositedAmountPerUser[_l1Token][_depositor] -= _amount; } else { require(totalDepositedAmountPerUser[_l1Token][_depositor] + _amount <= limitData.depositCap, "d1"); totalDepositedAmountPerUser[_l1Token][_depositor] += _amount; } }
This function does not enforce any condition or update the calculation of totalDepositedAmountPerUser
when depositLimitation
is false. Not updating the total deposit is what might cause a situation where the totalDepositedAmountPerUser
is less than amount, which can cause underflow error. This means the user will never be able to claim their failed deposit.
Here is the scenario:
Token A
in L1 Bridge.1000
Token A
to L1 Bridge.totalDepositedAmountPerUser[Token A][Alice]
is still zero due to no deposit limitclaimFailedDeposit
.Admin
imposes a deposit limit on Token A
on their discretion, not knowing (or caring) about Alice.claimFailedDeposit
and it fails due to underflow. remember , totalDepositedAmountPerUser[Token A][Alice]
is still zero, so deducting amount
from it will cause underflow.Because this can happen to any token, and the probability of it happening is not low, and the loss of access to funds is real, unless the user can have access to the admin which might not be the case in web3 environment, I think this should be considered as High Severity
.
Manual Review
Doing the total deposit accounting at all times, regardless of presence or absence of deposit limitation
can fix the underflow issue, but it will mean that the logic applies on any previous deposits since the launch of the project. I believe this is even fairer, compared to the existing logic that can impose limitations on all users on the same basis regardless of their past transactions.
One thing to note is that the current logic has already got some issues other than underflow as well. For example, if a token has deposit limitations, the total deposits are updated, if the limitations set to false, the total deposits don't set to zero, so later on when the limitations set, the old total deposits are back in the effect, which seems weird.
Any solution that involves resetting the total deposits will be vulnerable to possible unknown failed deposits, which won't be possible to claim. That's why I think keeping the accounting for total deposits is the best solution.
My least favourite solution is to set the totalDepositedAmountPerUser[_l1Token][_depositor]
to zero when underflow happens. This might make it harder to find future bugs.
Math
#0 - c4-pre-sort
2023-10-31T14:56:02Z
bytes032 marked the issue as duplicate of #425
#1 - c4-pre-sort
2023-10-31T14:56:41Z
bytes032 marked the issue as sufficient quality report
#2 - c4-judge
2023-11-24T19:55:33Z
GalloDaSballo changed the severity to 2 (Med Risk)
#3 - c4-judge
2023-11-24T20:00:59Z
GalloDaSballo marked the issue as satisfactory