Ensure CodeDeployer pseudo-contracts are not executable #2549
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Description
We received an interesting report from Czar102 about the CodeDeployer library. Since it only happens on factory deployment, it's not a vulnerability, and the likelihood of this actually happening is low. Nevertheless, it is a valid issue with a non-zero probability of happening, so we will fix it.
CodeDeployer is a library which "deploys" arbitrary code to an address; it need not be a valid contract. We use this in the BaseSplitCodeFactory, which underlies all our pool factories, so that we are able to deploy contracts with very large creationCode: up to 48k, twice the contract size limit. This is needed for some contracts with very tight bytecode constraints, and also a large amount of initialization code (e.g., filling in immutables).
Essentially, it splits the bytecode in half, uses CodeDeployer to deploy each "contract half" separately, then rejoins them for the actual deployment. Every time a new pool is deployed, it reassembles the bytecode from these two contracts, then appends the constructor arguments for the specific pool.
Since the BaseSplitCodeFactory logic splits the bytecode exactly in half, the second half could have any random sequence of bytes. Depending on the layout (which could theoretically change in the future), it might be in the middle of a string or data section.
So here's the problem: what if that random sequence of bytes accidentally (or maliciously) happens to start with valid opcodes? Sending a transaction would try to execute it, and it wouldn't necessarily revert. If it happened to contain a "selfdestruct" opcode, it would destroy itself, effectively deleting the second half of the pool's bytecode, and bricking the factory.
To ensure that the second "half" can never be executed, "protect" it by prepending an invalid opcode. Rather than have the option, it could just always do it, but there might be use cases where exact fidelity is important.
In BaseSplitCodeFactory, we apply the protection to the second "half" of the bytecode, and then skip that extra byte during reassembly.
Type of change
Checklist:
master
, or there's a description of how to mergeIssue Resolution