vyper: `create_from_blueprint` offset validation bypass through unsigned wraparound
What fixes it
No source in this snapshot names a fixed version.
Description
Summary
Vyper's create_from_blueprint can deploy a contract with empty runtime bytecode instead of reverting when supplied with an invalid code_offset. Contracts that allow attackers to control this parameter are affected.
Details
The builtin computes the remaining blueprint code size using EVM subtraction, then validates the result with a signed comparison:
codesize = ["sub", ["extcodesize", target], code_offset] check_codesize = ["assert", ["sgt", codesize, 0]]
Because code_offset is a uint256, subtraction can wrap modulo 2^256 and produce a small positive result that passes the check.
For example, given a blueprint with 200 bytes of code:
code_offset = 2^256 - 1 codesize = 200 - (2^256 - 1) mod 2^256 = 201 sgt(201, 0) = true
EXTCODECOPY then reads beyond the blueprint's code and fills the destination with zeros. The resulting initcode begins with STOP, so contract creation succeeds and returns an address with no runtime code. The oversized source offset does not itself cause excessive gas consumption.
Impact
An attacker who controls code_offset can cause a factory to deploy empty contracts instead of the intended blueprint instances. Depending on the application, this can disrupt initialization, violate assumptions about deployed contracts, or leave ETH and tokens sent to those addresses inaccessible.
The common pattern using a fixed, valid offset such as code_offset=3 is unaffected by this bypass.
Remediation
Validate **code_offset < extcodesize(target) using an unsigned comparison before subtraction**. Checking only the wrapped result, even with an unsigned comparison, does not prevent the bypass.
As a workaround, use a fixed, valid offset or explicitly enforce this bound before calling create_from_blueprint.
Text from GitHub Advisory Database
Affected versions 1
- vyper, pip>= 0.3.4GitHub