createGameWithEth()
and createGameWithToken()
accept any odd uint256
as totalTurns
.
Because each turn requires a commit → reveal → winner cycle that writes to storage and emits events, a sufficiently large totalTurns
will push the cumulative gas required to finish the game above the block‑gas limit. Once this threshold is crossed the game can never reach the _finishGame()
/ _handleTie()
paths, so:
players’ ETH bet or staked WinningToken
is locked forever;
accrued protocol fees remain stuck;
storage slots stay occupied permanently (“state bloat”).
No admin or rescue function can revive the game because _determineWinner()
is the only path that completes it.
Attacker calls createGameWithEth{value: 0.01 ETH}(2 147 483 647, 5 minutes)
(largest 31‑bit odd number, for example).
A victim joins with the matching bet.
Both parties commit and reveal for a handful of turns, until gas per block is insufficient for _determineWinner()
to loop through the remaining turns.
From that point on:
commitMove()
reverts because state == GameState.Committed
but deadline cannot progress;
timeoutReveal()
reverts unless one side has already revealed;
_finishGame()
is unreachable.
ETH / tokens and protocol fees are now irrecoverable.
Funds at risk: locked indefinitely (users cannot withdraw, admin cannot reclaim fees).
Permanent DoS: the game ID stays in Committed
state forever; new rounds cannot be created with same gameId
.
No mitigating controls: neither maximum gas‑per‑turn nor owner rescue.
Introduce a MAX_TURNS
cap in the protocol
Code suggestions or observations that do not pose a direct security risk.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.