TwentyOne

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

Denial of Service (DoS) Due to Exhausted Card Deck in drawCard Function

Summary

The drawCard function in the contract is responsible for providing a player with a card from a pool of available cards. However, the current implementation lacks proper checks for when the deck is exhausted. Specifically, once all cards are drawn, the contract attempts to access an empty deck, which could result in an infinite loop, excessive gas consumption, or transaction failure due to exceeding the block's gas limit. This could cause the game to be stuck in a non-functional state, blocking further interactions and rendering the game unusable for the player.

Vulnerability Details

The following test case demonstrates how the lack of proper deck exhaustion handling can cause the contract to fail:
// In the `TwentyOne.t.sol` test file:
<details>
<summary>Code</summary>
```javascript
function testDeckExhaustion() public {
twentyOne = new TwentyOne();
address player = address(this);
vm.deal(player, 1 ether); // Fund the player
twentyOne.startGame{value: 1 ether}();
bool deckExhausted = false;
while (true) {
try twentyOne.hit() {
// Keep drawing cards until the player goes bust or the deck is empty
} catch Error(string memory reason) {
if (keccak256(bytes(reason)) == keccak256("Game not started")) {
// The player went bust, exit the loop
break;
} else if (keccak256(bytes(reason)) == keccak256("No cards left to draw for this player")) {
deckExhausted = true;
break; // The deck is empty
} else {
// Unexpected error, fail the test
fail();
}
}
}
}
}
```
</details>

Impact

This flaw in the contract can cause a Denial of Service (DoS) attack where the game cannot proceed once the card deck is exhausted. If a player attempts to draw a card when the deck is empty, the contract will attempt to execute operations that may fail due to an invalid state (drawing from an empty deck), resulting in excessive gas consumption or a failed transaction. This leads to the game being stuck, making it unplayable for the player and potentially causing disruption for all users interacting with the contract.
The impact includes:
1. Failed Transactions:
Transactions related to drawing cards will fail once the deck is exhausted, preventing the player from progressing in the game.
2. Excessive Gas Consumption:
If the game logic is not properly controlled, the contract may consume all available gas, potentially blocking other operations and disrupting the contract's functionality.
3. Unplayable Game State:
The game may become stuck, as no new cards can be drawn once the deck is empty. This would break the game's flow and could result in player frustration or loss of trust in the contract.
4. Denial of Service (DoS):
If an attacker or a player maliciously triggers the drawCard function in an already-exhausted deck, they could cause an unhandled failure or excessive gas consumption, leading to a DoS attack.

Tools Used

MANUAL REVIEW
FOUNDRY

Recommendations

To address this issue and prevent a DoS attack, the following solutions should be considered:
1. **Graceful Handling of Deck Exhaustion:**
Add logic to notify players that the deck is exhausted. For example, an event could be emitted to inform the player that they can no longer draw cards, and the game should be gracefully ended or paused.
<details>
<summary>Code</summary>
```diff
+ event DeckExhausted(address player);
function drawCard(address player) internal returns (uint256) {
+ if (availableCards[player].length == 0) {
+ emit DeckExhausted(player); // Notify that the deck is empty
+ revert("No cards left to draw");
+ }
// Continue with the drawing logic...
}
```
</details>
2. **Avoid Infinite Loops or Uncontrolled Gas Usage:**
Ensure that any function relying on drawCard does not attempt to repeatedly draw cards without proper checks. This can be handled by adding a condition to stop the process when the deck is exhausted.
3. **Test for Empty Deck Scenario:**
Ensure that there are appropriate unit tests that simulate an exhausted deck and confirm that the contract behaves as expected (e.g., by reverting with the correct error message when attempting to draw from an empty deck).
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.