Open code423n4 opened 1 year ago
trust1995 marked the issue as primary issue
trust1995 marked the issue as satisfactory
This is true but the mitigation would introduce a race condition allowing users to redeem and retry the same deposit, as such we will introduce a redemptionHistory
in the root allowing deposits with redemption and execution set to true to be re-retrieved for fallback but not executed again in root
0xBugsy marked the issue as sponsor confirmed
For further context, the issue that is being described is that in some cases a retrieve may fail on the branch and due to lack of gas for branch execution and at that point the deposit will have been given has executed in root blocking re-retrieval of said deposit.
Calling retryDeposit
should only be allowed until the first successful anyFallback is triggered and retrieveDeposit
should always be callable.
In addition in your example when executing the intial request that fails we should always set the executionHistory
to true since a fallback will be in fact triggered (avoids double spending) but we should also set the deposit as retrievable via a mapping (or save a uint8 instead of bool for deposit state). And when running anyExecute in Root for a deposit retrieval we simply check if the deposit is retrievable meaning the deposit has never run successfully without triggering anyFallback,
In short the retry, retrieve, redeem pattern works as expected but in order to accommodate for off-cases like the one described in this issue retrieveDeposit
should be callable indefinite times for a deposit that never executed successfully in the root since whenever the deposit is redeemed from the branch it will be deleted.
trust1995 marked the issue as selected for report
Lines of code
https://github.com/code-423n4/2023-05-maia/blob/78e49c651fd119b85bb79296620d5cb39efa7cdd/src/ulysses-omnichain/RootBridgeAgent.sol#L1141-L1156
Vulnerability details
Impact
The purpose of the retrieveDeposit function is to enable a user to be able to redeem a deposit he entered into the system. the mechanism works based on the promise that this function will be able to forcefully make the root bridge agent trigger the fallback function.
by returning false, the anycall contract will attempt to trigger the fallback function in the branch bridge, which would in turn set the status of the deposit as failed. the user can then redeem his deposit because its status is now failed.
The problem is that according to how anycall protocol works, it is completely feasible that the execution in root bridge completes succesfully, but the fallback in branch might still fail to execute.
for example, the anycall to the rootbridge might succeed due to enough gas stipend, while the fallback execution fails due to low gas stipend.
if this is the case then the deposit nonce would be stored in the executionHistory during the initial call, so when the retrievedeposit call is made it, it would think that the transaction is already completed, which would trigger this block instead:
The impact of this is that if the deposit transaction is recorded in root side as completed, a user will never be able to use retrievedeposit function redeem his deposit from the system.
Proof of Concept
Tools Used
Manual Review
Recommended Mitigation Steps
just make the root bridge return (false, "") regardles of whether the transaction linked to the original deposit was completed or not.
to avoid also spamming the usage of the retrievedeposit function, it is advisable to add a check in the retrieveDeposit function to see whether the deposit still exists, it doesnt make sense to try and retrieve a deposit that has already been redeemed.
Assessed type
Other