Severity: Medium
The RootUpgrade::replace and RootUpgrade::remove functions use address(this) to access the address of the current contract, UpgradeBranch. Because the functions are executed in a delegateCall, address(this) returns the RootProxy address instead of the UpgradeBranch.
The RootUpgrade::replace and RootUpgrade::remove functions are intended to ensure that the UpgradeBranch branch's selectors cannot be replaced nor removed. This is to prevent owner from accidentally removing the selector for upgrade function, if removed then the upgrade function cannot be called and the upgrade functionality will be bricked.
The functions compare the target branch with the address(this) to determine if the UpgradeBranch is being modified.
Because the functions are executed in a delegatecall from the RootProxy, address(this) will return RootProxy address not the address of UpgradeBranch. As a result, the comparison will not succeed allowing for possibility of bricking the upgradeability feature.
Snippet of RootUpgrade::replace using address(this): RootUpgrade.sol#L150-L152
Snippet of RootUpgrade::remove using address(this): RootUpgrade.sol#L178-L180
The RootUpgrade::replace, and RootUpgrade::remove functions fail to prevent modifying functions in UpgradeBranch branch allowing for the owner to brick the upgradeability feature.
Manual Review
Add an immutable variable to UpgradeBranch contract and initialze it with address(this) in the constructor. Use the immutable variable value to perform the comparison in the vulnerable functions instead of address(this).
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.