Skip to content

Commit e615ae7

Browse files
authored
Merge pull request diffblue#574 from diffblue/export_descriptions_of_applied_rules
SEC-658: Propagation of descriptions of rule applications to allow pretty error-traces browsing.
2 parents 771e0b0 + 02ca5c5 commit e615ae7

6 files changed

+146
-19
lines changed

driver/analyser.py

+33
Original file line numberDiff line numberDiff line change
@@ -224,13 +224,46 @@ def run_search_for_error_traces(
224224
if was_trace_saved:
225225
trace_id += 1
226226

227+
if len(traces_list) > 0:
228+
loc_info_file = os.path.abspath(os.path.join(results_dir, "..", "program_slicing", "map_from_functions_to_rule_application_sites.json"))
229+
loc_descriptions = generate_location_descriptions(loc_info_file)
230+
with open(os.path.join(results_dir, "location_descriptions.json"), "w") as descriptions_file:
231+
json.dump(loc_descriptions, descriptions_file, sort_keys=True, indent=4)
232+
for error_trace in traces_list:
233+
if error_trace["file"] in loc_descriptions:
234+
file_description = loc_descriptions[error_trace["file"]]
235+
if error_trace["line"] in file_description:
236+
file_line_description = file_description[error_trace["line"]]
237+
# When using CSVSA some functions get suffixed with "$spec_".
238+
# So, instead of equality check we use "startswith" check.
239+
if file_line_description["function"].startswith(error_trace["function"]):
240+
error_trace["message"] = file_line_description["message"]
241+
else:
242+
error_trace["message"] = ""
243+
227244
with open(os.path.join(results_dir, "error_traces.json"), "w") as traces_file:
228245
traces_file.write(json.dumps(traces_list, sort_keys=True, indent=4))
229246

230247
prof["duration"] = time.time() - prof_start_time
231248
return prof
232249

233250

251+
def generate_location_descriptions(loc_info_file):
252+
result = {}
253+
with open(loc_info_file, "r") as ifile:
254+
loc_info = json.load(ifile)
255+
for rule_id, per_fn_infos in loc_info.items():
256+
for fnname, iid_and_description in per_fn_infos.items():
257+
for iid, description in iid_and_description.items():
258+
if description["file"] not in result:
259+
result[description["file"]] = {}
260+
file_result = result[description["file"]]
261+
if description["line"] not in file_result:
262+
file_result[description["line"]] = {}
263+
file_line_result = file_result[description["line"]]
264+
file_line_result["function"] = fnname
265+
file_line_result["message"] = " ".join(description["messages"])
266+
return result
234267

235268

236269
if __name__ == "__main__":

driver/presentation.py

+17-2
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,14 @@ def build_HTML_interface_to_slicer_instrumentation_props(props,fname):
145145
ofile.write(" <td>" + str(loc["instruction_id"]) + "</td>\n")
146146
ofile.write(" </tr>\n")
147147
ofile.write(" <tr>\n")
148+
ofile.write(" <td>Source file</td>\n")
149+
ofile.write(" <td>" + escape_text_to_HTML(loc["file"]) + "</td>\n")
150+
ofile.write(" </tr>\n")
151+
ofile.write(" <tr>\n")
152+
ofile.write(" <td>Source line</td>\n")
153+
ofile.write(" <td>" + str(loc["line"]) + "</td>\n")
154+
ofile.write(" </tr>\n")
155+
ofile.write(" <tr>\n")
148156
ofile.write(" <td>Rule ID</td>\n")
149157
ofile.write(" <td>" + loc["rule_id"] + "</td>\n")
150158
ofile.write(" </tr>\n")
@@ -170,6 +178,10 @@ def build_HTML_interface_to_slicer_instrumentation_props(props,fname):
170178
ofile.write(" <td>Is sink?</td>\n")
171179
ofile.write(" <td>" + str(state_index in props["sinks"]) + "</td>\n")
172180
ofile.write(" </tr>\n")
181+
ofile.write(" <tr>\n")
182+
ofile.write(" <td>Description</td>\n")
183+
ofile.write(" <td>" + escape_text_to_HTML(" ".join(loc["messages"])) + "</td>\n")
184+
ofile.write(" </tr>\n")
173185
ofile.write("</table>\n")
174186
ofile.write("<p></p>\n")
175187
state_index += 1
@@ -380,14 +392,15 @@ def make_reduction_string(orig_value, reduced_value, add_percentage=True):
380392
ofile.write("</table>\n")
381393

382394

383-
def build_HTML_interface_to_group_of_error_traces(traces,tool_name,tgt_file,tgt_line,ofile):
395+
def build_HTML_interface_to_group_of_error_traces(traces, tool_name, tgt_file, tgt_line, message, ofile):
384396
ofile.write(get_html_prefix("Error traces " + tool_name))
385397

386398
ofile.write("<h1>Error traces found by " + tool_name + "</h1>\n")
387399
ofile.write("<p>Target location:</p>\n")
388400
ofile.write("<ul>\n")
389-
ofile.write(" <li><b>File</b>: " + tgt_file + "</li>\n")
401+
ofile.write(" <li><b>File</b>: " + escape_text_to_HTML(tgt_file) + "</li>\n")
390402
ofile.write(" <li><b>Line</b>: " + str(tgt_line) + "</li>\n")
403+
ofile.write(" <li><b>Message</b>: " + escape_text_to_HTML(message) + "</li>\n")
391404
ofile.write("</ul>\n")
392405
ofile.write("<table>\n"
393406
"<caption>Error traces leading to the traget location.</caption>\n"
@@ -441,6 +454,7 @@ def build_HTML_interface_to_error_traces(root_dir,sub_dir,ofile):
441454
"jbmc",
442455
traces_group["file"],
443456
traces_group["line"],
457+
traces_group["message"],
444458
group_ofile)
445459
else:
446460
ofile.write(" <td>none</td>\n")
@@ -453,6 +467,7 @@ def build_HTML_interface_to_error_traces(root_dir,sub_dir,ofile):
453467
"symex",
454468
traces_group["file"],
455469
traces_group["line"],
470+
traces_group["message"],
456471
group_ofile)
457472
else:
458473
ofile.write(" <td>none</td>\n")

