Skip to content

Commit 08a60c9

Browse files
authored
new(tests): Add EOF validation tests for stack underflow (#1331)
1 parent 2a74743 commit 08a60c9

File tree

5 files changed

+265
-3
lines changed

5 files changed

+265
-3
lines changed

converted-ethereum-tests.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ EOFTests/efStack/forwards_rjumpi_.json
3838
EOFTests/efStack/forwards_rjumpi_variable_stack_.json
3939
EOFTests/efStack/forwards_rjumpv_.json
4040
EOFTests/efStack/forwards_rjumpv_variable_stack_.json
41+
EOFTests/efStack/underflow_.json
42+
EOFTests/efStack/underflow_variable_stack_.json
4143
EOFTests/efStack/unreachable_instructions_.json
4244
EOFTests/efValidation/callf_into_nonreturning_.json
4345
EOFTests/efValidation/dataloadn_.json

tests/osaka/eip7692_eof_v1/eip4750_functions/test_code_validation.py

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -724,6 +724,113 @@ def test_callf_stack_overflow_by_height(eof_test, callee_stack_height):
724724
eof_test(container=container, expect_exception=EOFException.STACK_OVERFLOW)
725725

726726

727+
@pytest.mark.parametrize(
728+
"container",
729+
[
730+
Container(
731+
name="underflow_1",
732+
sections=[
733+
Section.Code(
734+
code=Op.CALLF[1] + Op.STOP,
735+
max_stack_height=1,
736+
),
737+
Section.Code(
738+
code=Op.PUSH0 + Op.RETF,
739+
code_inputs=1,
740+
code_outputs=2,
741+
max_stack_height=2,
742+
),
743+
],
744+
),
745+
Container(
746+
name="underflow_2",
747+
sections=[
748+
Section.Code(
749+
code=Op.CALLF[1] + Op.STOP,
750+
max_stack_height=2,
751+
),
752+
Section.Code(
753+
code=Op.PUSH0 + Op.RETF,
754+
code_inputs=1,
755+
code_outputs=2,
756+
max_stack_height=2,
757+
),
758+
],
759+
),
760+
Container(
761+
name="underflow_variable_stack_2",
762+
sections=[
763+
Section.Code(
764+
code=Op.PUSH0 + Op.RJUMPI[2](0) + Op.PUSH0 + Op.PUSH0 + Op.CALLF[1] + Op.STOP,
765+
max_stack_height=4,
766+
),
767+
Section.Code(
768+
code=Op.PUSH0 + Op.RETF,
769+
code_inputs=4,
770+
code_outputs=5,
771+
max_stack_height=5,
772+
),
773+
],
774+
),
775+
Container(
776+
name="underflow_variable_stack_2a",
777+
sections=[
778+
Section.Code(
779+
code=Op.PUSH0
780+
+ Op.PUSH0
781+
+ Op.RJUMPI[2](0)
782+
+ Op.PUSH0
783+
+ Op.PUSH0
784+
+ Op.CALLF[1]
785+
+ Op.STOP,
786+
max_stack_height=5,
787+
),
788+
Section.Code(
789+
code=Op.PUSH0 + Op.RETF,
790+
code_inputs=4,
791+
code_outputs=5,
792+
max_stack_height=5,
793+
),
794+
],
795+
),
796+
Container(
797+
name="underflow_variable_stack_3",
798+
sections=[
799+
Section.Code(
800+
code=Op.PUSH0 + Op.RJUMPI[2](0) + Op.PUSH0 + Op.PUSH0 + Op.CALLF[1] + Op.STOP,
801+
max_stack_height=4,
802+
),
803+
Section.Code(
804+
code=Op.PUSH0 + Op.RETF,
805+
code_inputs=3,
806+
code_outputs=4,
807+
max_stack_height=4,
808+
),
809+
],
810+
),
811+
Container(
812+
name="underflow_variable_stack_4",
813+
sections=[
814+
Section.Code(
815+
code=Op.PUSH0 * 3 + Op.RJUMPI[1](0) + Op.POP + Op.CALLF[1] + Op.STOP,
816+
max_stack_height=4,
817+
),
818+
Section.Code(
819+
code=Op.PUSH0 + Op.RETF,
820+
code_inputs=3,
821+
code_outputs=4,
822+
max_stack_height=4,
823+
),
824+
],
825+
),
826+
],
827+
ids=lambda x: x.name,
828+
)
829+
def test_callf_stack_underflow_examples(eof_test, container):
830+
"""Test CALLF instruction causing validation time stack underflow."""
831+
eof_test(container=container, expect_exception=EOFException.STACK_UNDERFLOW)
832+
833+
727834
def test_returning_section_aborts(
728835
eof_test: EOFTestFiller,
729836
):

tests/osaka/eip7692_eof_v1/eip5450_stack/test_code_validation.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,3 +520,50 @@ def test_all_opcodes_stack_underflow(
520520
validity_error=EOFException.STACK_UNDERFLOW,
521521
)
522522
)
523+
524+
525+
@pytest.mark.parametrize(
526+
"container",
527+
[
528+
Container(
529+
name="underflow_0",
530+
sections=[
531+
Section.Code(
532+
code=Op.ADD + Op.STOP,
533+
max_stack_height=1,
534+
),
535+
],
536+
),
537+
Container(
538+
name="underflow_variable_stack_0",
539+
sections=[
540+
Section.Code(
541+
code=Op.PUSH0 + Op.RJUMPI[2](0) + Op.PUSH0 + Op.PUSH0 + Op.LOG2 + Op.STOP,
542+
max_stack_height=3,
543+
),
544+
],
545+
),
546+
Container(
547+
name="underflow_variable_stack_1",
548+
sections=[
549+
Section.Code(
550+
code=Op.PUSH0 + Op.RJUMPI[2](0) + Op.PUSH0 + Op.PUSH0 + Op.ADD + Op.STOP,
551+
max_stack_height=3,
552+
),
553+
],
554+
),
555+
Container(
556+
name="underflow_variable_stack_2",
557+
sections=[
558+
Section.Code(
559+
code=Op.PUSH0 * 2 + Op.RJUMPI[1](0) + Op.POP + Op.ADD + Op.STOP,
560+
max_stack_height=3,
561+
),
562+
],
563+
),
564+
],
565+
ids=lambda x: x.name,
566+
)
567+
def test_stack_underflow_examples(eof_test, container):
568+
"""Test EOF validation failing due to stack underflow at basic instructions."""
569+
eof_test(container=container, expect_exception=EOFException.STACK_UNDERFLOW)

