diff --git a/.github/workflows/run_mypy.yaml b/.github/workflows/run_mypy.yaml index e126f78f..cf267ebe 100644 --- a/.github/workflows/run_mypy.yaml +++ b/.github/workflows/run_mypy.yaml @@ -8,11 +8,14 @@ jobs: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v7 - name: Install mypy - run: sudo apt-get install -y mypy python3-mypy + run: | + sudo apt-get install -y python3 + python3 -m venv .venv + .venv/bin/pip install mypy==2.4.0 - name: Create default local config run: cp chb/util/ConfigLocal.template chb/util/ConfigLocal.py - name: Run mypy run: | - PYTHONPATH=$PWD mypy chb + PYTHONPATH=$PWD .venv/bin/mypy --strict chb diff --git a/chb/api/BTerm.py b/chb/api/BTerm.py index c840a54a..ca681414 100644 --- a/chb/api/BTerm.py +++ b/chb/api/BTerm.py @@ -59,7 +59,7 @@ from chb.util.IndexedTable import IndexedTableValue if TYPE_CHECKING: - from chb.api.InterfaceDictionary import FtsParameter + from chb.api.FtsParameter import FtsParameter from chb.api.InterfaceDictionary import InterfaceDictionary diff --git a/chb/api/FormatStringSpec.py b/chb/api/FormatStringSpec.py index 81cdc974..bc5880ce 100644 --- a/chb/api/FormatStringSpec.py +++ b/chb/api/FormatStringSpec.py @@ -124,7 +124,7 @@ def arg_type(self) -> str: def flags(self) -> str: return "".join(chr(i) for i in self.args) - def __str__(self): + def __str__(self) -> str: return ( "%" + self.flags diff --git a/chb/app/AppAccess.py b/chb/app/AppAccess.py index 74b87a3d..ea519394 100644 --- a/chb/app/AppAccess.py +++ b/chb/app/AppAccess.py @@ -30,7 +30,6 @@ from abc import ABC, abstractmethod from typing import ( - Any, Callable, cast, Dict, @@ -38,13 +37,10 @@ Mapping, Optional, Sequence, - Generic, Tuple, Type, TYPE_CHECKING, - TypeVar, - Union, - overload) +) from chb.api.CallTarget import CallTarget, IndirectTarget, CallbackTableTarget from chb.api.InterfaceDictionary import InterfaceDictionary @@ -91,25 +87,23 @@ if TYPE_CHECKING: from chb.app.Instruction import Instruction -HeaderTy = TypeVar('HeaderTy', PEHeader, ELFHeader, Union[PEHeader, ELFHeader]) - -class AppAccess(ABC, Generic[HeaderTy]): +class AppAccess(ABC): def __init__( self, path: str, filename: str, - fileformat: Type[HeaderTy], + fileformat: Type[UF.FileHeader], deps: List[str] = []) -> None: """Initializes access to analysis results.""" self._path = path self._filename = filename self._deps = deps # list of summary zips registered as dependencies - self._header_ty: Type[HeaderTy] = fileformat # currently supported: elf, pe + self._header_ty: Type[UF.FileHeader] = fileformat # currently supported: elf, pe self._userdata: Optional[UserData] = None - self._header: Optional[HeaderTy] = None + self._header: Optional[UF.FileHeader] = None # functions self._appresultdata: Optional[AppResultData] = None @@ -227,13 +221,30 @@ def bcfiles(self) -> BCFiles: # File format -------------------------------------------------------------- @property - def header(self) -> HeaderTy: + def header(self) -> UF.FileHeader: if self._header is None: x = self._header_ty.get_xnode(self.path, self.filename) self._header = self._header_ty( self.path, self.filename, x, self.dependencies) return self._header + # Format specific. Will raise exceptions if the wrong format is requested + @property + def elf_header(self) -> ELFHeader: + if not self.elf: + raise RuntimeError("Requested ELF header but binary is not ELF: %s" % self._header_ty) + header = self.header + assert isinstance(header, ELFHeader) + return header + + @property + def pe_header(self) -> PEHeader: + if not self.pe: + raise RuntimeError("Requested PE header but binary is not PE: %s" % self._header_ty) + header = self.header + assert isinstance(header, PEHeader) + return header + # Systeminfo --------------------------------------------------------------- @property diff --git a/chb/app/Cfg.py b/chb/app/Cfg.py index 451db56b..d108c0eb 100644 --- a/chb/app/Cfg.py +++ b/chb/app/Cfg.py @@ -188,7 +188,7 @@ def inverse_with_phantom_exit_node(self) -> 'FlowGraph': augedges[phantomend] = terminators return FlowGraph(list(self.nodes) + [phantomend], augedges, phantomend) - def ipostdoms(rrg: 'FlowGraph') -> Dict[UserNodeID, UserNodeID]: + def ipostdoms(self, rrg: 'FlowGraph') -> Dict[UserNodeID, UserNodeID]: idoms = rrg.idoms.copy() # The start node of the reverse graph is a phantom node that doesn't # exist in the original graph. @@ -211,7 +211,7 @@ def intersect(b1: UserNodeID, b2: UserNodeID) -> UserNodeID: def idom(n: UserNodeID) -> UserNodeID: i = idoms[n] assert i is not None # Should always hit start node. - return cast(UserNodeID, i) + return i finger1: UserNodeID = b1 finger2: UserNodeID = b2 @@ -582,7 +582,7 @@ def node_within(x: str, merges: List[str], ctx: ControlFlowContext) -> List[AST. for succ in succs: if jumptable.has_target(succ) and succ not in succlabels: cvs = jumptable.get_target(succ) - def rawlabel(c): + def rawlabel(c: int) -> AST.ASTCaseLabel: r = astree.mk_case_label(astree.mk_integer_constant(c)) astree.add_instruction_span(r.locationid, succ, "") return r @@ -598,7 +598,7 @@ def switch_case_ctx(mb_nextsucc: Optional[str]) -> ControlFlowContext: return ctx.in_switch(mergeaddr, mb_nextsucc) return ctx - def switch_case_stmts(succ, mb_nextsucc): + def switch_case_stmts(succ: str, mb_nextsucc: Optional[str]) -> List[AST.ASTStmt]: sctx = switch_case_ctx(mb_nextsucc) casebody = do_branch(x, succ, sctx) if casebody == [] and mb_nextsucc is None: @@ -607,7 +607,12 @@ def switch_case_stmts(succ, mb_nextsucc): # we insert a synthetic break statement to maintain # syntactic compatibility. casebody = [astree.mk_break_stmt()] - return succlabels.get(succ, []) + casebody + # typing note: mypy complains that we're mixing up ASTStmts (casebody) with + # ASTStmtLabels (from succlabels). With the current implementation, + # making ASTStmtLabels into ASTStmts would cause other issues. + # This function is also not well understood so we're wary of mucking with + # it too much. type: ignore is the best approach for the time being. + return succlabels.get(succ, []) + casebody # type: ignore[return-value] bodystmts: List[AST.ASTStmt] = [] for idx in range(1, len(succs)): @@ -627,8 +632,7 @@ def switch_case_stmts(succ, mb_nextsucc): bodystmts.append(defaultcase) switchbody = astree.mk_block(bodystmts) - switchstmt = cast(AST.ASTSwitchStmt, - astree.mk_switch_stmt(switchcondition, switchbody, mergeaddr)) + switchstmt = astree.mk_switch_stmt(switchcondition, switchbody, mergeaddr) return (xstmts + [switchstmt]) # We don't use with_last_insn_span(x, switchstmt) because when we have # a switch lifted from a conditional jump (for explicit cases) plus an diff --git a/chb/arm/ARMAccess.py b/chb/arm/ARMAccess.py index a510fdad..227ca504 100644 --- a/chb/arm/ARMAccess.py +++ b/chb/arm/ARMAccess.py @@ -29,24 +29,22 @@ from typing import Callable, Dict, List, Mapping, Optional, Type from chb.app.Callgraph import Callgraph, mk_tgt_callgraph_node, mk_app_callgraph_node -from chb.app.AppAccess import AppAccess, HeaderTy +from chb.app.AppAccess import AppAccess from chb.arm.ARMDictionary import ARMDictionary from chb.arm.ARMFunction import ARMFunction from chb.arm.ARMInstruction import ARMInstruction -from chb.elfformat.ELFHeader import ELFHeader - import chb.util.fileutil as UF -class ARMAccess(AppAccess[HeaderTy]): +class ARMAccess(AppAccess): def __init__( self, path: str, filename: str, - fileformat: Type[HeaderTy], + fileformat: Type[UF.FileHeader], deps: List[str] = []) -> None: AppAccess.__init__(self, path, filename, fileformat, deps) self._armd: Optional[ARMDictionary] = None diff --git a/chb/arm/ARMFunction.py b/chb/arm/ARMFunction.py index d4ecba91..74daffb9 100644 --- a/chb/arm/ARMFunction.py +++ b/chb/arm/ARMFunction.py @@ -209,12 +209,12 @@ def f(baddr: str, block: ARMBlock) -> None: def iter_blocks(self, f: Callable[[str, ARMBlock], None]) -> None: for (ba, block) in self.blocks.items(): - armblock = cast(ARMBlock, block) + armblock = block f(ba, armblock) def iter_instructions(self, f: Callable[[str, ARMInstruction], None]) -> None: for (ia, instr) in self.instructions.items(): - arminstr = cast(ARMInstruction, instr) + arminstr = instr f(ia, arminstr) @property diff --git a/chb/arm/ARMOperandKind.py b/chb/arm/ARMOperandKind.py index c938f0e6..d86ec04a 100644 --- a/chb/arm/ARMOperandKind.py +++ b/chb/arm/ARMOperandKind.py @@ -612,7 +612,11 @@ def scale_factor(self) -> Optional[int]: return 1 else: if srt.is_shift_left: - return 2 ** shiftamount + # For some unknown reason, mypy (2.4.0) complains that we're returning + # `Any` here instead of [int | None]. This only happens when we use the + # `**` operator, so work around it by using `<<` Original code was: + # return 2 ** shiftamount + return 1 << shiftamount else: return None else: diff --git a/chb/arm/opcodes/ARMLoadMultipleIncrementAfter.py b/chb/arm/opcodes/ARMLoadMultipleIncrementAfter.py index 92bde135..e405ed62 100644 --- a/chb/arm/opcodes/ARMLoadMultipleIncrementAfter.py +++ b/chb/arm/opcodes/ARMLoadMultipleIncrementAfter.py @@ -116,7 +116,7 @@ def memrhss(self) -> List["XXpr"]: def are_memrhss_ok(self) -> bool: return all(self.is_xpr_ok(i) for i in self.memrhs_range) - def memrhs(self, index) -> "XXpr": + def memrhs(self, index: int) -> "XXpr": """returns the rhs expression at (1-based) position index""" if self.has_index(index): @@ -135,7 +135,7 @@ def rmemrhss(self) -> List["XXpr"]: def are_rmemrhss_ok(self) -> bool: return all(self.is_xpr_ok(i) for i in self.rmemrhs_range) - def rmemrhs(self, index) -> "XXpr": + def rmemrhs(self, index: int) -> "XXpr": """returns the rewritten rhs expr at (1-based) position index""" if self.has_index(index): @@ -154,7 +154,7 @@ def xaddrs(self) -> List["XXpr"]: def are_xaddrs_ok(self) -> bool: return all(self.is_xpr_ok(i) for i in self.xaddr_range) - def xaddr(self, index) -> "XXpr": + def xaddr(self, index: int) -> "XXpr": """returns the rhs addr at (1-based) position index""" if self.has_index(index): @@ -173,7 +173,7 @@ def cmemrhss(self) -> List["XXpr"]: def are_cmemrhss_ok(self) -> bool: return all(self.is_cxpr_ok(i) for i in range(0, self.regcount)) - def cmemrhs(self, index) -> "XXpr": + def cmemrhs(self, index: int) -> "XXpr": """returns the rhs addr at (1-based) position index""" if self.has_index(index): diff --git a/chb/arm/opcodes/ARMLoadMultipleIncrementBefore.py b/chb/arm/opcodes/ARMLoadMultipleIncrementBefore.py index 870a2b57..b1d33cd9 100644 --- a/chb/arm/opcodes/ARMLoadMultipleIncrementBefore.py +++ b/chb/arm/opcodes/ARMLoadMultipleIncrementBefore.py @@ -116,7 +116,7 @@ def memrhss(self) -> List["XXpr"]: def are_memrhss_ok(self) -> bool: return all(self.is_xpr_ok(i) for i in self.memrhs_range) - def memrhs(self, index) -> "XXpr": + def memrhs(self, index: int) -> "XXpr": """returns the rhs expression at (1-based) position index""" if self.has_index(index): @@ -135,7 +135,7 @@ def rmemrhss(self) -> List["XXpr"]: def are_rmemrhss_ok(self) -> bool: return all(self.is_xpr_ok(i) for i in self.rmemrhs_range) - def rmemrhs(self, index) -> "XXpr": + def rmemrhs(self, index: int) -> "XXpr": """returns the rewritten rhs expr at (1-based) position index""" if self.has_index(index): @@ -154,7 +154,7 @@ def xaddrs(self) -> List["XXpr"]: def are_xaddrs_ok(self) -> bool: return all(self.is_xpr_ok(i) for i in self.xaddr_range) - def xaddr(self, index) -> "XXpr": + def xaddr(self, index: int) -> "XXpr": """returns the rhs addr at (1-based) position index""" if self.has_index(index): @@ -173,7 +173,7 @@ def cmemrhss(self) -> List["XXpr"]: def are_cmemrhss_ok(self) -> bool: return all(self.is_cxpr_ok(i) for i in range(0, self.regcount)) - def cmemrhs(self, index) -> "XXpr": + def cmemrhs(self, index: int) -> "XXpr": """returns the rhs addr at (1-based) position index""" if self.has_index(index): diff --git a/chb/arm/opcodes/ARMVectorMoveDS.py b/chb/arm/opcodes/ARMVectorMoveDS.py index a591c531..1c3b40ed 100644 --- a/chb/arm/opcodes/ARMVectorMoveDS.py +++ b/chb/arm/opcodes/ARMVectorMoveDS.py @@ -154,14 +154,14 @@ def _unpack_imm32(self, x: XXpr) -> float: # https://stackoverflow.com/questions/33483846/how-to-convert-32-bit-binary-to-float ci = cast(XprConstant, x).intvalue - return struct.unpack('f', struct.pack('I', ci))[0] + return cast(float, struct.unpack('f', struct.pack('I', ci))[0]) def _unpack_imm64(self, x: XXpr) -> float: # from StackOverflow: # https://stackoverflow.com/questions/8751653/how-to-convert-a-binary-string-into-a-float-value ci = cast(XprConstant, x).intvalue b8 = struct.pack('Q', ci) - return struct.unpack('d', b8)[0] + return cast(float, struct.unpack('d', b8)[0]) def annotation(self, xdata: InstrXData) -> str: xd = ARMVectorMoveDSXData(xdata) diff --git a/chb/arm/opcodes/ARMVectorMultiplySubtract.py b/chb/arm/opcodes/ARMVectorMultiplySubtract.py index 6f71d8aa..a4e417b1 100644 --- a/chb/arm/opcodes/ARMVectorMultiplySubtract.py +++ b/chb/arm/opcodes/ARMVectorMultiplySubtract.py @@ -154,7 +154,7 @@ def _unpack_imm32(self, x: XXpr) -> float: # https://stackoverflow.com/questions/33483846/how-to-convert-32-bit-binary-to-float ci = cast(XprConstant, x).intvalue - return struct.unpack('f', struct.pack('I', ci))[0] + return cast(float, struct.unpack('f', struct.pack('I', ci))[0]) def ast_prov( self, diff --git a/chb/ast/ASTDeserializer.py b/chb/ast/ASTDeserializer.py index 61e32cbf..15ba5be3 100644 --- a/chb/ast/ASTDeserializer.py +++ b/chb/ast/ASTDeserializer.py @@ -26,7 +26,7 @@ # ------------------------------------------------------------------------------ """Simplified version of AST to check serialization.""" -from typing import Any, cast, Dict, List, NewType, Optional, Tuple, Union +from typing import Any, cast, Dict, List, Optional, Tuple from chb.ast.AbstractSyntaxTree import ( AbstractSyntaxTree, nooffset, voidtype, ASTSpanRecord) @@ -362,7 +362,7 @@ def find_instr_by_id( def find_instr_address_by_locationid(locationid: int) -> str: for node in fdata["spans"]: if "locationid" in node and int(node["locationid"]) == locationid: - return node["spans"][0]["base_va"] + return cast(str, node["spans"][0]["base_va"]) else: raise Exception( "No span found for locationid: " + str(locationid)) @@ -370,7 +370,7 @@ def find_instr_address_by_locationid(locationid: int) -> str: def find_expr_address_by_exprid(exprid: int) -> str: for node in fdata["spans"]: if "exprid" in node and int(node["exprid"]) == exprid: - return node["spans"][0]["base_va"] + return cast(str, node["spans"][0]["base_va"]) else: raise Exception( "Deserializer: No span found for exprid: " + str(exprid)) diff --git a/chb/ast/ASTNode.py b/chb/ast/ASTNode.py index 76af620d..e3eb7c0c 100644 --- a/chb/ast/ASTNode.py +++ b/chb/ast/ASTNode.py @@ -2904,7 +2904,6 @@ def field_at_offset(self, offset: int) -> Tuple["ASTFieldInfo", int]: prev = (i, fname) else: if prev is not None: - prev = cast(Tuple[int, str], prev) return (self.fieldinfo(prev[1]), offset - prev[0]) else: raise Exception( diff --git a/chb/astinterface/ASTIProvenance.py b/chb/astinterface/ASTIProvenance.py index 0ae892d8..741cd2f7 100644 --- a/chb/astinterface/ASTIProvenance.py +++ b/chb/astinterface/ASTIProvenance.py @@ -424,7 +424,6 @@ def set_ast_provenance(self, p: ASTProvenance) -> None: def resolve_reaching_defs(self) -> None: for (xid, rds) in self.expr_rdefs.items(): for rd in rds: - rd = cast("ReachingDefFact", rd) v = str(rd.variable) if v == "PC": continue @@ -494,7 +493,6 @@ def resolve_reaching_defs(self) -> None: def resolve_flag_reaching_defs(self) -> None: for (xid, frds) in self.flag_expr_rdefs.items(): for frd in frds: - frd = cast("FlagReachingDefFact", frd) addrs = [str(d) for d in frd.deflocations] for addr in addrs: if addr in self.address_instructions: @@ -507,7 +505,6 @@ def resolve_flag_reaching_defs(self) -> None: def resolve_definitions_used(self) -> None: for (lvalid, defuse) in self.lval_defuses.items(): - defuse = cast("DefUse", defuse) addrs = [str(u) for u in defuse.uselocations if str(u) != "exit"] for addr in addrs: if addr in self.address_instructions: @@ -520,7 +517,6 @@ def resolve_definitions_used(self) -> None: def resolve_definitions_used_high(self) -> None: for (lvalid, defuse) in self.lval_defuses_high.items(): - defuse = cast("DefUseHigh", defuse) addrs = [str(u) for u in defuse.uselocations if str(u) != "exit"] for addr in addrs: if addr in self.address_instructions: diff --git a/chb/astinterface/ASTInterfaceBasicBlock.py b/chb/astinterface/ASTInterfaceBasicBlock.py index 5028e462..2c0d2e2b 100644 --- a/chb/astinterface/ASTInterfaceBasicBlock.py +++ b/chb/astinterface/ASTInterfaceBasicBlock.py @@ -400,7 +400,6 @@ def trampoline_payload_ast(self, astree: "ASTInterface") -> AST.ASTStmt: # case 1 if chkinstr2.mnemonic_stem == "MOV": - chkinstr2 = cast("ARMInstruction", chkinstr2) if chkinstr2.has_instruction_condition(): if chkinstr2.has_valid_instruction_c_condition(): condition = chkinstr2.get_instruction_c_condition() diff --git a/chb/bctypes/BCFiles.py b/chb/bctypes/BCFiles.py index 98589102..5f467223 100644 --- a/chb/bctypes/BCFiles.py +++ b/chb/bctypes/BCFiles.py @@ -90,7 +90,7 @@ def fsum_compinfos(self) -> List[BCCompInfo]: if typ.is_typedef: typedef = cast("BCTypNamed", typ).typedef if typedef: - typ = typ = cast("BCTypeInfo", typedef).ttype + typ = typedef.ttype if typ.is_struct: compinfo = cast("BCTypComp", typ).compinfo result.append(compinfo) diff --git a/chb/bctypes/TypeConstraintStore.py b/chb/bctypes/TypeConstraintStore.py index 94161112..83b04c74 100644 --- a/chb/bctypes/TypeConstraintStore.py +++ b/chb/bctypes/TypeConstraintStore.py @@ -342,8 +342,7 @@ def function_type_constraints(self) -> Dict[str, FunctionTypeConstraints]: addr = c.typevar.base_addr if c.typevar.is_function: self.add_function_constraints(addr) - self._functionconstraints[addr].add_var_constraint( - cast(TC.TypeVariableConstraint, c)) + self._functionconstraints[addr].add_var_constraint(c) elif c.is_subtype_constraint: c = cast(TC.SubTypeConstraint, c) basevars = c.basevars diff --git a/chb/cmdline/PatchResults.py b/chb/cmdline/PatchResults.py index af08fd74..636924fc 100644 --- a/chb/cmdline/PatchResults.py +++ b/chb/cmdline/PatchResults.py @@ -45,9 +45,8 @@ """ import json -from typing import Any, Dict, List, Optional +from typing import Any, Dict, List, Optional, cast -import chb.util.fileutil as UF from chb.util.loggingutil import chklogger @@ -58,23 +57,23 @@ def __init__(self, d: Dict[str, Any]) -> None: @property def offset(self) -> int: - return self._d.get("offset", -1) + return cast(int, self._d.get("offset", -1)) @property def removed(self) -> int: - return self._d.get("removed", 0) + return cast(int, self._d.get("removed", 0)) @property def inserted(self) -> int: - return self._d.get("inserted", 0) + return cast(int, self._d.get("inserted", 0)) @property def offsethex(self) -> str: - return self._d.get("offsethex", "0x0") + return cast(str, self._d.get("offsethex", "0x0")) @property def vahex(self) -> str: - return self._d.get("vahex", "0x0") + return cast(str, self._d.get("vahex", "0x0")) def __str__(self) -> str: return ( @@ -94,25 +93,25 @@ def __init__(self, d: Dict[str, Any]) -> None: @property def offset(self) -> int: - return self._d.get("offset", -1) + return cast(int, self._d.get("offset", -1)) @property def removed(self) -> int: - return self._d.get("removed", 0) + return cast(int, self._d.get("removed", 0)) @property def inserted(self) -> int: - return self._d.get("inserted", 0) + return cast(int, self._d.get("inserted", 0)) @property def offsethex(self) -> str: - return self._d.get("offsethex", "0x0") + return cast(str, self._d.get("offsethex", "0x0")) @property def vahex(self) -> str: """Returns the hex start address of the wrapper.""" - return self._d.get("vahex", "0x0") + return cast(str, self._d.get("vahex", "0x0")) def in_wrapper(self, addr: str) -> bool: """Returns true if addr is contained within the wrapper.""" @@ -156,7 +155,7 @@ def payload(self) -> PatchPayload: @property def cases(self) -> List[str]: - return self._d.get("cases", []) + return cast(List[str], self._d.get("cases", [])) def __str__(self) -> str: lines: List[str] = [] @@ -186,7 +185,7 @@ def _convert_offset(self, offset: int) -> str: return hex(offset + int(self.base, 16)) def label_offset(self, label: str) -> int: - return self._d.get(label, 0) + return cast(int, self._d.get(label, 0)) def label_address(self, label: str) -> str: return self._convert_offset(self.label_offset(label)) @@ -244,7 +243,7 @@ def return_dst(self) -> str: return hex(self._d.get("return", 0)) def __str__(self) -> str: - lines: List = [] + lines: List[str] = [] lines.append("payload : " + self.payload) lines.append("fallthrough : " + self.fallthrough) lines.append("break : " + self.break_dst) @@ -306,15 +305,15 @@ def __init__(self, d: Dict[str, Any]) -> None: @property def event_type(self) -> str: - return self._d["event"] + return cast(str, self._d["event"]) @property def patchkind(self) -> str: - return self._d["patchkind"] + return cast(str, self._d["patchkind"]) @property def logicalva(self) -> str: - return self._d["logicalva"] + return cast(str, self._d["logicalva"]) @property def is_trampoline(self) -> bool: diff --git a/chb/cmdline/XComparison.py b/chb/cmdline/XComparison.py index c5a7bec3..aa332185 100644 --- a/chb/cmdline/XComparison.py +++ b/chb/cmdline/XComparison.py @@ -53,6 +53,10 @@ def __init__( app1: "AppAccess", app2: "AppAccess", pdfiledata: Optional[Dict[str, Any]] = None) -> None: + # We only support ELF binaries. Complain loudly if we're not given one + if not app1.elf or not app2.elf: + raise NotImplementedError("XComparison only supports ELF binaries. Was given " + "%s and %s" % (app1._header_ty, app2._header_ty)) self._is_thumb = is_thumb self._path1 = path1 self._path2 = path2 @@ -111,11 +115,11 @@ def messages(self) -> List[str]: @property def programheaders1(self) -> List["ELFProgramHeader"]: - return self.app1.header.programheaders + return self.app1.elf_header.programheaders @property def programheaders2(self) -> List["ELFProgramHeader"]: - return self.app2.header.programheaders + return self.app2.elf_header.programheaders @property def newsegments(self) -> List["ELFProgramHeader"]: @@ -123,11 +127,11 @@ def newsegments(self) -> List["ELFProgramHeader"]: @property def sectionheaders1(self) -> List["ELFSectionHeader"]: - return self.app1.header.sectionheaders + return self.app1.elf_header.sectionheaders @property def sectionheaders2(self) -> List["ELFSectionHeader"]: - return self.app2.header.sectionheaders + return self.app2.elf_header.sectionheaders @property def newsections(self) -> List["ELFSectionHeader"]: @@ -210,7 +214,7 @@ def compare_segments(self) -> None: for ph1 in self.programheaders1: index = ph1.index if not index in self._programheaderpairs: - ph2 = self.app2.header.get_programheader_by_index(index) + ph2 = self.app2.elf_header.get_programheader_by_index(index) self._programheaderpairs[index] = (ph1, ph2) else: chklogger.logger.warning("Duplicate header index: %d", index) @@ -240,7 +244,7 @@ def compare_sections(self) -> None: for sh1 in self.sectionheaders1: name = sh1.name if not name in self._sectionheaderpairs: - sh2 = self.app2.header.get_sectionheader_by_name(name) + sh2 = self.app2.elf_header.get_sectionheader_by_name(name) self._sectionheaderpairs[name] = (sh1, sh2) else: chklogger.logger.warning("Duplicate section name: " + name) diff --git a/chb/cmdline/commandutil.py b/chb/cmdline/commandutil.py index 96524154..3844579e 100644 --- a/chb/cmdline/commandutil.py +++ b/chb/cmdline/commandutil.py @@ -203,7 +203,6 @@ def get_app(path: str, xfile: str, xinfo: XI.XInfo) -> AppAccess: def get_asm(app: AppAccess) -> Assembly: if isinstance(app, MIPSAccess): - app = cast(MIPSAccess, app) return MIPSAssembly(app, UF.get_mips_asm_xnode(app.path, app.filename)) elif isinstance(app, ARMAccess): return ARMAssembly(app, UF.get_arm_asm_xnode(app.path, app.filename)) @@ -608,7 +607,7 @@ def analyzecmd(args: argparse.Namespace) -> NoReturn: udata = userdata["userdata"] = {} dbdata = udata["data-blocks"] = [] for db in datablocks: - dbrec: Dict = {} + dbrec: Dict[str, List[str]] = {} dbrec["r"] = [db.startaddr, db.endaddr] dbdata.append(dbrec) with open(outputfile + ".json", "w") as fp: @@ -1847,7 +1846,7 @@ def results_extract(args: argparse.Namespace) -> NoReturn: exit(1) app = get_app(path, xfile, xinfo) - elfheader = app.header + elfheader = app.elf_header iaddr = int(addr, 16) sectionindex = elfheader.get_elf_section_index(iaddr) @@ -1899,6 +1898,7 @@ def results_extract(args: argparse.Namespace) -> NoReturn: sectionindex = elfheader.get_elf_section_index(iaddr) if sectionindex is None: print_error("Address not found in elf section: " + addr) + continue stable: List[str] = [] while True: data = elfheader.get_doubleword_value( @@ -1908,6 +1908,9 @@ def results_extract(args: argparse.Namespace) -> NoReturn: break else: strindex = elfheader.get_elf_section_index(data) + if strindex is None: + print_error("No section index found for %#x" % data) + continue strdata = elfheader.get_string(strindex, data) stable.append(strdata) table.append(stable) @@ -1926,7 +1929,9 @@ def results_extract(args: argparse.Namespace) -> NoReturn: strindex = elfheader.get_elf_section_index(data) if strindex is None: print_error("String address not found: " + hex(data)) - strdata = elfheader.get_string(strindex, data) + strdata = hex(data) + else: + strdata = elfheader.get_string(strindex, data) r.append(strdata) elif t == "int": r.append(str(data)) @@ -2014,6 +2019,7 @@ def results_struct_arguments(args: argparse.Namespace) -> NoReturn: xinfo.load(path, xfile) app = get_app(path, xfile, xinfo) + elf_header = app.elf_header results: Dict[int, Tuple[int, "BCCompInfo"]] = {} callinstrs = app.call_instructions() @@ -2058,7 +2064,7 @@ def results_struct_arguments(args: argparse.Namespace) -> NoReturn: structtype = cast("BCTypComp", structtype) addr = x.intvalue - elfsection = app.header.get_elf_section_index(addr) + elfsection = elf_header.get_elf_section_index(addr) if elfsection is not None: print( "struct: " @@ -2072,7 +2078,7 @@ def results_struct_arguments(args: argparse.Namespace) -> NoReturn: for (addr, (elfsection, compinfo)) in sorted(results.items()): print("\nstruct " + compinfo.cname + " at " + hex(addr)) for (foffset, fieldinfo) in compinfo.fieldoffsets(): - fieldval = app.header.get_doubleword_value(elfsection, addr + foffset) + fieldval = elf_header.get_doubleword_value(elfsection, addr + foffset) print( str(foffset).rjust(4) + " " @@ -2084,9 +2090,9 @@ def results_struct_arguments(args: argparse.Namespace) -> NoReturn: if ( fieldtype.tgttyp.is_integer and fieldtype.tgttyp.byte_size() == 1): - fieldsection = app.header.get_elf_section_index(fieldval) + fieldsection = elf_header.get_elf_section_index(fieldval) if fieldsection is not None: - fieldstring = app.header.get_string( + fieldstring = elf_header.get_string( fieldsection, fieldval) print(" Constant string: " + fieldstring) diff --git a/chb/cmdline/fileformatcmds.py b/chb/cmdline/fileformatcmds.py index 2b0a16ba..dd4a0666 100644 --- a/chb/cmdline/fileformatcmds.py +++ b/chb/cmdline/fileformatcmds.py @@ -79,7 +79,7 @@ def pedatacmd(args: argparse.Namespace) -> NoReturn: exit(1) app = UC.get_app(path, xfile, xinfo) - peheader = app.header + peheader = app.pe_header if headeronly: print(peheader) exit(0) @@ -140,7 +140,7 @@ def elfdatacmd(args: argparse.Namespace) -> NoReturn: try: app = UC.get_app(path, xfile, xinfo) - elfheader = app.header + elfheader = app.elf_header except UF.CHBError as e: print(str(e.wrap())) exit(1) @@ -167,8 +167,8 @@ def elfdatacmd(args: argparse.Namespace) -> NoReturn: result["file"] = xinfo.file result["md5"] = xinfo.md5 result["section-headers"] = [] - for s in elfheader.sectionheaders: - result["section-headers"].append(s.attribute_values()) + for sh in elfheader.sectionheaders: + result["section-headers"].append(sh.attribute_values()) filename = xname + "_section_headers.json" with open(filename, "w") as fp: json.dump(result, fp, indent=3) diff --git a/chb/cmdline/jsonresultutil.py b/chb/cmdline/jsonresultutil.py index 02e0ca49..8c35216a 100644 --- a/chb/cmdline/jsonresultutil.py +++ b/chb/cmdline/jsonresultutil.py @@ -77,7 +77,7 @@ def jsonok(schemaname: str, content: Dict[str, Any]) -> Dict[str, Any]: return jresult -def jsonappdata(xinfo: "XInfo", includepath=True) -> Dict[str, str]: +def jsonappdata(xinfo: "XInfo", includepath: bool = True) -> Dict[str, str]: result: Dict[str, str] = {} if includepath: result["path"] = xinfo.path diff --git a/chb/cmdline/relationalcmds.py b/chb/cmdline/relationalcmds.py index 5da061f7..934889f1 100644 --- a/chb/cmdline/relationalcmds.py +++ b/chb/cmdline/relationalcmds.py @@ -122,9 +122,6 @@ def compare_executable_content( app1 = UC.get_app(path1, xfile1, xinfo1) app2 = UC.get_app(path2, xfile2, xinfo2) - sectionheaders1 = app1.header.sectionheaders - sectionheaders2 = app2.header.sectionheaders - xcomparison = XComparison( is_thumb, path1, @@ -1128,9 +1125,6 @@ def relational_compare_elfdata(args: argparse.Namespace) -> NoReturn: app1 = UC.get_app(path1, xfile1, xinfo1) app2 = UC.get_app(path2, xfile2, xinfo2) - sectionheaders1 = app1.header.sectionheaders - sectionheaders2 = app2.header.sectionheaders - xcomparison = XComparison( False, path1, diff --git a/chb/cmdline/reportcmds.py b/chb/cmdline/reportcmds.py index fe11ce8a..f6b68fb9 100644 --- a/chb/cmdline/reportcmds.py +++ b/chb/cmdline/reportcmds.py @@ -766,16 +766,16 @@ def report_proofobligations(args: argparse.Namespace) -> NoReturn: # fn_counts: faddr -> status_tag -> count fn_counts: Dict[str, Dict[str, int]] = {} # fn_open_pos: faddr -> iaddr -> list of open ProofObligations - fn_open_pos: Dict[str, Dict[str, List]] = {} + fn_open_pos: Dict[str, Dict[str, List['ProofObligation']]] = {} # fn_detail_pos: faddr -> iaddr -> list of safe/delegated ProofObligations - fn_detail_pos: Dict[str, Dict[str, List]] = {} + fn_detail_pos: Dict[str, Dict[str, List['ProofObligation']]] = {} # fn_labels: faddr -> display string fn_labels: Dict[str, str] = {} detail_statuses = {"dis", "local", "del"} # flat list of records for --json export - po_records: List[Dict] = [] + po_records: List[Dict[str, Any]] = [] for fn in app.functions.values(): faddr = fn.faddr @@ -1408,12 +1408,13 @@ def report_buffer_bounds(args: argparse.Namespace) -> NoReturn: exit(0) -def collect_known_fn_addrs(app: "AppAccess", patchcallsites: list) -> dict: +def collect_known_fn_addrs(app: "AppAccess", patchcallsites: list[LibraryCallSideeffect]) -> dict[str, str]: function_addr: Dict[str, str] = {} - def consider_pair(faddr: str, fname: Optional[str]): + def consider_pair(faddr: str, fname: Optional[str]) -> None: if fname and fname not in function_addr: function_addr[fname] = faddr - def consider(faddr: str): + + def consider(faddr: str) -> None: fname = ( app.function_name(faddr) if app.has_function_name(faddr) @@ -1807,7 +1808,7 @@ def is_cmd_delegation(po: 'ProofObligation') -> bool: if is_cmd_delegation(po): os_cmd_delegations[po.status.get_iaddr()].append(instr.iaddr) if type(po.xpo) == XPOTrustedOsCmdFmtString: - trusted_po = cast('XPOTrustedOsCmdFmtString', po.xpo) + trusted_po = po.xpo os_cmd_construction.append((faddr, instr, calltgt.name, trusted_po)) if calltgt.is_app_target and calltgt.name not in app_functions: app_functions[calltgt.name] = str(cast('AppTarget', calltgt).address) diff --git a/chb/elfformat/ELFHeader.py b/chb/elfformat/ELFHeader.py index 361dffa3..96d5007d 100644 --- a/chb/elfformat/ELFHeader.py +++ b/chb/elfformat/ELFHeader.py @@ -116,7 +116,7 @@ def get_value(d: Dict[str, str], v: str) -> str: } -class ELFHeader: +class ELFHeader(UF.FileHeader): @staticmethod def fmt_name() -> str: @@ -132,7 +132,7 @@ def __init__( filename: str, xnode: ET.Element, # ignored, used for compatibility with PEHeader - deps: Sequence[str] = []) -> None: + _: Sequence[str]) -> None: self._pathname = pathname self._filename = filename self.xnode = xnode @@ -410,10 +410,10 @@ def get_relocation_tables(self) -> List[Tuple[ELFSectionHeader, ELFSection]]: result: List[Tuple[ELFSectionHeader, ELFSection]] = [] for sh in self.sectionheaders: if sh.is_relocation_table: - if sh.index not in self.sections: + index = int(sh.index) + if index not in self.sections: xsection = UF.get_elf_section_xnode( self.pathname, self.filename, sh.index) - index = int(sh.index) self._sections[index] = ELFRelocationTable( self, self.sectionheaders[index], xsection) result.append((sh, self.sections[index])) @@ -465,7 +465,7 @@ def as_dictionary(self) -> Dict[str, Any]: if self.has_string_table(): result["stringtables"] = {} for ss in self.get_string_tables(): - result["stringtables"][ss.name] = s.as_dictionary() + result["stringtables"][ss.name] = ss.as_dictionary() if self.has_symbol_table(): result["symboltable"] = self.get_symbol_table().as_dictionary() if self.has_dynamic_symbol_table(): diff --git a/chb/invariants/FnDictionaryRecord.py b/chb/invariants/FnDictionaryRecord.py index 881f4b62..d8267697 100644 --- a/chb/invariants/FnDictionaryRecord.py +++ b/chb/invariants/FnDictionaryRecord.py @@ -93,7 +93,7 @@ def __init__( self._vd = vd @property - def vd(self) -> "chb.invariants.FnVarDictionary.FnVarDictionary": + def vd(self) -> "FnVarDictionary": return self._vd @property @@ -238,7 +238,7 @@ def handler(t: type) -> type: def mk_instance( self, - xd: "chb.invariants.FnXprDictionary.FnXprDictionary", + xd: "FnXprDictionary", ixval: IndexedTableValue, superclass: Type[XdR]) -> XdR: tag = ixval.tags[0] @@ -270,7 +270,7 @@ def handler(t: type) -> type: def mk_instance( self, - invd: "chb.invariants.FnInvDictionary.FnInvDictionary", + invd: "FnInvDictionary", ixval: IndexedTableValue, superclass: Type[IdR]) -> IdR: tag = ixval.tags[0] @@ -302,7 +302,7 @@ def handler(t: type) -> type: def mk_instance( self, - varinvd: "chb.invariants.FnVarInvDictionary.FnVarInvDictionary", + varinvd: "FnVarInvDictionary", ixval: IndexedTableValue, superclass: Type[VIdR]) -> VIdR: tag = ixval.tags[0] diff --git a/chb/invariants/XConstant.py b/chb/invariants/XConstant.py index ea5dbd24..cd8dd4e3 100644 --- a/chb/invariants/XConstant.py +++ b/chb/invariants/XConstant.py @@ -41,7 +41,7 @@ """ -from typing import Any, Dict, List, TYPE_CHECKING +from typing import Any, Dict, List, TYPE_CHECKING, cast from chb.invariants.FnDictionaryRecord import FnXprDictionaryRecord, xprregistry from chb.invariants.XNumerical import XNumerical diff --git a/chb/invariants/XXpr.py b/chb/invariants/XXpr.py index b1836e3b..8b71ebe1 100644 --- a/chb/invariants/XXpr.py +++ b/chb/invariants/XXpr.py @@ -506,10 +506,10 @@ def to_input_constraint_value(self) -> Optional[ICV.InputConstraintValue]: tgt = self.returnval_target() if tgt is None: return None - if tgt == 'getenv': + if tgt.name == 'getenv': envarg = self.returnval_arguments()[0] return ICV.EnvironmentInputValue(str(envarg)) - if tgt in ['strchr', 'strrchr']: + if tgt.name in ['strchr', 'strrchr']: strk = self.returnval_arguments()[0].to_input_constraint_value() if strk is not None: cchar = self.returnval_arguments()[1] @@ -517,7 +517,7 @@ def to_input_constraint_value(self) -> Optional[ICV.InputConstraintValue]: charval = cchar.constant.value charcode = "'" + chr(charval) + "'" return ICV.StringSuffixValue( - strk, charcode, lastpos=(tgt == 'strrchr')) + strk, charcode, lastpos=(tgt.name == 'strrchr')) else: return None else: diff --git a/chb/invariants/XXprUtil.py b/chb/invariants/XXprUtil.py index 2eb36b76..84a7f519 100644 --- a/chb/invariants/XXprUtil.py +++ b/chb/invariants/XXprUtil.py @@ -126,6 +126,7 @@ VMemoryOffsetArrayIndexOffset, VMemoryOffsetBasePtrArrayIndexOffset, VMemoryOffsetIndexOffset) + from chb.invariants.XVariable import XVariable from chb.mips.MIPSRegister import MIPSRegister @@ -1649,7 +1650,7 @@ def xxpr_to_ast_def_expr( def xvariable_to_ast_lvals( - xv: X.XVariable, + xv: 'XVariable', xdata: "InstrXData", astree: ASTInterface, size: int = 4, @@ -1883,7 +1884,7 @@ def global_variable_to_ast_lval( def xvariable_to_ast_lval( - xv: X.XVariable, + xv: 'XVariable', xdata: "InstrXData", iaddr: str, astree: ASTInterface, diff --git a/chb/jsoninterface/AuxiliaryClasses.py b/chb/jsoninterface/AuxiliaryClasses.py index 40e61c52..4f22aec8 100644 --- a/chb/jsoninterface/AuxiliaryClasses.py +++ b/chb/jsoninterface/AuxiliaryClasses.py @@ -27,7 +27,7 @@ import os -from typing import Any, Dict, List, Optional, Union +from typing import Any, Dict, List, Optional, Union, cast class XFilePath: @@ -41,11 +41,11 @@ def d(self) -> Dict[str, Any]: @property def path(self) -> str: - return self.d.get("path", "missing:xfilepath:path") + return cast(str, self.d.get("path", "missing:xfilepath:path")) @property def filename(self) -> str: - return self.d.get("filename", "missing:xfilepath:filename") + return cast(str, self.d.get("filename", "missing:xfilepath:filename")) @property def filepath(self) -> str: diff --git a/chb/jsoninterface/JSONAppComparison.py b/chb/jsoninterface/JSONAppComparison.py index 93653a76..58e8f91b 100644 --- a/chb/jsoninterface/JSONAppComparison.py +++ b/chb/jsoninterface/JSONAppComparison.py @@ -25,7 +25,7 @@ # SOFTWARE. # ------------------------------------------------------------------------------ -from typing import Any, Dict, List, Optional, TYPE_CHECKING, Union +from typing import Any, Dict, List, Optional, TYPE_CHECKING, cast import chb.jsoninterface.AuxiliaryClasses as AX from chb.jsoninterface.JSONFunctionComparison import JSONFunctionComparison @@ -42,7 +42,7 @@ def __init__(self, d: Dict[str, Any]) -> None: @property def changes(self) -> List[str]: - return self.d.get("changes", []) + return cast(List[str], self.d.get("changes", [])) def accept(self, visitor: "JSONObjectVisitor") -> None: visitor.visit_callgraph_comparison(self) @@ -55,7 +55,7 @@ def __init__(self, d: Dict[str, Any]) -> None: @property def changes(self) -> List[str]: - return self.d.get("changes", []) + return cast(List[str], self.d.get("changes", [])) def accept(self, visitor: "JSONObjectVisitor") -> None: visitor.visit_binary_comparison(self) @@ -68,7 +68,7 @@ def __init__(self, d: Dict[str, Any]) -> None: @property def gaddr(self) -> str: - return self.d.get("gaddr1", self.property_missing("gaddr1")) + return cast(str, self.d.get("gaddr1", self.property_missing("gaddr1"))) @property def name(self) -> Optional[str]: @@ -89,7 +89,7 @@ def __init__(self, d: Dict[str, Any]) -> None: @property def faddr(self) -> str: - return self.d.get("faddr", self.property_missing("faddr")) + return cast(str, self.d.get("faddr", self.property_missing("faddr"))) def accept(self, visitor: "JSONObjectVisitor") -> None: visitor.visit_function_added(self) @@ -103,11 +103,11 @@ def __init__(self, d: Dict[str, str]) -> None: @property def faddr(self) -> str: - return self.d.get("faddr", self.property_missing("faddr")) + return cast(str, self.d.get("faddr", self.property_missing("faddr"))) @property def md5(self) -> str: - return self.d.get("md5", self.property_missing("md5")) + return cast(str, self.d.get("md5", self.property_missing("md5"))) def accept(self, visitor: "JSONObjectVisitor") -> None: visitor.visit_function_md5(self) @@ -176,11 +176,11 @@ def file2(self) -> str: @property def changes(self) -> List[str]: - return self.d.get("changes", []) + return cast(List[str], self.d.get("changes", [])) @property def matches(self) -> List[str]: - return self.d.get("matches", []) + return cast(List[str], self.d.get("matches", [])) @property def functions_compared(self) -> List[str]: diff --git a/chb/jsoninterface/JSONAssemblyBlock.py b/chb/jsoninterface/JSONAssemblyBlock.py index fc070f27..da96d002 100644 --- a/chb/jsoninterface/JSONAssemblyBlock.py +++ b/chb/jsoninterface/JSONAssemblyBlock.py @@ -25,7 +25,7 @@ # SOFTWARE. # ------------------------------------------------------------------------------ -from typing import Any, Dict, List, Optional, TYPE_CHECKING, Union +from typing import Any, Dict, List, Optional, TYPE_CHECKING, cast from chb.jsoninterface.JSONAssemblyInstruction import JSONAssemblyInstruction from chb.jsoninterface.JSONObject import JSONObject @@ -42,11 +42,11 @@ def __init__(self, d: Dict[str, Any]) -> None: @property def startaddr(self) -> str: - return self.d.get("startaddr", self.property_missing("startaddr")) + return cast(str, self.d.get("startaddr", self.property_missing("startaddr"))) @property def endaddr(self) -> str: - return self.d.get("endaddr", self.property_missing("endaddr")) + return cast(str, self.d.get("endaddr", self.property_missing("endaddr"))) @property def instructions(self) -> List[JSONAssemblyInstruction]: diff --git a/chb/jsoninterface/JSONAssemblyInstruction.py b/chb/jsoninterface/JSONAssemblyInstruction.py index 9bf0bb5c..8ac8c3e6 100644 --- a/chb/jsoninterface/JSONAssemblyInstruction.py +++ b/chb/jsoninterface/JSONAssemblyInstruction.py @@ -25,7 +25,7 @@ # SOFTWARE. # ------------------------------------------------------------------------------ -from typing import Any, Dict, List, Tuple, Optional, TYPE_CHECKING +from typing import Any, Dict, List, Tuple, Optional, TYPE_CHECKING, cast from chb.jsoninterface.JSONObject import JSONObject from chb.jsoninterface.JSONProofObligationRecord import JSONProofObligationRecord @@ -44,7 +44,7 @@ def __init__(self, d: Dict[str, Any]) -> None: @property def addr(self) -> List[str]: - return self.d.get("addr", self.property_missing("addr")) + return cast(List[str], self.d.get("addr", self.property_missing("addr"))) @property def stackpointer(self) -> Optional[JSONStackpointerOffset]: @@ -64,15 +64,15 @@ def proofobligations(self) -> List[JSONProofObligationRecord]: @property def bytes(self) -> str: - return self.d.get("bytes", self.property_missing("bytes")) + return cast(str, self.d.get("bytes", self.property_missing("bytes"))) @property def opcode(self) -> Tuple[str, str]: - return self.d.get("opcode", self.property_missing("opcode")) + return cast(Tuple[str, str], self.d.get("opcode", self.property_missing("opcode"))) @property def annotation(self) -> str: - return self.d.get("annotation", self.property_missing("annotation")) + return cast(str, self.d.get("annotation", self.property_missing("annotation"))) def accept(self, visitor: "JSONObjectVisitor") -> None: visitor.visit_assembly_instruction(self) diff --git a/chb/jsoninterface/JSONBlockComparison.py b/chb/jsoninterface/JSONBlockComparison.py index 3a01ec62..e85481ec 100644 --- a/chb/jsoninterface/JSONBlockComparison.py +++ b/chb/jsoninterface/JSONBlockComparison.py @@ -25,7 +25,7 @@ # SOFTWARE. # ------------------------------------------------------------------------------ -from typing import Any, Dict, List, Optional, TYPE_CHECKING +from typing import Any, Dict, List, Optional, TYPE_CHECKING, cast from chb.jsoninterface.JSONAssemblyInstruction import JSONAssemblyInstruction from chb.jsoninterface.JSONInstructionComparison import JSONInstructionComparison @@ -57,17 +57,17 @@ def _get_instr_comparison_summary(self) -> Dict[str, Any]: @property def summary_instructions_added(self) -> List[str]: instr_summary = self._get_instr_comparison_summary() - return instr_summary.get("block-instructions-added", []) + return cast(List[str], instr_summary.get("block-instructions-added", [])) @property def summary_instructions_removed(self) -> List[str]: instr_summary = self._get_instr_comparison_summary() - return instr_summary.get("block-instructions-removed", []) + return cast(List[str], instr_summary.get("block-instructions-removed", [])) @property def summary_instructions_changed(self) -> List[str]: instr_summary = self._get_instr_comparison_summary() - return instr_summary.get("block-instructions-changed", []) + return cast(List[str], instr_summary.get("block-instructions-changed", [])) @property def instructions_changed(self) -> List[JSONInstructionComparison]: diff --git a/chb/jsoninterface/JSONCallgraph.py b/chb/jsoninterface/JSONCallgraph.py index 6683bce3..1f4894c3 100644 --- a/chb/jsoninterface/JSONCallgraph.py +++ b/chb/jsoninterface/JSONCallgraph.py @@ -25,7 +25,7 @@ # SOFTWARE. # ------------------------------------------------------------------------------ -from typing import Any, Dict, List, Optional, TYPE_CHECKING +from typing import Any, Dict, List, Optional, TYPE_CHECKING, cast from chb.jsoninterface.JSONObject import JSONObject @@ -41,11 +41,11 @@ def __init__(self, d: Dict[str, Any]) -> None: @property def src(self) -> str: - return self.d.get("src", self.property_missing("src")) + return cast(str, self.d.get("src", self.property_missing("src"))) @property def tgt(self) -> str: - return self.d.get("dst", self.property_missing("dst")) + return cast(str, self.d.get("dst", self.property_missing("dst"))) @property def type(self) -> Optional[str]: @@ -62,11 +62,11 @@ def __init__(self, d: Dict[str, Any]) -> None: @property def name(self) -> str: - return self.d.get("name", self.property_missing("name")) + return cast(str, self.d.get("name", self.property_missing("name"))) @property def label(self) -> str: - return self.d.get("label", self.name) + return cast(str, self.d.get("label", self.name)) @property def type(self) -> Optional[str]: diff --git a/chb/jsoninterface/JSONCallsiteRecords.py b/chb/jsoninterface/JSONCallsiteRecords.py index 451e15c8..6d2d4318 100644 --- a/chb/jsoninterface/JSONCallsiteRecords.py +++ b/chb/jsoninterface/JSONCallsiteRecords.py @@ -25,7 +25,7 @@ # SOFTWARE. # ------------------------------------------------------------------------------ -from typing import Any, Dict, List, Optional, Tuple, TYPE_CHECKING +from typing import Any, Dict, List, Optional, Tuple, TYPE_CHECKING, cast from chb.jsoninterface.JSONObject import JSONObject from chb.jsoninterface.JSONCallgraph import JSONCallgraph @@ -41,11 +41,11 @@ def __init__(self, d: Dict[str, Any]) -> None: @property def name(self) -> str: - return self.d.get("name", self.property_missing("name")) + return cast(str, self.d.get("name", self.property_missing("name"))) @property def roles(self) -> List[str]: - return self.d.get("parameter-roles", []) + return cast(List[str], self.d.get("parameter-roles", [])) def accept(self, visitor: "JSONObjectVisitor") -> None: visitor.visit_callsite_tgt_parameter(self) @@ -59,7 +59,7 @@ def __init__(self, d: Dict[str, Any]) -> None: @property def name(self) -> str: - return self.d.get("name", self.property_missing("name")) + return cast(str, self.d.get("name", self.property_missing("name"))) @property def parameters(self) -> List[JSONCallsiteTgtParameter]: @@ -72,7 +72,7 @@ def parameters(self) -> List[JSONCallsiteTgtParameter]: @property def varargs(self) -> bool: - return self.d.get("varargs", False) + return cast(bool, self.d.get("varargs", False)) def accept(self, visitor: "JSONObjectVisitor") -> None: visitor.visit_callsite_tgt_function(self) @@ -86,11 +86,11 @@ def __init__(self, d: Dict[str, Any]) -> None: @property def name(self) -> str: - return self.d.get("name", self.property_missing("name")) + return cast(str, self.d.get("name", self.property_missing("name"))) @property def value(self) -> str: - return self.d.get("value", self.property_missing("value")) + return cast(str, self.d.get("value", self.property_missing("value"))) @property def roles(self) -> List[Tuple[str, str]]: @@ -116,11 +116,11 @@ def __init__(self, d: Dict[str, Any]) -> None: @property def faddr(self) -> str: - return self.d.get("faddr", self.property_missing("faddr")) + return cast(str, self.d.get("faddr", self.property_missing("faddr"))) @property def iaddr(self) -> str: - return self.d.get("iaddr", self.property_missing("iaddr")) + return cast(str, self.d.get("iaddr", self.property_missing("iaddr"))) @property def arguments(self) -> List[JSONCallsiteArgument]: diff --git a/chb/jsoninterface/JSONControlFlowGraph.py b/chb/jsoninterface/JSONControlFlowGraph.py index 7316893e..2a49a92c 100644 --- a/chb/jsoninterface/JSONControlFlowGraph.py +++ b/chb/jsoninterface/JSONControlFlowGraph.py @@ -25,9 +25,8 @@ # SOFTWARE. # ------------------------------------------------------------------------------ -from typing import Any, Dict, List, Optional, Tuple, TYPE_CHECKING, Union +from typing import Any, Dict, List, Optional, TYPE_CHECKING, cast -import chb.jsoninterface.AuxiliaryClasses as AX from chb.jsoninterface.JSONAssemblyBlock import JSONAssemblyBlock from chb.jsoninterface.JSONObject import JSONObject @@ -43,7 +42,7 @@ def __init__(self, d: Dict[str, Any]) -> None: @property def baddr(self) -> str: - return self.d.get("baddr", self.property_missing("baddr")) + return cast(str, self.d.get("baddr", self.property_missing("baddr"))) @property def id(self) -> str: @@ -52,7 +51,7 @@ def id(self) -> str: This normally corresponds to BasicBlock.baddr, whereas baddr above corresponds to BasicBlock.real_baddr. """ - return self.d.get("id", self.property_missing("id")) + return cast(str, self.d.get("id", self.property_missing("id"))) @property def code(self) -> JSONAssemblyBlock: @@ -79,23 +78,23 @@ def __init__(self, d: Dict[str, Any]) -> None: @property def src(self) -> str: - return self.d.get("src", self.property_missing("src")) + return cast(str, self.d.get("src", self.property_missing("src"))) @property def tgt(self) -> str: - return self.d.get("tgt", self.property_missing("tgt")) + return cast(str, self.d.get("tgt", self.property_missing("tgt"))) @property def kind(self) -> str: - return self.d.get("kind", self.property_missing("kind")) + return cast(str, self.d.get("kind", self.property_missing("kind"))) @property def predicate(self) -> Optional[str]: if "predicate" in self.d: - return self.d.get( + return cast(str, self.d.get( "predicate", {"txtrep": "?"}).get("txtrep", - self.property_missing("predicate.txtrep")) + self.property_missing("predicate.txtrep"))) return None def accept(self, visitor: "JSONObjectVisitor") -> None: @@ -115,11 +114,11 @@ def name(self) -> Optional[str]: @property def faddr(self) -> str: - return self.d.get("faddr", self.property_missing("faddr")) + return cast(str, self.d.get("faddr", self.property_missing("faddr"))) @property def md5hash(self) -> str: - return self.d.get("md5hash", self.property_missing("md5hash")) + return cast(str, self.d.get("md5hash", self.property_missing("md5hash"))) @property def nodes(self) -> List[JSONCfgNode]: diff --git a/chb/jsoninterface/JSONFunctionComparison.py b/chb/jsoninterface/JSONFunctionComparison.py index 886afc50..5fcc9db2 100644 --- a/chb/jsoninterface/JSONFunctionComparison.py +++ b/chb/jsoninterface/JSONFunctionComparison.py @@ -25,7 +25,7 @@ # SOFTWARE. # ------------------------------------------------------------------------------ -from typing import Any, Dict, List, Optional, Tuple, TYPE_CHECKING +from typing import Any, Dict, List, Optional, Tuple, TYPE_CHECKING, cast from chb.jsoninterface.JSONBlockComparison import JSONBlockComparison from chb.jsoninterface.JSONControlFlowGraph import JSONControlFlowGraph @@ -44,23 +44,23 @@ def __init__(self, d: Dict[str, Any]) -> None: @property def changes(self) -> List[str]: - return self.d.get("changes", []) + return cast(List[str], self.d.get("changes", [])) @property def matches(self) -> List[str]: - return self.d.get("matches", []) + return cast(List[str], self.d.get("matches", [])) @property def cfg1_block_addr(self) -> str: - return self.d.get("cfg1-block-addr", self.property_missing("cfg1-block-addr")) + return cast(str, self.d.get("cfg1-block-addr", self.property_missing("cfg1-block-addr"))) @property def instr_count1(self) -> int: - return self.d.get("instr-count1", self.property_missing("instr-count1")) + return cast(int, self.d.get("instr-count1", self.property_missing("instr-count1"))) @property def instr_count2(self) -> int: - return self.d.get("instr-count2", self.property_missing("instr-count2")) + return cast(int, self.d.get("instr-count2", self.property_missing("instr-count2"))) @property def cfg2_blocks(self) -> List[Tuple[str, str]]: @@ -112,11 +112,11 @@ def __init__(self, d: Dict[str, Any]) -> None: @property def faddr1(self) -> str: - return self.d.get("faddr1", self.property_missing("faddr1")) + return cast(str, self.d.get("faddr1", self.property_missing("faddr1"))) @property def faddr2(self) -> str: - return self.d.get("faddr2", self.property_missing("faddr2")) + return cast(str, self.d.get("faddr2", self.property_missing("faddr2"))) @property def name1(self) -> Optional[str]: @@ -157,7 +157,7 @@ def num_blocks2(self) -> int: @property def blocks_changed(self) -> List[str]: - return self.d.get("blocks-changed", []) + return cast(List[str], self.d.get("blocks-changed", [])) @property def num_blocks_changed(self) -> int: @@ -165,7 +165,7 @@ def num_blocks_changed(self) -> int: @property def changes(self) -> List[str]: - return self.d.get("changes", [self.property_missing("changes")]) + return cast(List[str], self.d.get("changes", [self.property_missing("changes")])) @property def cfg_block_mapping(self) -> List[JSONCfgBlockMappingItem]: @@ -178,7 +178,7 @@ def cfg_block_mapping(self) -> List[JSONCfgBlockMappingItem]: @property def matches(self) -> List[str]: - return self.d.get("matches", []) + return cast(List[str], self.d.get("matches", [])) def accept(self, visitor: "JSONObjectVisitor") -> None: visitor.visit_function_comparison(self) diff --git a/chb/jsoninterface/JSONInstructionComparison.py b/chb/jsoninterface/JSONInstructionComparison.py index dd520275..80c565fe 100644 --- a/chb/jsoninterface/JSONInstructionComparison.py +++ b/chb/jsoninterface/JSONInstructionComparison.py @@ -25,7 +25,7 @@ # SOFTWARE. # ------------------------------------------------------------------------------ -from typing import Any, Dict, List, Optional, TYPE_CHECKING, Union +from typing import Any, Dict, List, Optional, TYPE_CHECKING, cast from chb.jsoninterface.JSONObject import JSONObject from chb.jsoninterface.JSONAssemblyInstruction import JSONAssemblyInstruction @@ -44,7 +44,7 @@ def __init__(self, d: Dict[str, Any]) -> None: @property def iaddr1(self) -> str: - return self.d.get("iaddr1", self.property_missing("iaddr1")) + return cast(str, self.d.get("iaddr1", self.property_missing("iaddr1"))) @property def iaddr2(self) -> Optional[str]: @@ -52,7 +52,7 @@ def iaddr2(self) -> Optional[str]: @property def changes(self) -> List[str]: - return self.d.get("changes", []) + return cast(List[str], self.d.get("changes", [])) @property def instr1(self) -> JSONAssemblyInstruction: diff --git a/chb/jsoninterface/JSONPatchComponent.py b/chb/jsoninterface/JSONPatchComponent.py index 7947f6da..b9e6a32a 100644 --- a/chb/jsoninterface/JSONPatchComponent.py +++ b/chb/jsoninterface/JSONPatchComponent.py @@ -26,7 +26,7 @@ # ------------------------------------------------------------------------------ """JSON objects related to patch components.""" -from typing import Any, Dict, List, Optional, Union, TYPE_CHECKING +from typing import Any, Dict, List, Optional, Union, TYPE_CHECKING, cast from chb.jsoninterface.JSONAssemblyInstruction import JSONAssemblyInstruction from chb.jsoninterface.JSONObject import JSONObject @@ -42,19 +42,19 @@ def __init__(self, d: Dict[str, Any]) -> None: @property def srca(self) -> str: - return self.d.get("srca", self.property_missing("srca")) + return cast(str, self.d.get("srca", self.property_missing("srca"))) @property def tgta(self) -> str: - return self.d.get("tgta", self.property_missing("tgta")) + return cast(str, self.d.get("tgta", self.property_missing("tgta"))) @property def size(self) -> int: - return self.d.get("size", self.property_missing("size")) + return cast(int, self.d.get("size", self.property_missing("size"))) @property def instr(self) -> JSONAssemblyInstruction: - return self.d.get("instr", self.property_missing("instr")) + return cast(JSONAssemblyInstruction, self.d.get("instr", self.property_missing("instr"))) def accept(self, visitor: "JSONObjectVisitor") -> None: visitor.visit_hookinstr(self) @@ -68,7 +68,7 @@ def __init__(self, d: Dict[str, Any]) -> None: @property def starta(self) -> str: - return self.d.get("starta", self.property_missing("starta")) + return cast(str, self.d.get("starta", self.property_missing("starta"))) @property def instructions(self) -> List[JSONAssemblyInstruction]: @@ -91,11 +91,11 @@ def __init__(self, d: Dict[str, Any]) -> None: @property def role(self) -> str: - return self.d.get("role", self.property_missing("role")) + return cast(str, self.d.get("role", self.property_missing("role"))) @property def kind(self) -> str: - return self.d.get("kind", self.property_missing("role")) + return cast(str, self.d.get("kind", self.property_missing("role"))) @property def value(self) -> Union[JSONHookInstruction, JSONCodeFragment]: diff --git a/chb/jsoninterface/JSONProofObligationRecord.py b/chb/jsoninterface/JSONProofObligationRecord.py index 0fe8ab61..4278a18f 100644 --- a/chb/jsoninterface/JSONProofObligationRecord.py +++ b/chb/jsoninterface/JSONProofObligationRecord.py @@ -25,7 +25,7 @@ # SOFTWARE. # ------------------------------------------------------------------------------ -from typing import Any, Dict, TYPE_CHECKING +from typing import Any, Dict, TYPE_CHECKING, cast from chb.jsoninterface.JSONObject import JSONObject @@ -40,15 +40,15 @@ def __init__(self, d: Dict[str, Any]) -> None: @property def predicate(self) -> str: - return self.d.get("predicate", "none") + return cast(str, self.d.get("predicate", "none")) @property def status(self) -> str: - return self.d.get("status", "none") + return cast(str, self.d.get("status", "none")) @property def msg(self) -> str: - return self.d.get("msg", "none") + return cast(str, self.d.get("msg", "none")) def __str__(self) -> str: return "PO: %s -> status: %s, msg: %s" % (self.predicate, self.status, self.msg) diff --git a/chb/jsoninterface/JSONStackpointerOffset.py b/chb/jsoninterface/JSONStackpointerOffset.py index 09d3a6f9..db307078 100644 --- a/chb/jsoninterface/JSONStackpointerOffset.py +++ b/chb/jsoninterface/JSONStackpointerOffset.py @@ -25,7 +25,7 @@ # SOFTWARE. # ------------------------------------------------------------------------------ -from typing import Any, Dict, List, Optional, Union +from typing import Any, Dict, List, Optional, Union, cast class JSONStackpointerOffset: @@ -39,7 +39,7 @@ def d(self) -> Dict[str, Any]: @property def txtrep(self) -> str: - return self.d.get("txtrep", "missing:stackpointeroffset:txtrep") + return cast(str, self.d.get("txtrep", "missing:stackpointeroffset:txtrep")) class JSONStackpointerOffsetUnknown(JSONStackpointerOffset): diff --git a/chb/mips/MIPSAccess.py b/chb/mips/MIPSAccess.py index 725835ef..a7e48e74 100644 --- a/chb/mips/MIPSAccess.py +++ b/chb/mips/MIPSAccess.py @@ -27,13 +27,12 @@ # SOFTWARE. # ------------------------------------------------------------------------------ -from chb.elfformat.ELFHeader import ELFHeader -from typing import Callable, Type, cast, Dict, List, Mapping, Optional, Sequence, Tuple +from typing import Callable, Type, cast, Dict, List, Mapping, Optional, Tuple from chb.api.CallTarget import CallTarget, IndirectTarget from chb.app.Callgraph import Callgraph, mk_tgt_callgraph_node, mk_app_callgraph_node -from chb.app.AppAccess import AppAccess, HeaderTy +from chb.app.AppAccess import AppAccess from chb.mips.MIPSDictionary import MIPSDictionary from chb.mips.MIPSFunction import MIPSFunction @@ -42,13 +41,13 @@ import chb.util.fileutil as UF -class MIPSAccess(AppAccess[HeaderTy]): +class MIPSAccess(AppAccess): def __init__( self, path: str, filename: str, - fileformat: Type[HeaderTy], + fileformat: Type[UF.FileHeader], deps: List[str] = []) -> None: AppAccess.__init__(self, path, filename, fileformat, deps) self._mipsd: Optional[MIPSDictionary] = None diff --git a/chb/mips/MIPSBlock.py b/chb/mips/MIPSBlock.py index d86ed7f1..3cba7039 100644 --- a/chb/mips/MIPSBlock.py +++ b/chb/mips/MIPSBlock.py @@ -78,8 +78,7 @@ def instructions(self) -> Dict[str, MIPSInstruction]: def iter(self, f: Callable[[str, MIPSInstruction], None]) -> None: for (ia, instr) in self.instructions.items(): - mipsinstr = cast(MIPSInstruction, instr) - f(ia, mipsinstr) + f(ia, instr) def get_sliced_instructions( self, registers: List[str]) -> List[MIPSInstruction]: diff --git a/chb/mips/MIPSFunction.py b/chb/mips/MIPSFunction.py index c19d998d..ca5682e1 100644 --- a/chb/mips/MIPSFunction.py +++ b/chb/mips/MIPSFunction.py @@ -133,13 +133,11 @@ def f(baddr: str, block: MIPSBlock) -> None: def iter_blocks(self, f: Callable[[str, MIPSBlock], None]) -> None: for (ba, block) in self.blocks.items(): - mipsblock = cast(MIPSBlock, block) - f(ba, mipsblock) + f(ba, block) def iter_instructions(self, f: Callable[[str, MIPSInstruction], None]) -> None: for (ia, instr) in self.instructions.items(): - mipsinstr = cast(MIPSInstruction, instr) - f(ia, mipsinstr) + f(ia, instr) @property def branchconditions(self) -> Mapping[str, MIPSInstruction]: @@ -149,7 +147,7 @@ def branchconditions(self) -> Mapping[str, MIPSInstruction]: if lastinstr.is_branch_instruction: ftconditions = lastinstr.ft_conditions if len(ftconditions) > 0: - result[b.baddr] = cast(MIPSInstruction, lastinstr) + result[b.baddr] = lastinstr return result def set_fnvar_dictionary(self, xnode: ET.Element) -> FnVarDictionary: diff --git a/chb/mips/MIPSInstruction.py b/chb/mips/MIPSInstruction.py index 54fbdb2b..4cb34741 100644 --- a/chb/mips/MIPSInstruction.py +++ b/chb/mips/MIPSInstruction.py @@ -64,6 +64,7 @@ from chb.mips.MIPSFunction import MIPSFunction from chb.mips.opcodes.MIPSLoadWord import MIPSLoadWord from chb.simulation.SimulationState import SimulationState + from chb.api.CallTarget import AppTarget class MIPSInstruction(Instruction): @@ -245,8 +246,11 @@ def return_value(self) -> Optional[XXpr]: def is_call_to_app_function(self, tgtaddr: str) -> bool: if self.is_call_instruction and self.opcode.has_call_target(self.xdata): opc = cast(MIPSJumpLinkRegister, self.opcode) - ctgtaddr = opc.call_target(self.xdata) - return ctgtaddr == tgtaddr + call_target = opc.call_target(self.xdata) + if not call_target.is_app_target: + return False + call_target = cast(AppTarget, call_target) + return str(call_target.address) == tgtaddr return False def call_facts(self) -> Mapping[str, Any]: @@ -257,7 +261,7 @@ def call_facts(self) -> Mapping[str, Any]: if callargs: result['args'] = callargs tgt = self.call_target - if tgt == 'call-target:u': + if tgt.name == 'call-target:u': result['t'] = '?' else: result['t'] = str(tgt) @@ -310,7 +314,7 @@ def has_branch_condition(self) -> bool: def branch_condition(self) -> XXpr: if self.has_branch_condition(): opc = cast(MIPSBranchOpcode, self.opcode) - return cast(XXpr, opc.branch_condition(self.xdata)) + return opc.branch_condition(self.xdata) else: raise UF.CHBError("Instruction does not have a branch condition") diff --git a/chb/mips/simulation/MIPSimStubs.py b/chb/mips/simulation/MIPSimStubs.py index 4a5f49fa..4df47eca 100644 --- a/chb/mips/simulation/MIPSimStubs.py +++ b/chb/mips/simulation/MIPSimStubs.py @@ -370,7 +370,6 @@ def get_string_at_address( iaddr, "String argument is not a valid address: " + str(saddr)) elif gaddr.is_address: - gaddr = cast(SSV.SimGlobalAddress, gaddr) return simstate.get_string_from_memaddr(iaddr, gaddr) raise SU.CHBSimError( @@ -1458,7 +1457,6 @@ def simulate(self, iaddr: str, simstate: "SimulationState") -> str: simstate.set_register(iaddr, "v0", fpresult) returnval = str(fpresult) elif a0.is_symbol: - a0 = cast(SSV.SimSymbol, a0) symresult = SSV.SimSymbol(a0.name + '_fildes') simstate.set_register(iaddr, "v0", symresult) returnval = str(symresult) @@ -3744,7 +3742,6 @@ def simulate(self, iaddr: str, simstate: "SimulationState") -> str: a0 = cast(SSV.SimBaseAddress, a0) if a0.has_buffer_size(): a0buffersize = cast(int, a0.buffersize) - a0 = cast(SSV.SimBaseAddress, a0) for i in range(0, a0buffersize): srcaddr = a0.add_offset(i) tgtaddr = address.add_offset(i) @@ -5200,7 +5197,7 @@ def simulate(self, iaddr: str, simstate: "SimulationState") -> str: "some argument to strstr is undefined") if a0.is_address: - addr: SV.SimValue = cast(SV.SimValue, a0) + addr: SV.SimValue = a0 elif a0.is_literal: addr = simstate.resolve_literal_address(iaddr, a0.literal_value) diff --git a/chb/peformat/PEHeader.py b/chb/peformat/PEHeader.py index 99c1127e..da45acc1 100644 --- a/chb/peformat/PEHeader.py +++ b/chb/peformat/PEHeader.py @@ -239,7 +239,7 @@ def subsystem(self) -> str: return self._get("subsystem") -class PEHeader: +class PEHeader(UF.FileHeader): """Main entry point to access the raw data in the executable.""" @staticmethod @@ -255,7 +255,7 @@ def __init__( pathname: str, filename: str, xnode: ET.Element, - deps: Sequence[str] = []) -> None: + deps: Sequence[str]) -> None: self._pathname = pathname self._filename = filename self.xnode = xnode @@ -462,6 +462,11 @@ def subsystem(self) -> str: def file_characteristics_text(self) -> List[str]: return self.coff_header.file_characteristics_text + @property + def is_big_endian(self) -> bool: + # According to the internet windows PE binaries are always little endian + return False + def as_dictionary(self) -> Dict[str, Any]: name = self.filename result: Dict[str, Any] = {} diff --git a/chb/pwr/PowerAccess.py b/chb/pwr/PowerAccess.py index 187db638..46c4acbf 100644 --- a/chb/pwr/PowerAccess.py +++ b/chb/pwr/PowerAccess.py @@ -29,7 +29,7 @@ from typing import Callable, Dict, List, Mapping, Optional, Type from chb.app.Callgraph import Callgraph, mk_tgt_callgraph_node, mk_app_callgraph_node -from chb.app.AppAccess import AppAccess, HeaderTy +from chb.app.AppAccess import AppAccess from chb.elfformat.ELFHeader import ELFHeader @@ -39,13 +39,13 @@ import chb.util.fileutil as UF -class PowerAccess(AppAccess[HeaderTy]): +class PowerAccess(AppAccess): def __init__( self, path: str, filename: str, - fileformat: Type[HeaderTy], + fileformat: Type[UF.FileHeader], deps: List[str] = []) -> None: AppAccess.__init__(self, path, filename, fileformat, deps) self._pwrd: Optional[PowerDictionary] = None diff --git a/chb/pwr/PowerFunction.py b/chb/pwr/PowerFunction.py index 60b0eae2..3adf325b 100644 --- a/chb/pwr/PowerFunction.py +++ b/chb/pwr/PowerFunction.py @@ -117,14 +117,12 @@ def f(baddr: str, block: PowerBlock) -> None: def iter_blocks(self, f: Callable[[str, PowerBlock], None]) -> None: for (ba, block) in self.blocks.items(): - pwrblock = cast(PowerBlock, block) - f(ba, pwrblock) + f(ba, block) def iter_instructions( self, f: Callable[[str, PowerInstruction], None]) -> None: for (ia, instr) in self.instructions.items(): - pwrinstr = cast(PowerInstruction, instr) - f(ia, pwrinstr) + f(ia, instr) @property def branchconditions(self) -> Mapping[str, PowerInstruction]: diff --git a/chb/simulation/SimSymbolicValue.py b/chb/simulation/SimSymbolicValue.py index 87e53819..9339bc5b 100644 --- a/chb/simulation/SimSymbolicValue.py +++ b/chb/simulation/SimSymbolicValue.py @@ -705,8 +705,7 @@ def add_offset(self, v: int) -> "SimBaseAddress": self.base, newoffset, buffersize=self.buffersize, tgttype=self.tgttype) def align(self, v: int) -> "SimBaseAddress": - newoffset = cast( - SV.SimDoubleWordValue, self.offset.bitwise_and(SV.mk_simvalue(v))) + newoffset = self.offset.bitwise_and(SV.mk_simvalue(v)) return SimBaseAddress( self.base, newoffset, buffersize=self.buffersize, tgttype=self.tgttype) @@ -1158,7 +1157,7 @@ def simfilename(self) -> str: @property def fp(self) -> IO[Any]: - return self._fp + return cast(IO[Any], self._fp) @property def is_file_pointer(self) -> bool: diff --git a/chb/simulation/SimulationState.py b/chb/simulation/SimulationState.py index 9330c5f3..45b434dc 100644 --- a/chb/simulation/SimulationState.py +++ b/chb/simulation/SimulationState.py @@ -148,6 +148,8 @@ def __init__( base: str, max_addr: str, loadaddr: Optional[str] = None) -> None: + if not app.elf: + raise NotImplementedError("Only have support for ELF binaries") self._name = name self._app = app self._base = base # base address in hex @@ -213,7 +215,7 @@ def imports(self) -> Dict[int, str]: @property def exports(self) -> Dict[str, int]: if len(self._exports) == 0: - symtab = cast("ELFSymbolTable", self.app.header.get_dynamic_symbol_table()) + symtab = cast("ELFSymbolTable", self.app.elf_header.get_dynamic_symbol_table()) for (sid, sym) in symtab.symbols.items(): if sym.is_exported: self._exports[sym.st_name] = int(sym.value, 16) @@ -295,7 +297,7 @@ def __init__( module: SimModule) -> None: self._module = module self._simstate = simstate - self._globalmem = ELFSimGlobalMemory(self, self.module.app.header) + self._globalmem = ELFSimGlobalMemory(self, self.module.app.elf_header) @property def module(self) -> SimModule: diff --git a/chb/tests/ELFThumbDisassemblyTestSet.py b/chb/tests/ELFThumbDisassemblyTestSet.py index bfba80b8..b3c901c6 100644 --- a/chb/tests/ELFThumbDisassemblyTestSet.py +++ b/chb/tests/ELFThumbDisassemblyTestSet.py @@ -51,8 +51,8 @@ class ELFThumbDisassemblyTestSet: def __init__(self) -> None: pass - def create_test(self, r) -> None: - files: Dict = {} + def create_test(self, r: List[str]) -> None: + files: Dict[str, str] = {} name = r[0] tca = ELFARMTestCreator(name, r[1], suite="DT") elfheader = tca.create_elf_header() @@ -63,19 +63,19 @@ def create_test(self, r) -> None: UF.save_test_files(name, "arm32", "elf", "DT", name, files, xinfo) - def remove_test(self, r) -> None: + def remove_test(self, r: List[str]) -> None: testfilename = UF.get_test_filename("arm32", "elf", "DT", r[0]) (path, xfile) = UC.get_path_filename(testfilename) os.remove(os.path.join(path, xfile)) shutil.rmtree(os.path.join(path, xfile + ".ch")) - def run_test(self, r) -> None: + def run_test(self, r: List[str]) -> None: testfilename = UF.get_test_filename("arm32", "elf", "DT", r[0]) result = self.analyze_test_case(testfilename) if result != 0: print("Error in analysis of " + testfilename) - def check_test(self, r) -> None: + def check_test(self, r: List[str]) -> None: testfilename = UF.get_test_filename("arm32", "elf", "DT", r[0]) try: (path, xfile) = UC.get_path_filename(testfilename) diff --git a/chb/userdata/UserHints.py b/chb/userdata/UserHints.py index f9641d99..cc4270b1 100644 --- a/chb/userdata/UserHints.py +++ b/chb/userdata/UserHints.py @@ -475,7 +475,7 @@ def has_start_address(self, addr: str) -> bool: def update(self, d: List[Dict[str, Union[List[str], str]]]) -> None: for db in d: - addr = cast(str, db["r"][0]) + addr = db["r"][0] if not self.has_start_address(addr): self._datablocks.append(db) self._addrs.append(addr) @@ -665,7 +665,7 @@ def typingrule(self) -> Dict[str, Any]: @property def action(self) -> str: - a = self.typingrule.get("action", "?") + a = cast(str, self.typingrule.get("action", "?")) if a in ["enable", "disable"]: return a else: @@ -673,7 +673,7 @@ def action(self) -> str: @property def locs(self) -> List[str]: - locs = self.typingrule.get("locs", []) + locs = cast(List[str], self.typingrule.get("locs", [])) if len(locs) == 0: raise UF.CHBError("No locations specified in typing rule") else: @@ -682,7 +682,7 @@ def locs(self) -> List[str]: @property def name(self) -> str: if "name" in self.typingrule: - return self.typingrule["name"] + return cast(str, self.typingrule["name"]) else: raise UF.CHBError("Name is missing in typing rule") @@ -711,15 +711,15 @@ def rdefspec(self) -> Dict[str, Any]: @property def uselocs(self) -> List[str]: - return self.rdefspec.get("uselocs", []) + return cast(List[str], self.rdefspec.get("uselocs", [])) @property def rdeflocs(self) -> List[str]: - return self.rdefspec.get("rdeflocs", []) + return cast(List[str], self.rdefspec.get("rdeflocs", [])) @property def var(self) -> str: - return self.rdefspec.get("var", "__no_var__") + return cast(str, self.rdefspec.get("var", "__no_var__")) def to_xml(self, node: ET.Element) -> None: xremrdef = ET.Element("remove-var-rdefs") @@ -749,7 +749,7 @@ def faddr(self) -> str: if not "faddr" in self.fnannotation: chklogger.logger.warning( "Function annotation without faddr, returning 0x0") - return self.fnannotation.get("faddr", "0x0") + return cast(str, self.fnannotation.get("faddr", "0x0")) @property def stack_variable_introductions(self) -> Dict[int, StackVarIntro]: @@ -785,7 +785,7 @@ def remove_reaching_definitions(self) -> List[RemoveReachingDefinitions]: @property def const_global_variables(self) -> List[str]: - return self.fnannotation.get("const-global-variables", []) + return cast(List[str], self.fnannotation.get("const-global-variables", [])) def has_register_variable_introduction(self, iaddr: str) -> bool: return iaddr in self.register_variable_introductions @@ -1451,7 +1451,6 @@ def to_xml(self, node: ET.Element) -> None: xgv = ET.Element("syma") xaddrs.append(xgv) xgv.set("a", gv) - gvname = cast(str, gvname) xgv.set("name", gvname) ptnode = ET.Element("type") xgv.append(ptnode) diff --git a/chb/util/fileutil.py b/chb/util/fileutil.py index c0d768dd..bf09b9fb 100644 --- a/chb/util/fileutil.py +++ b/chb/util/fileutil.py @@ -90,10 +90,10 @@ import json import os import subprocess -import shutil import xml.etree.ElementTree as ET +from abc import ABC, abstractmethod -from typing import Any, Dict, List, Mapping, Optional, Sequence, Tuple +from typing import Any, Dict, List, Mapping, Optional, Sequence, Tuple, cast from chb.util.Config import Config @@ -283,6 +283,55 @@ def __str__(self) -> str: + self.msg) +class FileHeader(ABC): + """Interface for binary header classes + + It provides the main entry point to access the raw data in the executable. + + The abstract class enables us to treat different types of binaries (ELF, PE) + the same way regardless of the underlying details. + """ + @staticmethod + @abstractmethod + def fmt_name() -> str: ... + + @staticmethod + @abstractmethod + def get_xnode(path: str, filename: str) -> ET.Element: ... + + + @abstractmethod + def __init__( + self, + pathname: str, + filename: str, + xnode: ET.Element, + deps: Sequence[str]) -> None: ... + + @property + @abstractmethod + def pathname(self) -> str: ... + + @property + @abstractmethod + def filename(self) -> str: ... + + @property + @abstractmethod + def image_base(self) -> str: ... + + @property + @abstractmethod + def max_address_space(self) -> str: ... + + @abstractmethod + def is_in_address_space(self, addr: int) -> bool: ... + + @property + @abstractmethod + def is_big_endian(self) -> bool: ... + + def get_path_filename(name: str) -> Tuple[str, str]: """Returns the path and filename of the target executable indicated by name.""" name = os.path.abspath(name) @@ -356,7 +405,7 @@ def get_locale_file() -> Dict[str, Any]: raise CHBFileNotFoundError(filename) try: with open(filename, "r") as fp: - return json.load(fp) + return cast(Dict[str, Any], json.load(fp)) except ValueError as e: raise CHBJSONParseError(filename, e) @@ -500,7 +549,7 @@ def get_chb_json(filename: str) -> Dict[str, Any]: if os.path.isfile(filename): try: with open(filename, "r") as fp: - return json.load(fp) + return cast(Dict[str, Any], json.load(fp)) except ValueError as e: raise CHBJSONParseError(filename, e) except Exception as e: @@ -955,7 +1004,7 @@ def get_cfg_replacement_texts(path: str, xfile: str) -> Dict[str, Any]: if os.path.isfile(filename): try: with open(filename, "r") as fp: - d = json.load(fp) + d = cast(Dict[str, Any], json.load(fp)) except ValueError as e: raise CHBJSONParseError(filename, e) return d @@ -1037,7 +1086,7 @@ def get_summaries_list() -> Dict[str, Any]: if os.path.isfile(summariesfile): try: with open(summariesfile, "r") as fp: - return json.load(fp) + return cast(Dict[str, Any], json.load(fp)) except ValueError as e: raise CHBJSONParseError(summariesfile, e) except Exception as e: @@ -1133,9 +1182,9 @@ def get_file_registered_options(md5: str) -> Dict[str, Any]: for f in config.commandline_options: filename = config.commandline_options[f] with open(filename, "r") as fp: - options = json.load(fp) + options = cast(Dict[str, Any], json.load(fp)) if md5 in options: - return options[md5] + return cast(Dict[str, Any], options[md5]) else: pass else: @@ -1155,11 +1204,11 @@ def get_file_registered_userdata(md5: str) -> Dict[str, Any]: for f in config.registered_userdata: filename = config.registered_userdata[f] with open(filename, "r") as fp: - userdatafile = json.load(fp) + userdatafile = cast(Dict[str, Any], json.load(fp)) if md5 in userdatafile["executables"]: - xuserdata = userdatafile["executables"][md5] + xuserdata = cast(Dict[str, Any], userdatafile["executables"][md5]) if "userdata" in xuserdata: - return xuserdata["userdata"] + return cast(Dict[str, Any], xuserdata["userdata"]) else: return {} else: @@ -1184,12 +1233,12 @@ def get_simsupport(kind: str, tag: str) -> Dict[str, Any]: if os.path.isfile(filename): try: with open(filename, "r") as fp: - simsupport = json.load(fp) + simsupport = cast(Dict[str, Any], json.load(fp)) except Exception as e: raise CHBError("Error loading " + filename + ": " + str(e)) if tag in simsupport["simsupport"]: - return simsupport["simsupport"][tag] + return cast(Dict[str, Any], simsupport["simsupport"][tag]) else: raise CHBError( "File " + filename + " does not provide data for " + tag) diff --git a/chb/util/loggingutil.py b/chb/util/loggingutil.py index af913da4..10350732 100644 --- a/chb/util/loggingutil.py +++ b/chb/util/loggingutil.py @@ -31,13 +31,19 @@ from contextlib import contextmanager import logging from enum import Enum -from typing import Optional, List +from typing import Optional, List, Iterator, TYPE_CHECKING, Any, MutableMapping + +# Work around mypy issue with LoggerAdapter: https://github.com/python/typeshed/issues/7855 +if TYPE_CHECKING: + _LoggerAdapter = logging.LoggerAdapter[logging.Logger] +else: + _LoggerAdapter = logging.LoggerAdapter _current_faddr: contextvars.ContextVar[Optional[str]] = contextvars.ContextVar( "chkx_faddr", default=None) @contextmanager -def function_context(faddr: str): +def function_context(faddr: str) -> Iterator[None]: token = _current_faddr.set(faddr) try: yield @@ -68,7 +74,7 @@ def options(cls) -> List[str]: class NativeJsonFormatter(logging.Formatter): - def format(self, record): + def format(self, record: logging.LogRecord) -> str: log_payload = { "ts": self.formatTime(record, self.datefmt), "lvl": record.levelname, @@ -133,8 +139,11 @@ class CHKLogID(str, Enum): USER_STRCTDEF_0002 = "USER-STRCTDEF-0002" # idem -class IDLogger(logging.LoggerAdapter): - def process(self, msg, kwargs): +class IDLogger(_LoggerAdapter): + def process( + self, + msg: Any, + kwargs: MutableMapping[str, Any]) -> tuple[Any, MutableMapping[str, Any]]: """ Interrupts the logging call and safely moves the dynamic 'log_id' from kwargs into the final 'extra' dictionary. @@ -145,23 +154,23 @@ def process(self, msg, kwargs): extra.setdefault('faddr', _current_faddr.get()) return msg, kwargs - def log_with_id(self, level, log_id, msg, *args, **kwargs): + def log_with_id(self, level: int, log_id: CHKLogID, msg: object, *args: Any, **kwargs: Any) -> None: kwargs['log_id'] = log_id.value self.log(level, msg, *args, **kwargs, stacklevel=4) - def debug_id(self, log_id: CHKLogID, msg, *args, **kwargs): + def debug_id(self, log_id: CHKLogID, msg: object, *args: Any, **kwargs: Any) -> None: self.log_with_id(logging.DEBUG, log_id, msg, *args, **kwargs) - def info_id(self, log_id: CHKLogID, msg, *args, **kwargs): + def info_id(self, log_id: CHKLogID, msg: object, *args: Any, **kwargs: Any) -> None: self.log_with_id(logging.INFO, log_id, msg, *args, **kwargs) - def warning_id(self, log_id: CHKLogID, msg, *args, **kwargs): + def warning_id(self, log_id: CHKLogID, msg: object, *args: Any, **kwargs: Any) -> None: self.log_with_id(logging.WARNING, log_id, msg, *args, **kwargs) - def error_id(self, log_id: CHKLogID, msg, *args, **kwargs): + def error_id(self, log_id: CHKLogID, msg: object, *args: Any, **kwargs: Any) -> None: self.log_with_id(logging.ERROR, log_id, msg, *args, **kwargs) - def critical_id(self, log_id: CHKLogID, msg, *args, **kwargs): + def critical_id(self, log_id: CHKLogID, msg: object, *args: Any, **kwargs: Any) -> None: self.log_with_id(logging.CRITICAL, log_id, msg, *args, **kwargs) diff --git a/chb/util/xmlutil.py b/chb/util/xmlutil.py index 0e51df0a..50499f65 100644 --- a/chb/util/xmlutil.py +++ b/chb/util/xmlutil.py @@ -85,7 +85,9 @@ def element_to_pretty(e: ET.Element, indent: int = 0) -> List[str]: def doc_to_pretty(t: ET.ElementTree) -> str: lines = ['\n'] - lines.extend(element_to_pretty(t.getroot())) + root = t.getroot() + assert root is not None + lines.extend(element_to_pretty(root)) return "".join(lines) diff --git a/chb/x86/X86Access.py b/chb/x86/X86Access.py index 009e36b0..579a3ece 100644 --- a/chb/x86/X86Access.py +++ b/chb/x86/X86Access.py @@ -27,11 +27,10 @@ # SOFTWARE. # ------------------------------------------------------------------------------ -from chb.elfformat.ELFHeader import ELFHeader -from typing import Dict, List, Mapping, Optional, Sequence, Type +from typing import Dict, List, Mapping, Optional, Type from chb.app.Callgraph import Callgraph -from chb.app.AppAccess import AppAccess, HeaderTy +from chb.app.AppAccess import AppAccess import chb.util.fileutil as UF @@ -40,13 +39,13 @@ from chb.x86.X86Instruction import X86Instruction -class X86Access(AppAccess[HeaderTy]): +class X86Access(AppAccess): def __init__( self, path: str, filename: str, - fileformat: Type[HeaderTy], + fileformat: Type[UF.FileHeader], deps: List[str] = []) -> None: AppAccess.__init__(self, path, filename, fileformat, deps) self._x86d: Optional[X86Dictionary] = None diff --git a/chb/x86/X86Function.py b/chb/x86/X86Function.py index 13ad5e08..566bf0a1 100644 --- a/chb/x86/X86Function.py +++ b/chb/x86/X86Function.py @@ -172,8 +172,7 @@ def cfg(self) -> X86Cfg: def iter_instructions(self, f: Callable[[str, X86Instruction], None]) -> None: for (ia, instr) in self.instructions.items(): - x86instr = cast(X86Instruction, instr) - f(ia, x86instr) + f(ia, instr) def arg_count(self) -> int: xvalues = self.vardictionary.constant_value_variables()