Beginner FriendlyFoundryNFT
100 EXP
View results
Submission Details
Severity: medium
Valid

Finding duplicates iterating large `storage array` can cause `Out-of-Gas` issue.

Summary

Reading from storage consumes gas for users, and there is a limit of up to 30,000,000 (30 million) gas on the Ethereum network per block. This implies that any transaction incurring more gas than this limit will encounter an 'out of gas' issue.

Vulnerability Details

function enterRaffle(address[] memory newPlayers) public payable {
require(
msg.value == entranceFee * newPlayers.length,
"PuppyRaffle: Must send enough to enter raffle"
);
for (uint256 i = 0; i < newPlayers.length; i++) {
players.push(newPlayers[i]);
}
// Check for duplicates
@> for (uint256 i = 0; i < players.length - 1; i++) {
@> for (uint256 j = i + 1; j < players.length; j++) {
@> require(
@> players[i] != players[j],
@> "PuppyRaffle: Duplicate player"
@> );
@> }
@> }
emit RaffleEnter(newPlayers);
}

PuppyRaffle.sol - Lines 86 - 90

In the PuppyRaffle::enterRaffle function, after adding new players to the storage array, the protocol checks for duplicate entries. This involves iterating through the entire storage array multiple times, even when adding a single new player. Consequently, users incur significant gas costs when entering the raffle.

The primary issue arises when the gas consumption approaches the Ethereum block's gas limit. If the player storage array contains, for instance, hundreds or thousands of players, those attempting to join afterward will face substantial gas expenses. Once the gas usage reaches the Ethereum block's limit, users will encounter 'out of gas' errors, preventing them from participating further in the PuppyRaffle::enterRaffle function, effectively blocking it.

Other Instances

PuppyRaffle.sol - Lines 111 - 115

PuppyRaffle.sol - Lines 174 - 178

PoC

View It

Follow the follwing steps:

  1. Start the local anvil blockchain using command make anvil.

  2. Update the DeployPuppyRaffle::run function; this will deploy the PuppyRaffle contract on anvil chain and return the address of it in tests.

