Closed sherlock-admin2 closed 6 months ago
MAX_GAME_DEPTH
is intended to be configured to 50-100 in almost all cases but this is a valid footgun and we will fix it to avoid potential issues in the future.
I believe this issue is invalid per sherlock rules, point 5.. The above comment is supported as noted by sponsor below:
Yes, there is public information on the intended configuration values in the deploy configuration here: https://github.com/ethereum-optimism/optimism/blob/34ce96a09e88543c95c8dd6ec09d7e3531568b90/packages/contracts-bedrock/deploy-config/sepolia.json#L47-L51
The protocol team fixed this issue in the following PRs/commits: https://github.com/ethereum-optimism/optimism/pull/10199
MiloTruck
medium
Dispute games can be infinitely played when
MAX_GAME_DEPTH = 127
Summary
When
MAX_GAME_DEPTH = 125
, dispute games can be played infinitely (ie.move()
can keep being called on parent claims, regardless of their depth).Vulnerability Detail
Position
is a user-defined types that wraps auint128
:DisputeTypes.sol#L69-L74
This means that it can hold up to
2 ** 128 - 1
unique positions. As such,MAX_GAME_DEPTH
can be assumed to have an upper bound (maximum value) of 127, sincePosition
can only hold nodes in the game tree up to a depth of 127.When calling
FaultDisputeGame.move()
to make a move from a parent position, the function checks that the depth of the next position does not exceedMAX_GAME_DEPTH
:FaultDisputeGame.sol#L247-L251
However, doing this instead of checking that
parentPos >= MAX_GAME_DEPTH
does not work whenMAX_GAME_DEPTH == 127
. This is becauseLibPosition.move()
does not account for the scenario where a position overflowsuint128
:LibPosition.sol#L174-L178
As seen from above, if
_position
is2 ** 127
or greater, the calculation ofnextPosition
will overflow inLibPosition.move()
, causing it to wrap around and become a smaller number. For example:parentPos = 2 ** 127
, callingattack()
would returnnextPosition = 0
.parentPos = 2 ** 127
, callingdefend()
would returnnextPosition = 2
.Due to the overflow, depending on the value of
parentPos
at depth 127, making a move could return any arbitrary position in the game tree with a depth smaller than 127.This becomes a problem as the
nextPositionDepth > MAX_GAME_DEPTH
check will not catch this. For example, whenmove()
is called on a node whereparentPos = 2 ** 127
,nextPosition = 0
andnextPositionDepth = 1
, thereby passing the check.As such, the
nextPositonDepth > MAX_GAME_DEPTH
condition will never be true and the check will never be reached.Impact
When
MAX_GAME_DEPTH = 127
, thenextPositonDepth > MAX_GAME_DEPTH
check will never be reached due to an overflow ofPosition
.As such,
move()
can be called an infinite number of times and the game can be played infinitely. This will force honest challengers and proposers to keep on playing the game until their game clock runs out, otherwise, there will be an uncontested child claim in the subgame.Note that this impact is not exhaustive - there may be more impacts depending on what
nextPosition
is calculated as whenPosition
overflows.Code Snippet
https://github.com/sherlock-audit/2024-02-optimism-2024/blob/f216b0d3ad08c1a0ead557ea74691aaefd5fd489/optimism/packages/contracts-bedrock/src/libraries/DisputeTypes.sol#L69-L74
https://github.com/sherlock-audit/2024-02-optimism-2024/blob/f216b0d3ad08c1a0ead557ea74691aaefd5fd489/optimism/packages/contracts-bedrock/src/dispute/lib/LibPosition.sol#L174-L178
https://github.com/sherlock-audit/2024-02-optimism-2024/blob/f216b0d3ad08c1a0ead557ea74691aaefd5fd489/optimism/packages/contracts-bedrock/src/dispute/FaultDisputeGame.sol#L247-L251
Tool used
Manual Review
Recommendation
Consider adding a check in the constructor to limit
MAX_GAME_DEPTH
to maximum of125
.