Closed c4-submissions closed 11 months ago
bytes032 marked the issue as duplicate of #259
GalloDaSballo marked the issue as not a duplicate
GalloDaSballo marked the issue as duplicate of #620
GalloDaSballo marked the issue as satisfactory
The comment clearly states that it is not guaranteed to call postOp
on the paymaster due to out of gas. So, it is known.
https://github.com/code-423n4/2023-10-zksync/blob/main/code/system-contracts/contracts/interfaces/IPaymaster.sol#L34-L36
miladpiri (sponsor) disputed
GalloDaSballo changed the severity to QA (Quality Assurance)
Lines of code
https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/system-contracts/bootloader/bootloader.yul#L726-L731 https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/system-contracts/bootloader/bootloader.yul#L1425
Vulnerability details
Vulnerability details
Currently
L2
supports user-specifiedpaymaster
to support custom ERC20 foreth
to pay for gas.The main steps to perform are as follows:
account.prepareForPaymaster()
(Usually approve token allowance to paymaster)paymaster.validateAndPayForPaymasterTransaction()
to pay gasbootloader
receiveseth
that is greater thanGasLimit * gasPrice
, return the overpaideth
to paymaster`.paymaster.postTransaction()
, and return the unused gasLeft + reservedGas topaymaster
.The most important thing is that
paymaster.postTransaction()
is not guaranteed to be called.So
paymaster
can't collect the correspondingERC20
from the user inpaymaster.postTransaction()
, if in this step, then the user can control the remaining gas equal to 0 to skip the payment ofERC20
.So
paymaster
can only collect the correspondingERC20
in the second steppaymaster.validateAndPayForPaymasterTransaction()
.For example the following scenario MAX_GAS_PER_TRANSACTION = 1000 Aliace performs a transaction specifying gasLimit = 1100 and paymaster, so reservedGas = 100
Based on the current logic the following steps are performed.
paymaster.validateAndPayForPaymasterTransaction()
, paymaster transfers the 1100Token and pays the 1100ETH.bootloader
does not exceed gasLimit then refund 0paymaster.postTransaction()
, but has returnedreservedGas=100
topaymaster
, paymaster can not return ERC20 to the userImpact
Since
paymaster.postTransaction()
doesn't always execute, which results in not being able to refund reservedGas to the user ifgasLeft==0 but reservedGas>0
Recommended Mitigation
It is still recommended that you need to ensure that
paymaster.postTransaction()
executes successfully. If it fails, we recommend rolling it back.Or simply determine that if
reservedGas>0
, you need to runpaymaster.postTransaction()
Assessed type
Context