Open code423n4 opened 2 years ago
It is a known issue. Some particular examples are listed here. We put attention of developers on it in our documentation.
Dup of #23
Creating QA Report for warden as judge downgraded. Preserving original title: Failed Message in postIncomingMessage() Result in Locked Fund
Lines of code
https://github.com/skalenetwork/ima-c4-audit/blob/main/contracts/mainnet/MessageProxyForMainnet.sol#L205-L253 https://github.com/skalenetwork/ima-c4-audit/blob/main/contracts/schain/MessageProxyForSchain.sol#L200-L227 https://github.com/skalenetwork/ima-c4-audit/blob/main/contracts/MessageProxy.sol#L415-L449
Vulnerability details
Impact
Message passing through the bridge occur in a two step process.
The first step is transferring the tokens or funds to the bridge for the chain we are currently on (e.g. if we are doing Eth->Schain the bridge smart contracts on the Eth chain will receive the tokens). In this was the ownership of the tokens of funds is transferred to the bridge (either a deposit box or a token manager).
The second step is undertaken by one of the validators for the SChain. One of the validators calls
postIncomingMessages()
on the other side of the bridge (e.g. Eth->SChain then we callpostIncomingMessages()
on the SChain). DuringpostIncomingMessages()
a batch of messages are processed individually, transferring the required assets to their destination.An issue arises in the second step because any individual message that fails will be ignored. This is due to the
try-catch
statement in the function_callReceiverContract()
when processing a message. Thistry-catch
statement is necessary to prevent one failed message causing the entire transaction to revert and that would mean the bridge is stuck and can't process any messages for that SChain.The funds are considered locked because in the case described above where step two fails due to the
postMessage()
reverting, the message has passed through step one, transferring the tokens into the bridge on one side. However, since thepostMessage()
has failed the tokens/funds are not transferred to the end receiver. There is no way to recover these stuck tokens and so they are lost in the bridge.Proof of Concept
MessageProxyForMainnet.sol
MessageProxyForSchain.sol
MessageProxy.sol
Recommended Mitigation Steps
One solution is to store failed message in the
MessageProxy.sol
and allowing the receiver of the tokens or funds to replay the message, potentially with slightly different parameters.Another option is to allow validators to vote on failed messages and have them returned to the owners on the original chain, though there is no guarantee the owner will be able to use the returned tokens (e.g. a smart contract whose accounting does not handle direct transfers or if this address is owned by a centralised exchange wallet).