TwentyOne

First Flight #29
Beginner FriendlyGameFiFoundrySolidity
100 EXP
View results
Submission Details
Severity: high
Invalid

Missing Validation for Card Initialization in initializeDeck()

Summary

The initializeDeck() function fails to fully validate and clear the state of a player's and dealer's decks before initializing a new game. While the function includes a check to prevent reinitialization of an already active deck, it does not ensure that all state variables related to the player are reset properly. This omission can lead to data corruption, game inconsistencies, and potential exploitation.

Here’s the current implementation:

require(
availableCards[player].length == 0,
"Player's deck is already initialized"
);
for (uint256 i = 1; i <= 52; i++) {
availableCards[player].push(i);
}

Vulnerability Details

  1. Incomplete State Validation:

    • The require statement checks whether availableCards[player].length is zero. However, this only ensures that the deck of available cards is empty.

    • It does not verify or clear:

      • The player's current hand (playersDeck[player].playersCards).

      • The dealer's current hand (dealersDeck[player].dealersCards).

    • If these arrays are not cleared from a previous game, the player or dealer could retain cards, leading to unpredictable outcomes in the game.

  2. Potential Corruption of Game State:

    • If startGame() is called multiple times by accident or through an exploit, the following issues could arise:

      • Duplicate Initialization: availableCards may be overwritten while the player's and dealer's cards from previous games persist, leading to an invalid state where a player starts with extra or invalid cards.

      • Inconsistent Game Logic: Game rules rely on accurate deck and hand states. Corrupted data could result in gameplay violations such as a player starting with more than two cards or retaining cards after a loss.

  3. Exploitation Potential:

    • Unfair Advantage for Players: Malicious players could attempt to manipulate the contract by retaining powerful cards from a previous game, giving themselves an advantage in the current game.

    • Denial of Service: A corrupted state might cause the contract to fail in certain operations, potentially preventing other players from participating or causing unexpected errors during gameplay.

Impact

Severity: High

  • Player Experience Compromise: Honest players could encounter errors or unfair scenarios due to state inconsistencies.

  • Exploitation by Malicious Players: Attackers could use this flaw to replay games with unintended advantages, undermining the fairness of the system.

  • System Reliability Issues: Over time, the accumulation of corrupted game states could lead to contract failures or require significant effort to resolve.

  • Example Exploit Scenario:

    1. A player starts a game by calling startGame().

    2. Without completing the first game, the player calls startGame() again.

    3. Due to incomplete validation, the playersDeck[player].playersCards and dealersDeck[player].dealersCards are not cleared.

    4. The player retains cards from the first game and gets additional cards from the second initialization, violating the rules and gaining an unfair advantage.

Tools Used

  • Manual Review: Identified the lack of comprehensive validation in initializeDeck().

  • MythX: Highlighted state inconsistencies and incomplete checks in the function.

Recommendations

1.Comprehensive State Reset Before Initialization: To ensure a clean state for each game, all state variables associated with the player must be reset before initializing a new deck. This includes clearing:

  • The player's hand (playersDeck[player].playersCards).

  • The dealer's hand (dealersDeck[player].dealersCards).

  • The player's deck of available cards (availableCards[player]).

Here’s the updated implementation of initializeDeck():

function initializeDeck(address player) internal {
// Clear any leftover data from previous games
delete playersDeck[player].playersCards; // Clear the player's hand
delete dealersDeck[player].dealersCards; // Clear the dealer's hand
delete availableCards[player]; // Reset the available deck
// Initialize a fresh deck of 52 cards
for (uint256 i = 1; i <= 52; i++) {
availableCards[player].push(i);
}
}

2.Prevention of Multiple Initializations: Enhance the require statement to ensure that the function is called only after the game state is properly reset. This acts as an additional safeguard against unintended behavior.

require(
playersDeck[player].playersCards.length == 0 &&
dealersDeck[player].dealersCards.length == 0 &&
availableCards[player].length == 0,
"Game state must be reset before initialization"
);

3.Add Unit Tests: Write unit tests to simulate multiple calls to startGame() and verify that all state variables are correctly reset. Test scenarios should include:

  • Starting a new game after a completed game.

  • Attempting to call startGame() during an active game.

  • Validating that no cards from previous games are retained.

4.Enhance startGame() Logic: To further safeguard against errors, startGame() should ensure that the player has no active game before proceeding with initialization.

function startGame() public payable returns (uint256) {
address player = msg.sender;
require(msg.value >= 1 ether, "not enough ether sent");
require(
playersDeck[player].playersCards.length == 0 &&
dealersDeck[player].dealersCards.length == 0,
"Finish the current game before starting a new one"
);
initializeDeck(player);
uint256 card1 = drawCard(player);
uint256 card2 = drawCard(player);
addCardForPlayer(player, card1);
addCardForPlayer(player, card2);
return playersHand(player);
}

Closing Remarks

This vulnerability significantly affects the fairness and reliability of the game. By ensuring that all relevant state variables are reset before deck initialization, the contract will avoid data corruption and maintain the integrity of gameplay. The proposed solution addresses both the immediate vulnerability and establishes stronger safeguards to prevent similar issues in the future.

Updates

Lead Judging Commences

inallhonesty Lead Judge 11 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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