Skip to content

Commit 3949eb7

Browse files
authored
Merge pull request diffblue#534 from diffblue/smowton-on-behalf-of-marek/simple-taint-flow-check
SEC-620, SEC-650: Added simple early check for impossibility of taint flow to sink
2 parents 38e6e1f + e61e3d0 commit 3949eb7

File tree

12 files changed

+386
-103
lines changed

12 files changed

+386
-103
lines changed

driver/presentation.py

Lines changed: 52 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -182,55 +182,56 @@ def build_HTML_interface_to_slicing_tasks(root_dir,sub_dir,ofile):
182182
if not os.path.exists(full_sub_dir):
183183
os.makedirs(full_sub_dir)
184184

185-
build_HTML_interface_to_slicer_call_graph(
186-
os.path.abspath(os.path.join(full_sub_dir,"call_graph.html"))
185+
instrumented_goto_program_fname = os.path.join(full_sub_dir, "instrumented_goto_program.json")
186+
if os.path.isfile(instrumented_goto_program_fname):
187+
build_HTML_interface_to_slicer_call_graph(
188+
os.path.abspath(os.path.join(full_sub_dir,"call_graph.html"))
187189
)
188-
build_HTML_interface_to_slicer_inverted_call_graph(
189-
os.path.abspath(os.path.join(full_sub_dir,"inverted_call_graph.html"))
190+
build_HTML_interface_to_slicer_inverted_call_graph(
191+
os.path.abspath(os.path.join(full_sub_dir,"inverted_call_graph.html"))
190192
)
191-
build_HTML_interface_to_slicer_tokens_propagation_graph(
192-
os.path.abspath(os.path.join(full_sub_dir,"tokens_propagation_graph.html"))
193+
build_HTML_interface_to_slicer_tokens_propagation_graph(
194+
os.path.abspath(os.path.join(full_sub_dir,"tokens_propagation_graph.html"))
193195
)
194196

195-
instrumentation_props,instrumentation_props_fname = load_instrumentation_props_of_slicer(full_sub_dir)
196-
fname = os.path.splitext(instrumentation_props_fname)[0] + ".html"
197-
build_HTML_interface_to_slicer_instrumentation_props(instrumentation_props, fname)
197+
instrumentation_props,instrumentation_props_fname = load_instrumentation_props_of_slicer(full_sub_dir)
198+
fname = os.path.splitext(instrumentation_props_fname)[0] + ".html"
199+
build_HTML_interface_to_slicer_instrumentation_props(instrumentation_props, fname)
198200

199-
ofile.write("<table>\n"
200-
"<caption>Supporting and intermediate data structures.</caption>\n"
201-
" <tr>\n"
202-
" <th>Property</th>\n"
203-
" <th>Value</th>\n"
204-
" </tr>\n")
205-
ofile.write(" <tr>\n")
206-
ofile.write(" <td>Call graph</td>\n")
207-
ofile.write(" <td align=\"center\"><a href=\"./" + sub_dir + "/call_graph.html\">here</a></td>\n")
208-
ofile.write(" </tr>\n")
209-
ofile.write(" <tr>\n")
210-
ofile.write(" <td>Inverted call graph</td>\n")
211-
ofile.write(" <td align=\"center\"><a href=\"./" + sub_dir + "/inverted_call_graph.html\">here</a></td>\n")
212-
ofile.write(" </tr>\n")
213-
ofile.write(" <tr>\n")
214-
ofile.write(" <td>Tokens propagation graph</td>\n")
215-
ofile.write(" <td align=\"center\"><a href=\"./" + sub_dir + "/tokens_propagation_graph.html\">here</a></td>\n")
216-
ofile.write(" </tr>\n")
217-
ofile.write(" <tr>\n")
218-
ofile.write(" <td>Map from rules to their application sites (in JSON format)</td>\n")
219-
ofile.write(" <td align=\"center\"><a href=\"./" + sub_dir +
220-
"/map_from_functions_to_rule_application_sites.json\">here</a></td>\n")
221-
ofile.write(" </tr>\n")
222-
ofile.write(" <td>Instrumentation properties</td>\n")
223-
ofile.write(" <td align=\"center\"><a href=\"./" +
224-
os.path.join(sub_dir,os.path.splitext(os.path.basename(instrumentation_props_fname))[0] + ".html") +
225-
"\">here</a></td>\n")
226-
ofile.write(" </tr>\n")
227-
ofile.write("</table>\n")
201+
ofile.write("<table>\n"
202+
"<caption>Supporting and intermediate data structures.</caption>\n"
203+
" <tr>\n"
204+
" <th>Property</th>\n"
205+
" <th>Value</th>\n"
206+
" </tr>\n")
207+
ofile.write(" <tr>\n")
208+
ofile.write(" <td>Call graph</td>\n")
209+
ofile.write(" <td align=\"center\"><a href=\"./" + sub_dir + "/call_graph.html\">here</a></td>\n")
210+
ofile.write(" </tr>\n")
211+
ofile.write(" <tr>\n")
212+
ofile.write(" <td>Inverted call graph</td>\n")
213+
ofile.write(" <td align=\"center\"><a href=\"./" + sub_dir + "/inverted_call_graph.html\">here</a></td>\n")
214+
ofile.write(" </tr>\n")
215+
ofile.write(" <tr>\n")
216+
ofile.write(" <td>Tokens propagation graph</td>\n")
217+
ofile.write(" <td align=\"center\"><a href=\"./" + sub_dir + "/tokens_propagation_graph.html\">here</a></td>\n")
218+
ofile.write(" </tr>\n")
219+
ofile.write(" <tr>\n")
220+
ofile.write(" <td>Map from rules to their application sites (in JSON format)</td>\n")
221+
ofile.write(" <td align=\"center\"><a href=\"./" + sub_dir +
222+
"/map_from_functions_to_rule_application_sites.json\">here</a></td>\n")
223+
ofile.write(" </tr>\n")
224+
ofile.write(" <td>Instrumentation properties</td>\n")
225+
ofile.write(" <td align=\"center\"><a href=\"./" +
226+
os.path.join(sub_dir,os.path.splitext(os.path.basename(instrumentation_props_fname))[0] + ".html") +
227+
"\">here</a></td>\n")
228+
ofile.write(" </tr>\n")
229+
ofile.write("</table>\n")
228230

229-
ofile.write("<p></p>\n")
231+
ofile.write("<p></p>\n")
230232

231-
with open(os.path.join(full_sub_dir,"instrumented_goto_program.json")) as json_file:
232-
instrumented_goto_program = json.load(json_file)
233-
if instrumented_goto_program is not None:
233+
with open(instrumented_goto_program_fname) as json_file:
234+
instrumented_goto_program = json.load(json_file)
234235
ofile.write("<table>\n"
235236
"<caption>Instrumented GOTO program</caption>\n"
236237
" <tr>\n"
@@ -250,7 +251,9 @@ def build_HTML_interface_to_slicing_tasks(root_dir,sub_dir,ofile):
250251
ofile.write(" </tr>\n")
251252
ofile.write("</table>\n")
252253
return True
253-
return False
254+
else:
255+
ofile.write("<p>The program is safe. No tainted data may reach any sink.</p>\n")
256+
return False
254257

255258
def build_HTML_interface_to_the_slicer(root_dir,sub_dir,ofile):
256259
full_sub_dir=os.path.join(root_dir, sub_dir)
@@ -686,12 +689,9 @@ def build_HTML_interface_to_results_and_statistics(
686689
#######################################################################################################
687690

688691

689-
ofile.write("<h3>Phase 2: Application of program slicing</h3>\n")
690-
ofile.write("<p>It is performed by the tool 'goto-instrument'.</p>\n")
691-
692-
if not has_instrumented_program:
693-
ofile.write("<p>The instrumented GOTO binary was not generated from the previous stage.</p>\n")
694-
else:
692+
if has_instrumented_program:
693+
ofile.write("<h3>Phase 2: Application of program slicing</h3>\n")
694+
ofile.write("<p>It is performed by the tool 'goto-instrument'.</p>\n")
695695
build_HTML_interface_to_the_slicer(
696696
cmdline.results_dir,
697697
"program_slicing",
@@ -704,12 +704,9 @@ def build_HTML_interface_to_results_and_statistics(
704704
#######################################################################################################
705705

706706

707-
ofile.write("<h3>Phase 3: Search for error-traces in the sliced program</h3>\n")
708-
ofile.write("<p>It is currently performed only by the tool 'JBMC'.</p>\n")
709-
710-
if not has_instrumented_program:
711-
ofile.write("<p>There are no error traces, because there is no GOTO binary available from previous stage(s).</p>\n")
712-
else:
707+
if has_instrumented_program:
708+
ofile.write("<h3>Phase 3: Search for error-traces in the sliced program</h3>\n")
709+
ofile.write("<p>It is currently performed only by the tool 'JBMC'.</p>\n")
713710
build_HTML_interface_to_error_traces(
714711
cmdline.results_dir,
715712
"search_for_error_traces",

driver/run.py

Lines changed: 23 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -294,32 +294,29 @@ def run_scan(cmdline):
294294

295295
instrumented_program_json_path = os.path.join(cmdline.results_dir, "program_slicing", "instrumented_goto_program.json")
296296

297-
if not os.path.exists(instrumented_program_json_path):
298-
print("Instrumented program .json don't exist at " + instrumented_program_json_path + ".")
299-
return
300-
301-
with open(instrumented_program_json_path, "r") as f:
302-
instrumented_program = json.load(f)
303-
# Instrumented GOTO binaries are specified relative to the results directory
304-
# (the security-analyser working directory). Rewrite them with absolute paths:
305-
instrumented_program["goto_binary_file"] = os.path.abspath(os.path.join(cmdline.results_dir, instrumented_program["goto_binary_file"]))
306-
307-
print("Starting program slicing.")
308-
prof["program_slicing"] = analyser.run_program_slicing(
309-
instrumented_program,
310-
os.path.abspath(os.path.join(cmdline.results_dir,"program_slicing")),
311-
cmdline.timeout,
312-
cmdline.verbosity,
313-
cmdline.dump_html_instrumented_goto
314-
)
315-
316-
print("Starting the search for error traces.")
317-
prof["search_for_error_traces"] = analyser.run_search_for_error_traces(
318-
os.path.abspath(os.path.join(cmdline.results_dir,"program_slicing","sliced_goto_program.json")),
319-
os.path.abspath(os.path.join(cmdline.results_dir,"search_for_error_traces")),
320-
cmdline.timeout,
321-
cmdline.verbosity
322-
)
297+
if os.path.exists(instrumented_program_json_path):
298+
with open(instrumented_program_json_path, "r") as f:
299+
instrumented_program = json.load(f)
300+
# Instrumented GOTO binaries are specified relative to the results directory
301+
# (the security-analyser working directory). Rewrite them with absolute paths:
302+
instrumented_program["goto_binary_file"] = os.path.abspath(os.path.join(cmdline.results_dir, instrumented_program["goto_binary_file"]))
303+
304+
print("Starting program slicing.")
305+
prof["program_slicing"] = analyser.run_program_slicing(
306+
instrumented_program,
307+
os.path.abspath(os.path.join(cmdline.results_dir,"program_slicing")),
308+
cmdline.timeout,
309+
cmdline.verbosity,
310+
cmdline.dump_html_instrumented_goto
311+
)
312+
313+
print("Starting the search for error traces.")
314+
prof["search_for_error_traces"] = analyser.run_search_for_error_traces(
315+
os.path.abspath(os.path.join(cmdline.results_dir,"program_slicing","sliced_goto_program.json")),
316+
os.path.abspath(os.path.join(cmdline.results_dir,"search_for_error_traces")),
317+
cmdline.timeout,
318+
cmdline.verbosity
319+
)
323320

324321
print("Building performance plots.")
325322
prof_plots = {}

regression/end_to_end/driver.py

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@
1111

1212
class ErrorTraces:
1313

14-
def __init__(self, traces, cmdline):
14+
def __init__(self, traces, pretty_cmdline, cmdline):
1515
self.traces = traces
16+
self.pretty_cmdline = pretty_cmdline
1617
self.cmdline = cmdline
1718

1819
def count_traces(self):
@@ -64,12 +65,21 @@ def trace_goes_through(
6465
pass
6566
return False
6667

68+
def get_cmdline(self):
69+
return self.cmdline
70+
71+
def get_results_dir(self):
72+
return self.cmdline[self.cmdline.index("-R") + 1]
73+
74+
def get_temp_dir(self):
75+
return self.cmdline[self.cmdline.index("-T") + 1]
76+
6777
def __enter__(self):
6878
return self
6979

7080
def __exit__(self, exc_type, exc_value, traceback):
7181
if exc_value is not None:
72-
print("Failure may relate to command: ", self.cmdline, file=sys.stderr)
82+
print("Failure may relate to command: ", self.pretty_cmdline, file=sys.stderr)
7383

7484

7585
def pretty_print_commandline(cmdline):
@@ -95,12 +105,17 @@ def run_security_driver_script(
95105
cmdline = ["python3", pipeline_driver_path]
96106
cmdline.extend(extra_commandline)
97107

98-
executable_runner = ExecutableRunner(cmdline)
99-
(stdout, stderr, ret) = executable_runner.run()
100-
if ret != 0:
101-
raise Exception(
102-
"Failed running \"%s\":\nstdout:\n\n%s\nstderr\n\n%s" % \
103-
(pretty_print_commandline(cmdline), stdout, stderr))
108+
analyzer_home = utils.get_security_analyzer_home()
109+
if analyzer_home is None:
110+
raise Exception("Set SECURITY_SCANNER_HOME to a path containing the 'security-analyzer' binary")
111+
112+
with utils.working_dir(analyzer_home):
113+
executable_runner = ExecutableRunner(cmdline)
114+
(stdout, stderr, ret) = executable_runner.run()
115+
if ret != 0:
116+
raise Exception(
117+
"Failed running \"%s\":\nstdout:\n\n%s\nstderr\n\n%s" % \
118+
(pretty_print_commandline(cmdline), stdout, stderr))
104119

105120

106121
def run_security_analyser_pipeline(
@@ -109,14 +124,12 @@ def run_security_analyser_pipeline(
109124
base_path,
110125
entry_point,
111126
load_strategy=LoadStrategy.conventional_lazy_loading,
112-
extra_args=None):
127+
extra_args=None,
128+
keep_results=False):
113129

114130
if extra_args is None:
115131
extra_args = []
116132

117-
analyzer_home = utils.get_security_analyzer_home()
118-
if analyzer_home is None:
119-
raise Exception("Set SECURITY_SCANNER_HOME to a path containing the 'security-analyzer' binary")
120133
absolute_binary_path = \
121134
os.path.join(base_path, relative_binary_path)
122135
absolute_rules_path = \
@@ -147,18 +160,20 @@ def run_security_analyser_pipeline(
147160
# optimisation matches:
148161
cmdline.append("--verify-csvsa-sparse-domains")
149162

150-
with utils.working_dir(analyzer_home), \
151-
utils.temp_dir_deleter(results_dir), \
152-
utils.temp_dir_deleter(temporary_dir), \
153-
utils.temp_dir_deleter(common_dir):
163+
with utils.temp_dir_deleter(results_dir, keep_results), \
164+
utils.temp_dir_deleter(temporary_dir, keep_results), \
165+
utils.temp_dir_deleter(common_dir, keep_results):
154166

155167
run_security_driver_script(cmdline)
156168

157169
trace_list = \
158170
os.path.join(
159171
results_dir, entry_point, "search_for_error_traces", "error_traces.json")
160-
with open(trace_list, "r") as f:
161-
traces = json.load(f)
172+
if os.path.isfile(trace_list):
173+
with open(trace_list, "r") as f:
174+
traces = json.load(f)
175+
else:
176+
traces = []
162177

163178
# Replace all trace JSON file references with the trace itself:
164179
for i in range(len(traces)):
@@ -176,4 +191,4 @@ def run_security_analyser_pipeline(
176191
print("Test \"%s\" kept results (%s) and temporary directory (%s)" %
177192
(pretty_cmdline, results_dir, temporary_dir))
178193

179-
return ErrorTraces(traces, pretty_cmdline)
194+
return ErrorTraces(traces, pretty_cmdline, cmdline)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<project name="Main" basedir="." default="compile">
2+
3+
<property name="root.dir" value="./"/>
4+
<property name="src.dir" value="${root.dir}/src"/>
5+
<property name="classes.dir" value="${root.dir}/build"/>
6+
7+
<target name="compile">
8+
<antcall target="clean" />
9+
<mkdir dir="${classes.dir}"/>
10+
<javac srcdir="${src.dir}" destdir="${classes.dir}" includeantruntime="false" debug="on" />
11+
</target>
12+
13+
<target name="clean">
14+
<delete dir="${classes.dir}"/>
15+
</target>
16+
17+
</project>
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"rules":
3+
[
4+
{
5+
"comment": "Obtaining a tainted string.",
6+
"class": "Main",
7+
"method": "source:()Ljava/lang/String;",
8+
"result": {
9+
"location": "return_value",
10+
"taint": "Tainted string"
11+
}
12+
},
13+
{
14+
"comment": "Returning a sanitized string.",
15+
"class": "Main",
16+
"method": "sanitize:(Ljava/lang/String;)Ljava/lang/String;",
17+
"sanitizes": {
18+
"location": "returns",
19+
"taint": "Clean string"
20+
}
21+
},
22+
{
23+
"comment": "Writing a potentially tainted data into the sink",
24+
"class": "Main",
25+
"method": "sink:(Ljava/lang/String;)V",
26+
"sinkTarget": {
27+
"location": "arg0",
28+
"vulnerability": "Tainted string"
29+
}
30+
}
31+
]
32+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
public class Main {
2+
3+
public static String source() {
4+
return "Tainted string";
5+
}
6+
7+
public static void sink(String s) {
8+
}
9+
10+
public static String sanitize(String s) {
11+
return "sanitised string";
12+
}
13+
14+
// Test cases
15+
16+
public static void catch_impossible_taint_flow_01() {
17+
String s = "no taint";
18+
sink(s);
19+
}
20+
21+
public static void catch_impossible_taint_flow_02() {
22+
String s = source();
23+
s = sanitize(s);
24+
sink(s);
25+
}
26+
27+
public static void miss_impossible_taint_flow_01() {
28+
String s = source();
29+
int i = 0;
30+
if (i == 1)
31+
sink(s);
32+
}
33+
34+
public static void miss_impossible_taint_flow_02() {
35+
String s = source();
36+
int i = 20;
37+
if (i == 20)
38+
s = sanitize(s);
39+
sink(s);
40+
}
41+
}
42+

0 commit comments

Comments
 (0)