- function run() public {
+ function run() public returns (PuppyRaffle) {
feeAddress = msg.sender;
vm.broadcast();
- PuppyRaffle puppyRaffle = new PuppyRaffle(1e18, feeAddress, duration);
+ return new PuppyRaffle(1e18, feeAddress, duration);
}
  1. Update the PuppyRaffleTest::setUp function; instead of deploying the PuppyRaffle contract get the deployed instance from the deployment file.

- // puppyRaffle = new PuppyRaffle(entranceFee, feeAddress, duration);
+ puppyRaffle = new DeployPuppyRaffle().run();
  1. Add this test inside the PuppyRaffleTest.t.sol file; it enter the unique player in the raffle on each run.

function test_ReachBlockGasLimit() public {
for (uint i = 1; i <= 30000000; i++) {
address[] memory players = new address[](1);
players[0] = address(uint160(i));
puppyRaffle.enterRaffle{value: entranceFee}(players);
}
}
  1. Now in another terimal (remember the run the anvil on a saperate terminal) run this command forge test --match-test test_ReachBlockGasLimit -vvv --fork-url http://localhost:8545 --gas-limit 30000000.

Here I have set the gas limit to 30,000,000 (30 million) which is the max limit of ethereum block.

Output

Running 1 test for test/PuppyRaffleTest.t.sol:PuppyRaffleTest
[FAIL. Reason: EvmError: Revert] test_ReachBlockGasLimit() (gas: 29971035)
Traces:
[29971035] PuppyRaffleTest::test_ReachBlockGasLimit()
├─ [46984] PuppyRaffle::enterRaffle([0x0000000000000000000000000000000000000001])
│ ├─ emit RaffleEnter(newPlayers: [0x0000000000000000000000000000000000000001])
│ └─ ← ()
├─ [26163] PuppyRaffle::enterRaffle([0x0000000000000000000000000000000000000002])
│ ├─ emit RaffleEnter(newPlayers: [0x0000000000000000000000000000000000000002])
│ └─ ← ()
├─ [28031] PuppyRaffle::enterRaffle([0x0000000000000000000000000000000000000003])
│ ├─ emit RaffleEnter(newPlayers: [0x0000000000000000000000000000000000000003])
│ └─ ← ()
├─ [30688] PuppyRaffle::enterRaffle([0x0000000000000000000000000000000000000004])
│ ├─ emit RaffleEnter(newPlayers: [0x0000000000000000000000000000000000000004])
│ └─ ← ()
├─ [34134] PuppyRaffle::enterRaffle([0x0000000000000000000000000000000000000005])
│ ├─ emit RaffleEnter(newPlayers: [0x0000000000000000000000000000000000000005])
│ └─ ← ()
├─ [38369] PuppyRaffle::enterRaffle([0x0000000000000000000000000000000000000006])
│ ├─ emit RaffleEnter(newPlayers: [0x0000000000000000000000000000000000000006])
│ └─ ← ()
├─ [43393] PuppyRaffle::enterRaffle([0x0000000000000000000000000000000000000007])
│ ├─ emit RaffleEnter(newPlayers: [0x0000000000000000000000000000000000000007])
│ └─ ← ()
├─ [49206] PuppyRaffle::enterRaffle([0x0000000000000000000000000000000000000008])
│ ├─ emit RaffleEnter(newPlayers: [0x0000000000000000000000000000000000000008])
│ └─ ← ()
├─ [55808] PuppyRaffle::enterRaffle([0x0000000000000000000000000000000000000009])
│ ├─ emit RaffleEnter(newPlayers: [0x0000000000000000000000000000000000000009])
│ └─ ← ()
├─ [63199] PuppyRaffle::enterRaffle([0x000000000000000000000000000000000000000A])
│ ├─ emit RaffleEnter(newPlayers: [0x000000000000000000000000000000000000000A])
│ └─ ← ()
├─ [71379] PuppyRaffle::enterRaffle([0x000000000000000000000000000000000000000b])
│ ├─ emit RaffleEnter(newPlayers: [0x000000000000000000000000000000000000000b])
│ └─ ← ()
├─ [80348] PuppyRaffle::enterRaffle([0x000000000000000000000000000000000000000C])
│ ├─ emit RaffleEnter(newPlayers: [0x000000000000000000000000000000000000000C])
│ └─ ← ()
├─ [90106] PuppyRaffle::enterRaffle([0x000000000000000000000000000000000000000d])
│ ├─ emit RaffleEnter(newPlayers: [0x000000000000000000000000000000000000000d])
│ └─ ← ()
├─ [100653] PuppyRaffle::enterRaffle([0x000000000000000000000000000000000000000E])
│ ├─ emit RaffleEnter(newPlayers: [0x000000000000000000000000000000000000000E])
│ └─ ← ()
├─ [111989] PuppyRaffle::enterRaffle([0x000000000000000000000000000000000000000F])
│ ├─ emit RaffleEnter(newPlayers: [0x000000000000000000000000000000000000000F])
│ └─ ← ()
├─ [124114] PuppyRaffle::enterRaffle([0x0000000000000000000000000000000000000010])
│ ├─ emit RaffleEnter(newPlayers: [0x0000000000000000000000000000000000000010])
│ └─ ← ()
├─ [137028] PuppyRaffle::enterRaffle([0x0000000000000000000000000000000000000011])
│ ├─ emit RaffleEnter(newPlayers: [0x0000000000000000000000000000000000000011])
│ └─ ← ()
├─ [150731] PuppyRaffle::enterRaffle([0x0000000000000000000000000000000000000012])
│ ├─ emit RaffleEnter(newPlayers: [0x0000000000000000000000000000000000000012])
│ └─ ← ()
├─ [165223] PuppyRaffle::enterRaffle([0x0000000000000000000000000000000000000013])
│ ├─ emit RaffleEnter(newPlayers: [0x0000000000000000000000000000000000000013])
│ └─ ← ()
├─ [180504] PuppyRaffle::enterRaffle([0x0000000000000000000000000000000000000014])
│ ├─ emit RaffleEnter(newPlayers: [0x0000000000000000000000000000000000000014])
│ └─ ← ()
├─ [196574] PuppyRaffle::enterRaffle([0x0000000000000000000000000000000000000015])
│ ├─ emit RaffleEnter(newPlayers: [0x0000000000000000000000000000000000000015])
│ └─ ← ()
├─ [213433] PuppyRaffle::enterRaffle([0x0000000000000000000000000000000000000016])
│ ├─ emit RaffleEnter(newPlayers: [0x0000000000000000000000000000000000000016])
│ └─ ← ()
├─ [231081] PuppyRaffle::enterRaffle([0x0000000000000000000000000000000000000017])
│ ├─ emit RaffleEnter(newPlayers: [0x0000000000000000000000000000000000000017])
│ └─ ← ()
├─ [249518] PuppyRaffle::enterRaffle([0x0000000000000000000000000000000000000018])
│ ├─ emit RaffleEnter(newPlayers: [0x0000000000000000000000000000000000000018])
│ └─ ← ()
├─ [268744] PuppyRaffle::enterRaffle([0x0000000000000000000000000000000000000019])
│ ├─ emit RaffleEnter(newPlayers: [0x0000000000000000000000000000000000000019])
│ └─ ← ()
├─ [288759] PuppyRaffle::enterRaffle([0x000000000000000000000000000000000000001a])
│ ├─ emit RaffleEnter(newPlayers: [0x000000000000000000000000000000000000001a])
│ └─ ← ()
├─ [309563] PuppyRaffle::enterRaffle([0x000000000000000000000000000000000000001B])
│ ├─ emit RaffleEnter(newPlayers: [0x000000000000000000000000000000000000001B])
│ └─ ← ()
├─ [331156] PuppyRaffle::enterRaffle([0x000000000000000000000000000000000000001c])
│ ├─ emit RaffleEnter(newPlayers: [0x000000000000000000000000000000000000001c])
│ └─ ← ()
├─ [353538] PuppyRaffle::enterRaffle([0x000000000000000000000000000000000000001D])
│ ├─ emit RaffleEnter(newPlayers: [0x000000000000000000000000000000000000001D])
│ └─ ← ()
├─ [376709] PuppyRaffle::enterRaffle([0x000000000000000000000000000000000000001e])
│ ├─ emit RaffleEnter(newPlayers: [0x000000000000000000000000000000000000001e])
│ └─ ← ()
├─ [400669] PuppyRaffle::enterRaffle([0x000000000000000000000000000000000000001F])
│ ├─ emit RaffleEnter(newPlayers: [0x000000000000000000000000000000000000001F])
│ └─ ← ()
├─ [425418] PuppyRaffle::enterRaffle([0x0000000000000000000000000000000000000020])
│ ├─ emit RaffleEnter(newPlayers: [0x0000000000000000000000000000000000000020])
│ └─ ← ()
├─ [450956] PuppyRaffle::enterRaffle([0x0000000000000000000000000000000000000021])
│ ├─ emit RaffleEnter(newPlayers: [0x0000000000000000000000000000000000000021])
│ └─ ← ()
├─ [477283] PuppyRaffle::enterRaffle([0x0000000000000000000000000000000000000022])
│ ├─ emit RaffleEnter(newPlayers: [0x0000000000000000000000000000000000000022])
│ └─ ← ()
├─ [504399] PuppyRaffle::enterRaffle([0x0000000000000000000000000000000000000023])
│ ├─ emit RaffleEnter(newPlayers: [0x0000000000000000000000000000000000000023])
│ └─ ← ()
├─ [532304] PuppyRaffle::enterRaffle([0x0000000000000000000000000000000000000024])
│ ├─ emit RaffleEnter(newPlayers: [0x0000000000000000000000000000000000000024])
│ └─ ← ()
├─ [560998] PuppyRaffle::enterRaffle([0x0000000000000000000000000000000000000025])
│ ├─ emit RaffleEnter(newPlayers: [0x0000000000000000000000000000000000000025])
│ └─ ← ()
├─ [590481] PuppyRaffle::enterRaffle([0x0000000000000000000000000000000000000026])
│ ├─ emit RaffleEnter(newPlayers: [0x0000000000000000000000000000000000000026])
│ └─ ← ()
├─ [620753] PuppyRaffle::enterRaffle([0x0000000000000000000000000000000000000027])
│ ├─ emit RaffleEnter(newPlayers: [0x0000000000000000000000000000000000000027])
│ └─ ← ()
├─ [651814] PuppyRaffle::enterRaffle([0x0000000000000000000000000000000000000028])
│ ├─ emit RaffleEnter(newPlayers: [0x0000000000000000000000000000000000000028])
│ └─ ← ()
├─ [683664] PuppyRaffle::enterRaffle([0x0000000000000000000000000000000000000029])
│ ├─ emit RaffleEnter(newPlayers: [0x0000000000000000000000000000000000000029])
│ └─ ← ()
├─ [716303] PuppyRaffle::enterRaffle([0x000000000000000000000000000000000000002A])
│ ├─ emit RaffleEnter(newPlayers: [0x000000000000000000000000000000000000002A])
│ └─ ← ()
├─ [749731] PuppyRaffle::enterRaffle([0x000000000000000000000000000000000000002b])
│ ├─ emit RaffleEnter(newPlayers: [0x000000000000000000000000000000000000002b])
│ └─ ← ()
├─ [783948] PuppyRaffle::enterRaffle([0x000000000000000000000000000000000000002c])
│ ├─ emit RaffleEnter(newPlayers: [0x000000000000000000000000000000000000002c])
│ └─ ← ()
├─ [818954] PuppyRaffle::enterRaffle([0x000000000000000000000000000000000000002D])
│ ├─ emit RaffleEnter(newPlayers: [0x000000000000000000000000000000000000002D])
│ └─ ← ()
├─ [854749] PuppyRaffle::enterRaffle([0x000000000000000000000000000000000000002E])
│ ├─ emit RaffleEnter(newPlayers: [0x000000000000000000000000000000000000002E])
│ └─ ← ()
├─ [891333] PuppyRaffle::enterRaffle([0x000000000000000000000000000000000000002F])
│ ├─ emit RaffleEnter(newPlayers: [0x000000000000000000000000000000000000002F])
│ └─ ← ()
├─ [928706] PuppyRaffle::enterRaffle([0x0000000000000000000000000000000000000030])
│ ├─ emit RaffleEnter(newPlayers: [0x0000000000000000000000000000000000000030])
│ └─ ← ()
├─ [966868] PuppyRaffle::enterRaffle([0x0000000000000000000000000000000000000031])
│ ├─ emit RaffleEnter(newPlayers: [0x0000000000000000000000000000000000000031])
│ └─ ← ()
├─ [1005819] PuppyRaffle::enterRaffle([0x0000000000000000000000000000000000000032])
│ ├─ emit RaffleEnter(newPlayers: [0x0000000000000000000000000000000000000032])
│ └─ ← ()
├─ [1045559] PuppyRaffle::enterRaffle([0x0000000000000000000000000000000000000033])
│ ├─ emit RaffleEnter(newPlayers: [0x0000000000000000000000000000000000000033])
│ └─ ← ()
├─ [1086088] PuppyRaffle::enterRaffle([0x0000000000000000000000000000000000000034])
│ ├─ emit RaffleEnter(newPlayers: [0x0000000000000000000000000000000000000034])
│ └─ ← ()
├─ [1127406] PuppyRaffle::enterRaffle([0x0000000000000000000000000000000000000035])
│ ├─ emit RaffleEnter(newPlayers: [0x0000000000000000000000000000000000000035])
│ └─ ← ()
├─ [1169513] PuppyRaffle::enterRaffle([0x0000000000000000000000000000000000000036])
│ ├─ emit RaffleEnter(newPlayers: [0x0000000000000000000000000000000000000036])
│ └─ ← ()
├─ [1212409] PuppyRaffle::enterRaffle([0x0000000000000000000000000000000000000037])
│ ├─ emit RaffleEnter(newPlayers: [0x0000000000000000000000000000000000000037])
│ └─ ← ()
├─ [1256094] PuppyRaffle::enterRaffle([0x0000000000000000000000000000000000000038])
│ ├─ emit RaffleEnter(newPlayers: [0x0000000000000000000000000000000000000038])
│ └─ ← ()
├─ [1300568] PuppyRaffle::enterRaffle([0x0000000000000000000000000000000000000039])
│ ├─ emit RaffleEnter(newPlayers: [0x0000000000000000000000000000000000000039])
│ └─ ← ()
├─ [1345831] PuppyRaffle::enterRaffle([0x000000000000000000000000000000000000003a])
│ ├─ emit RaffleEnter(newPlayers: [0x000000000000000000000000000000000000003a])
│ └─ ← ()
├─ [1391883] PuppyRaffle::enterRaffle([0x000000000000000000000000000000000000003b])
│ ├─ emit RaffleEnter(newPlayers: [0x000000000000000000000000000000000000003b])
│ └─ ← ()
├─ [502404] PuppyRaffle::enterRaffle([0x000000000000000000000000000000000000003c])
│ └─ ← "EvmError: OutOfGas"
└─ ← "EvmError: Revert"
Test result: FAILED. 0 passed; 1 failed; 0 skipped; finished in 179.61ms
Ran 1 test suites: 0 tests passed, 1 failed, 0 skipped (1 total tests)
Failing tests:
Encountered 1 failing test in test/PuppyRaffleTest.t.sol:PuppyRaffleTest
[FAIL. Reason: EvmError: Revert] test_ReachBlockGasLimit() (gas: 29971035)
Encountered a total of 1 failing tests, 0 tests succeeded

We can see the increase of gas cost in each run.

Impact

Users will not be able to enter the raffle once the gas limit of transaction reaches the ethereum block limit.

Tools Used

Manual Review

Recommendations

Cache the storage array.

+ address[] memory _players = players;
- for (uint256 i = 0; i < players.length - 1; i++) {
+ for (uint256 i = 0; i < _players.length - 1; i++) {
- for (uint256 j = i + 1; j < players.length; j++) {
+ for (uint256 j = i + 1; j < _players.length; j++) {
require(
- players[i] != players[j],
+ _players[i] != _players[j],
"PuppyRaffle: Duplicate player"
);
}
}
Updates

Lead Judging Commences

patrickalphac Lead Judge about 2 years ago
Submission Judgement Published
Validated
Assigned finding tags:

denial-of-service-in-enter-raffle

Support

FAQs

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

Give us feedback!