Rock Paper Scissors

First Flight #38
Beginner FriendlySolidity
100 EXP
View results
Submission Details
Severity: medium
Invalid

Unbounded Timeout Parameters Can Lead to Fund Locks and Denial of Service

Summary

The RockPaperScissors contract lacks maximum bounds for both join timeout and reveal timeout parameters. This oversight allows game creators and administrators to set arbitrarily long timeouts, potentially leading to indefinite fund locks and denial of service conditions.

Vulnerability Details

The vulnerability exists in two key timeout mechanisms:

  • Join Timeout - Set by admin:

function setJoinTimeout(uint256 _newTimeout) external {
require(msg.sender == owner(), "Only owner can set timeout");
require(_newTimeout >= 1 hours, "Timeout must be at least 1 hour");
// No maximum limit check
joinTimeout = _newTimeout;
}
  • Reveal Timeout - Set during game creation:

// When creating game with ETH
function createGameWithEth(uint256 _totalTurns, uint256 _timeoutInterval) external payable {
require(_timeoutInterval >= 5 minutes, "Timeout must be at least 5 minutes");
// No maximum limit check
game.timeoutInterval = _timeoutInterval;
}
  • Proof of concept

function testUnboundedTimeoutVulnerability() public {
// Admin can set extremely long join timeout
uint256 hundredYears = 365 days * 100;
game.setJoinTimeout(hundredYears); // Will succeed
// Malicious player can create game with very long reveal timeout
uint256 fiftyYears = 365 days * 50;
uint256 gameId = game.createGameWithEth{value: 1 ether}(3, fiftyYears); // Will succeed
// Second player joins
vm.prank(playerB);
game.joinGameWithEth{value: 1 ether}(gameId);
// Funds are now locked for an extremely long period
// Players must wait for the excessive timeout to expire before recovering funds
}

Impact

  1. Fund locking: Players' funds can be locked for extremely long periods

  2. Denial of Service: Games can be rendered effectively unusable due to excessive timeouts

  3. Resource consumption: Long-running games consume blockchain storage indefinitely

  4. Player frustration: Legitimate players may be unable to recover their funds for extended periods

Tools Used

  • Manual review

  • Forge testing framework

Recommendations

  • Implement maximum timeout limits:

// Add reasonable maximum limits
uint256 public constant MAX_JOIN_TIMEOUT = 7 days;
uint256 public constant MAX_REVEAL_TIMEOUT = 24 hours;
function setJoinTimeout(uint256 _newTimeout) external {
require(msg.sender == owner(), "Only owner can set timeout");
require(_newTimeout >= 1 hours, "Timeout must be at least 1 hour");
require(_newTimeout <= MAX_JOIN_TIMEOUT, "Timeout exceeds maximum allowed");
joinTimeout = _newTimeout;
}
function createGameWithEth(uint256 _totalTurns, uint256 _timeoutInterval) external payable {
require(_timeoutInterval >= 5 minutes, "Timeout must be at least 5 minutes");
require(_timeoutInterval <= MAX_REVEAL_TIMEOUT, "Timeout exceeds maximum allowed");
game.timeoutInterval = _timeoutInterval;
}
  1. Consider implementing an emergency withdrawal mechanism for games with excessive timeouts

  2. Add a function to allow mutual agreement between players to reduce timeout periods

  3. Document recommended timeout ranges in the contract interface

These changes would prevent potential abuse while maintaining the flexibility needed for legitimate gameplay scenarios.

Updates

Appeal created

m3dython Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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