tests/osaka/eip7692_eof_v1/eip6206_jumpf/test_jumpf_validation.py

Lines changed: 107 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ def test_jumpf_self_stack_overflow(eof_test: EOFTestFiller, stack_height: int):
162162
def test_jumpf_other_stack_overflow(
163163
eof_test: EOFTestFiller, stack_height: int, stack_height_other: int
164164
):
165-
"""Test JUMPF instruction jumping to itself causing validation time stack overflow."""
165+
"""Test JUMPF instruction jumping to other section causing validation time stack overflow."""
166166
container = Container(
167167
sections=[
168168
Section.Code(
@@ -357,3 +357,109 @@ def test_jumpf_to_returning_variable_stack_3(eof_test: EOFTestFiller):
357357
container=container,
358358
expect_exception=EOFException.STACK_HIGHER_THAN_OUTPUTS,
359359
)
360+
361+
362+
@pytest.mark.parametrize(
363+
"container",
364+
[
365+
Container(
366+
name="underflow_2",
367+
sections=[
368+
Section.Code(
369+
code=Op.CALLF[1] + Op.STOP,
370+
max_stack_height=2,
371+
),
372+
Section.Code(
373+
code=Op.JUMPF[2],
374+
code_outputs=2,
375+
max_stack_height=0,
376+
),
377+
Section.Code(
378+
code=Op.PUSH0 + Op.RETF,
379+
code_inputs=1,
380+
code_outputs=2,
381+
max_stack_height=2,
382+
),
383+
],
384+
),
385+
Container(
386+
name="underflow_3",
387+
sections=[
388+
Section.Code(
389+
code=Op.JUMPF[1],
390+
),
391+
Section.Code(
392+
code=Op.REVERT(0, 0),
393+
code_inputs=1,
394+
max_stack_height=3,
395+
),
396+
],
397+
),
398+
Container(
399+
name="underflow_variable_stack_4",
400+
sections=[
401+
Section.Code(
402+
code=Op.CALLF[1] + Op.STOP,
403+
max_stack_height=3,
404+
),
405+
Section.Code(
406+
code=Op.PUSH0 + Op.RJUMPI[2](0) + Op.PUSH0 + Op.PUSH0 + Op.JUMPF[2],
407+
code_outputs=3,
408+
max_stack_height=3,
409+
),
410+
Section.Code(
411+
code=Op.POP + Op.POP + Op.RETF,
412+
code_inputs=5,
413+
code_outputs=3,
414+
max_stack_height=5,
415+
),
416+
],
417+
),
418+
Container(
419+
name="underflow_variable_stack_6",
420+
sections=[
421+
Section.Code(
422+
code=Op.PUSH0 + Op.RJUMPI[2](0) + Op.PUSH0 + Op.PUSH0 + Op.JUMPF[1],
423+
max_stack_height=3,
424+
),
425+
Section.Code(
426+
code=Op.REVERT(0, 0),
427+
code_inputs=4,
428+
max_stack_height=6,
429+
),
430+
],
431+
),
432+
Container(
433+
name="underflow_variable_stack_7",
434+
sections=[
435+
Section.Code(
436+
code=Op.PUSH0 + Op.RJUMPI[2](0) + Op.PUSH0 + Op.PUSH0 + Op.JUMPF[1],
437+
max_stack_height=3,
438+
),
439+
Section.Code(
440+
code=Op.REVERT(0, 0),
441+
code_inputs=3,
442+
max_stack_height=5,
443+
),
444+
],
445+
),
446+
Container(
447+
name="underflow_variable_stack_8",
448+
sections=[
449+
Section.Code(
450+
code=Op.PUSH0 * 3 + Op.RJUMPI[1](0) + Op.POP + Op.JUMPF[1],
451+
max_stack_height=3,
452+
),
453+
Section.Code(
454+
code=Op.REVERT(0, 0),
455+
code_inputs=3,
456+
max_stack_height=5,
457+
),
458+
],
459+
),
460+
],
461+
ids=lambda x: x.name,
462+
)
463+
def test_jumpf_stack_underflow_examples(eof_test: EOFTestFiller, container: Container):
464+
"""Test JUMPF instruction causing validation time stack underflow."""
465+
eof_test(container=container, expect_exception=EOFException.STACK_UNDERFLOW)

tests/osaka/eip7692_eof_v1/eof_tracker.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,8 +246,8 @@
246246

247247
- [ ] Valid CALLFs to functions with inputs (ethereum/tests: src/EOFTestsFiller/efStack/callf_stack_validation_Copier.json)
248248
- [ ] CALLF stack underflows (ethereum/tests: ./src/EOFTestsFiller/efExample/validInvalidFiller.yml src/EOFTestsFiller/EIP5450/validInvalidFiller.yml src/EOFTestsFiller/efStack/callf_stack_validation_Copier.json)
249-
- [ ] CALLF stack underflow in variable stack segment, only min underflow (ethereum/tests: src/EOFTestsFiller/efStack/underflow_variable_stack_Copier.json)
250-
- [ ] CALLF stack underflow in variable stack segment, both min and max underflow (ethereum/tests: src/EOFTestsFiller/efStack/underflow_variable_stack_Copier.json)
249+
- [x] CALLF stack underflow in variable stack segment, only min underflow ([`tests/osaka/eip7692_eof_v1/eip4750_functions/test_code_validation.py::test_callf_stack_underflow_examples`](./eip4750_functions/test_code_validation/test_callf_stack_underflow_examples.md))
250+
- [x] CALLF stack underflow in variable stack segment, both min and max underflow ([`tests/osaka/eip7692_eof_v1/eip4750_functions/test_code_validation.py::test_callf_stack_underflow_examples`](./eip4750_functions/test_code_validation/test_callf_stack_underflow_examples.md))
251251
- [ ] Branching to CALLFs with the same number of outputs (ethereum/tests: src/EOFTestsFiller/EIP5450/validInvalidFiller.yml)
252252
- [ ] Check that CALLF stack inputs/outputs equal to target section type definition
253253

0 commit comments

Comments
 (0)