Hawk High

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

The `LevelOne::graduateAndUpgrade` function does not mark the session as ended, leaving the system in an inconsistent state after the upgrade

Description:

The graduateAndUpgrade(address _levelTwo, bytes memory) function is intended to complete the educational period and update the contract's implementation to LevelTwo. However, the function does not update the inSession variable to false, leaving the system in an internal state where the session is still considered active.

This variable (inSession) is used as a control condition in various functions (e.g., addTeacher(...)) and may also be used in the new implementation (LevelTwo) to restrict actions until the session ends.

The problem is magnified because the contract's state persists after the upgrade (the proxy retains the storage). Therefore, if inSession is not modified before the upgrade, the new implementation will also operate with inSession = true, even though the session has formally ended.

Impact:

  • The system may enter an inconsistent state where students have graduated, but inSession remains true.

  • Functions like addTeacher(), which depend on notYetInSession, may be incorrectly blocked.

  • The new implementation (LevelTwo) may operate under a false assumption, thinking the session is still active.

  • It may cause partial functional denial, requiring an additional upgrade just to fix this flag.

  • Confusion for users and external systems, as the apparent and actual state of the contract do not match.

Proof of Concept:

This test verifies that after completing the course and executing graduateAndUpgrade(), the inSession variable is not marked as false, leaving the system in an inconsistent state. To verify this, the slot 0 of storage is accessed directly, where inSession is packed along with principal. The byte corresponding to inSession is read, and it is confirmed to still be true, even though the session should have formally ended. This situation demonstrates that the internal state was not correctly updated before the upgrade.

function test_inSessionRemainsTrueAfterUpgrade() public {
...
vm.prank(principal);
LevelOne(proxy).graduateAndUpgrade(address(levelTwo), data);
bytes32 slot0 = vm.load(address(proxy), bytes32(0));
uint8 inSessionByte = uint8(uint256(slot0) >> 160);
bool inSessionFlag = (inSessionByte & 0x01) != 0;
assertTrue(inSessionFlag);
console2.log("inSession flag:", inSessionFlag);
}

Recommende Mitigation:

function graduateAndUpgrade(address _levelTwo, bytes memory _data) 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;
+ inSession = false;
_authorizeUpgrade(_levelTwo);
}
Updates

Lead Judging Commences

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

session state not updated

`inSession` not updated after during upgrade

Support

FAQs

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