Tadle

Tadle
DeFi
30,000 USDC
View results
Submission Details
Severity: high
Invalid

Flawed proxy upgrade mechanism creates new proxies instead of upgrading, causing potential state loss and inconsistencies

Summary

The TadleFactory contract's deployUpgradeableProxy function creates new proxy contracts for each upgrade instead of upgrading existing proxies. While the system shows preparations for upgradability (such as storage gaps and inheritance from UpgradeableStorage), the deployUpgradeableProxy function creates new UpgradeableProxy instances each time it's called, overwriting the address in the relatedContracts mapping.

This approach doesn't leverage the upgradeable design of the contracts and can lead to state loss and system inconsistencies.

Vulnerability Details

Consider a scenario where an admin deploys a contract at index 1, and later, the same admin calls deployUpgradeableProxy with index 1 again, deploying a new contract. The previous contract's state is lost, and any functionality relying on that contract will fail.

In the TadleFactory::deployUpgradeableProxy :

function deployUpgradeableProxy(
uint8 _relatedContractIndex,
address _logic,
bytes memory _data
) external onlyGuardian returns (address) {
// ...
@> UpgradeableProxy _proxy = new UpgradeableProxy(
_logic,
guardian,
address(this),
_data
);
@> relatedContracts[_relatedContractIndex] = address(_proxy);
// ...
}

This function creates a new UpgradeableProxy each time it's called, even for existing indices, leading to state loss and potential inconsistencies.

The UpgradeableProxy contract suggests that future variables will be added because of the __gap variable. The curent implementation will lead to a loss of the state of these future variables, as it won’t be possible to modify the deployUpgradeableProxy function.

contract UpgradeableProxy is TransparentUpgradeableProxy {
ITadleFactory public tadleFactory;
// ...
// @audit - What if in future version, we erase proxies? Data lost?
uint256[49] private __gap;

Impact

  • Loss of State: Overwriting existing proxies can lead to the loss of all state data held by the previous contract, disrupting the functionality of the system.

  • Operational Disruption: If contracts are actively being used, replacing them can cause significant operational issues, leading to failures in dependent systems and processes.

  • Inability to Upgrade Safely: The lack of a proper upgrade mechanism prevents the system from adapting to new requirements or fixing bugs without risking data loss or service interruptions.

Tools Used

Manual review

Recommendations

Implement a true upgrade mechanism:

  • Modify the UpgradeableProxy contract to include functions for upgrading the logic contract without changing the proxy address (e.g., upgradeToAndCall).

  • Add an upgradeProxy function to TadleFactory that calls the upgrade function on the existing proxy:

function upgradeProxy(
uint8 _relatedContractIndex,
address _newLogic,
bytes memory _data
) external onlyGuardian {
address proxyAddress = relatedContracts[_relatedContractIndex];
require(proxyAddress != address(0), "Proxy does not exist");
UpgradeableProxy proxy = UpgradeableProxy(proxyAddress);
if (_data.length > 0) {
proxy.upgradeToAndCall(_newLogic, _data);
} else {
proxy.upgradeToAndCall(_newLogic, bytes("");
}
emit ProxyUpgraded(_relatedContractIndex, proxyAddress, _newLogic);
}
  • Implement validation for the _relatedContractIndex parameter to prevent overwriting existing proxies without checks.

  • Modify deployUpgradeableProxy to only deploy a new proxy if one doesn't exist for the given index. If a proxy already exists, it should revert or call the upgrade function instead, ensuring that the state is preserved.

  • Consider using the upgrade function upgradeToAndCall provided by TransparentUpgradeableProxy effectively, in order to change the implementation.

Updates

Lead Judging Commences

0xnevi Lead Judge
10 months ago
0xnevi Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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