Skip to content

Commit 79b454d

Browse files
committed
improved pcode
1 parent 42fa6f1 commit 79b454d

File tree

3 files changed

+46
-35
lines changed

3 files changed

+46
-35
lines changed

src/kdrag/contrib/pcode/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,6 @@
7979
for bits, MemSort in MemSorts.items()
8080
}
8181

82-
type StateExpr = smt.ExprRef # NewType("StateExpr", smt.ExprRef)
83-
type StateProp = smt.BoolRef # NewType("StateProp", smt.BoolRef)
8482
"""
8583
class StateExpr(NamedTuple):
8684
ctx: BinaryContext
@@ -95,6 +93,9 @@ def __call__(self, memstate: MemState) -> smt.ExprRef:
9593
return self.ctx.substitute(memstate, self.expr)
9694
"""
9795

96+
type StateExpr = smt.ExprRef # NewType("StateExpr", smt.ExprRef)
97+
type StateProp = smt.BoolRef # NewType("StateProp", smt.BoolRef)
98+
9899

99100
@dataclass(frozen=True)
100101
class _TestVarnode:

src/kdrag/contrib/pcode/asmspec.py

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ class AsmSpec:
129129
130130
"""
131131

132+
ctx: pcode.BinaryContext
132133
addrmap: defaultdict[int, list[SpecStmt]] = dataclasses.field(
133134
default_factory=lambda: defaultdict(list)
134135
)
@@ -151,6 +152,15 @@ def add_cut(self, label: str, addr: int, expr: pcode.StateProp):
151152
def add_assign(self, label: str, addr: int, name: str, expr: pcode.StateExpr):
152153
self.addrmap[addr].append(Assign(label, addr, name, expr))
153154

155+
def find_label(self, label: str) -> int:
156+
assert self.ctx.loader is not None, (
157+
"BinaryContext must be loaded before disassembling"
158+
)
159+
sym = self.ctx.loader.find_symbol(label)
160+
if sym is None:
161+
raise Exception(f"Symbol {label} not found in binary {self.ctx.filename}")
162+
return sym.rebased_addr
163+
154164
@classmethod
155165
def of_file(cls, filename: str, ctx: pcode.BinaryContext):
156166
COMMASEP = r"\s*(?:,\s*)?"
@@ -173,16 +183,7 @@ def of_file(cls, filename: str, ctx: pcode.BinaryContext):
173183
decls["read"] = smt.Array(
174184
"read", smt.BitVecSort(ctx.bits), smt.BoolSort()
175185
).decl()
176-
spec = cls()
177-
178-
def find_label(label: str) -> int:
179-
assert ctx.loader is not None, (
180-
"BinaryContext must be loaded before disassembling"
181-
)
182-
sym = ctx.loader.find_symbol(label)
183-
if sym is None:
184-
raise Exception(f"Symbol {label} not found in binary {ctx.filename}")
185-
return sym.rebased_addr
186+
spec = cls(ctx)
186187

187188
def parse_smt(smt_bool: str, linemo: int, line: str) -> smt.BoolRef:
188189
# parse with slightly nicer error throwing, saying which line in asm responsible
@@ -210,7 +211,7 @@ def parse_smt(smt_bool: str, linemo: int, line: str) -> smt.BoolRef:
210211
): # A little bit of sanity checking that we don't miss any "kd_" line
211212
if match := boolstmt_pattern.match(line):
212213
cmd, label, expr = match.groups()
213-
addr = find_label(label)
214+
addr = spec.find_label(label)
214215
smt_string = "\n".join(preludes + ["(assert ", expr, ")"])
215216
expr = parse_smt(smt_string, lineno, line)
216217
if cmd == "kd_entry":
@@ -226,7 +227,7 @@ def parse_smt(smt_bool: str, linemo: int, line: str) -> smt.BoolRef:
226227
elif match := assign_pattern.match(line):
227228
label, name, expr = match.groups()
228229
# Turn expression `expr` into dummy assertion `expr == expr` for parsing
229-
addr = find_label(label)
230+
addr = spec.find_label(label)
230231
smt_string = "\n".join(
231232
preludes + ["(assert (=", expr, expr, "))"]
232233
)
@@ -242,10 +243,8 @@ def parse_smt(smt_bool: str, linemo: int, line: str) -> smt.BoolRef:
242243

243244
return spec
244245

245-
def __repr__(self):
246-
return json.dumps(
247-
dataclasses.asdict(self), indent=2, default=lambda b: b.sexpr()
248-
)
246+
def json(self):
247+
return json.dumps(self.addrmap, indent=2, default=lambda b: b.sexpr())
249248

250249

251250
type Trace = list[int] # TODO | SpecStmt
@@ -633,9 +632,18 @@ def __repr__(self):
633632
return super().__repr__() + f"\n\nfrom VC: {self.vc}"
634633

635634

635+
@dataclass(frozen=True)
636+
class AsmProof:
637+
spec: AsmSpec
638+
pfs: tuple[kd.Proof]
639+
640+
def __repr__(self):
641+
return f"AsmProof(spec={self.spec})"
642+
643+
636644
class AsmProofState:
637-
def __init__(self, ctx: pcode.BinaryContext, spec: AsmSpec):
638-
self.ctx = ctx
645+
def __init__(self, spec: AsmSpec):
646+
self.ctx = spec.ctx
639647
self.spec = spec
640648
# self._spec = spec.copy() # TODO copy
641649
mem = self.ctx.init_mem()
@@ -662,22 +670,23 @@ def exact(self, pf: kd.Proof):
662670
self.pfs.append(pf)
663671

664672
def qed(self):
665-
assert len(self.vcs) == len(self.pfs)
666-
assert all(
667-
isinstance(pf, kd.Proof) and pf.thm.eq(vc.vc(self.ctx))
668-
for vc, pf in zip(self.vcs, self.pfs)
669-
)
673+
assert all(isinstance(pf, kd.Proof) for pf in self.pfs)
674+
for vc in self.vcs:
675+
assert any(pf.thm.eq(vc.vc(self.ctx)) for pf in self.pfs), (
676+
f"VC not proved: {vc}"
677+
)
678+
return AsmProof(self.spec, tuple(self.pfs)) # Todo: More in here?
670679

671680

672681
class Debug:
673682
def __init__(
674683
self,
675684
ctx: pcode.BinaryContext,
676-
spec: AsmSpec = AsmSpec(),
685+
spec: Optional[AsmSpec] = None,
677686
verbose=True,
678687
):
679688
self.ctx = ctx
680-
self.spec: AsmSpec = spec
689+
self.spec: AsmSpec = spec if spec is not None else AsmSpec(ctx)
681690
self.tracestates: list[TraceState] = []
682691
self.vcs: list[VerificationCondition] = []
683692
self.breakpoints = set()

src/kdrag/tactics.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1588,15 +1588,16 @@ def qed(self, **kwargs) -> kd.kernel.Proof:
15881588
thm = self.thm.to_expr()
15891589
pf0 = self.lemmas[-1].get(thm.get_id())
15901590
if pf0 is not None:
1591-
return pf0
1592-
if "by" in kwargs:
1593-
kwargs["by"].extend(self.lemmas[-1].values())
1591+
pf = pf0
15941592
else:
1595-
kwargs["by"] = list(self.lemmas[-1].values())
1596-
pf = kd.kernel.prove(thm, **kwargs)
1597-
kdrag.config.perf_event(
1598-
"ProofState", self.thm, time.perf_counter() - self.start_time
1599-
)
1593+
if "by" in kwargs:
1594+
kwargs["by"].extend(self.lemmas[-1].values())
1595+
else:
1596+
kwargs["by"] = list(self.lemmas[-1].values())
1597+
pf = kd.kernel.prove(thm, **kwargs)
1598+
kdrag.config.perf_event(
1599+
"ProofState", self.thm, time.perf_counter() - self.start_time
1600+
)
16001601
if self._parent is not None:
16011602
self._parent.exact(pf)
16021603
return pf

0 commit comments

Comments
 (0)