Skip to content

Commit

Permalink
new(tests): EOF validation of opcodes
Browse files Browse the repository at this point in the history
Add missing opcode tests:
- invalid opcode placed after a STOP instruction,
- opcodes with truncated immediate bytes.

Mark rest of the "opcode tests" as done by providing links to tests.

Co-authored-by: Mario Vega <[email protected]>
  • Loading branch information
chfast and marioevz committed Nov 7, 2024
1 parent e69aae5 commit 463438d
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 6 deletions.
4 changes: 4 additions & 0 deletions converted-ethereum-tests.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,14 @@ GeneralStateTests/stCreate2/call_then_create2_successful_then_returndatasize.jso
([#598](https://github.com/ethereum/execution-spec-tests/pull/598))
EOFTests/EIP3540/validInvalid.json

EOFTests/EIP3670/validInvalid.json
EOFTests/efValidation/EOF1_embedded_container_.json
EOFTests/efValidation/EOF1_eofcreate_valid_.json
EOFTests/efValidation/EOF1_section_order_.json
EOFTests/efValidation/EOF1_truncated_section_.json
EOFTests/efValidation/EOF1_undefined_opcodes_.json
EOFTests/efValidation/EOF1_truncated_push_.json
EOFTests/efValidation/deprecated_instructions_.json
EOFTests/efValidation/unreachable_code_sections_.json

([#647](https://github.com/ethereum/execution-spec-tests/pull/647))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@
Op.JUMPF,
}

data_portion_opcodes = {op for op in all_opcodes if op.has_data_portion()}


# NOTE: `sorted` is used to ensure that the tests are collected in a deterministic order.

Expand Down Expand Up @@ -118,6 +120,24 @@ def test_all_opcodes_in_container(
)


@pytest.mark.parametrize(
"opcode",
sorted(invalid_eof_opcodes | undefined_opcodes),
)
def test_invalid_opcodes_after_stop(
eof_test: EOFTestFiller,
opcode: Opcode,
):
"""
Test that an invalid opcode placed after STOP (terminating instruction) invalidates EOF.
"""
eof_code = Container(sections=[Section.Code(code=Op.STOP + opcode), Section.Data("00" * 32)])
eof_test(
data=eof_code,
expect_exception=EOFException.UNDEFINED_INSTRUCTION,
)


@pytest.mark.parametrize(
"opcode",
sorted(
Expand Down Expand Up @@ -382,3 +402,55 @@ def test_all_opcodes_stack_overflow(
data=eof_code,
expect_exception=exception,
)


@pytest.mark.parametrize(
"opcode",
sorted(data_portion_opcodes),
)
@pytest.mark.parametrize(
"truncate_all",
[False, True],
)
@pytest.mark.parametrize(
"compute_max_stack_height",
[False, True],
)
def test_truncated_data_portion_opcodes(
eof_test: EOFTestFiller,
opcode: Opcode,
truncate_all,
compute_max_stack_height: bool,
):
"""
Test that an instruction with data portion and truncated immediate bytes
(therefore a terminating instruction is also missing) invalidates EOF.
"""
opcode_with_data_portion: bytes = bytes(opcode[1])

if len(opcode_with_data_portion) == 2 and truncate_all:
pytest.skip("can only be truncated one-way")

# Compose instruction bytes with empty imm bytes (truncate_all) or 1 byte shorter imm bytes.
opcode_bytes = opcode_with_data_portion[0:1] if truncate_all else opcode_with_data_portion[:-1]

if opcode.min_stack_height > 0:
opcode_bytes = bytes(Op.PUSH0 * opcode.min_stack_height) + opcode_bytes

max_stack_height = (
max(opcode.min_stack_height, opcode.pushed_stack_items) if compute_max_stack_height else 0
)
if compute_max_stack_height and max_stack_height == 0:
pytest.skip("max_stack_height is 0")

eof_code = Container(
sections=[
Section.Code(opcode_bytes, max_stack_height=max_stack_height),
# Provide data section potentially confused with missing imm bytes.
Section.Data(b"\0" * 64),
]
)
eof_test(
data=eof_code,
expect_exception=EOFException.TRUNCATED_INSTRUCTION,
)
12 changes: 6 additions & 6 deletions tests/osaka/eip7692_eof_v1/eof_tracker.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,12 @@

### Validation

- [ ] Code section with invalid opcodes is rejected (ethereum/tests: ./src/EOFTestsFiller/efExample/validInvalidFiller.yml src/EOFTestsFiller/efValidation/EOF1_undefined_opcodes_Copier.json src/EOFTestsFiller/EIP3670/validInvalidFiller.yml)
- [ ] INVALID opcode is valid (ethereum/tests: ./src/EOFTestsFiller/efExample/validInvalidFiller.yml)
- [ ] Truncated PUSH data (ethereum/tests: ./src/EOFTestsFiller/efExample/validInvalidFiller.yml src/EOFTestsFiller/efValidation/EOF1_truncated_push_Copier.json src/EOFTestsFiller/EIP3670/validInvalidFiller.yml)
- [ ] Opcodes deprecated in EOF are rejected (ethereum/tests: src/EOFTestsFiller/efValidation/deprecated_instructions_Copier.json ethereum/tests: src/EOFTestsFiller/EIP3670/validInvalidFiller.yml)
- [ ] Codes with each valid opcodes (ethereum/tests: src/EOFTestsFiller/EIP3670/validInvalidFiller.yml)
- [ ] Undefined instruction after terminating instruction (ethereum/tests: src/EOFTestsFiller/EIP3670/validInvalidFiller.yml)
- [x] Code section with invalid opcodes is rejected ([`tests/osaka/eip7692_eof_v1/eip3540_eof_v1/test_all_opcodes_in_container.py::test_all_opcodes_in_container`](./eip3540_eof_v1/test_all_opcodes_in_container/test_all_opcodes_in_container.md))
- [x] INVALID opcode is valid ([`tests/osaka/eip7692_eof_v1/eip3540_eof_v1/test_all_opcodes_in_container.py::test_all_opcodes_in_container`](./eip3540_eof_v1/test_all_opcodes_in_container/test_all_opcodes_in_container.md))
- [x] Truncated PUSH data ([`tests/osaka/eip7692_eof_v1/eip3540_eof_v1/test_all_opcodes_in_container.py::test_truncated_data_portion_opcodes`](./eip3540_eof_v1/test_all_opcodes_in_container/test_truncated_data_portion_opcodes.md))
- [x] Opcodes deprecated in EOF are rejected ([`tests/osaka/eip7692_eof_v1/eip3540_eof_v1/test_all_opcodes_in_container.py::test_all_opcodes_in_container`](./eip3540_eof_v1/test_all_opcodes_in_container/test_all_opcodes_in_container.md))
- [x] Codes with each valid opcodes ([`tests/osaka/eip7692_eof_v1/eip3540_eof_v1/test_all_opcodes_in_container.py::test_all_opcodes_in_container`](./eip3540_eof_v1/test_all_opcodes_in_container/test_all_opcodes_in_container.md))
- [x] Undefined instruction after terminating instruction ([`tests/osaka/eip7692_eof_v1/eip3540_eof_v1/test_all_opcodes_in_container.py::test_invalid_opcodes_after_stop`](./eip3540_eof_v1/test_all_opcodes_in_container/test_invalid_opcodes_after_stop.md))

## EIP-4200: EOF - Static relative jumps

Expand Down

0 comments on commit 463438d

Please sign in to comment.