Skip to content
This repository was archived by the owner on Feb 14, 2025. It is now read-only.

Commit e3155cd

Browse files
committed
Fix merge_stack
Add unit tests Replace #34 Partially fix #31
1 parent 98c9e96 commit e3155cd

File tree

6 files changed

+201
-24
lines changed

6 files changed

+201
-24
lines changed

.github/workflows/pytest.yml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
name: Pytest
2+
3+
defaults:
4+
run:
5+
# To load bashrc
6+
shell: bash -ieo pipefail {0}
7+
8+
on:
9+
push:
10+
branches:
11+
- main
12+
- dev
13+
pull_request:
14+
branches: [main, dev]
15+
schedule:
16+
# run CI every day even if no PRs/merges occur
17+
- cron: '0 12 * * *'
18+
19+
jobs:
20+
tests:
21+
runs-on: ubuntu-latest
22+
23+
steps:
24+
- uses: actions/checkout@v1
25+
- name: Set up Python 3.6
26+
uses: actions/setup-python@v1
27+
with:
28+
python-version: 3.6
29+
30+
# Used by ci_test.sh
31+
- name: Install dependencies
32+
run: |
33+
python setup.py install
34+
pip install pytest
35+
pip install pytest-cov
36+
- name: Run value set analysis tests
37+
run: |
38+
pytest python_tests/value_set_analysis.py

evm_cfg_builder/__main__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
import pstats
77
import sys
88
from typing import Optional, Union
9+
from pkg_resources import require
910

1011
from crytic_compile import cryticparser, CryticCompile, InvalidCompilation, is_supported
11-
from pkg_resources import require
1212

1313
from evm_cfg_builder.cfg.cfg import CFG
1414
from evm_cfg_builder.known_hashes.known_hashes import known_hashes

evm_cfg_builder/cfg/function.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,14 +169,16 @@ def output_to_dot(self, base_filename: str) -> None:
169169
instructions_ = [f"{hex(ins.pc)}:{str(ins)}" for ins in basic_block.instructions]
170170
instructions = "\n".join(instructions_)
171171

172-
f.write(f'{basic_block.start.pc}[label="{instructions}"]\n')
172+
f.write(f'{basic_block.start.pc}[label="{instructions}", shape=box]\n')
173173

174174
for son in basic_block.outgoing_basic_blocks(self.key):
175175
f.write(f"{basic_block.start.pc} -> {son.start.pc}\n")
176176

177177
if not basic_block.outgoing_basic_blocks(self.key):
178178
if basic_block.ends_with_jump_or_jumpi():
179-
logger.error(f"Missing branches {self.name}:{hex(basic_block.end.pc)}")
179+
logger.error(
180+
f"Missing branches {self.name} ({self.key}):{hex(basic_block.end.pc)}"
181+
)
180182

181183
f.write("\n}")
182184

