Skip to content

Commit 3c37b4f

Browse files
committed
feat(tests): Add tests for stack overflow
1 parent 9563a51 commit 3c37b4f

File tree

1 file changed

+59
-1
lines changed

1 file changed

+59
-1
lines changed

tests/frontier/opcodes/test_all_opcodes.py

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
opcode is supported by the fork supports and fails otherwise.
44
"""
55

6-
from typing import Dict
6+
from typing import Dict, Iterator
77

88
import pytest
99
from execution_testing import (
@@ -19,6 +19,7 @@
1919
Transaction,
2020
UndefinedOpcodes,
2121
)
22+
from execution_testing.forks import Byzantium
2223

2324
REFERENCE_SPEC_GIT_PATH = "N/A"
2425
REFERENCE_SPEC_VERSION = "N/A"
@@ -135,3 +136,60 @@ def test_cover_revert(state_test: StateTestFiller, pre: Alloc) -> None:
135136
)
136137

137138
state_test(env=Environment(), pre=pre, post={}, tx=tx)
139+
140+
141+
def fork_opcodes_increasing_stack(
142+
fork: Fork,
143+
) -> Iterator[Op]:
144+
"""
145+
Yields opcodes which are valid for `fork` and increase the operand stack.
146+
"""
147+
for opcode in fork.valid_opcodes():
148+
if opcode.pushed_stack_items > opcode.popped_stack_items:
149+
yield opcode
150+
151+
152+
@pytest.mark.parametrize_by_fork("opcode", fork_opcodes_increasing_stack)
153+
@pytest.mark.parametrize("fails", [True, False])
154+
def test_stack_overflow(
155+
state_test: StateTestFiller,
156+
pre: Alloc,
157+
fork: Fork,
158+
opcode: Op,
159+
fails: bool,
160+
env: Environment,
161+
) -> None:
162+
"""Test that opcodes which leave new items on the stack can overflow."""
163+
pre_stack_items = fork.max_stack_height()
164+
if not fails:
165+
pre_stack_items -= (
166+
opcode.pushed_stack_items - opcode.popped_stack_items
167+
)
168+
slot_code_worked = 1
169+
value_code_failed = 0xDEADBEEF
170+
value_code_worked = 1
171+
172+
contract = pre.deploy_contract(
173+
code=Op.SSTORE(slot_code_worked, value_code_worked)
174+
+ Op.PUSH1(0) * pre_stack_items
175+
+ opcode
176+
+ Op.STOP,
177+
storage={slot_code_worked: value_code_failed},
178+
)
179+
180+
tx = Transaction(
181+
gas_limit=100_000,
182+
to=contract,
183+
sender=pre.fund_eoa(),
184+
protected=fork >= Byzantium,
185+
)
186+
expected_storage = {
187+
slot_code_worked: value_code_failed if fails else value_code_worked
188+
}
189+
190+
state_test(
191+
env=env,
192+
pre=pre,
193+
tx=tx,
194+
post={contract: Account(storage=expected_storage)},
195+
)

0 commit comments

Comments
 (0)