llvm-project
193 строки · 4.4 Кб
1#!/usr/bin/env python
2
3# To use:
4# 1) Update the 'decls' list below with your fuzzing configuration.
5# 2) Run with the clang binary as the command-line argument.
6
7from __future__ import absolute_import, division, print_function8import random9import subprocess10import sys11import os12
13clang = sys.argv[1]14none_opts = 0.315
16
17class Decl(object):18def __init__(self, text, depends=[], provides=[], conflicts=[]):19self.text = text20self.depends = depends21self.provides = provides22self.conflicts = conflicts23
24def valid(self, model):25for i in self.depends:26if i not in model.decls:27return False28for i in self.conflicts:29if i in model.decls:30return False31return True32
33def apply(self, model, name):34for i in self.provides:35model.decls[i] = True36model.source += self.text % {"name": name}37
38
39decls = [40Decl("struct X { int n; };\n", provides=["X"], conflicts=["X"]),41Decl('static_assert(X{.n=1}.n == 1, "");\n', depends=["X"]),42Decl("X %(name)s;\n", depends=["X"]),43]
44
45
46class FS(object):47def __init__(self):48self.fs = {}49self.prevfs = {}50
51def write(self, path, contents):52self.fs[path] = contents53
54def done(self):55for f, s in self.fs.items():56if self.prevfs.get(f) != s:57f = file(f, "w")58f.write(s)59f.close()60
61for f in self.prevfs:62if f not in self.fs:63os.remove(f)64
65self.prevfs, self.fs = self.fs, {}66
67
68fs = FS()69
70
71class CodeModel(object):72def __init__(self):73self.source = ""74self.modules = {}75self.decls = {}76self.i = 077
78def make_name(self):79self.i += 180return "n" + str(self.i)81
82def fails(self):83fs.write(84"module.modulemap",85"".join(86'module %s { header "%s.h" export * }\n' % (m, m)87for m in self.modules.keys()88),89)90
91for m, (s, _) in self.modules.items():92fs.write("%s.h" % m, s)93
94fs.write("main.cc", self.source)95fs.done()96
97return (98subprocess.call(99[clang, "-std=c++11", "-c", "-fmodules", "main.cc", "-o", "/dev/null"]100)101!= 0102)103
104
105def generate():106model = CodeModel()107m = []108
109try:110for d in mutations(model):111d(model)112m.append(d)113if not model.fails():114return115except KeyboardInterrupt:116print()117return True118
119sys.stdout.write("\nReducing:\n")120sys.stdout.flush()121
122try:123while True:124assert m, "got a failure with no steps; broken clang binary?"125i = random.choice(list(range(len(m))))126x = m[0:i] + m[i + 1 :]127m2 = CodeModel()128for d in x:129d(m2)130if m2.fails():131m = x132model = m2133else:134sys.stdout.write(".")135sys.stdout.flush()136except KeyboardInterrupt:137# FIXME: Clean out output directory first.138model.fails()139return model140
141
142def choose(options):143while True:144i = int(random.uniform(0, len(options) + none_opts))145if i >= len(options):146break147yield options[i]148
149
150def mutations(model):151options = [create_module, add_top_level_decl]152for opt in choose(options):153yield opt(model, options)154
155
156def create_module(model, options):157n = model.make_name()158
159def go(model):160model.modules[n] = (model.source, model.decls)161(model.source, model.decls) = ("", {})162
163options += [lambda model, options: add_import(model, options, n)]164return go165
166
167def add_top_level_decl(model, options):168n = model.make_name()169d = random.choice([decl for decl in decls if decl.valid(model)])170
171def go(model):172if not d.valid(model):173return174d.apply(model, n)175
176return go177
178
179def add_import(model, options, module_name):180def go(model):181if module_name in model.modules:182model.source += '#include "%s.h"\n' % module_name183model.decls.update(model.modules[module_name][1])184
185return go186
187
188sys.stdout.write("Finding bug: ")189while True:190if generate():191break192sys.stdout.write(".")193sys.stdout.flush()194