Era

ZKsync
FoundryLayer 2
500,000 USDC
View results
Submission Details
Severity: medium
Valid

Migration blocked due to chain limit in `MessageRoot` contract

Summary

The protocol's goal of enabling migration to any chain is failed when the maximum number of chains is reached, as this prevents further migrations.

Vulnerability Details

The protocol intends to permit any chain to be migrated, even when the maximum chain limit has been reached:
Link to source

To achieve this, when bridgeMint is invoked during migration on the destination chain, it registers the new zk chain with the _checkMaxNumberOfZKChains flag set to false. This flag bypasses the MAX_NUMBER_OF_ZK_CHAINS limit, allowing the registration of new chains regardless of the existing limit:

function _registerNewZKChain(uint256 _chainId, address _zkChain, bool _checkMaxNumberOfZKChains) internal {
// slither-disable-next-line unused-return
zkChainMap.set(_chainId, _zkChain);
if (_checkMaxNumberOfZKChains && zkChainMap.length() > MAX_NUMBER_OF_ZK_CHAINS) {
revert ZKChainLimitReached();
}
}

Link to source

However, simply setting the flag to false is insufficient to bypass the limit when registering a new zk chain, as the new chain also needs to be added to the message root. This addition triggers another check for the MAX_NUMBER_OF_ZK_CHAINS limit:
Link to source

This function also performs the same check for the chain limit:

function _addNewChain(uint256 _chainId) internal {
uint256 cachedChainCount = chainCount;
if (cachedChainCount >= MAX_NUMBER_OF_ZK_CHAINS) {
revert TooManyChains(cachedChainCount, MAX_NUMBER_OF_ZK_CHAINS);
}
++chainCount;
chainIndex[_chainId] = cachedChainCount;
chainIndexToId[cachedChainCount] = _chainId;
// slither-disable-next-line unused-return
bytes32 initialHash = chainTree[_chainId].setup(CHAIN_TREE_EMPTY_ENTRY_HASH);
// slither-disable-next-line unused-return
sharedTree.pushNewLeaf(MessageHashing.chainIdLeafHash(initialHash, _chainId));
emit AddedChain(_chainId, cachedChainCount);
}

Link to source

Currently, the maximum number of zk chains (MAX_NUMBER_OF_ZK_CHAINS) is set to 100:
Link to source

If 100 chains have already been added, the maximum limit is reached. In such a case, migrating any additional chains should still be allowed as per the protocol's objectives. The protocol states:

We want to allow any chain to be migrated,
Link to source

Additionally, there is a note that:

Note, that in case of a malicious Bridgehub admin, the total number of chains can be up to 2 times higher. This may be possible, in case the old ChainTypeManager had 100 chains and these were migrated to the Bridgehub only after MAX_NUMBER_OF_ZK_CHAINS were added to the bridgehub via creation of new chains.
Link to source

However, the chain count is also limited in the MessageRoot contract, and if the limit of 100 chains is reached, the process will fail with a TooManyChains error:
Link to source

Impact

The protocol aims to allow migration of any chain, even when the maximum number of chains has been reached, but the current implementation in the MessageRoot contract prevents this functionality.

Tools Used

Recommendations

The same mechanism for bypassing the MAX_NUMBER_OF_ZK_CHAINS limit (i.e., using the _checkMaxNumberOfZKChains flag) should be implemented in the MessageRoot contract to ensure that migrations can proceed even after the maximum number of chains is reached.

Updates

Lead Judging Commences

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Migration blocked due to chain limit in `MessageRoot` contract

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.