The joinGameWithEth function in the RockPaperScissors.sol contract allows any user to become playerB in a game that is in the Created state, even if another user has already successfully joined as playerB. This allows the second joiner to overwrite the first, potentially disrupting the game and loss of fund.
The joinGameWithEth function performs the necessary checks:
Verifies the game exists (game.playerA != address(0)).
Ensures the game is accepting players (game.state == GameState.Created).
Checks if the join deadline has passed (block.timestamp <= game.joinDeadline).
Confirms the caller is not Player A (msg.sender != game.playerA).
Validates the correct ETH amount is sent (msg.value == game.bet).
If these checks pass, it sets game.playerB = msg.sender. However, crucially, it does not change the game.state from GameState.Created.
Because the state remains Created, another user can call joinGameWithEth before the joinDeadline expires and before either player calls commitMove (which would change the state). This second caller will pass the same checks and will overwrite the game.playerB address set by the first joiner.
This vulnerability allows a malicious user or simply a concurrent user to replace the intended playerB. This disrupts the game setup process. The originally joined playerB might expect to play but finds they have been replaced.
While the ETH sent by the first joiner might eventually be reclaimable via timeoutJoin if the game never starts properly, the user is denied participation in that specific game instance. It can be used as a griefing vector to prevent specific players from joining games.
Manual code review.
To fix this vulnerability, the joinGameWithEth function should immediately transition the game state out of Created once playerB is successfully set. Changing the state to Committed seems appropriate, as the next step is for players to commit their moves.
Add the following line within joinGameWithEth after setting game.playerB:
Game state remains Created after a player joins
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.