Summary
TheselectPresident
function definition lacks a length check fors_candidateList
. In_selectPresidentRecursive
,candidateList[0]
is used directly, which will trigger an array out-of-bounds access error when the length ofcandidateList
is 0, causing the transaction to fail.
function _selectPresidentRecursive(
address[] memory candidateList,
uint256 roundNumber
) internal returns (address[] memory) {
if (candidateList.length == 1) {
return candidateList;
}
………………
address fewestVotesCandidate = candidateList[0];
……
}
Vulnerability Details
this is the POC, add the code to the RankedChoiceTest.t.sol
file, the selectPresident function will call the _selectPresidentRecursive with the parameter candidateList=[], when use the candidateList[0], the array out-of-bounds access error is triggered.
function testSelectPresidentOninit() public {
vm.warp(block.timestamp + rankedChoice.getDuration());
vm.expectRevert();
rankedChoice.selectPresident();
}
Impact
TheselectPresident
function will revert if it been called without settings_candidateList
.
Tools Used
foundry, vscode
Recommendations
++ error RankedChoice__CandidateIsEmpty();
function selectPresident() external {
if (
block.timestamp - s_previousVoteEndTimeStamp <=
i_presidentalDuration
) {
revert RankedChoice__NotTimeToVote();
}
for (uint256 i = 0; i < VOTERS.length; i++) {
address[] memory orderedCandidates = s_rankings[VOTERS[i]][
s_voteNumber
];
for (uint256 j = 0; j < orderedCandidates.length; j++) {
if (!_isInArray(s_candidateList, orderedCandidates[j])) {
s_candidateList.push(orderedCandidates[j]);
}
}
}
++ if(s_candidateList.length == 0){
++ revert RankedChoice__CandidateIsEmpty();
++ }
address[] memory winnerList = _selectPresidentRecursive(
s_candidateList,
0
);
if (winnerList.length != 1) {
revert RankedChoice__SomethingWentWrong();
}
// Reset the election and set President
s_currentPresident = winnerList[0];
s_candidateList = new address[](0);
s_previousVoteEndTimeStamp = block.timestamp;
s_voteNumber += 1;
}