Skip to content

Commit b4a3839

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 b4a3839

File tree

2 files changed

+129
-57
lines changed

2 files changed

+129
-57
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 & 57 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)
@@ -1181,50 +1183,55 @@ def include_target(target: 'CallTarget') -> bool:
11811183
return target.name in xtargets
11821184

11831185
for (faddr, blocks) in app.call_instructions().items():
1184-
fn = app.function(faddr)
1185-
11861186
for (baddr, instrs) in blocks.items():
11871187
for instr in instrs:
11881188
n_calls += 1
11891189
calltgt = instr.call_target
11901190
if calltgt.is_so_target and include_target(calltgt):
11911191
libcalls.add_library_callsite(faddr, baddr, instr)
11921192

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

11951195
patchcallsites = libcalls.patch_callsites()
11961196

1197+
content: Dict[str, Any] = {}
1198+
if xjson:
1199+
xinfodata = xinfo.to_json_result()
1200+
if xinfodata.is_ok:
1201+
content["identification"] = xinfodata.content
1202+
else:
1203+
write_json_result(xoutput, xinfodata, "patchcandidates")
1204+
1205+
patch_records = []
1206+
11971207
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))
1204-
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))
1208+
jresult = pc.to_json_result(app)
1209+
if not jresult.is_ok:
1210+
chklogger.logger.warning("Couldn't process patch callsite %s", pc)
12131211
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))
1212+
1213+
patch_records.append(jresult.content)
1214+
1215+
content["patch-records"] = patch_records
1216+
chklogger.logger.debug("Number of patch callsites: %s", len(content['patch-records']))
1217+
1218+
if xjson:
1219+
jcontent = JSONResult("patchcandidates", content, "ok")
1220+
write_json_result(xoutput, jcontent, "patchcandidates")
1221+
exit(0)
1222+
1223+
for patch_record in content["patch-records"]:
1224+
print(" " + patch_record['iaddr'] + " " + patch_record['annotation'])
1225+
print(" - faddr: %s" % patch_record['faddr'])
1226+
print(" - iaddr: %s" % patch_record['iaddr'])
1227+
print(" - target function: %s" % patch_record['target-function'])
1228+
print(" - stack offset: %s" % patch_record['stack-offset'])
1229+
print(" - length argument: %s" % patch_record['length-argument'])
1230+
print(" - buffersize: %s" % patch_record['buffersize'])
1231+
print(" - spare: %s" % patch_record['spare'])
12261232
print("")
12271233

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

12301237
exit(0)

0 commit comments

Comments
 (0)