Hawk High

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

Missing _upgradeTo Call in graduateAndUpgrade Results in Broken UUPS Upgrade Flow

Summary

The graduateAndUpgrade function in the LevelOne contract is intended to finalize student graduation and upgrade the system to a new implementation (LevelTwo) using the UUPS proxy pattern. However, it fails to execute the actual upgrade due to a missing call to _upgradeTo, resulting in the contract remaining permanently on the outdated logic.


Vulnerability Details

The graduateAndUpgrade function correctly calls _authorizeUpgrade(_levelTwo) to validate the caller's permissions to upgrade the proxy. However, it does not follow through with a call to _upgradeTo(_levelTwo) or upgradeToAndCall(_levelTwo, data) to perform the upgrade. In the UUPS upgrade pattern, authorization and upgrade are two separate steps — one validates, the other mutates. Omitting the second step results in an incomplete upgrade process.

The function contains other logic (such as paying teachers and the principal) that does execute, potentially misleading developers into thinking the upgrade succeeded.

Relevant Code Snippet:

_authorizeUpgrade(_levelTwo);
// Missing: _upgradeTo(_levelTwo);

Impact

The proxy’s implementation address is never updated, and the contract remains stuck on the current version (LevelOne). This violates upgrade expectations and renders the LevelTwo implementation unreachable, potentially blocking access to new features, fixes, or critical security patches. The upgrade mechanism becomes non-functional despite appearing valid at the surface level.

This is especially critical in time-sensitive systems (e.g., upgrades after 4 weeks) where logical progression is enforced or expected.


Tools Used

Manual review


Recommendations

Update the graduateAndUpgrade function to include a call to _upgradeTo(_levelTwo) Immediately after the authorization check. Alternatively, if initialization logic is required during the upgrade, consider using upgradeToAndCall.

Recommended Fix:

_authorizeUpgrade(_levelTwo);
_upgradeTo(_levelTwo); // Perform actual upgrade

Or, if calling an initializer:

_authorizeUpgrade(_levelTwo);
upgradeToAndCall(_levelTwo, abi.encodeWithSignature("initializeNewLogic()"));

Ensure that the contract inherits from UUPSUpgradeable and follows all upgrade safety patterns to avoid introducing new risks.

Updates

Lead Judging Commences

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

failed upgrade

The system doesn't implement UUPS properly.

Support

FAQs

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