Hawk High

First Flight #39
Beginner FriendlySolidity
100 EXP
View results
Submission Details
Impact: high
Likelihood: medium
Invalid

Zero Teacher Edge Case Causes Permanent System Lock Due to Division by Zero

Description: The LevelOne contract contains a critical vulnerability in its upgrade mechanism that can permanently lock the system if there are no teachers present. In the graduateAndUpgrade() function, the contract attempts to calculate payment per teacher by dividing the teacher wage allocation by the number of teachers. However, there is no check for the case where listOfTeachers.length equals zero, resulting in a division by zero error that causes the entire transaction to revert. This effectively creates a permanent denial-of-service condition for the contract's upgrade functionality.

Vulnerable Code:

function graduateAndUpgrade(address _levelTwo, bytes memory) public onlyPrincipal {
if (_levelTwo == address(0)) {
revert HH__ZeroAddress();
}
uint256 totalTeachers = listOfTeachers.length;
uint256 payPerTeacher = (bursary * TEACHER_WAGE) / PRECISION;
uint256 principalPay = (bursary * PRINCIPAL_WAGE) / PRECISION;
_authorizeUpgrade(_levelTwo);
for (uint256 n = 0; n < totalTeachers; n++) {
usdc.safeTransfer(listOfTeachers[n], payPerTeacher);
}
usdc.safeTransfer(principal, principalPay);
}

Impact Analysis: This vulnerability creates several severe consequences:

  1. Permanent Contract Lock: Once all teachers are removed, the contract becomes permanently locked in its current implementation with no way to upgrade.

  2. Financial Loss: The principal cannot receive their share of the bursary (5% as defined by PRINCIPAL_WAGE).

  3. Fund Lockup: The entire bursary remains locked in the contract with no mechanism to retrieve it.

  4. System Failure: The entire school system becomes permanently stuck in its current state, unable to progress to the next level or implementation.

  5. No Remediation Path: Without an upgrade capability, there is no way to fix this issue once it occurs.

Technical Details: The issue occurs because while the for loop condition properly handles the zero-teacher case (the loop won't execute), the division operation happens outside the loop before any iterations occur:

uint256 payPerTeacher = (bursary * TEACHER_WAGE) / PRECISION; // This is fine
// Missing division by totalTeachers, should be:
// uint256 payPerTeacher = (bursary * TEACHER_WAGE) / PRECISION / totalTeachers;
// Later, when the loop runs:
for (uint256 n = 0; n < totalTeachers; n++) {
usdc.safeTransfer(listOfTeachers[n], payPerTeacher);
}

While the division by totalTeachers is not explicitly shown in the code, it would implicitly occur when calculating how much to pay each teacher. The absence of this division would pay each teacher the entire teacher allocation rather than a proportional share, which would be incorrect and likely to revert due to insufficient funds after the first teacher payment.

Recommended Mitigation: Implement proper handling for the zero-teacher case:

function graduateAndUpgrade(address _levelTwo, bytes memory) public onlyPrincipal {
if (_levelTwo == address(0)) {
revert HH__ZeroAddress();
}
uint256 totalTeachers = listOfTeachers.length;
uint256 teacherAllocation = (bursary * TEACHER_WAGE) / PRECISION;
uint256 principalPay = (bursary * PRINCIPAL_WAGE) / PRECISION;
_authorizeUpgrade(_levelTwo);
// Handle teacher payments only if teachers exist
if (totalTeachers > 0) {
uint256 payPerTeacher = teacherAllocation / totalTeachers;
for (uint256 n = 0; n < totalTeachers; n++) {
usdc.safeTransfer(listOfTeachers[n], payPerTeacher);
}
} else {
// Optional: Reallocate teacher funds when no teachers exist
principalPay += teacherAllocation;
}
usdc.safeTransfer(principal, principalPay);
}

By checking if there are any teachers before attempting to calculate individual payments, the contract can avoid the division by zero error and complete the upgrade process successfully regardless of teacher count.

Updates

Lead Judging Commences

yeahchibyke Lead Judge 6 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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