-
Notifications
You must be signed in to change notification settings - Fork 11.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update initializer modifier to prevent reentrancy during initialization #3006
Conversation
looking at the old mocks, and what they have become, I really think it was better before ... despite re-entrancy potential :/ |
Hello everyone,i'm Lucas_LMC. I'm sorry to bother you. I'm a little care about whether this issue will be included in the Security advisories section of the openzepplin library. |
Hi @slendermaan yes once we release. We had to postpone the release until tomorrow. |
update initializer modifiers as described in: OpenZeppelin/openzeppelin-contracts#3006
Hey guys, I stumbled upon this PR after reading the comments added in the latest implementation of I'm trying to wrap my head around this, though:
@Amxx, could you share an example for when the previous behavior would have been problematic? |
Let's say you have:
Now the flow:
By spliting the If the initializer is done during construction, then we don't have this threat because E would not be able to call P (because P is not yet constructed) |
Very interesting. But I think this was low-risk, wasn't it? Firstly, most upgradeable proxy deployers take care of initialization atomically - I think this is what hardhat-upgrades does. Secondly, I haven't seen many contracts making calls to external contracts during construction. The vast majority of contracts just call some internal function inherited from other contracts. |
Yes, this was low severity. Have you been able to fix your issue? You should probably use |
Yeah that's exactly what I did. Thanks for confirming! |
refactor(protocol): mark init function as "internal" in OwnableUpgradeable refactor(protocol): rename "__OwnableUpgradeable_init" to "__Ownable_init" test(protocol): new GodModeOwnableUpgradeable to expose init function See releated discussion in OpenZeppelin/openzeppelin-contracts#3006
But how does initialization invokes E? Why it should make a call into some external contract which is not under our control? |
This may be part of the logic of the initializer body for some reason. To be clear: the |
This issue was identified and reported by @chaitinblockchain through our bug bounty on Immunefi.
It is currently possible for
initializer()
-protected functions to be executed twice, if this happens in the same transaction. For this to happen, either one call has to be a subcall the other, or both call have to be subcalls of a commoninitializer()
-protected function.This can particularly be dangerous is the initialization is not part of the proxy construction, and reentrancy is possible by executing an external call to an untrusted address.
This PR is a move toward fixing this potential issue, by forbidding
initializer()
-protected function to be nested when the contract is already constructed and re-entrance is possible.⚠ This change is breaking in some cases. ⚠
This change require changes to the upgradeable contracts:
__XXX_init
function must use the newonlyInitializing()
modifier instead ofinitializer()
PR Checklist