Rock Paper Scissors

First Flight #38
Beginner FriendlySolidity
100 EXP
View results
Submission Details
Severity: high
Valid

Missing check for playerB already set in joinGameWithToken()

Summary

The function joinGameWithToken() does not verify whether playerB has already joined the game. In edge cases with two concurrent calls, both could pass validation and overwrite the playerB field.

Vulnerability Details

Without a check that game.playerB == address(0), two users calling joinGameWithToken() at the same time could both enter the game, race to store their address, and cause inconsistent state.

Impact

  • Race condition during game joining

  • Multiple users believe they joined same game

Tools Used

  • Manual code review

Recommendations

Add a condition to enforce single joiner:

function joinGameWithToken(uint256 _gameId) external {
Game storage game = games[_gameId];
+ require(game.playerB == address(0), "Game already joined");
require(game.state == GameState.Created, "Game not open to join");
require(game.playerA != msg.sender, "Cannot join your own game");
require(block.timestamp <= game.joinDeadline, "Join deadline passed");
require(game.bet == 0, "This game requires ETH bet");
require(winningToken.balanceOf(msg.sender) >= 1, "Must have winning token");
// Transfer token to contract
winningToken.transferFrom(msg.sender, address(this), 1);
game.playerB = msg.sender;
emit PlayerJoined(_gameId, msg.sender);
}

This protects against race conditions and ensures that only one player joins per game.

Updates

Appeal created

m3dython Lead Judge 4 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Absence of State Change on Join Allows Player B Hijacking

Game state remains Created after a player joins

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.