diff --git a/chb/buffer/LibraryCallCallsites.py b/chb/buffer/LibraryCallCallsites.py index 28e4efa6..afb2dbdd 100644 --- a/chb/buffer/LibraryCallCallsites.py +++ b/chb/buffer/LibraryCallCallsites.py @@ -233,6 +233,31 @@ def lentype(self) -> str: else: return lenterm + def to_json_result(self, + dstoffset: int, + buffersize: int, + spare: Optional[str], + ) -> JSONResult: + if self.dstarg is None: + chklogger.logger.warning( + "No expression found for destination argument: %s", self) + return JSONResult("librarycallsideeffect", {}, "fail", + "No expression found for destination argument") + content: Dict[str, Any] = {} + content["annotation"] = self.instr.annotation + content["faddr"] = self.faddr + content["iaddr"] = self.instr.iaddr + content["buffersize"] = buffersize + content["target-function"] = self.summary.name + content["stack-offset"] = dstoffset + if self.lenarg is not None: + content["length-argument"] = str(self.lenarg) + else: + content["length-argument"] = None + content["spare"] = spare + + return JSONResult("librarycallsideeffect", content, "ok") + def __str__(self) -> str: return self.instr.iaddr + ": " + str(self.instr.annotation) diff --git a/chb/cmdline/chkx b/chb/cmdline/chkx index 7ee9209a..aaed6597 100755 --- a/chb/cmdline/chkx +++ b/chb/cmdline/chkx @@ -1184,8 +1184,9 @@ def parse() -> argparse.Namespace: report_patchcandidates.add_argument( "--targets", nargs="*", - default=[], - help="list of target library functions to include") + default=['all'], + help="list of target library functions to include. If not passed, all " + "library functions found will be included.") report_patchcandidates.add_argument( "--verbose", "-v", action="store_true", diff --git a/chb/cmdline/reportcmds.py b/chb/cmdline/reportcmds.py index 610df7cd..87bb1dce 100644 --- a/chb/cmdline/reportcmds.py +++ b/chb/cmdline/reportcmds.py @@ -66,7 +66,8 @@ from chb.util.loggingutil import chklogger if TYPE_CHECKING: - from chb.api.CallTarget import StubTarget + from chb.api.CallTarget import ( + StubTarget, CallTarget) from chb.api.FunctionStub import SOFunction from chb.app.AppAccess import AppAccess from chb.app.BasicBlock import BasicBlock @@ -990,6 +991,30 @@ def perc(v: float) -> str: exit(0) +def write_json_result(xfilename: Optional[str], + jresult: JSONResult, + schema_name: str, + ) -> None: + if jresult.is_ok: + okresult = JU.jsonok(schema_name, jresult.content) + if xfilename is not None: + filename = xfilename + ".json" + with open(filename, "w") as fp: + json.dump(okresult, fp, indent=2) + chklogger.logger.info("JSON output written to " + filename) + else: + print(json.dumps(okresult)) + else: + failresult = JU.jsonfail(jresult.reason) + if xfilename is not None: + filename = xfilename + ".json" + with open(filename, "w") as fp: + json.dump(failresult, fp, indent=2) + chklogger.logger.warning( + "JSON failure output written to " + filename) + else: + print(json.dumps(failresult)) + def report_buffer_bounds(args: argparse.Namespace) -> NoReturn: # arguments @@ -1023,49 +1048,27 @@ def report_buffer_bounds(args: argparse.Namespace) -> NoReturn: if calltgt.is_so_target: libcalls.add_library_callsite(faddr, baddr, instr) - def write_json_result( - xfilename: Optional[str], jresult: JSONResult) -> None: - if jresult.is_ok: - okresult = JU.jsonok("bufferboundsassessment", jresult.content) - if xfilename is not None: - filename = xfilename + ".json" - with open(filename, "w") as fp: - json.dump(okresult, fp, indent=2) - chklogger.logger.info("JSON output written to " + filename) - else: - print(json.dumps(okresult)) - else: - failresult = JU.jsonfail(jresult.reason) - if xfilename is not None: - filename = xfilename + ".json" - with open(filename, "w") as fp: - json.dump(failresult, fp, indent=2) - chklogger.logger.warning( - "JSON failure output written to " + filename) - else: - print(json.dumps(failresult)) - if xjson: content: Dict[str, Any] = {} xinfodata = xinfo.to_json_result() if xinfodata.is_ok: content["identification"] = xinfodata.content else: - write_json_result(xoutput, xinfodata) + write_json_result(xoutput, xinfodata, "bufferboundsassessment") analysisstats = app.result_metrics.to_json_result() if analysisstats.is_ok: content["analysis"] = analysisstats.content else: - write_json_result(xoutput, analysisstats) + write_json_result(xoutput, analysisstats, "bufferboundsassessment") libcallsresult = libcalls.to_json_result() if libcallsresult.is_ok: content["bounds"] = libcallsresult.content else: - write_json_result(xoutput, libcallsresult) + write_json_result(xoutput, libcallsresult, "bufferboundsassessment" ) write_json_result(xoutput, JSONResult( - "libcboundsanalysis", content, "ok")) + "libcboundsanalysis", content, "ok"), "bufferboundsassessment") if not xverbose: exit(0) @@ -1172,20 +1175,35 @@ def find_spare_instruction( n_calls: int = 0 libcalls = LibraryCallCallsites() - for (faddr, blocks) in app.call_instructions().items(): - fn = app.function(faddr) + include_all = xtargets == ['all'] + def include_target(target: 'CallTarget') -> bool: + if include_all: + return True + return target.name in xtargets + + for (faddr, blocks) in app.call_instructions().items(): for (baddr, instrs) in blocks.items(): for instr in instrs: n_calls += 1 calltgt = instr.call_target - if calltgt.is_so_target and calltgt.name in xtargets: + if calltgt.is_so_target and include_target(calltgt): libcalls.add_library_callsite(faddr, baddr, instr) - print("Number of calls: " + str(n_calls)) + chklogger.logger.debug("Number of calls: %s", n_calls) patchcallsites = libcalls.patch_callsites() + content: Dict[str, Any] = {} + if xjson: + xinfodata = xinfo.to_json_result() + if xinfodata.is_ok: + content["identification"] = xinfodata.content + else: + write_json_result(xoutput, xinfodata, "patchcandidates") + + patch_records = [] + for pc in sorted(patchcallsites, key=lambda pc:pc.faddr): instr = pc.instr dstarg = pc.dstarg @@ -1207,16 +1225,33 @@ def find_spare_instruction( basicblock = fn.block(pc.baddr) spare = find_spare_instruction(basicblock, instr.iaddr) - print(" " + pc.instr.iaddr + " " + pc.instr.annotation) - print(" - faddr: " + pc.faddr) - print(" - iaddr: " + pc.instr.iaddr) - print(" - target function: " + str(pc.summary.name)) - print(" - stack offset: " + str(dstoffset)) - print(" - length argument: " + str(pc.lenarg)) - print(" - buffersize: " + str(buffersize)) - print(" - spare: " + str(spare)) + jresult = pc.to_json_result(dstoffset, buffersize, spare) + if not jresult.is_ok: + chklogger.logger.warning("Couldn't process patch callsite %s", pc) + continue + + patch_records.append(jresult.content) + + content["patch-records"] = patch_records + chklogger.logger.debug("Number of patch callsites: %s", len(content['patch-records'])) + + if xjson: + jcontent = JSONResult("patchcandidates", content, "ok") + write_json_result(xoutput, jcontent, "patchcandidates") + exit(0) + + for patch_record in content["patch-records"]: + print(" " + patch_record['iaddr'] + " " + patch_record['annotation']) + print(" - faddr: %s" % patch_record['faddr']) + print(" - iaddr: %s" % patch_record['iaddr']) + print(" - target function: %s" % patch_record['target-function']) + print(" - stack offset: %s" % patch_record['stack-offset']) + print(" - length argument: %s" % patch_record['length-argument']) + print(" - buffersize: %s" % patch_record['buffersize']) + print(" - spare: %s" % patch_record['spare']) print("") - print("Number of patch callsites: " + str(len(patchcallsites))) + print("Generated %d patch records from %d library calls" % + (len(content['patch-records']), n_calls)) exit(0)