1*67e74705SXin Li#! /usr/bin/env python 2*67e74705SXin Li 3*67e74705SXin Li# To use: 4*67e74705SXin Li# 1) Update the 'decls' list below with your fuzzing configuration. 5*67e74705SXin Li# 2) Run with the clang binary as the command-line argument. 6*67e74705SXin Li 7*67e74705SXin Liimport random 8*67e74705SXin Liimport subprocess 9*67e74705SXin Liimport sys 10*67e74705SXin Liimport os 11*67e74705SXin Li 12*67e74705SXin Liclang = sys.argv[1] 13*67e74705SXin Linone_opts = 0.3 14*67e74705SXin Li 15*67e74705SXin Liclass Decl: 16*67e74705SXin Li def __init__(self, text, depends=[], provides=[], conflicts=[]): 17*67e74705SXin Li self.text = text 18*67e74705SXin Li self.depends = depends 19*67e74705SXin Li self.provides = provides 20*67e74705SXin Li self.conflicts = conflicts 21*67e74705SXin Li 22*67e74705SXin Li def valid(self, model): 23*67e74705SXin Li for i in self.depends: 24*67e74705SXin Li if i not in model.decls: 25*67e74705SXin Li return False 26*67e74705SXin Li for i in self.conflicts: 27*67e74705SXin Li if i in model.decls: 28*67e74705SXin Li return False 29*67e74705SXin Li return True 30*67e74705SXin Li 31*67e74705SXin Li def apply(self, model, name): 32*67e74705SXin Li for i in self.provides: 33*67e74705SXin Li model.decls[i] = True 34*67e74705SXin Li model.source += self.text % {'name': name} 35*67e74705SXin Li 36*67e74705SXin Lidecls = [ 37*67e74705SXin Li Decl('struct X { int n; };\n', provides=['X'], conflicts=['X']), 38*67e74705SXin Li Decl('static_assert(X{.n=1}.n == 1, "");\n', depends=['X']), 39*67e74705SXin Li Decl('X %(name)s;\n', depends=['X']), 40*67e74705SXin Li] 41*67e74705SXin Li 42*67e74705SXin Liclass FS: 43*67e74705SXin Li def __init__(self): 44*67e74705SXin Li self.fs = {} 45*67e74705SXin Li self.prevfs = {} 46*67e74705SXin Li 47*67e74705SXin Li def write(self, path, contents): 48*67e74705SXin Li self.fs[path] = contents 49*67e74705SXin Li 50*67e74705SXin Li def done(self): 51*67e74705SXin Li for f, s in self.fs.items(): 52*67e74705SXin Li if self.prevfs.get(f) != s: 53*67e74705SXin Li f = file(f, 'w') 54*67e74705SXin Li f.write(s) 55*67e74705SXin Li f.close() 56*67e74705SXin Li 57*67e74705SXin Li for f in self.prevfs: 58*67e74705SXin Li if f not in self.fs: 59*67e74705SXin Li os.remove(f) 60*67e74705SXin Li 61*67e74705SXin Li self.prevfs, self.fs = self.fs, {} 62*67e74705SXin Li 63*67e74705SXin Lifs = FS() 64*67e74705SXin Li 65*67e74705SXin Liclass CodeModel: 66*67e74705SXin Li def __init__(self): 67*67e74705SXin Li self.source = '' 68*67e74705SXin Li self.modules = {} 69*67e74705SXin Li self.decls = {} 70*67e74705SXin Li self.i = 0 71*67e74705SXin Li 72*67e74705SXin Li def make_name(self): 73*67e74705SXin Li self.i += 1 74*67e74705SXin Li return 'n' + str(self.i) 75*67e74705SXin Li 76*67e74705SXin Li def fails(self): 77*67e74705SXin Li fs.write('module.modulemap', 78*67e74705SXin Li ''.join('module %s { header "%s.h" export * }\n' % (m, m) 79*67e74705SXin Li for m in self.modules.keys())) 80*67e74705SXin Li 81*67e74705SXin Li for m, (s, _) in self.modules.items(): 82*67e74705SXin Li fs.write('%s.h' % m, s) 83*67e74705SXin Li 84*67e74705SXin Li fs.write('main.cc', self.source) 85*67e74705SXin Li fs.done() 86*67e74705SXin Li 87*67e74705SXin Li return subprocess.call([clang, '-std=c++11', '-c', '-fmodules', 'main.cc', '-o', '/dev/null']) != 0 88*67e74705SXin Li 89*67e74705SXin Lidef generate(): 90*67e74705SXin Li model = CodeModel() 91*67e74705SXin Li m = [] 92*67e74705SXin Li 93*67e74705SXin Li try: 94*67e74705SXin Li for d in mutations(model): 95*67e74705SXin Li d(model) 96*67e74705SXin Li m.append(d) 97*67e74705SXin Li if not model.fails(): 98*67e74705SXin Li return 99*67e74705SXin Li except KeyboardInterrupt: 100*67e74705SXin Li print 101*67e74705SXin Li return True 102*67e74705SXin Li 103*67e74705SXin Li sys.stdout.write('\nReducing:\n') 104*67e74705SXin Li sys.stdout.flush() 105*67e74705SXin Li 106*67e74705SXin Li try: 107*67e74705SXin Li while True: 108*67e74705SXin Li assert m, 'got a failure with no steps; broken clang binary?' 109*67e74705SXin Li i = random.choice(range(len(m))) 110*67e74705SXin Li x = m[0:i] + m[i+1:] 111*67e74705SXin Li m2 = CodeModel() 112*67e74705SXin Li for d in x: 113*67e74705SXin Li d(m2) 114*67e74705SXin Li if m2.fails(): 115*67e74705SXin Li m = x 116*67e74705SXin Li model = m2 117*67e74705SXin Li else: 118*67e74705SXin Li sys.stdout.write('.') 119*67e74705SXin Li sys.stdout.flush() 120*67e74705SXin Li except KeyboardInterrupt: 121*67e74705SXin Li # FIXME: Clean out output directory first. 122*67e74705SXin Li model.fails() 123*67e74705SXin Li return model 124*67e74705SXin Li 125*67e74705SXin Lidef choose(options): 126*67e74705SXin Li while True: 127*67e74705SXin Li i = int(random.uniform(0, len(options) + none_opts)) 128*67e74705SXin Li if i >= len(options): 129*67e74705SXin Li break 130*67e74705SXin Li yield options[i] 131*67e74705SXin Li 132*67e74705SXin Lidef mutations(model): 133*67e74705SXin Li options = [create_module, add_top_level_decl] 134*67e74705SXin Li for opt in choose(options): 135*67e74705SXin Li yield opt(model, options) 136*67e74705SXin Li 137*67e74705SXin Lidef create_module(model, options): 138*67e74705SXin Li n = model.make_name() 139*67e74705SXin Li def go(model): 140*67e74705SXin Li model.modules[n] = (model.source, model.decls) 141*67e74705SXin Li (model.source, model.decls) = ('', {}) 142*67e74705SXin Li options += [lambda model, options: add_import(model, options, n)] 143*67e74705SXin Li return go 144*67e74705SXin Li 145*67e74705SXin Lidef add_top_level_decl(model, options): 146*67e74705SXin Li n = model.make_name() 147*67e74705SXin Li d = random.choice([decl for decl in decls if decl.valid(model)]) 148*67e74705SXin Li def go(model): 149*67e74705SXin Li if not d.valid(model): 150*67e74705SXin Li return 151*67e74705SXin Li d.apply(model, n) 152*67e74705SXin Li return go 153*67e74705SXin Li 154*67e74705SXin Lidef add_import(model, options, module_name): 155*67e74705SXin Li def go(model): 156*67e74705SXin Li if module_name in model.modules: 157*67e74705SXin Li model.source += '#include "%s.h"\n' % module_name 158*67e74705SXin Li model.decls.update(model.modules[module_name][1]) 159*67e74705SXin Li return go 160*67e74705SXin Li 161*67e74705SXin Lisys.stdout.write('Finding bug: ') 162*67e74705SXin Liwhile True: 163*67e74705SXin Li if generate(): 164*67e74705SXin Li break 165*67e74705SXin Li sys.stdout.write('.') 166*67e74705SXin Li sys.stdout.flush() 167