-
Notifications
You must be signed in to change notification settings - Fork 13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
retryDeposit() doesn’t function to specification #647
Comments
trust1995 marked the issue as primary issue |
trust1995 marked the issue as satisfactory |
0xBugsy marked the issue as sponsor confirmed |
Changing nonce of same deposit info would open doors to race conditions through use of retrieve and retry, ideal approach is to leave nonces that triggered a fallback as unexecuted so its not only useful in out of gas conditions |
trust1995 marked the issue as selected for report |
0xBugsy marked the issue as sponsor disputed |
Similar consequence to #684 but different approach by updating nonces A deposit which has triggered a fallback and is potentially redeemable in a Branch chain cannot be left as unexecuted since it has in fact been executed otherwise this would allow for double spending in root and branch. |
0xBugsy requested judge review |
Functioning of |
![]() retryDeposit, according to the natspec is used to retry a failed deposit that hasn't been executed yet. But the problem is that all failed deposits' execution history is set to true, so it won't be possible to retry those failed deposits because even though they have not been executed, their executionHistory is set to true: //Try to execute remote request
try RootBridgeAgentExecutor(bridgeAgentExecutorAddress).executeSignedWithDepositMultiple(
address(userAccount), localRouterAddress, data, fromChainId
) returns (bool, bytes memory res) {
(success, result) = (true, res);
} catch (bytes memory reason) {
result = reason;
}
//Toggle Router Virtual Account use for tx execution
IPort(localPortAddress).toggleVirtualAccountApproved(userAccount, localRouterAddress);
//Update tx state as executed
executionHistory[fromChainId][nonce] = true; If a deposit fails, it is caught in try-catch block, but function is not exited so executionHistory for that deposit which failed is set to true. In the catch block, the function should be exited using the "return" keyword to avoid setting the executionHistory for a failed deposit to true(because that would prevent the deposit from being retried). |
If For the feature you are suggesting we could have 3 states instead of 2 which is covered in the following unrelated issue #183 . |
This is how it should be. But now, if a deposit fails, the error that caused the failure is caught in a try-catch, and the remaining parts of the anyExecute function still gets executed(meaning that executionHistory for that failed deposit will WRONGLY be set to true): //DEPOSIT FLAG: 2 (Call with Deposit)
} else if (flag == 0x02) {
//Get Deposit Nonce
uint32 nonce = uint32(bytes4(data[PARAMS_START:PARAMS_TKN_START]));
//Check if tx has already been executed
if (executionHistory[fromChainId][nonce]) {
_forceRevert();
//Return true to avoid triggering anyFallback in case of `_forceRevert()` failure
return (true, "already executed tx");
}
//Try to execute remote request
try RootBridgeAgentExecutor(bridgeAgentExecutorAddress).executeWithDeposit(
localRouterAddress, data, fromChainId
) returns (bool, bytes memory res) {
(success, result) = (true, res);
} catch (bytes memory reason) {
result = reason;
}
//Update tx state as executed
executionHistory[fromChainId][nonce] = true; If //Update tx state as executed
executionHistory[fromChainId][nonce] = true; So deposits that failed within the The recommendation is that within the catch block(which will only be entered if a deposit fails), the function should be exited with a return value of false. This will prevent executionHistory for that FAILED deposit to be set to true, allowing retry of a deposit that failed. |
This is invalid. If gas management is successful and fallback is executed that is already an execution, if we left it to false we would be open to double spending since we cant have a tx be redeemable and retry-able in two completely different networks at the same time |
trust1995 marked the issue as not selected for report |
trust1995 marked the issue as unsatisfactory: |
Lines of code
https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/BranchBridgeAgent.sol#L319-L399
https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/RootBridgeAgent.sol#L860-L1174
Vulnerability details
Impact
Ulysses-omnichain
retryDeposit(...)
inBranchBridgeAgent.sol
doesn’t function properly.Proof of Concept
The idea behind
retryDeposit(...)
is to try and re-send a message that could have failed on the Root Chain, moreover, it is specific to messages that carry token information to perform token bridging.retryDeposit(...)
reuses the same deposit nonce and re-sends the message to Root with that deposit nonce. A message could have failed for a variety of reasons whenanyExecute(...)
is called on the RootBridgeAgent - this could be gas issues, faulty router integrations or incorrectly formed extradata
for further execution (we can also pass newdata
toretryDeposit(...)
). The problem is that in some cases, if a message failsexecutionHistory[fromChainId][nonce] = true
- meaning that if we try and use theretryDeposit
functionality the nonce will have already been used and we won’t be executing anything. In the code snippet below we can see that thetry{}
statement will catch any reverts that could be due to non-existing token addresses, router integrations with extra data supplied etc. , however it will still set executionHistory totrue
for that nonce value, therefore, if we were toretryDeposit(...)
it will fail because it was already executed, therefore,retryDeposit(...)
functionality is rendered useless in that case.Tools used
Manual inspection
Recommendation
Rework
retryDeposit
to supply a new nonce, or consider to leaveexecutionHistory[fromChainId][nonce] = false
for some reverts.Assessed type
Context
The text was updated successfully, but these errors were encountered: