Skip to content

Commit d9eb924

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

File tree

1 file changed

+64
-1
lines changed

1 file changed

+64
-1
lines changed

tests/frontier/opcodes/test_all_opcodes.py

Lines changed: 64 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,8 @@
1919
Transaction,
2020
UndefinedOpcodes,
2121
)
22+
from execution_testing.base_types.base_types import ZeroPaddedHexNumber
23+
from execution_testing.forks import Byzantium
2224

2325
REFERENCE_SPEC_GIT_PATH = "N/A"
2426
REFERENCE_SPEC_VERSION = "N/A"
@@ -135,3 +137,64 @@ def test_cover_revert(state_test: StateTestFiller, pre: Alloc) -> None:
135137
)
136138

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

0 commit comments

Comments
 (0)