src/taint-instrumenter/instrumentation_props.cpp

+18-7
Original file line numberDiff line numberDiff line change
@@ -159,12 +159,12 @@ taint_instrumentation_propst::taint_instrumentation_propst(
159159
assumption,
160160
turn_on,
161161
turn_off);
162-
for(const auto &fn_vec : rid_and_map.second)
162+
for(const auto &fn_map : rid_and_map.second)
163163
{
164-
for(const auto &iit : fn_vec.second)
164+
for(const auto &iit_and_descriptions : fn_map.second)
165165
{
166166
INVARIANT(
167-
iit->type == FUNCTION_CALL,
167+
iit_and_descriptions.first->type == FUNCTION_CALL,
168168
"An application rule must be associated with a function call "
169169
"instruction");
170170
if(
@@ -185,18 +185,19 @@ taint_instrumentation_propst::taint_instrumentation_propst(
185185
}
186186

187187
const symbolt *function_symbol =
188-
program.get_symbol_table().lookup(fn_vec.first);
188+
program.get_symbol_table().lookup(fn_map.first);
189189
location_props.push_back(
190190
{rid_and_map.first,
191-
fn_vec.first,
191+
fn_map.first,
192192
id2string(function_symbol->pretty_name),
193-
iit,
193+
iit_and_descriptions.first,
194+
iit_and_descriptions.second,
194195
assumption,
195196
turn_on,
196197
turn_off});
197198
}
198199
if(use_data_flow_insensitive_instrumentation)
199-
suppressed.insert(fn_vec.first);
200+
suppressed.insert(fn_map.first);
200201
}
201202
}
202203

@@ -383,6 +384,16 @@ void dump_as_json(const taint_instrumentation_propst &props, json_objectt &out)
383384
out_loc_props["function_id"] = json_stringt(loc.get_function_id());
384385
out_loc_props["instruction_id"] =
385386
json_numbert(msgstream() << loc.get_instruction_id()->location_number);
387+
out_loc_props["file"] =
388+
json_stringt(loc.get_description().get_source_file());
389+
out_loc_props["line"] =
390+
json_numbert(loc.get_description().get_source_line());
391+
json_arrayt out_messages;
392+
{
393+
for(const auto &msg : loc.get_description().get_messages())
394+
out_messages.push_back(json_stringt(msg));
395+
}
396+
out_loc_props["messages"] = out_messages;
386397
json_arrayt out_assumption;
387398
{
388399
for(const auto &assumption : loc.get_assumption())

src/taint-instrumenter/instrumentation_props.h

+8
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,15 @@ class taint_instrumentation_propst
6767
const taint_function_idt &_function_id,
6868
const taint_pretty_function_idt &_pretty_function_name,
6969
const taint_instruction_idt &_instruction_id,
70+
const description_of_rule_applicationt &description,
7071
const std::vector<argidx_and_tokennamet> &_assumption,
7172
const std::vector<argidx_and_tokennamet> &_turn_on,
7273
const std::vector<argidx_and_tokennamet> &_turn_off)
7374
: rule_id(_rule_id),
7475
function_id(_function_id),
7576
pretty_function_name(_pretty_function_name),
7677
instruction_id(_instruction_id),
78+
description(description),
7779
assumption(_assumption),
7880
turn_on(_turn_on),
7981
turn_off(_turn_off)
@@ -109,11 +111,17 @@ class taint_instrumentation_propst
109111
return turn_off;
110112
}
111113

