President Elector

First Flight #24
Beginner FriendlyFoundry
100 EXP
View results
Submission Details
Severity: medium
Valid

selectPresident will run out of gas in particular scenerio

Summary

selectPresident function is susceptible to out-of-gas errors when dealing with a large number of voters, each selecting different candidates. This could lead to failed elections or incomplete vote counting, compromising the integrity of the voting process.

Vulnerability Details

While the contract limits the maximum number of candidates to 10 (but these 10 are also not fixed), there's no limit on the number of voters. The _selectPresidentRecursive function, which is called by selectPresident, consume excessive gas when processing votes from many voters, especially if each voter ranks candidates differently.

POC

Add the following test in existing test suite:

/// generate unique candidate
function generateUniqueCandidate(uint i) public pure returns (address) {
return address(uint160(i + 1000));
}
function testOutOfGasVulnerabilityWithDiverseCandidates() public {
// Assuming the default test suite already sets up 100 voters
require(voters.length >= 100, "Test requires at least 200 voters");
// Have each voter rank candidates, with 8 out of 10 being unique
for (uint256 i = 0; i < 100; i++) {
address[] memory voterCandidates = new address[]();
for (uint256 j = 0; j < 10; j++) {
if (j < 8) {
// Generate a unique candidate for this voter
voterCandidates[j] = generateUniqueCandidate(i * 10 + j);
} else {
// Use two common candidates for all voters
voterCandidates[j] = generateUniqueCandidate(2000 + j - 8);
}
}
// Shuffle the candidates array to randomize the ranking
for (uint256 j = 0; j < 10; j++) {
uint256 swapIndex = j +
(uint256(keccak256(abi.encodePacked(i, j))) % (10 - j));
(voterCandidates[j], voterCandidates[swapIndex]) = (
voterCandidates[swapIndex],
voterCandidates[j]
);
}
vm.prank(voters[i]);
rankedChoice.rankCandidates(voterCandidates);
}
// Advance time to allow for president selection
vm.warp(block.timestamp + rankedChoice.getDuration());
// Attempt to select president, which is likely to run out of gas
vm.expectRevert();
rankedChoice.selectPresident();
}

Run forge test --mt testOutOfGasVulnerabilityWithDiverseCandidates -vvvvv in your terminal, it will show following output:

[⠊] Compiling...
[⠔] Compiling 1 files with Solc 0.8.24
[⠒] Solc 0.8.24 finished in 1.44s
Compiler run successful!
..
..
..
│ └─ ← [Stop]
├─ [236] RankedChoice::getDuration() [staticcall]
│ └─ ← [Return] 126144000 [1.261e8]
├─ [0] VM::warp(126144001 [1.261e8])
│ └─ ← [Return]
├─ [0] VM::expectRevert(custom error f4844814:)
│ └─ ← [Return]
├─ [1028284011] RankedChoice::selectPresident()
│ └─ ← [OutOfGas] EvmError: OutOfGas
│ └─ ← [Stop]
└─ ← [Stop]
Ran 1 test for test/RankedChoiceTest.t.sol:RankedChoiceTest
[PASS] testOutOfGasVulnerabilityWithDiverseCandidates() (gas: 1057401684)

Impact

President will be never be choosen due to out of gas, which won't be a good in democracy. This undermines the whole purpose of the protocol.

Tools Used

Manual Review, Foundry

Recommendations

candidate list must also be initialized so users can choose from them.
Voters must also be limited.

Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

A high number of candidates could cause an OOG

Support

FAQs

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