The joinGameWithToken function fails to transition the game state after a player successfully joins as playerB. This oversight allows subsequent calls to the same function (before the game moves to the commit phase) to replace the initially joined playerB, specifically in games designed to use the WinningToken.
The intended flow for a token game involves:
Player A creates a game using createGameWithToken. The game state is Created.
Player B approves the RockPaperScissors contract to spend their WinningToken.
Player B calls joinGameWithToken. The function verifies conditions, including game.state == GameState.Created, transfers the token via transferFrom, and sets game.playerB.
The vulnerability lies in the fact that after step 3, the game.state remains GameState.Created.
Consider this scenario:
Alice creates a token game (ID: 101). games[101].state is Created.
Bob approves the contract and calls joinGameWithToken(101). The token is transferred, and games[101].playerB becomes Bob's address. Crucially, games[101].state is still Created.
Before Alice or Bob calls commitMove, Charlie (who has also approved the contract) calls joinGameWithToken(101).
Charlie's call finds games[101].state is still Created, passes all checks, transfers his token, and overwrites games[101].playerB to Charlie's address.
The commitMove function is the first place the state would change to Committed, but this happens too late to prevent the overwrite during the joining phase.
This vulnerability allows the second player (playerB) slot in token games to be usurped before the game properly starts. The first user (Bob in the example) successfully joins, has their token taken by the contract, but is then silently replaced by a subsequent joiner (Charlie). This leads to:
Game Disruption: The intended player B cannot participate.
Potential Griefing: Malicious actors can specifically target players trying to join games by quickly joining after them.
Locked Tokens : Bob's token is held by the contract leading to loss of funds.
Manual code review.
The vulnerability is resolved by ensuring the game state transitions immediately upon a successful join, preventing further joins. Add a state change within joinGameWithToken right after playerB is set.
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.