When the compiler is run in -f opcodes
or -f opcodes_runtime
mode, it translates the final bytecode into opcodes. However, due to incorrect padding placed on PUSH
values, the return values will be incorrect for any bytes with leading zeros.
When the compiler is run with a target output of opcodes, we run the final bytecode through the following function:
This function iterates through each instruction in the bytecode and translates it to the corresponding opcode. In the case of PUSH
instructions, it parses the number of bytes to include (let's call it x
), and then assumes the following x
instructions are the value passed to PUSH
.
For each of these two byte chunks, it parses the bytes with hex(bytecode_sequence.popleft())[2:]
and joins them together.
The problem is that for two bytes that begin with a leading 0
(such as 0x05
), this simply appends the non-zero digit to the sequence. The result is a sequence that is not as long as expected by the PUSH instruction, and therefore is prepended (or appended, depending on the type) with 0s in order to reach the expected length.
Consider the following Vyper contract, with a single function that returns a bytes4 value of 0x350f872d
:
Because the second byte of the return value starts with a 0, the translation will return 0xf
instead of 0x0f
.
The result is are these incorrect opcodes returned from the compiler (see the PUSH32
instruction in the middle):
The compiler will return incorrect values when run in opcode mode and there is any PUSH instruction that includes bytes with leading zeros.
Manual Review
Ensure that the push_values
value is padded to be two digits before being joined into a bytestring.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.