From 1e84bcc8ede7f6f9e22bceb5acfd1fc19575a0ac Mon Sep 17 00:00:00 2001 From: Henny Sipma Date: Mon, 25 Aug 2025 11:00:40 -0700 Subject: [PATCH] REL: remove support for TrampolineMinimalPair2and3 --- chb/app/CHVersion.py | 2 +- chb/app/CfgBlock.py | 6 +- chb/arm/ARMCfg.py | 8 +- chb/arm/ARMCfgBlock.py | 6 +- chb/cmdline/PatchResults.py | 16 +- chb/cmdline/astcmds.py | 4 +- chb/cmdline/relationalcmds.py | 10 +- chb/jsoninterface/JSONFunctionComparison.py | 7 - chb/relational/FunctionRelationalAnalysis.py | 59 ++-- chb/relational/PatchParticulars.py | 348 +------------------ 10 files changed, 42 insertions(+), 424 deletions(-) diff --git a/chb/app/CHVersion.py b/chb/app/CHVersion.py index 56071fe9..b37682b7 100644 --- a/chb/app/CHVersion.py +++ b/chb/app/CHVersion.py @@ -1 +1 @@ -chbversion: str = "0.3.0-20250823" +chbversion: str = "0.3.0-20250825" diff --git a/chb/app/CfgBlock.py b/chb/app/CfgBlock.py index 50d5fd03..146ea894 100644 --- a/chb/app/CfgBlock.py +++ b/chb/app/CfgBlock.py @@ -4,7 +4,7 @@ # ------------------------------------------------------------------------------ # The MIT License (MIT) # -# Copyright (c) 2021-2024 Aarno Labs LLC +# Copyright (c) 2021-2025 Aarno Labs LLC # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -92,10 +92,6 @@ def is_in_trampoline(self) -> bool: def is_trampoline(self) -> bool: return False - @property - def is_trampoline_minimal_pair_2_and_3(self) -> bool: - return False - @property def roles(self) -> Dict[str, str]: return {} diff --git a/chb/arm/ARMCfg.py b/chb/arm/ARMCfg.py index be2f94f7..f356a738 100644 --- a/chb/arm/ARMCfg.py +++ b/chb/arm/ARMCfg.py @@ -4,7 +4,7 @@ # ------------------------------------------------------------------------------ # The MIT License (MIT) # -# Copyright (c) 2021-2024 Aarno Labs LLC +# Copyright (c) 2021-2025 Aarno Labs LLC # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -201,12 +201,6 @@ def get_inlinemap_addrs(start: str, size: int) -> List[str]: trampolines[baddr] = trinfo = TrampolineInfo(patchevent) - if patchevent.is_trampoline_pair_minimal_2_and_3: - chklogger.logger.info("Trampoline pair minimal 2 and 3 %s", baddr) - trinfo.add_role("payload", baddr) - trampolineblocks[baddr] = baddr - continue - trinfo.add_role("setupblock", baddr) trampolineblocks[baddr] = baddr diff --git a/chb/arm/ARMCfgBlock.py b/chb/arm/ARMCfgBlock.py index b29ebe46..575398f6 100644 --- a/chb/arm/ARMCfgBlock.py +++ b/chb/arm/ARMCfgBlock.py @@ -4,7 +4,7 @@ # ------------------------------------------------------------------------------ # The MIT License (MIT) # -# Copyright (c) 2021-2024 Aarno Labs LLC +# Copyright (c) 2021-2025 Aarno Labs LLC # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -129,10 +129,6 @@ def lastaddr(self) -> str: def is_trampoline(self) -> bool: return self.trampoline_info.patchevent.is_trampoline - @property - def is_trampoline_minimal_pair_2_and_3(self) -> bool: - return self.trampoline_info.patchevent.is_trampoline_pair_minimal_2_and_3 - @property def trampoline_info(self) -> "TrampolineInfo": return self._trinfo diff --git a/chb/cmdline/PatchResults.py b/chb/cmdline/PatchResults.py index 0e68338c..f128f533 100644 --- a/chb/cmdline/PatchResults.py +++ b/chb/cmdline/PatchResults.py @@ -320,13 +320,9 @@ def logicalva(self) -> str: def is_trampoline(self) -> bool: return self.patchkind == "Trampoline" - @property - def is_trampoline_pair_minimal_2_and_3(self) -> bool: - return self.patchkind == "TrampolinePairMinimal2and3" - @property def is_supported(self) -> bool: - return (self.is_trampoline or self.is_trampoline_pair_minimal_2_and_3) + return self.is_trampoline def has_details(self) -> bool: return "details" in self._d @@ -435,16 +431,6 @@ def trampoline_addresses(self) -> List[Dict[str, str]]: if e.case_fallthrough_jump is not None: r["fallthrough-jump"] = e.case_fallthrough_jump result.append(r) - elif e.is_trampoline_pair_minimal_2_and_3: - r["logicalva"] = e.logicalva - r["kind"] = "trampoline-pair-minimal-2-and-3" - if e.has_wrapper(): - r["wrapper"] = e.wrapper.vahex - if e.has_fallthrough_destination() and e.fallthrough_destination: - r["fallthrough"] = e.fallthrough_destination - if e.case_fallthrough_jump is not None: - r["fallthrough-jump"] = e.case_fallthrough_jump - result.append(r) return result diff --git a/chb/cmdline/astcmds.py b/chb/cmdline/astcmds.py index 202723d6..f284ae88 100644 --- a/chb/cmdline/astcmds.py +++ b/chb/cmdline/astcmds.py @@ -182,9 +182,7 @@ def buildast(args: argparse.Namespace) -> NoReturn: if patchresultsdata is not None: patchresults = PatchResults(patchresultsdata) for event in patchresults.events: - if ( - event.is_trampoline - or event.is_trampoline_pair_minimal_2_and_3): + if event.is_trampoline: if event.has_wrapper(): startaddr = event.wrapper.vahex patchevents[startaddr] = event diff --git a/chb/cmdline/relationalcmds.py b/chb/cmdline/relationalcmds.py index ad816a7a..9775687e 100644 --- a/chb/cmdline/relationalcmds.py +++ b/chb/cmdline/relationalcmds.py @@ -4,7 +4,7 @@ # ------------------------------------------------------------------------------ # The MIT License (MIT) # -# Copyright (c) 2021-2024 Aarno Labs, LLC +# Copyright (c) 2021-2025 Aarno Labs, LLC # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -372,9 +372,7 @@ def _relational_compare_generate_json(xname1: str, if patchresultsdata is not None: patchresults = PatchResults(patchresultsdata) for event in patchresults.events: - if ( - event.is_trampoline - or event.is_trampoline_pair_minimal_2_and_3): + if event.is_trampoline: if event.has_wrapper(): startaddr = event.wrapper.vahex patchevents[startaddr] = event @@ -600,9 +598,7 @@ def relational_compare_cfgs_cmd(args: argparse.Namespace) -> NoReturn: if patchresultsdata is not None: patchresults = PatchResults(patchresultsdata) for event in patchresults.events: - if ( - event.is_trampoline - or event.is_trampoline_pair_minimal_2_and_3): + if event.is_trampoline: if event.has_wrapper(): startaddr = event.wrapper.vahex patchevents[startaddr] = event diff --git a/chb/jsoninterface/JSONFunctionComparison.py b/chb/jsoninterface/JSONFunctionComparison.py index 2dee04f4..9db0fa3a 100644 --- a/chb/jsoninterface/JSONFunctionComparison.py +++ b/chb/jsoninterface/JSONFunctionComparison.py @@ -95,13 +95,6 @@ def trampoline_address(self) -> Optional[str]: if role == "setupblock": return b_addr - else: - # the TrampolinePairMinimal2and3 patch has just one block: payload - # (which is called wrapper in the patch results file) - for (b_addr, role) in self.cfg2_blocks: - if role == "payload": - return b_addr - return None def accept(self, visitor: "JSONObjectVisitor") -> None: diff --git a/chb/relational/FunctionRelationalAnalysis.py b/chb/relational/FunctionRelationalAnalysis.py index 955c83a3..195ecbaa 100644 --- a/chb/relational/FunctionRelationalAnalysis.py +++ b/chb/relational/FunctionRelationalAnalysis.py @@ -4,7 +4,7 @@ # ------------------------------------------------------------------------------ # The MIT License (MIT) # -# Copyright (c) 2021-2024 Aarno Labs, LLC +# Copyright (c) 2021-2025 Aarno Labs, LLC # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -384,11 +384,6 @@ def block_analyses(self) -> Mapping[str, BlockRelationalAnalysis]: trampoline = self.has_trampoline() if trampoline is not None: self.setup_trampoline_analysis(trampoline) - else: - trampoline_minimal_pair_2_and_3 = self.has_minimal_pair_2_and_3_trampoline() - if trampoline_minimal_pair_2_and_3 is not None: - self.setup_minimal_pair_2_and_3_trampoline_analysis( - trampoline_minimal_pair_2_and_3) return self._blockanalyses @@ -408,8 +403,8 @@ def setup_trampoline_analysis(self, b: str) -> None: tpre[0] in cfg2unmapped and tpost[0] in cfg2unmapped and tpre[0] in cfg1unmapped): - # Case where trampoline has an early return and a fallthrough case - # (what we mark as the exit block) + # Case where trampoline has an early return and a fallthrough + # case (what we mark as the exit block) roles["entry"] = self.basic_blocks2[tpre[0]] roles["exit"] = self.basic_blocks2[tpost[0]] for (role, addr) in trampoline.roles.items(): @@ -420,33 +415,40 @@ def setup_trampoline_analysis(self, b: str) -> None: self.app2, roles) else: - chklogger.logger.warning("Found unhandled trampoline case. Should be an early return " - "trampoline (pre %s, post %s), but the blocks are not " - "in the unmapped lists. cfg1: %s, cfg2: %s", - tpre[0], tpost[0], cfg1unmapped, cfg2unmapped) + chklogger.logger.warning( + "Found unhandled trampoline case. Should be an early " + "return trampoline (pre %s, post %s), but the blocks " + " are not in the unmapped lists. cfg1: %s, cfg2: %s", + tpre[0], tpost[0], cfg1unmapped, cfg2unmapped) elif len(tpre) == 1 and len(tpost) == 2: # Trampoline has a continue statement. - # One of the post blocks is the fallthrough/exit, one is the loop continuation. + # One of the post blocks is the fallthrough/exit, one is the + # loop continuation. # Need to figure out which one is which if not 'continuepath' in trampoline.roles: - chklogger.logger.error("Found unsupported trampoline case, " - "There are 2 post trampoline blocks %s but no continuepath role %s", - tpost, trampoline.roles) + chklogger.logger.error( + "Found unsupported trampoline case, " + "There are 2 post trampoline blocks %s but no " + "continuepath role %s", + tpost, trampoline.roles) return cont = self.basic_blocks2[trampoline.roles['continuepath']] cont_post = self.cfg2.edges[cont.baddr] if len(cont_post) != 1: - chklogger.logger.error("Found unsupported trampoline case, " - "continue block %s has more than one outgoing edges: %s", - cont.baddr, cont_post) + chklogger.logger.error( + "Found unsupported trampoline case, " + "continue block %s has more than one outgoing edges: %s", + cont.baddr, cont_post) return fallthrough = self.basic_blocks2[trampoline.roles['fallthrough']] fallthrough_post = self.cfg2.edges[fallthrough.baddr] if len(fallthrough_post) != 1: - chklogger.logger.error("Found unsupported trampoline case, " - "fallthrough block %s has more than one outgoing edges: %s", - fallthrough.baddr, fallthrough_post) + chklogger.logger.error( + "Found unsupported trampoline case, " + "fallthrough block %s has more than one outgoing edges: " + "%s", + fallthrough.baddr, fallthrough_post) return roles["entry"] = self.basic_blocks2[tpre[0]] @@ -459,9 +461,10 @@ def setup_trampoline_analysis(self, b: str) -> None: self.app2, roles) else: - chklogger.logger.warning("Found unhandled trampoline case. Have %d pre-blocks " - "%d post-blocks. pre: %s, post %s", - len(tpre), len(tpost), tpre, tpost) + chklogger.logger.warning( + "Found unhandled trampoline case. Have %d pre-blocks " + "%d post-blocks. pre: %s, post %s", + len(tpre), len(tpost), tpre, tpost) def setup_minimal_pair_2_and_3_trampoline_analysis(self, b: str) -> None: chklogger.logger.info("Setup minimal_pair_2_and_3_trampoline at %s", b) @@ -494,12 +497,6 @@ def has_trampoline(self) -> Optional[str]: return b return None - def has_minimal_pair_2_and_3_trampoline(self) -> Optional[str]: - for (b, cfgb) in self.cfgtc_blocks2.items(): - if cfgb.is_trampoline_minimal_pair_2_and_3: - return b - return None - def blocks_changed(self) -> List[str]: """Return a list of block addresses that are not md5-equal.""" diff --git a/chb/relational/PatchParticulars.py b/chb/relational/PatchParticulars.py index 84ee2e0e..84b2cff3 100644 --- a/chb/relational/PatchParticulars.py +++ b/chb/relational/PatchParticulars.py @@ -4,7 +4,7 @@ # ------------------------------------------------------------------------------ # The MIT License (MIT) # -# Copyright (c) 2024 Aarno Labs, LLC +# Copyright (c) 2024-2025 Aarno Labs, LLC # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -60,46 +60,6 @@ Patch event structures ====================== -TrampolinePairMinimal2and3 -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The simplest trampoline patch consists of just a wrapper, a single basic -block that both implements the desired functionality and replicates the -instructions overwritten by the trampoline hook, and an unconditional jump -back to the next instruction in the original function. - -Strictly speaking this patch consists of two patches combined into a single -trampoline, with potentially code before the first (asm) insertion, code between -the two insertions, and code after the second (asm) insertion. - -An example of such a patch -is the insertion of interrupt-enable/interrupt-disable instructions to -provide synchronization for memory operations that otherwise may give rise -to race conditions. - -The only relevant component of this kind of patch event is the wrapper, with -the following properties: - -- logicalva: the hex address of the hook in the original function -- details.wrapper.vahex: the hex address of the first instruction of the wrapper - (the target address of the hook) -- extras.labeloffsets.case_fallthrough_jump: the (integer) offset of the - trampoline unhook relative to the start of the wrapper -- destinations.fallthrough: the (integer) address of the target of the - trampline unhook -- cases: the control-flow options following the payload - -The structure of the payload itself is described by the following properties -(all in extras.labeloffsets, represented by integer offsets from the start -of the wrapper): - -- execute_before_payload -- asm1 -- execute_mid_payload -- asm2 -- execute_after_payload - - Trampoline ^^^^^^^^^^ @@ -137,62 +97,6 @@ a narrowly specified set of patch events and performing a predefined set of conservative consistency and compliance checks of the presented assembly code in relation to the patch event data based on patches encountered so far. - -TrampolinePairMinimal2and3 -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Patch events currently accepted: - -- logicalva: any address in the patched function F -- details.wrapper.vahex: any address in the code -- extras.labeloffsets.case_fallthrough_jump: any positive integer (aligned) -- destinations.fallthrough: an integer that translates to an address in the - patched function -- details.cases = ["fallthrough"] - -extras.labeloffsets: - -- execute_before_payload: 0 -- asm1: non-negative integer -- execute_mid_payload: non-negative integer -- asm2: non-negative integer -- execute_after_payload: non-negative integer - -Checks performed: - -- logicalva is in F and is the address of an unconditional jump instruction - (to address W) -- details.wrapper.vahex is equal to W -- extras.labeloffsets.case_fallthrough_jump + W reachable from W and is the - address of an unconditional jump instruction (to address H') -- address H' is reachable from logicalva in the unpatched function - -- asm1 >= 0 -- execute_mid_payload > asm1 -- asm2 > execute_mid_payload -- execute_after_payload > asm2 -- case_fallthrough_jump >= execute_after_payload - -Data gathered: - -- instruction(s) overwritten by the hook -- instruction(s) bypassed by the trampoline (instructions on the path - between hook and H') -- execute_before instructions -- asm1 instructions -- execute_mid instructions -- execute_after instructions - -Validation: - -- instructions bypassed by the trampoline = {} -- execute_before instructions = {} -- execute_mid instructions = instructions overwritten by the hook - (syntactically and semantically) -- execute_after instructions = {} -- asm1 is a semantic NOP -- asm2 is a semantic NOP - """ from dataclasses import dataclass from typing import Any, cast, Dict, List, Optional, TYPE_CHECKING @@ -465,244 +369,6 @@ def __str__(self) -> str: return self.patchkind -class Min2And3PatchEventParticulars(PatchEventParticulars): - - def __init__( - self, - patchevent: "PatchEvent", - xfun1: "Function", - xfun2: "Function") -> None: - PatchEventParticulars.__init__(self, patchevent, xfun1, xfun2) - - @property - def is_trampoline(self) -> bool: - return True - - @property - def hook(self) -> PatchComponentResult: - logicalva = self.event.logicalva - f2instr = self.function2.instructions[logicalva] - if f2instr.is_jump_instruction: - if f2instr.jump_target is None: - return PatchComponentFailure(["Jump instruction has no target"]) - else: - if f2instr.jump_target.is_int_constant: - return PatchComponentSuccess(HookInstruction(f2instr)) - else: - return PatchComponentFailure( - ["Jump instruction does not have a constant target"]) - else: - return PatchComponentFailure(["Hook is not a jump instruction"]) - - @property - def unhook(self) -> PatchComponentResult: - jumpaddr = self.event.case_fallthrough_jump - if jumpaddr is not None: - jinstr = self.function2.instructions[jumpaddr] - if jinstr.is_jump_instruction: - if jinstr.jump_target is None: - return PatchComponentFailure( - ["Jump instruction has no target"]) - else: - if jinstr.jump_target.is_int_constant: - return PatchComponentSuccess(HookInstruction(jinstr)) - else: - return PatchComponentFailure( - ["Unhook is an indirect jump"]) - else: - return PatchComponentFailure(["Unhook is not a jump instruction"]) - else: - return PatchComponentFailure( - ["Unhook address not found in patch event"]) - - @property - def execute_before_payload(self) -> str: - return self.event.label_address("execute_before_payload") - - @property - def body(self) -> str: - return self.event.label_address("body") - - @property - def asm1(self) -> str: - return self.event.label_address("asm1") - - @property - def asm2(self) -> str: - return self.event.label_address("asm2") - - @property - def execute_mid_payload(self) -> str: - return self.event.label_address("execute_mid_payload") - - @property - def execute_after_payload(self) -> str: - return self.event.label_address("execute_after_payload") - - @property - def wrapper(self) -> "PatchWrapper": - return self.event.wrapper - - def mk_code_fragment(self, s: int, e: int) -> PatchComponentResult: - n = e - s - instrs: List["Instruction"] = [] - offset: int = 0 - while offset < n: - iaddr = hex(s + offset) - instr = self.function2.instructions[iaddr] - instrs.append(instr) - offset += instr.size - return PatchComponentSuccess(CodeFragment(hex(s), instrs)) - - @property - def pre_asm_code_fragment(self) -> PatchComponentResult: - return self.mk_code_fragment( - int(self.execute_before_payload, 16), int(self.asm1, 16)) - - @property - def post_asm_code_fragment(self) -> PatchComponentResult: - fallthrough = self.event.case_fallthrough_jump - if fallthrough is not None: - return self.mk_code_fragment( - int(self.execute_after_payload, 16), int(fallthrough, 16)) - else: - return PatchComponentFailure(["Fallthrough jump address is missing"]) - - @property - def asm1_code_fragment(self) -> PatchComponentResult: - return self.mk_code_fragment( - int(self.asm1, 16), int(self.execute_mid_payload, 16)) - - @property - def asm2_code_fragment(self) -> PatchComponentResult: - return self.mk_code_fragment( - int(self.asm2, 16), int(self.execute_after_payload, 16)) - - @property - def instrs_overwritten(self) -> PatchComponentResult: - if self.hook.is_ok: - hookinstr = cast(HookInstruction, self.hook.get_ok()) - n_overwritten = hookinstr.size - hiaddr = hookinstr.srca - i_hiaddr = hookinstr.i_srca - instrs: List["Instruction"] = [] - offset: int = 0 - while offset < n_overwritten: - iaddr = hex(i_hiaddr + offset) - instr = self.function1.instructions[iaddr] - instrs.append(instr) - offset += instr.size - return PatchComponentSuccess(CodeFragment(hiaddr, instrs)) - else: - msgs = self.hook.get_error() + ["No hook available"] - return PatchComponentFailure(msgs) - - @property - def instrs_midpayload(self) -> PatchComponentResult: - n_midpayload = int(self.asm2, 16) - int(self.execute_mid_payload, 16) - instrs: List["Instruction"] = [] - offset: int = 0 - while offset < n_midpayload: - iaddr = hex(int(self.execute_mid_payload, 16) + offset) - instr = self.function2.instructions[iaddr] - instrs.append(instr) - offset += instr.size - return PatchComponentSuccess(CodeFragment( - self.execute_mid_payload, instrs)) - - @property - def patchcomponents(self) -> Dict[str, PatchComponentResult]: - if self._patchcomponents is None: - self._patchcomponents = {} - d = self._patchcomponents - d["hook"] = self.hook - d["unhook"] = self.unhook - d["pre_asm_code_fragment"] = self.pre_asm_code_fragment - d["post_asm_code_fragment"] = self.post_asm_code_fragment - d["asm1_code_fragment"] = self.asm1_code_fragment - d["asm2_code_fragment"] = self.asm2_code_fragment - d["instrs_overwritten"] = self.instrs_overwritten - d["instrs_midpayload"] = self.instrs_midpayload - return self._patchcomponents - - def lockstep_simulation(self) -> JSONResult: - """Specify lock step simulation relationships. - - Checks performed: - - invariants at entry states of instructions overwritten match - - instructions overwritten are duplicated exactly (semantically) - """ - - content: Dict[str, Any] = {} - content["simsteps"] = [] - cr_overwritten = self.patchcomponents["instrs_overwritten"] - cr_midpayload = self.patchcomponents["instrs_midpayload"] - if cr_overwritten.is_error: - msgs = "; ".join(cr_overwritten.get_error()) - return JSONResult("patchlockstep", {}, "fail", msgs) - c_overwritten = cast("CodeFragment", cr_overwritten.get_ok()) - if cr_midpayload.is_error: - msgs = "; ".join(cr_midpayload.get_error()) - return JSONResult("patchlockstep", {}, "fail", msgs) - c_midpayload = cast("CodeFragment", cr_midpayload.get_ok()) - if len(c_overwritten.code_fragment) != len(c_midpayload.code_fragment): - len1 = len(c_overwritten.code_fragment) - len2 = len(c_midpayload.code_fragment) - reason = ( - "length of overwritten code and midpayload differ: " - + str(len1) - + " vs " - + str(len2)) - return JSONResult("patchlockstep", {}, "fail", reason) - for (i1, i2) in zip( - c_overwritten.code_fragment, c_midpayload.code_fragment): - sim = LockStepSimulationStep( - self.function1, - self.function2, - i1.iaddr, - i2.iaddr) - sim.compare_invariants() - sim.compare_transitions() - simresult = sim.to_json_result() - if simresult.is_ok: - content["simsteps"].append(simresult.content) - else: - return JSONResult("patchlockstep", {}, "fail", simresult.reason) - return JSONResult("patchlockstep", {}, "ok") - - - def to_json_result(self) -> JSONResult: - """Validate existence and internal consistency of all patch components.""" - content: Dict[str, Any] = {} - content["patchkind"] = "TrampolinePairMin2And3" - content["components"] = [] - for (role, pc) in self.patchcomponents.items(): - componentrole: Dict[str, Any] = {} - r = pc.to_json_result() - if r.is_ok: - componentrole["role"] = role - componentrole["component"] = r.content - content["components"].append(componentrole) - else: - return JSONResult("patchvalidation", {}, "fail", r.reason) - simulationresult = self.lockstep_simulation() - if simulationresult.is_ok: - content["simulation"] = simulationresult.content - else: - return JSONResult( - "patchvalidation", {}, "fail", simulationresult.reason) - return JSONResult("patchvalidation", content, "ok") - - def __str__(self) -> str: - lines: List[str] = [] - lines.append("min2and3 for " + self.faddr) - for pc in self.patchcomponents: - lines.append(pc) - lines.append(str(self.patchcomponents[pc])) - lines.append("") - return "\n".join(lines) - - class PatchParticulars: def __init__( @@ -750,13 +416,9 @@ def patchevent_particulars( if fn1.within_function_extent(va): event = self.events[va] kind = event.patchkind - if kind == "TrampolinePairMinimal2and3": - return Min2And3PatchEventParticulars( - self.events[va], fn1, fn2) - else: - chklogger.logger.critical( - "patch kind %s not yet supported for comparison", - kind) - return None + chklogger.logger.critical( + "patch kind %s not yet supported for comparison", + kind) + return None else: raise UF.CHBError("No patch event found for function {fn.faddr]")