Skip to content

Commit ca959b2

Browse files
committed
REP: add json output to chkx report patchcandidates
Moved all the json functionality into the LibrarySideEffect class so that the json and txt output could share the same path.
1 parent 3afbcaa commit ca959b2

File tree

2 files changed

+129
-71
lines changed

2 files changed

+129
-71
lines changed

chb/buffer/LibraryCallCallsites.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,13 @@
4040
from chb.api.CallTarget import StubTarget
4141
from chb.api.FunctionStub import SOFunction
4242
from chb.app.Instruction import Instruction
43+
from chb.app.AppAccess import AppAccess
44+
from chb.app.Function import Function
4345
from chb.models.BTerm import BTerm, BTermArithmetic
4446
from chb.models.FunctionSummary import FunctionSummary
4547
from chb.models.FunctionPrecondition import (
4648
FunctionPrecondition, PreDerefWrite)
49+
from chb.mips.MIPSInstruction import MIPSInstruction
4750

4851

4952
class LibraryCallSideeffect:
@@ -233,6 +236,68 @@ def lentype(self) -> str:
233236
else:
234237
return lenterm
235238

239+
@property
240+
def dstoffset(self) -> Optional[int]:
241+
"""Returns the stack offset for our destination buffer"""
242+
if self.dstarg is None:
243+
return None
244+
return self.dstarg.stack_address_offset()
245+
246+
def find_spare_instruction(self,
247+
app: 'AppAccess',
248+
fn: 'Function',
249+
) -> Optional[str]:
250+
if app.is_mips:
251+
block = fn.block(self.baddr)
252+
found = None
253+
for (addr, instr) in block.instructions.items():
254+
instr = cast("MIPSInstruction", instr)
255+
if instr.iaddr > self.instr.iaddr:
256+
break
257+
if instr.is_load_instruction:
258+
if str(instr.operands[0]) == "t9":
259+
found = instr.iaddr
260+
return found
261+
return None
262+
263+
def to_json_result(self, app: 'AppAccess') -> JSONResult:
264+
if self.dstarg is None:
265+
chklogger.logger.warning(
266+
"No expression found for destination argument: %s", self)
267+
return JSONResult("librarycallsideeffect", {}, "fail",
268+
"No expression found for destination argument")
269+
dstoffset = self.dstoffset
270+
if dstoffset is None:
271+
chklogger.logger.warning(
272+
"No value found for destination stack offset : %s", self)
273+
return JSONResult("librarycallsideeffect", {}, "fail",
274+
"No value found for destination stack offset")
275+
276+
fn = app.function(self.faddr)
277+
stackframe = fn.stackframe
278+
stackbuffer = stackframe.get_stack_buffer(dstoffset)
279+
if stackbuffer is None:
280+
chklogger.logger.warning(
281+
"No stackbuffer found for %s at offset %s", self.instr, dstoffset)
282+
return JSONResult("librarycallsideeffect", {}, "fail",
283+
"No stackbuffer found for %s at offset %s" %
284+
(self.instr, dstoffset))
285+
286+
buffersize = stackbuffer.size
287+
spare = self.find_spare_instruction(app, fn)
288+
289+
content: Dict[str, Any] = {}
290+
content["annotation"] = self.instr.annotation
291+
content["faddr"] = self.faddr
292+
content["iaddr"] = self.instr.iaddr
293+
content["buffersize"] = buffersize
294+
content["target-function"] = self.summary.name
295+
content["stack-offset"] = dstoffset
296+
content["length-argument"] = str(self.lenarg)
297+
content["spare"] = spare
298+
299+
return JSONResult("librarycallsideeffect", content, "ok")
300+
236301
def __str__(self) -> str:
237302
return self.instr.iaddr + ": " + str(self.instr.annotation)
238303

chb/cmdline/reportcmds.py

Lines changed: 64 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -991,6 +991,30 @@ def perc(v: float) -> str:
991991
exit(0)
992992

993993

