A duplicate dispute game may be created for the same output proposal due to an issue inside FaultDisputeGame.initialize()
Summary
Due to an issue with a revert condition inside the function FaultDisputeGame.initialize(), a duplicate dispute game for the same output proposal may be created.
Vulnerability Detail
Inside the function FaultDisputeGame.initialize(), the calldatasize() is checked to not be greater than 0x66 (line 546-550 in FaultDisputeGame.sol).
According to the comment on line 541-545 (FaultDisputeGame.sol), this is done to prevent the creation of multiple games for the same output proposal.
However, as shown in this POC test_game_with_same_data() which can be added to DisputeGameFactory.t.sol, a duplicate game for the same output proposal may be created.
In the POC, a game is created with _extraData0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00 by calling the DisputeGameFactory.create() function via the disputeGameFactory.create.selector. (the ff can be anything as long as it matches with the _extraData below)
Then a second game is created with _extraDataffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff which is 1 byte shorter than the _extraData from the first game, missing the 00 at the end. The extra data from the second game is identical to the extra data from the first game, except the missing last byte.
Then as a proof it is shown that rootClaim, l1Head and l2BlockNumber of both games are identical in the last 3 lines of the POC.
The POC shows that a duplicate game can be created for the same output proposal, where the games have the same combination of gametype, rootclaim and l2BlockNumber.
The reason is that when creating a new game with shorter _extraData, the missing byte is "filled" with zeros upon creation.
Impact
A malicious proposer can create a duplicate game for the same output proposal at the same l2BlockNumber.
Challengers have to challenge both games.
A duplicate game for the same proposal may create confusion and may make it difficult to follow the duplicated game. Also, if automated monitoring is done using properly formed extraData (= l2BlockNumber) only, these monitoring may miss malformed cases like above, resulting in untimely invalidation or even fail to invalidate those games.
There should only be one game per proposal to maintain the integrity and trust in the dispute resolution process. Otherwise this issue could undermine the fairness and reliability of the dispute resolution process.
Also the risk for malicious actors may decrease since they have more than one chance to have their proposal accepted.
lemonmon
medium
A duplicate dispute game may be created for the same output proposal due to an issue inside
FaultDisputeGame.initialize()
Summary
Due to an issue with a revert condition inside the function
FaultDisputeGame.initialize()
, a duplicate dispute game for the same output proposal may be created.Vulnerability Detail
Inside the function
FaultDisputeGame.initialize()
, thecalldatasize()
is checked to not be greater than 0x66 (line 546-550 in FaultDisputeGame.sol).According to the comment on line 541-545 (FaultDisputeGame.sol), this is done to prevent the creation of multiple games for the same output proposal.
However, as shown in this POC
test_game_with_same_data()
which can be added toDisputeGameFactory.t.sol
, a duplicate game for the same output proposal may be created.POC:
_extraData
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00
by calling theDisputeGameFactory.create()
function via thedisputeGameFactory.create.selector
. (theff
can be anything as long as it matches with the _extraData below)_extraData
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
which is 1 byte shorter than the_extraData
from the first game, missing the00
at the end. The extra data from the second game is identical to the extra data from the first game, except the missing last byte.rootClaim
,l1Head
andl2BlockNumber
of both games are identical in the last 3 lines of the POC.The POC shows that a duplicate game can be created for the same output proposal, where the games have the same combination of gametype, rootclaim and l2BlockNumber.
The reason is that when creating a new game with shorter
_extraData
, the missing byte is "filled" with zeros upon creation.Impact
A duplicate game for the same proposal may create confusion and may make it difficult to follow the duplicated game. Also, if automated monitoring is done using properly formed
extraData
(=l2BlockNumber
) only, these monitoring may miss malformed cases like above, resulting in untimely invalidation or even fail to invalidate those games.There should only be one game per proposal to maintain the integrity and trust in the dispute resolution process. Otherwise this issue could undermine the fairness and reliability of the dispute resolution process.
Also the risk for malicious actors may decrease since they have more than one chance to have their proposal accepted.
Code Snippet
https://github.com/sherlock-audit/2024-02-optimism-2024/blob/f216b0d3ad08c1a0ead557ea74691aaefd5fd489/optimism/packages/contracts-bedrock/src/dispute/FaultDisputeGame.sol#L541-L552
https://github.com/sherlock-audit/2024-02-optimism-2024/blob/f216b0d3ad08c1a0ead557ea74691aaefd5fd489/optimism/packages/contracts-bedrock/src/dispute/DisputeGameFactory.sol#L84-L106
Tool used
Manual Review
Recommendation
Consider checking the call data size to be exactly 0x66, which is the expected length.
Duplicate of #203