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