114+
const description_of_rule_applicationt &get_description() const
115+
{
116+
return description;
117+
}
118+
112119
private:
113120
taint_rule_idt rule_id;
114121
taint_function_idt function_id;
115122
taint_pretty_function_idt pretty_function_name;
116123
taint_instruction_idt instruction_id;
124+
description_of_rule_applicationt description;
117125
std::vector<argidx_and_tokennamet> assumption;
118126
std::vector<argidx_and_tokennamet> turn_on;
119127
std::vector<argidx_and_tokennamet> turn_off;

src/taint-instrumenter/search_for_rule_applications.cpp

+30-7
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,16 @@ void taint_search_for_rule_applications(
4343
target->type == FUNCTION_CALL,
4444
"A transition property must be associated with a function call "
4545
"instruction");
46-
result[props.get_rule_id()][as_string(summary.first)].push_back(
47-
target);
46+
{
47+
auto &location_descriptions =
48+
result[props.get_rule_id()][as_string(summary.first)];
49+
if(location_descriptions.count(target) == 0UL)
50+
location_descriptions.insert(
51+
{target, {target, props.get_description()}});
52+
else
53+
location_descriptions.at(target).add_message(
54+
props.get_description());
55+
}
4856
break;
4957
default:
5058
break;
@@ -64,12 +72,27 @@ void to_json(
6472
for(const auto &rid_map : map_from_functions_to_rule_application_sites)
6573
{
6674
json_objectt rid;
67-
for(const auto &fn_vec : rid_map.second)
75+
for(const auto &fn_map : rid_map.second)
6876
{
69-
json_arrayt locs;
70-
for(const auto &iit : fn_vec.second)
71-
locs.push_back(json_numbert(std::to_string(iit->location_number)));
72-
rid[fn_vec.first] = locs;
77+
json_objectt locs;
78+
for(const auto &iit_and_description : fn_map.second)
79+
{
80+
json_objectt loc;
81+
{
82+
loc["file"] =
83+
json_stringt(iit_and_description.second.get_source_file());
84+
loc["line"] =
85+
json_numbert(iit_and_description.second.get_source_line());
86+
json_arrayt out_messages;
87+
{
88+
for(const auto &msg : iit_and_description.second.get_messages())
89+
out_messages.push_back(json_stringt(msg));
90+
}
91+
loc["messages"] = out_messages;
92+
}
93+
locs[std::to_string(iit_and_description.first->location_number)] = loc;
94+
}
95+
rid[fn_map.first] = locs;
7396
}
7497
result[rid_map.first] = rid;
7598
}

src/taint-instrumenter/search_for_rule_applications.h

+40-3
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,49 @@
1515
#include <util/json.h>
1616
#include <vector>
1717

18+
class description_of_rule_applicationt
19+
{
20+
public:
21+
22+
description_of_rule_applicationt(
23+
const goto_programt::instructionst::const_iterator iit,
24+
const std::string &message)
25+
: source_file(as_string(iit->source_location.get_file())),
26+
source_line(as_string(iit->source_location.get_line())),
27+
messages({message})
28+
{}
29+
30+
const std::string &get_source_file() const
31+
{
32+
return source_file;
33+
}
34+
35+
const std::string &get_source_line() const
36+
{
37+
return source_line;
38+
}
39+
40+
const std::set<std::string> &get_messages() const
41+
{
42+
return messages;
43+
}
44+
45+
void add_message(const std::string &new_message)
46+
{
47+
messages.insert(new_message);
48+
}
49+
50+
private:
51+
std::string source_file;
52+
std::string source_line;
53+
std::set<std::string> messages;
54+
};
55+
1856
typedef std::map<
1957
taint_rule_idt,
2058
std::map<std::string, // Name of a function
21-
std::vector<goto_programt::instructionst::const_iterator>
22-
// Locations inside the function
23-
>>
59+
std::map<goto_programt::instructionst::const_iterator,
60+
description_of_rule_applicationt>>>
2461
rule_application_sitest;
2562

2663
/// The function computes a map from transition rules to locations in GOTO

0 commit comments

Comments
 (0)