evm_cfg_builder/value_analysis/value_set_analysis.py

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,15 @@ class AbsStackElem:
3939
4040
Thus our analysis is an under-approximation of an over-approximation
4141
and is not sound.
42+
4243
"""
4344

4445
def __init__(
4546
self, auhtorized_values: Optional[Set[int]], vals: Optional[Set[Optional[int]]] = None
4647
):
4748
if vals:
49+
if auhtorized_values:
50+
vals = {v if v in auhtorized_values else None for v in vals}
4851
self._vals: Optional[Set[Optional[int]]] = vals
4952
else:
5053
self._vals = set()
@@ -351,29 +354,26 @@ def merge_stack(stacks: List[Stack], authorized_values: Set[int]) -> Stack:
351354

352355
_max_number_of_elements = len(authorized_values) if authorized_values else 100
353356

354-
found = True
355-
i = 0
356-
while found:
357+
elemss = [stack.get_elems()[::-1] for stack in stacks]
358+
max_depth = max(len(elems) for elems in elemss)
359+
360+
for i in range(max_depth):
357361
vals: Optional[Set[Optional[int]]] = set()
358-
found = False
359-
for stack in stacks:
360-
elems = stack.get_elems()
361-
if len(elems) <= i:
362-
continue
363-
found = True
364-
next_vals = elems[i].get_vals()
365-
if next_vals is None:
366-
vals = None
367-
break
368-
assert vals is not None
369-
vals |= next_vals
370-
if len(vals) > _max_number_of_elements:
371-
vals = None
372-
break
362+
for elems in elemss:
363+
if len(elems) > i:
364+
next_vals = elems[i].get_vals()
365+
if next_vals is None:
366+
vals = None
367+
break
368+
assert vals is not None
369+
vals |= next_vals
370+
if len(vals) > _max_number_of_elements:
371+
vals = None
372+
break
373373
stack_elements.append(AbsStackElem(authorized_values, vals))
374-
i = i + 1
374+
375375
newSt = Stack(authorized_values)
376-
newSt.set_elems(stack_elements)
376+
newSt.set_elems(stack_elements[::-1])
377377
return newSt
378378

379379

@@ -486,7 +486,6 @@ def _transfer_func_ins(self, ins: Instruction, addr: int, stack: Stack) -> Stack
486486
(is_stub, stub_ret) = self.stub(ins, addr, stack)
487487
if is_stub:
488488
return stub_ret
489-
490489
op = ins.name
491490
if op.startswith("PUSH"):
492491
stack.push(ins.operand)

python_tests/__init__.py

Whitespace-only changes.

python_tests/value_set_analysis.py

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
from typing import Set
2+
3+
from evm_cfg_builder.value_analysis.value_set_analysis import (
4+
merge_stack,
5+
Stack,
6+
AbsStackElem,
7+
)
8+
9+
10+
def test_merge_stack_1() -> None:
11+
authorized_values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
12+
st1 = Stack(authorized_values)
13+
14+
st1.push(1)
15+
st1.push(2)
16+
st1.push(10)
17+
st1.push(3)
18+
19+
st2 = Stack(authorized_values)
20+
21+
st2.push(1)
22+
st2.push(3)
23+
st2.push(5)
24+
25+
st_merged = merge_stack([st1, st2], authorized_values)
26+
27+
st_real = Stack(authorized_values)
28+
st_real.push(1)
29+
st_real.push(AbsStackElem(authorized_values, {1, 2}))
30+
st_real.push(AbsStackElem(authorized_values, {3, 10}))
31+
st_real.push(AbsStackElem(authorized_values, {3, 5}))
32+
33+
print(st1)
34+
print(st2)
35+
print(st_merged)
36+
print(st_real)
37+
assert st_real.equals(st_merged)
38+
39+
40+
def test_merge_stack_2() -> None:
41+
authorized_values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
42+
st1 = Stack(authorized_values)
43+
44+
st1.push(1)
45+
st1.push(AbsStackElem(authorized_values, {2, 4}))
46+
st1.push(10)
47+
st1.push(3)
48+
49+
st2 = Stack(authorized_values)
50+
51+
st2.push(1)
52+
st2.push(3)
53+
st2.push(5)
54+
55+
st_merged = merge_stack([st1, st2], authorized_values)
56+
57+
st_real = Stack(authorized_values)
58+
st_real.push(1)
59+
st_real.push(AbsStackElem(authorized_values, {1, 2, 4}))
60+
st_real.push(AbsStackElem(authorized_values, {3, 10}))
61+
st_real.push(AbsStackElem(authorized_values, {3, 5}))
62+
63+
print(st1)
64+
print(st2)
65+
print(st_merged)
66+
print(st_real)
67+
assert st_real.equals(st_merged)
68+
69+
70+
def test_merge_stack_no_authorized_value() -> None:
71+
authorized_values: Set[int] = set()
72+
st1 = Stack(authorized_values)
73+
74+
st1.push(1)
75+
st1.push(2)
76+
st1.push(3)
77+
st1.push(4)
78+
st1.push(5)
79+
80+
st2 = Stack(authorized_values)
81+
82+
st2.push(3)
83+
st2.push(4)
84+
st2.push(5)
85+
86+
st_merged = merge_stack([st1, st2], authorized_values)
87+
88+
st_real = Stack(authorized_values)
89+
st_real.push(1)
90+
st_real.push(2)
91+
st_real.push(3)
92+
st_real.push(4)
93+
st_real.push(5)
94+
95+
print(st1)
96+
print(st2)
97+
print(st_merged)
98+
print(st_real)
99+
assert st_real.equals(st_merged)
100+
101+
102+
def test_pop_push() -> None:
103+
authorized_values: Set[int] = set()
104+
st1 = Stack(authorized_values)
105+
st1.push(1)
106+
st1.push(2)
107+
assert st1.top().equals(AbsStackElem(authorized_values, {2}))
108+
109+
110+
def test_merge_stack_diff_size() -> None:
111+
authorized_values: Set[int] = set()
112+
st1 = Stack(authorized_values)
113+
st2 = Stack(authorized_values)
114+
115+
st1.push(1)
116+
st1.push(2)
117+
118+
st2.push(2)
119+
120+
st_merged = merge_stack([st1, st2], authorized_values)
121+
122+
st_real = Stack(authorized_values)
123+
st_real.push(1)
124+
st_real.push(2)
125+
126+
print(st1)
127+
print(st2)
128+
print(st_merged)
129+
print(st_real)
130+
assert st_real.equals(st_merged)
131+
132+
133+
if __name__ == "__main__":
134+
test_pop_push()
135+
test_merge_stack_diff_size()
136+
test_merge_stack_1()
137+
test_merge_stack_2()
138+
test_merge_stack_no_authorized_value()

0 commit comments

Comments
 (0)