994+
def write_json_result(xfilename: Optional[str],
995+
jresult: JSONResult,
996+
schema_name: str,
997+
) -> None:
998+
if jresult.is_ok:
999+
okresult = JU.jsonok(schema_name, jresult.content)
1000+
if xfilename is not None:
1001+
filename = xfilename + ".json"
1002+
with open(filename, "w") as fp:
1003+
json.dump(okresult, fp, indent=2)
1004+
chklogger.logger.info("JSON output written to " + filename)
1005+
else:
1006+
print(json.dumps(okresult))
1007+
else:
1008+
failresult = JU.jsonfail(jresult.reason)
1009+
if xfilename is not None:
1010+
filename = xfilename + ".json"
1011+
with open(filename, "w") as fp:
1012+
json.dump(failresult, fp, indent=2)
1013+
chklogger.logger.warning(
1014+
"JSON failure output written to " + filename)
1015+
else:
1016+
print(json.dumps(failresult))
1017+
9941018
def report_buffer_bounds(args: argparse.Namespace) -> NoReturn:
9951019

9961020
# arguments
@@ -1024,49 +1048,27 @@ def report_buffer_bounds(args: argparse.Namespace) -> NoReturn:
10241048
if calltgt.is_so_target:
10251049
libcalls.add_library_callsite(faddr, baddr, instr)
10261050

1027-
def write_json_result(
1028-
xfilename: Optional[str], jresult: JSONResult) -> None:
1029-
if jresult.is_ok:
1030-
okresult = JU.jsonok("bufferboundsassessment", jresult.content)
1031-
if xfilename is not None:
1032-
filename = xfilename + ".json"
1033-
with open(filename, "w") as fp:
1034-
json.dump(okresult, fp, indent=2)
1035-
chklogger.logger.info("JSON output written to " + filename)
1036-
else:
1037-
print(json.dumps(okresult))
1038-
else:
1039-
failresult = JU.jsonfail(jresult.reason)
1040-
if xfilename is not None:
1041-
filename = xfilename + ".json"
1042-
with open(filename, "w") as fp:
1043-
json.dump(failresult, fp, indent=2)
1044-
chklogger.logger.warning(
1045-
"JSON failure output written to " + filename)
1046-
else:
1047-
print(json.dumps(failresult))
1048-
10491051
if xjson:
10501052
content: Dict[str, Any] = {}
10511053
xinfodata = xinfo.to_json_result()
10521054
if xinfodata.is_ok:
10531055
content["identification"] = xinfodata.content
10541056
else:
1055-
write_json_result(xoutput, xinfodata)
1057+
write_json_result(xoutput, xinfodata, "bufferboundsassessment")
10561058
analysisstats = app.result_metrics.to_json_result()
10571059
if analysisstats.is_ok:
10581060
content["analysis"] = analysisstats.content
10591061
else:
1060-
write_json_result(xoutput, analysisstats)
1062+
write_json_result(xoutput, analysisstats, "bufferboundsassessment")
10611063

10621064
libcallsresult = libcalls.to_json_result()
10631065
if libcallsresult.is_ok:
10641066
content["bounds"] = libcallsresult.content
10651067
else:
1066-
write_json_result(xoutput, libcallsresult)
1068+
write_json_result(xoutput, libcallsresult,"bufferboundsassessment" )
10671069

10681070
write_json_result(xoutput, JSONResult(
1069-
"libcboundsanalysis", content, "ok"))
1071+
"libcboundsanalysis", content, "ok"), "bufferboundsassessment")
10701072

10711073
if not xverbose:
10721074
exit(0)
@@ -1154,20 +1156,6 @@ def report_patch_candidates(args: argparse.Namespace) -> NoReturn:
11541156
xinfo = XI.XInfo()
11551157
xinfo.load(path, xfile)
11561158

1157-
def find_spare_instruction(
1158-
block: "BasicBlock", iaddr: str) -> Optional[str]:
1159-
if xinfo.is_mips:
1160-
found = None
1161-
for (addr, instr) in block.instructions.items():
1162-
instr = cast("MIPSInstruction", instr)
1163-
if instr.iaddr > iaddr:
1164-
break
1165-
if instr.is_load_instruction:
1166-
if str(instr.operands[0]) == "t9":
1167-
found = instr.iaddr
1168-
return found
1169-
return None
1170-
11711159
app = UC.get_app(path, xfile, xinfo)
11721160

