5
5
"""Run fuzz test targets.
6
6
"""
7
7
8
+ from concurrent .futures import ThreadPoolExecutor , as_completed
8
9
import argparse
9
10
import configparser
11
+ import logging
10
12
import os
11
- import sys
12
13
import subprocess
13
- import logging
14
+ import sys
14
15
15
16
16
17
def main ():
@@ -35,6 +36,12 @@ def main():
35
36
'--exclude' ,
36
37
help = "A comma-separated list of targets to exclude" ,
37
38
)
39
+ parser .add_argument (
40
+ '--par' ,
41
+ type = int ,
42
+ default = 4 ,
43
+ help = 'How many targets to merge or execute in parallel.' ,
44
+ )
38
45
parser .add_argument (
39
46
'seed_dir' ,
40
47
help = 'The seed corpus to run on (must contain subfolders for each fuzz target).' ,
@@ -124,25 +131,29 @@ def main():
124
131
logging .error ("subprocess timed out: Currently only libFuzzer is supported" )
125
132
sys .exit (1 )
126
133
127
- if args .m_dir :
128
- merge_inputs (
134
+ with ThreadPoolExecutor (max_workers = args .par ) as fuzz_pool :
135
+ if args .m_dir :
136
+ merge_inputs (
137
+ fuzz_pool = fuzz_pool ,
138
+ corpus = args .seed_dir ,
139
+ test_list = test_list_selection ,
140
+ build_dir = config ["environment" ]["BUILDDIR" ],
141
+ merge_dir = args .m_dir ,
142
+ )
143
+ return
144
+
145
+ run_once (
146
+ fuzz_pool = fuzz_pool ,
129
147
corpus = args .seed_dir ,
130
148
test_list = test_list_selection ,
131
149
build_dir = config ["environment" ]["BUILDDIR" ],
132
- merge_dir = args .m_dir ,
150
+ use_valgrind = args .valgrind ,
133
151
)
134
- return
135
-
136
- run_once (
137
- corpus = args .seed_dir ,
138
- test_list = test_list_selection ,
139
- build_dir = config ["environment" ]["BUILDDIR" ],
140
- use_valgrind = args .valgrind ,
141
- )
142
152
143
153
144
- def merge_inputs (* , corpus , test_list , build_dir , merge_dir ):
154
+ def merge_inputs (* , fuzz_pool , corpus , test_list , build_dir , merge_dir ):
145
155
logging .info ("Merge the inputs in the passed dir into the seed_dir. Passed dir {}" .format (merge_dir ))
156
+ jobs = []
146
157
for t in test_list :
147
158
args = [
148
159
os .path .join (build_dir , 'src' , 'test' , 'fuzz' , t ),
@@ -153,12 +164,20 @@ def merge_inputs(*, corpus, test_list, build_dir, merge_dir):
153
164
]
154
165
os .makedirs (os .path .join (corpus , t ), exist_ok = True )
155
166
os .makedirs (os .path .join (merge_dir , t ), exist_ok = True )
156
- logging .debug ('Run {} with args {}' .format (t , args ))
157
- output = subprocess .run (args , check = True , stderr = subprocess .PIPE , universal_newlines = True ).stderr
158
- logging .debug ('Output: {}' .format (output ))
159
167
168
+ def job (t , args ):
169
+ output = 'Run {} with args {}\n ' .format (t , " " .join (args ))
170
+ output += subprocess .run (args , check = True , stderr = subprocess .PIPE , universal_newlines = True ).stderr
171
+ logging .debug (output )
172
+
173
+ jobs .append (fuzz_pool .submit (job , t , args ))
174
+
175
+ for future in as_completed (jobs ):
176
+ future .result ()
160
177
161
- def run_once (* , corpus , test_list , build_dir , use_valgrind ):
178
+
179
+ def run_once (* , fuzz_pool , corpus , test_list , build_dir , use_valgrind ):
180
+ jobs = []
162
181
for t in test_list :
163
182
corpus_path = os .path .join (corpus , t )
164
183
os .makedirs (corpus_path , exist_ok = True )
@@ -169,18 +188,26 @@ def run_once(*, corpus, test_list, build_dir, use_valgrind):
169
188
]
170
189
if use_valgrind :
171
190
args = ['valgrind' , '--quiet' , '--error-exitcode=1' ] + args
172
- logging .debug ('Run {} with args {}' .format (t , args ))
173
- result = subprocess .run (args , stderr = subprocess .PIPE , universal_newlines = True )
174
- output = result .stderr
175
- logging .debug ('Output: {}' .format (output ))
191
+
192
+ def job (t , args ):
193
+ output = 'Run {} with args {}' .format (t , args )
194
+ result = subprocess .run (args , stderr = subprocess .PIPE , universal_newlines = True )
195
+ output += result .stderr
196
+ return output , result
197
+
198
+ jobs .append (fuzz_pool .submit (job , t , args ))
199
+
200
+ for future in as_completed (jobs ):
201
+ output , result = future .result ()
202
+ logging .debug (output )
176
203
try :
177
204
result .check_returncode ()
178
205
except subprocess .CalledProcessError as e :
179
206
if e .stdout :
180
207
logging .info (e .stdout )
181
208
if e .stderr :
182
209
logging .info (e .stderr )
183
- logging .info ("Target \" {}\" failed with exit code {}: {} " .format (t , e . returncode , " " .join (args )))
210
+ logging .info ("Target \" {}\" failed with exit code {}" .format (" " .join (result . args ), e . returncode ))
184
211
sys .exit (1 )
185
212
186
213
0 commit comments