Skip to content
This repository was archived by the owner on Apr 30, 2021. It is now read-only.

Commit 895ee2f

Browse files
committed
Add help message describing the mutators available and filtering.
Mutators can now be listed as part of a help command, and may then be filtered by the user supplying a filter specification to disable or enable only certain mutators.
1 parent d685d2d commit 895ee2f

3 files changed

Lines changed: 47 additions & 10 deletions

File tree

pythonfuzz/corpus.py

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ class CorpusError(Exception):
351351

352352
class Corpus(object):
353353

354-
def __init__(self, dirs=None, max_input_size=4096):
354+
def __init__(self, dirs=None, max_input_size=4096, mutators_filter=None):
355355
self._inputs = []
356356
self._max_input_size = max_input_size
357357
self._dirs = dirs if dirs else []
@@ -370,15 +370,36 @@ def __init__(self, dirs=None, max_input_size=4096):
370370
self._seed_idx = 0
371371
self._save_corpus = dirs and os.path.isdir(dirs[0])
372372

373+
# Work out what we'll filter
374+
filters = mutators_filter.split(' ')
375+
negative_filters = [f[1:] for f in filters if f and f[0] == '!']
376+
required_filters = [f for f in filters if f and f[0] != '!']
377+
378+
def acceptable(cls):
379+
# No filters => everything's fine!
380+
if mutators_filter is None:
381+
return True
382+
383+
# First check that the required mutator types are set
384+
for f in required_filters:
385+
if f not in cls.types:
386+
return False
387+
# Now remove any that are not allowed
388+
for f in negative_filters:
389+
if f in cls.types:
390+
return False
391+
392+
return True
393+
373394
# Construct an object for each mutator we can use
374-
self._mutators = [cls(self) for cls in mutator_classes]
375-
if not self._mutators:
395+
self.mutators = [cls(self) for cls in mutator_classes if acceptable(cls)]
396+
if not self.mutators:
376397
raise CorpusError("No mutators are available")
377398

378399
def __repr__(self):
379400
return "<{}(corpus of {}, %i mutators)>".format(self.__class__.__name__,
380401
len(self._inputs),
381-
len(self._mutators))
402+
len(self.mutators))
382403

383404
def _add_file(self, path):
384405
with open(path, 'rb') as f:
@@ -439,8 +460,8 @@ def mutate(self, buf):
439460

440461
# Select a mutator from those we can apply
441462
while True:
442-
x = self._rand(len(self._mutators))
443-
mutator = self._mutators[x]
463+
x = self._rand(len(self.mutators))
464+
mutator = self.mutators[x]
444465

445466
newres = mutator.mutate(res)
446467
if newres is not None:

pythonfuzz/fuzzer.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,22 +88,32 @@ def __init__(self,
8888
regression=False,
8989
max_input_size=4096,
9090
close_fd_mask=0,
91-
runs=-1):
91+
runs=-1,
92+
mutators_filter=None):
9293
self._target = target
9394
self._dirs = [] if dirs is None else dirs
9495
self._exact_artifact_path = exact_artifact_path
9596
self._rss_limit_mb = rss_limit_mb
9697
self._timeout = timeout
9798
self._regression = regression
9899
self._close_fd_mask = close_fd_mask
99-
self._corpus = corpus.Corpus(self._dirs, max_input_size)
100+
self._corpus = corpus.Corpus(self._dirs, max_input_size, mutators_filter)
100101
self._total_executions = 0
101102
self._executions_in_sample = 0
102103
self._last_sample_time = time.time()
103104
self._total_coverage = 0
104105
self._p = None
105106
self.runs = runs
106107

108+
def help_mutators(self):
109+
print("Mutators currently available (and their types):")
110+
active_mutators = [mutator.__class__ for mutator in self._corpus.mutators]
111+
for mutator in corpus.mutator_classes:
112+
active = mutator in active_mutators
113+
indicator = '-' if not active else ' '
114+
print(" {}{:<60s} [{}]".format(indicator, mutator.name, ', '.join(sorted(mutator.types))))
115+
print("\nMutators prefixed by '-' are currently disabled.")
116+
107117
def log_stats(self, log_type):
108118
rss = (psutil.Process(self._p.pid).memory_info().rss + psutil.Process(os.getpid()).memory_info().rss) / 1024 / 1024
109119

pythonfuzz/main.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,19 @@ def __call__(self, *args, **kwargs):
1919
parser.add_argument('--max-input-size', type=int, default=4096, help='Max input size in bytes')
2020
parser.add_argument('--close-fd-mask', type=int, default=0, help='Indicate output streams to close at startup')
2121
parser.add_argument('--runs', type=int, default=-1, help='Number of individual test runs, -1 (the default) to run indefinitely.')
22+
parser.add_argument('--help-mutators', action='store_true', help='Display help on the mutators')
23+
parser.add_argument('--mutator-filter', type=str, default=None, help='Filter for mutator types to use; prefix with ! to disable')
2224
parser.add_argument('--timeout', type=int, default=30,
2325
help='If input takes longer then this timeout the process is treated as failure case')
2426
args = parser.parse_args()
2527
f = fuzzer.Fuzzer(self.function, args.dirs, args.exact_artifact_path,
2628
args.rss_limit_mb, args.timeout, args.regression, args.max_input_size,
27-
args.close_fd_mask, args.runs)
28-
f.start()
29+
args.close_fd_mask, args.runs, args.mutator_filter)
30+
31+
if args.help_mutators:
32+
f.help_mutators()
33+
else:
34+
f.start()
2935

3036

3137
if __name__ == '__main__':

0 commit comments

Comments
 (0)