11731161
n_calls: int = 0
@@ -1181,50 +1169,55 @@ def include_target(target: 'CallTarget') -> bool:
11811169
return target.name in xtargets
11821170

11831171
for (faddr, blocks) in app.call_instructions().items():
1184-
fn = app.function(faddr)
1185-
11861172
for (baddr, instrs) in blocks.items():
11871173
for instr in instrs:
11881174
n_calls += 1
11891175
calltgt = instr.call_target
11901176
if calltgt.is_so_target and include_target(calltgt):
11911177
libcalls.add_library_callsite(faddr, baddr, instr)
11921178

1193-
print("Number of calls: " + str(n_calls))
1179+
chklogger.logger.debug("Number of calls: %s", n_calls)
11941180

11951181
patchcallsites = libcalls.patch_callsites()
11961182

1183+
content: Dict[str, Any] = {}
1184+
if xjson:
1185+
xinfodata = xinfo.to_json_result()
1186+
if xinfodata.is_ok:
1187+
content["identification"] = xinfodata.content
1188+
else:
1189+
write_json_result(xoutput, xinfodata, "patchcandidates")
1190+
1191+
patch_records = []
1192+
11971193
for pc in sorted(patchcallsites, key=lambda pc:pc.faddr):
1198-
instr = pc.instr
1199-
dstarg = pc.dstarg
1200-
if dstarg is None:
1201-
chklogger.logger.warning(
1202-
"No expression found for destination argument: %s",
1203-
str(instr))
1194+
jresult = pc.to_json_result(app)
1195+
if not jresult.is_ok:
1196+
chklogger.logger.warning("Couldn't process patch callsite %s", pc)
12041197
continue
1205-
dstoffset = dstarg.stack_address_offset()
1206-
fn = app.function(pc.faddr)
1207-
stackframe = fn.stackframe
1208-
stackbuffer = stackframe.get_stack_buffer(dstoffset)
1209-
if stackbuffer is None:
1210-
chklogger.logger.warning(
1211-
"No stackbuffer found for %s at offset %s",
1212-
str(instr), str(dstoffset))
1213-
continue
1214-
buffersize = stackbuffer.size
1215-
basicblock = fn.block(pc.baddr)
1216-
spare = find_spare_instruction(basicblock, instr.iaddr)
1217-
1218-
print(" " + pc.instr.iaddr + " " + pc.instr.annotation)
1219-
print(" - faddr: " + pc.faddr)
1220-
print(" - iaddr: " + pc.instr.iaddr)
1221-
print(" - target function: " + str(pc.summary.name))
1222-
print(" - stack offset: " + str(dstoffset))
1223-
print(" - length argument: " + str(pc.lenarg))
1224-
print(" - buffersize: " + str(buffersize))
1225-
print(" - spare: " + str(spare))
1198+
1199+
patch_records.append(jresult.content)
1200+
1201+
content["patch-records"] = patch_records
1202+
chklogger.logger.debug("Number of patch callsites: %s", len(content['patch-records']))
1203+
1204+
if xjson:
1205+
jcontent = JSONResult("patchcandidates", content, "ok")
1206+
write_json_result(xoutput, jcontent, "patchcandidates")
1207+
exit(0)
1208+
1209+
for patch_record in content["patch-records"]:
1210+
print(" " + patch_record['iaddr'] + " " + patch_record['annotation'])
1211+
print(" - faddr: %s" % patch_record['faddr'])
1212+
print(" - iaddr: %s" % patch_record['iaddr'])
1213+
print(" - target function: %s" % patch_record['target-function'])
1214+
print(" - stack offset: %s" % patch_record['stack-offset'])
1215+
print(" - length argument: %s" % patch_record['length-argument'])
1216+
print(" - buffersize: %s" % patch_record['buffersize'])
1217+
print(" - spare: %s" % patch_record['spare'])
12261218
print("")
12271219

1228-
print("Number of patch callsites: " + str(len(patchcallsites)))
1220+
print("Generated %d patch records from %d library calls" %
1221+
(len(content['patch-records']), n_calls))
12291222

12301223
exit(0)

0 commit comments

Comments
 (0)