From 6d77ca2a8535e1ee122962d11797e7a48e9180ac Mon Sep 17 00:00:00 2001 From: Henny Sipma Date: Wed, 27 May 2026 16:02:07 -0700 Subject: [PATCH 1/3] API: update with new predicate variants --- chb/api/XXPredicate.py | 171 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 168 insertions(+), 3 deletions(-) diff --git a/chb/api/XXPredicate.py b/chb/api/XXPredicate.py index 77536b7f..37cdca3f 100644 --- a/chb/api/XXPredicate.py +++ b/chb/api/XXPredicate.py @@ -4,7 +4,7 @@ # ------------------------------------------------------------------------------ # The MIT License (MIT) # -# Copyright (c) 2023 Aarno Labs LLC +# Copyright (c) 2023-2026 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 @@ -165,14 +165,38 @@ def is_xp_starts_thread(self) -> bool: def is_xp_tainted(self) -> bool: return False + @property + def is_xp_trusted_string(self) -> bool: + return False + + @property + def is_xp_trusted_os_cmd_string(self) -> bool: + return False + + @property + def is_xp_trusted_os_cmd_fmt_string(self) -> bool: + return False + + @property + def is_xp_trusted_os_cmd_fmt_arg_string(self) -> bool: + return False + @property def is_xp_validmem(self) -> bool: return False + @property + def is_xp_writes_string_from_fmt_string(self) -> bool: + return False + @property def is_xp_disjunction(self) -> bool: return False + @property + def is_xp_conditional(self) -> bool: + return False + @apiregistry.register_tag("ab", XXPredicate) class XXPAllocationBase(XXPredicate): @@ -925,15 +949,59 @@ def __str__(self) -> str: return "tainted(" + str(self.term) + ")" +@apiregistry.register_tag("ts", XXPredicate) +class XXTrustedString(XXPredicate): + + def __init__( + self, ixd: "InterfaceDictionary", ixval: IndexedTableValue) -> None: + XXPredicate.__init__(self, ixd, ixval) + + @property + def is_xp_trusted_string(self) -> bool: + return True + + @property + def term(self) -> "BTerm": + return self.id.bterm(self.args[0]) + + def __str__(self) -> str: + return "trusted-string(" + str(self.term) + ")" + + +@apiregistry.register_tag("tc", XXPredicate) +class XXTrustedOsCmdString(XXPredicate): + """String is safe to pass as an argument to system or popen.""" + + def __init__( + self, ixd: "InterfaceDictionary", ixval: IndexedTableValue) -> None: + XXPredicate.__init__(self, ixd, ixval) + + @property + def is_xp_trusted_os_cmd_string(self) -> bool: + return True + + @property + def term(self) -> "BTerm": + return self.id.bterm(self.args[0]) + + def __str__(self) -> str: + return "trusted-os-cmd-string(" + str(self.term) + ")" + + @apiregistry.register_tag("tfs", XXPredicate) class XXPTrustedOsCmdFmtString(XXPredicate): + """The string constructed by the format string is safe to pass to system. + + If a length is given, the string must evaluate to a value that has string + length less than the given length. + """ def __init__( self, ixd: "InterfaceDictionary", ixval: IndexedTableValue) -> None: XXPredicate.__init__(self, ixd, ixval) @property - def is_xp_trusted_os_cmd_string(self) -> bool: + def is_xp_trusted_os_cmd_fmt_string(self) -> bool: return True @property @@ -951,7 +1019,104 @@ def optlen(self) -> Optional["BTerm"]: return self.id.bterm(self.args[1]) def __str__(self) -> str: - return "trusted-os-cmd-fmt-string(" + str(self.fmt) + ", " + self.fmtkind + ", " + str(self.optlen) + ")" + return ( + "trusted-os-cmd-fmt-string(" + + str(self.fmt) + + ", " + + self.fmtkind + + ", " + + str(self.optlen) + + ")") + + +@apiregistry.register_tag("tfa", XXPredicate) +class XXPTrustedOsCmdFmtArgString(XXPredicate): + """String is safe to pass as a format argument to a string passed to system. + + It is safe if the format specifier associated with this argument is + enclosed in quotes according to the quotes property. + + If a length is given the length of the argument string must be less than + or equal to the given length. + """ + + def __init__( + self, ixd: "InterfaceDictionary", ixval: IndexedTableValue) -> None: + XXPredicate.__init__(self, ixd, ixval) + + @property + def is_xp_trusted_os_cmd_fmt_arg_string(self) -> bool: + return True + + @property + def fmt(self) -> "BTerm": + return self.id.bterm(self.args[0]) + + @property + def quotes(self) -> str: + return self.tags[1] + + @property + def optlen(self) -> Optional["BTerm"]: + if self.args[1] == -1: + return None + return self.id.bterm(self.args[1]) + + def __str__(self) -> str: + return ( + "trusted-os-cmd-fmt-string(" + + str(self.fmt) + + ", " + + self.quotes + + ", " + + str(self.optlen) + + ")") + + +@apiregistry.register_tag("wfs", XXPredicate) +class XXPWritesStringFromFmtString(XXPredicate): + """Side effect that asserts that a string is constructed from the given format string. + + An optional length imposes a maximum length on the string being written. + """ + + def __init__( + self, ixd: "InterfaceDictionary", ixval: IndexedTableValue) -> None: + XXPredicate.__init__(self, ixd, ixval) + + @property + def is_xp_writes_string_from_fmt_string(self) -> bool: + return True + + @property + def destination(self) -> "BTerm": + return self.id.bterm(self.args[0]) + + @property + def fmt(self) -> "BTerm": + return self.id.bterm(self.args[1]) + + @property + def fmtkind(self) -> str: + return self.tags[1] + + @property + def optlen(self) -> Optional["BTerm"]: + if self.args[2] == -1: + return None + return self.id.bterm(self.args[2]) + + def __str__(self) -> str: + return ( + "writes-string-from-fmt-string(" + + str(self.destination) + + ", " + + str(self.fmt) + + ", " + + self.fmtkind + + ", " + + str(self.optlen) + + ")") @apiregistry.register_tag("v", XXPredicate) From 5af1fe2b21777ff46da553e22e9975dd8e96fe36 Mon Sep 17 00:00:00 2001 From: Henny Sipma Date: Wed, 27 May 2026 21:29:52 -0700 Subject: [PATCH 2/3] SUMMARIES: update precondition and summaries --- chb/models/FunctionPrecondition.py | 28 +++++++++++++++++++++++++++- chb/models/ModelsAccess.py | 2 +- chb/summaries/bchsummaries.jar | Bin 3330443 -> 3330483 bytes 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/chb/models/FunctionPrecondition.py b/chb/models/FunctionPrecondition.py index ec760ce4..140c4810 100644 --- a/chb/models/FunctionPrecondition.py +++ b/chb/models/FunctionPrecondition.py @@ -41,7 +41,7 @@ from chb.models.BTerm import BTerm, btermregistry from chb.models.ModelsType import ModelsType, mk_type - + import chb.util.fileutil as UF @@ -347,3 +347,29 @@ def __str__(self) -> str: + ", " + str(self.arg2) + ")") + + +@preconditionregistry.register_tag("trusted-os-cmd-string", FunctionPrecondition) +class PreTrustedOsCmdString(FunctionPrecondition): + + def __init__( + self, + fsem: "FunctionSemantics", + tag: str, + xnode: Optional[ET.Element] = None) -> None: + FunctionPrecondition.__init__(self, fsem, tag, xnode) + self._arg1: Optional[BTerm] = None + + @property + def arg1(self) -> BTerm: + if self._arg1 is None: + self._arg1 = ( + btermregistry.mk_instance(self.semantics, self.xterm(0), BTerm)) + return self._arg1 + + def __str__(self) -> str: + return ( + self.tag + + "(" + + str(self.arg1) + + ")") diff --git a/chb/models/ModelsAccess.py b/chb/models/ModelsAccess.py index a5a9dd5f..9d248b11 100644 --- a/chb/models/ModelsAccess.py +++ b/chb/models/ModelsAccess.py @@ -6,7 +6,7 @@ # # Copyright (c) 2016-2020 Kestrel Technology LLC # Copyright (c) 2020 Henny Sipma -# Copyright (c) 2021-2023 Aarno Labs LLC +# Copyright (c) 2021-2026 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 diff --git a/chb/summaries/bchsummaries.jar b/chb/summaries/bchsummaries.jar index 26ca2467066dc85fe760f56329fd4d996a3eb939..0bf72b5202072e948bc6828d89c3df83c7390b05 100644 GIT binary patch delta 15683 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 BKtKQh delta 15866 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 uav5y$`O;$$+UN7d_# Date: Wed, 27 May 2026 21:30:32 -0700 Subject: [PATCH 3/3] CMD: add option to include callees for fns_included --- chb/app/CHVersion.py | 2 +- chb/cmdline/AnalysisManager.py | 4 ++++ chb/cmdline/chkx | 4 ++++ chb/cmdline/commandutil.py | 2 ++ 4 files changed, 11 insertions(+), 1 deletion(-) diff --git a/chb/app/CHVersion.py b/chb/app/CHVersion.py index 8ee59334..23f57e87 100644 --- a/chb/app/CHVersion.py +++ b/chb/app/CHVersion.py @@ -1,3 +1,3 @@ chbversion: str = "0.3.0-20260527" -minimum_required_chb_version = "0.6.0_20260407" +minimum_required_chb_version = "0.6.0_20260527" diff --git a/chb/cmdline/AnalysisManager.py b/chb/cmdline/AnalysisManager.py index 6fc102f6..51708e10 100644 --- a/chb/cmdline/AnalysisManager.py +++ b/chb/cmdline/AnalysisManager.py @@ -75,6 +75,7 @@ def __init__( fns_no_lineq: List[str] = [], fns_exclude: List[str] = [], fns_include: List[str] = [], + fns_include_callees: bool = False, show_function_timing: bool = False, gc_compact: int = 0, lineq_instr_cutoff: int = 0, @@ -114,6 +115,7 @@ def __init__( self.fns_no_lineq = fns_no_lineq self.fns_exclude = fns_exclude self.fns_include = fns_include + self.fns_include_callees = fns_include_callees self.show_function_timing = show_function_timing self.gc_compact = gc_compact self.lineq_instr_cutoff = lineq_instr_cutoff @@ -483,6 +485,8 @@ def _analyze_until_stable( cmd.extend(["-fn_exclude", s]) for s in self.fns_include: cmd.extend(["-fn_include", s]) + if self.fns_include_callees: + cmd.append("-fn_include_callees") for s in self.specializations: cmd.extend(["-specialization", s]) if analysisrepeats > 1: diff --git a/chb/cmdline/chkx b/chb/cmdline/chkx index 4e352f29..4f6dcd21 100755 --- a/chb/cmdline/chkx +++ b/chb/cmdline/chkx @@ -476,6 +476,10 @@ def parse() -> argparse.Namespace: nargs="*", default=[], help="addresses of function to include in the analysis") + analyzecmd.add_argument( + "--fns_include_callees", + help="include callees of all fns_included", + action="store_true") analyzecmd.add_argument( "--analyze_all_named", help="include all functions named in userdata in the analysis", diff --git a/chb/cmdline/commandutil.py b/chb/cmdline/commandutil.py index 4cdf7761..d5082f29 100644 --- a/chb/cmdline/commandutil.py +++ b/chb/cmdline/commandutil.py @@ -416,6 +416,7 @@ def analyzecmd(args: argparse.Namespace) -> NoReturn: fns_no_lineq: List[str] = args.fns_no_lineq # function hex addresses fns_exclude: List[str] = args.fns_exclude # function hex addresses fns_include: List[str] = args.fns_include # function hex addresses + fns_include_callees: bool = args.fns_include_callees analyze_all_named: bool = args.analyze_all_named analyze_range_entry_points: List[str] = args.analyze_range_entry_points gc_compact: int = args.gc_compact @@ -553,6 +554,7 @@ def analyzecmd(args: argparse.Namespace) -> NoReturn: fns_no_lineq=fns_no_lineq, fns_exclude=fns_exclude, fns_include=fns_include, + fns_include_callees=fns_include_callees, gc_compact=gc_compact, show_function_timing=show_function_timing, lineq_instr_cutoff=lineq_instr_cutoff,