diff --git a/chb/api/FormatStringType.py b/chb/api/FormatStringType.py new file mode 100644 index 00000000..46c244cc --- /dev/null +++ b/chb/api/FormatStringType.py @@ -0,0 +1,122 @@ +# ------------------------------------------------------------------------------ +# CodeHawk Binary Analyzer +# Author: Henny Sipma +# ------------------------------------------------------------------------------ +# The MIT License (MIT) +# +# Copyright (c) 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 +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# ------------------------------------------------------------------------------ + + +from typing import List, TYPE_CHECKING + +from chb.api.InterfaceDictionaryRecord import ( + InterfaceDictionaryRecord, apiregistry) + +import chb.util.fileutil as UF + +from chb.util.IndexedTable import IndexedTableValue + +if TYPE_CHECKING: + from chb.api.InterfaceDictionary import InterfaceDictionary + + +class FormatStringType(InterfaceDictionaryRecord): + + def __init__( + self, ixd: "InterfaceDictionary", ixval: IndexedTableValue) -> None: + InterfaceDictionaryRecord.__init__(self, ixd, ixval) + + @property + def is_input_formatstring(self) -> bool: + return False + + @property + def is_output_formatstring(self) -> bool: + return False + + @property + def is_restricted_output_formatstring(self) -> bool: + return False + + +@apiregistry.register_tag("s", FormatStringType) +class ScanFormatStringType(FormatStringType): + + def __init__( + self, ixd: "InterfaceDictionary", ixval: IndexedTableValue) -> None: + FormatStringType.__init__(self, ixd, ixval) + + @property + def is_input_formatstring(self) -> bool: + return True + + def __str__(self) -> str: + return "scanformat" + + +@apiregistry.register_tag("p", FormatStringType) +class PrintFormatStringType(FormatStringType): + + def __init__( + self, ixd: "InterfaceDictionary", ixval: IndexedTableValue) -> None: + FormatStringType.__init__(self, ixd, ixval) + + @property + def is_output_formatstring(self) -> bool: + return True + + def __str__(self) -> str: + return "printformat" + + +@apiregistry.register_tag("rp", FormatStringType) +class RestrictedPrintFormatStringType(FormatStringType): + + def __init__( + self, ixd: "InterfaceDictionary", ixval: IndexedTableValue) -> None: + FormatStringType.__init__(self, ixd, ixval) + + @property + def is_restricted_output_formatstring(self) -> bool: + return True + + @property + def specifiers(self) -> List[str]: + return self.tags[1:] + + def __str__(self) -> str: + return "restricted-printformat(" + ",".join(self.specifiers) + ")" + + +@apiregistry.register_tag("n", FormatStringType) +class NoFormatStringType(FormatStringType): + + def __init__( + self, ixd: "InterfaceDictionary", ixval: IndexedTableValue) -> None: + FormatStringType.__init__(self, ixd, ixval) + + @property + def is_no_formatstring(self) -> bool: + return True + + def __str__(self) -> str: + return "printformat" diff --git a/chb/api/InterfaceDictionary.py b/chb/api/InterfaceDictionary.py index 30600a40..f5d1ea8a 100644 --- a/chb/api/InterfaceDictionary.py +++ b/chb/api/InterfaceDictionary.py @@ -33,6 +33,7 @@ from typing import List, TYPE_CHECKING from chb.api.BTerm import BTerm +from chb.api.FormatStringType import FormatStringType from chb.api.FtsParameter import FtsParameter from chb.api.FtsParameterLocation import FtsParameterLocation from chb.api.AppFunctionInterface import AppFunctionInterface @@ -63,6 +64,7 @@ def __init__( self._app = app self.function_stub_table = IT.IndexedTable("function-stub-table") self.call_target_table = IT.IndexedTable("call-target-table") + self.formatstring_type_table = IT.IndexedTable("formatstring-type-table") self.parameter_location_table = IT.IndexedTable("parameter-location-table") self.parameter_location_list_table = ( IT.IndexedTable("parameter-location-list-table")) @@ -76,6 +78,7 @@ def __init__( self.xxpredicate_table = IT.IndexedTable("xxpredicate-table") self.bterm_table = IT.IndexedTable("bterm-table") self.tables: List[IT.IndexedTable] = [ + self.formatstring_type_table, self.parameter_location_table, self.parameter_location_list_table, self.function_stub_table, @@ -106,6 +109,10 @@ def bcdictionary(self) -> "BCDictionary": # -------------- Retrieve items from dictionary tables --------------------- + def formatstring_type(self, ix: int) -> FormatStringType: + return apiregistry.mk_instance( + self, self.formatstring_type_table.retrieve(ix), FormatStringType) + def parameter_location(self, ix: int) -> FtsParameterLocation: return apiregistry.mk_instance( self, self.parameter_location_table.retrieve(ix), FtsParameterLocation) diff --git a/chb/app/CHVersion.py b/chb/app/CHVersion.py index f37f2cab..67272060 100644 --- a/chb/app/CHVersion.py +++ b/chb/app/CHVersion.py @@ -1,3 +1,3 @@ -chbversion: str = "0.3.0-20260611" +chbversion: str = "0.3.0-20260614" -minimum_required_chb_version = "0.6.0_20260611" +minimum_required_chb_version = "0.6.0_20260614" diff --git a/chb/app/XPOPredicate.py b/chb/app/XPOPredicate.py index 58866ff5..89039d01 100644 --- a/chb/app/XPOPredicate.py +++ b/chb/app/XPOPredicate.py @@ -726,6 +726,38 @@ def __str__(self) -> str: return "output-format-string(" + str(self.pointer) + ")" +@xporegistry.register_tag("rofs", XPOPredicate) +class XPORestrictedOutputFormatString(XPOPredicate): + """Pointer points to a format string for output (printf) + + args[0]: index of pointer in xprdictionary + args[1:]: indices of specifier strings in bcdictionary + """ + + def __init__( + self, xpod: "FnXPODictionary", ixval: IndexedTableValue) -> None: + XPOPredicate.__init__(self, xpod, ixval) + + @property + def is_xpo_restricted_output_format_string(self) -> bool: + return True + + @property + def specifiers(self) -> List[str]: + return [self.bd.string(index) for index in self.args[1:]] + + @property + def pointer(self) -> "XXpr": + return self.xd.xpr(self.args[0]) + + def __str__(self) -> str: + return ("restricted-output-format-string(" + + str(self.pointer) + + ", " + + ", ".join(self.specifiers) + + ")") + + @xporegistry.register_tag("pos", XPOPredicate) class XPOPositive(XPOPredicate): """Expression is positive.