Hawk High

First Flight #39
Beginner FriendlySolidity
100 EXP
View results
Submission Details
Severity: low
Valid

Gas Limit DoS Vulnerability in Student Expulsion Mechanism

Description: The LevelOne contract's expel() function contains a critical vulnerability that could render the function inoperable as the number of students increases. The function uses a linear search (O(n) complexity) through the entire listOfStudents array to locate and remove a specific student, which becomes problematic at scale due to Ethereum's block gas limitations.

Code Snippet:

function expel(address _student) public onlyPrincipal {
if (inSession == false) {
revert();
}
if (_student == address(0)) {
revert HH__ZeroAddress();
}
if (!isStudent[_student]) {
revert HH__StudentDoesNotExist();
}
uint256 studentLength = listOfStudents.length;
for (uint256 n = 0; n < studentLength; n++) {
if (listOfStudents[n] == _student) {
listOfStudents[n] = listOfStudents[studentLength - 1];
listOfStudents.pop();
break;
}
}
isStudent[_student] = false;
emit Expelled(_student);
}

Impact: This vulnerability creates a serious limitation on the contract's scalability. As the school grows beyond a certain size, the principal will lose the ability to expel students, which is a core administrative function. This effectively places an implicit cap on the number of students the system can handle, contrary to what would be expected from the contract's design.

Detailed Analysis: Each iteration of the loop requires:

  • Reading an address from storage (~2,100 gas)

  • Comparison operation (~3 gas)

  • Potential array manipulation operations if a match is found (~5,000+ gas)

Considering Ethereum's current block gas limit (~30M gas), the theoretical maximum number of students before the function becomes unusable is approximately 10,000 in the worst case. However, the practical limit is likely much lower when considering other operation costs in the function.

Proof of Concept:

  1. Deploy the contract and enroll 5,000 students.

  2. Attempt to expel a student who is at the end of the array or doesn't exist.

  3. The transaction will fail with an "out of gas" error as the required gas exceeds the block limit.

Recommended Mitigation: Implement constant-time (O(1)) student removal by tracking the index of each student in the array:

// Add a mapping to track student positions
mapping(address => uint256) private studentToIndex;
function enroll() external notYetInSession {
// Existing validation...
studentToIndex[msg.sender] = listOfStudents.length;
listOfStudents.push(msg.sender);
isStudent[msg.sender] = true;
studentScore[msg.sender] = 100;
bursary += schoolFees;
emit Enrolled(msg.sender);
}
function expel(address _student) public onlyPrincipal {
// Existing validation...
uint256 indexToRemove = studentToIndex[_student];
address lastStudent = listOfStudents[listOfStudents.length - 1];
// Move the last student to the removed position (unless it's the last student being removed)
if (_student != lastStudent) {
listOfStudents[indexToRemove] = lastStudent;
studentToIndex[lastStudent] = indexToRemove;
}
// Remove the last element and clean up mappings
listOfStudents.pop();
delete studentToIndex[_student];
isStudent[_student] = false;
emit Expelled(_student);
}
Updates

Lead Judging Commences

yeahchibyke Lead Judge
6 months ago
yeahchibyke Lead Judge 6 months ago
Submission Judgement Published
Validated
Assigned finding tags:

possible DoS when expelling students

Unbounded loops in student lists could result in high gas usage when trying to expel a students when students are plenty. This could result in a possible DoS

Support

FAQs

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