FoundrySolidityLayer 2
7.25 ETH
Submission Details
Impact: medium
Likelihood: low

Factory missing `renounceOwnership` override — owner can permanently brick the factory

Author Revealed upon completion

DESCRIPTION

The ConfidencePoolFactory inherits from OpenZeppelin's Ownable2StepUpgradeable, which exposes a public renounceOwnership() function. The factory does not override this function. If called by the owner, ownership is permanently renounced to address(0).

Root cause:

@> ConfidencePoolFactory.sol:6

contract ConfidencePoolFactory is
Initializable,
Ownable2StepUpgradeable, // inherits renounceOwnership()
UUPSUpgradeable,
PausableUpgradeable,
IConfidencePoolFactory
{

@> ConfidencePoolFactory.sol:162-164

// No renounceOwnership override exists — the contract relies entirely on OZ's default.
function _authorizeUpgrade(address) internal override onlyOwner {}

The only overrides in the factory are for _authorizeUpgrade and the constructor _disableInitializers(). There is no renounceOwnership override anywhere in the file.

RISK

Likelihood: Low

  • Requires the owner to intentionally call renounceOwnership(). This is a self-inflicted action that no reasonable deployer would take.

  • The two-step ownership transfer (Ownable2Step) does not trigger this — it requires a conscious, direct function call.

Impact: Medium (if it istriggered)

  • Factory becomes permanently inoperable: no new pools can be created (createPool blocked by onlyOwner on the factory? No — createPool uses whenNotPaused not onlyOwner. But the setters are onlyOwner.)

  • Wait — re-verifying: createPool is external whenNotPaused — it does NOT have onlyOwner. So pools can still be created after renounceOwnership. But setStakeTokenAllowed IS onlyOwner, so no new tokens can be allowlisted.

  • More critically: _authorizeUpgrade IS onlyOwner, so the factory can never be upgraded to a fix.

  • Existing pools continue to function independently.

PROOF OF CONCEPT

// Factory inherits renounceOwnership from OZ — no override
factory.renounceOwnership();
// All onlyOwner functions now revert
vm.expectRevert();
factory.setPoolImplementation(address(0xdead));
vm.expectRevert();
factory.setSafeHarborRegistry(address(0xdead));
vm.expectRevert();
factory.setStakeTokenAllowed(address(token), false);
// _authorizeUpgrade is also bricked — factory can never be upgraded
vm.expectRevert();
factory.upgradeToAndCall(address(0xdead), "");

RECOMMENDED MITIGATION

Override renounceOwnership to revert with a descriptive error:

+ error CannotRenounceOwnership();
+
+ function renounceOwnership() public pure override {
+ revert CannotRenounceOwnership();
+ }

Support

FAQs

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

Give us feedback!