16e2a64f2SRichard Smith#!/usr/bin/env python 26e2a64f2SRichard Smith 36e2a64f2SRichard Smith# To use: 46e2a64f2SRichard Smith# 1) Update the 'decls' list below with your fuzzing configuration. 56e2a64f2SRichard Smith# 2) Run with the clang binary as the command-line argument. 66e2a64f2SRichard Smith 7b748c0e6SSerge Gueltonfrom __future__ import absolute_import, division, print_function 86e2a64f2SRichard Smithimport random 96e2a64f2SRichard Smithimport subprocess 106e2a64f2SRichard Smithimport sys 116e2a64f2SRichard Smithimport os 126e2a64f2SRichard Smith 136e2a64f2SRichard Smithclang = sys.argv[1] 146e2a64f2SRichard Smithnone_opts = 0.3 156e2a64f2SRichard Smith 16*dd3c26a0STobias Hieta 1709616bdbSSerge Gueltonclass Decl(object): 186e2a64f2SRichard Smith def __init__(self, text, depends=[], provides=[], conflicts=[]): 196e2a64f2SRichard Smith self.text = text 206e2a64f2SRichard Smith self.depends = depends 216e2a64f2SRichard Smith self.provides = provides 226e2a64f2SRichard Smith self.conflicts = conflicts 236e2a64f2SRichard Smith 246e2a64f2SRichard Smith def valid(self, model): 256e2a64f2SRichard Smith for i in self.depends: 266e2a64f2SRichard Smith if i not in model.decls: 276e2a64f2SRichard Smith return False 286e2a64f2SRichard Smith for i in self.conflicts: 296e2a64f2SRichard Smith if i in model.decls: 306e2a64f2SRichard Smith return False 316e2a64f2SRichard Smith return True 326e2a64f2SRichard Smith 336e2a64f2SRichard Smith def apply(self, model, name): 346e2a64f2SRichard Smith for i in self.provides: 356e2a64f2SRichard Smith model.decls[i] = True 36*dd3c26a0STobias Hieta model.source += self.text % {"name": name} 37*dd3c26a0STobias Hieta 386e2a64f2SRichard Smith 396e2a64f2SRichard Smithdecls = [ 40*dd3c26a0STobias Hieta Decl("struct X { int n; };\n", provides=["X"], conflicts=["X"]), 41*dd3c26a0STobias Hieta Decl('static_assert(X{.n=1}.n == 1, "");\n', depends=["X"]), 42*dd3c26a0STobias Hieta Decl("X %(name)s;\n", depends=["X"]), 436e2a64f2SRichard Smith] 446e2a64f2SRichard Smith 45*dd3c26a0STobias Hieta 4609616bdbSSerge Gueltonclass FS(object): 476e2a64f2SRichard Smith def __init__(self): 486e2a64f2SRichard Smith self.fs = {} 496e2a64f2SRichard Smith self.prevfs = {} 506e2a64f2SRichard Smith 516e2a64f2SRichard Smith def write(self, path, contents): 526e2a64f2SRichard Smith self.fs[path] = contents 536e2a64f2SRichard Smith 546e2a64f2SRichard Smith def done(self): 556e2a64f2SRichard Smith for f, s in self.fs.items(): 566e2a64f2SRichard Smith if self.prevfs.get(f) != s: 57*dd3c26a0STobias Hieta f = file(f, "w") 586e2a64f2SRichard Smith f.write(s) 596e2a64f2SRichard Smith f.close() 606e2a64f2SRichard Smith 616e2a64f2SRichard Smith for f in self.prevfs: 626e2a64f2SRichard Smith if f not in self.fs: 636e2a64f2SRichard Smith os.remove(f) 646e2a64f2SRichard Smith 656e2a64f2SRichard Smith self.prevfs, self.fs = self.fs, {} 666e2a64f2SRichard Smith 67*dd3c26a0STobias Hieta 686e2a64f2SRichard Smithfs = FS() 696e2a64f2SRichard Smith 70*dd3c26a0STobias Hieta 7109616bdbSSerge Gueltonclass CodeModel(object): 726e2a64f2SRichard Smith def __init__(self): 73*dd3c26a0STobias Hieta self.source = "" 746e2a64f2SRichard Smith self.modules = {} 756e2a64f2SRichard Smith self.decls = {} 766e2a64f2SRichard Smith self.i = 0 776e2a64f2SRichard Smith 786e2a64f2SRichard Smith def make_name(self): 796e2a64f2SRichard Smith self.i += 1 80*dd3c26a0STobias Hieta return "n" + str(self.i) 816e2a64f2SRichard Smith 826e2a64f2SRichard Smith def fails(self): 83*dd3c26a0STobias Hieta fs.write( 84*dd3c26a0STobias Hieta "module.modulemap", 85*dd3c26a0STobias Hieta "".join( 86*dd3c26a0STobias Hieta 'module %s { header "%s.h" export * }\n' % (m, m) 87*dd3c26a0STobias Hieta for m in self.modules.keys() 88*dd3c26a0STobias Hieta ), 89*dd3c26a0STobias Hieta ) 906e2a64f2SRichard Smith 916e2a64f2SRichard Smith for m, (s, _) in self.modules.items(): 92*dd3c26a0STobias Hieta fs.write("%s.h" % m, s) 936e2a64f2SRichard Smith 94*dd3c26a0STobias Hieta fs.write("main.cc", self.source) 956e2a64f2SRichard Smith fs.done() 966e2a64f2SRichard Smith 97*dd3c26a0STobias Hieta return ( 98*dd3c26a0STobias Hieta subprocess.call( 99*dd3c26a0STobias Hieta [clang, "-std=c++11", "-c", "-fmodules", "main.cc", "-o", "/dev/null"] 100*dd3c26a0STobias Hieta ) 101*dd3c26a0STobias Hieta != 0 102*dd3c26a0STobias Hieta ) 103*dd3c26a0STobias Hieta 1046e2a64f2SRichard Smith 1056e2a64f2SRichard Smithdef generate(): 1066e2a64f2SRichard Smith model = CodeModel() 1076e2a64f2SRichard Smith m = [] 1086e2a64f2SRichard Smith 1096e2a64f2SRichard Smith try: 1106e2a64f2SRichard Smith for d in mutations(model): 1116e2a64f2SRichard Smith d(model) 1126e2a64f2SRichard Smith m.append(d) 1136e2a64f2SRichard Smith if not model.fails(): 1146e2a64f2SRichard Smith return 1156e2a64f2SRichard Smith except KeyboardInterrupt: 116c0ebe773SSerge Guelton print() 1176e2a64f2SRichard Smith return True 1186e2a64f2SRichard Smith 119*dd3c26a0STobias Hieta sys.stdout.write("\nReducing:\n") 1206e2a64f2SRichard Smith sys.stdout.flush() 1216e2a64f2SRichard Smith 1226e2a64f2SRichard Smith try: 1236e2a64f2SRichard Smith while True: 124*dd3c26a0STobias Hieta assert m, "got a failure with no steps; broken clang binary?" 125c5d97e3eSSerge Guelton i = random.choice(list(range(len(m)))) 1266e2a64f2SRichard Smith x = m[0:i] + m[i + 1 :] 1276e2a64f2SRichard Smith m2 = CodeModel() 1286e2a64f2SRichard Smith for d in x: 1296e2a64f2SRichard Smith d(m2) 1306e2a64f2SRichard Smith if m2.fails(): 1316e2a64f2SRichard Smith m = x 1326e2a64f2SRichard Smith model = m2 1336e2a64f2SRichard Smith else: 134*dd3c26a0STobias Hieta sys.stdout.write(".") 1356e2a64f2SRichard Smith sys.stdout.flush() 1366e2a64f2SRichard Smith except KeyboardInterrupt: 1376e2a64f2SRichard Smith # FIXME: Clean out output directory first. 1386e2a64f2SRichard Smith model.fails() 1396e2a64f2SRichard Smith return model 1406e2a64f2SRichard Smith 141*dd3c26a0STobias Hieta 1426e2a64f2SRichard Smithdef choose(options): 1436e2a64f2SRichard Smith while True: 1446e2a64f2SRichard Smith i = int(random.uniform(0, len(options) + none_opts)) 1456e2a64f2SRichard Smith if i >= len(options): 1466e2a64f2SRichard Smith break 1476e2a64f2SRichard Smith yield options[i] 1486e2a64f2SRichard Smith 149*dd3c26a0STobias Hieta 1506e2a64f2SRichard Smithdef mutations(model): 1516e2a64f2SRichard Smith options = [create_module, add_top_level_decl] 1526e2a64f2SRichard Smith for opt in choose(options): 1536e2a64f2SRichard Smith yield opt(model, options) 1546e2a64f2SRichard Smith 155*dd3c26a0STobias Hieta 1566e2a64f2SRichard Smithdef create_module(model, options): 1576e2a64f2SRichard Smith n = model.make_name() 158*dd3c26a0STobias Hieta 1596e2a64f2SRichard Smith def go(model): 1606e2a64f2SRichard Smith model.modules[n] = (model.source, model.decls) 161*dd3c26a0STobias Hieta (model.source, model.decls) = ("", {}) 162*dd3c26a0STobias Hieta 1636e2a64f2SRichard Smith options += [lambda model, options: add_import(model, options, n)] 1646e2a64f2SRichard Smith return go 1656e2a64f2SRichard Smith 166*dd3c26a0STobias Hieta 1676e2a64f2SRichard Smithdef add_top_level_decl(model, options): 1686e2a64f2SRichard Smith n = model.make_name() 1696e2a64f2SRichard Smith d = random.choice([decl for decl in decls if decl.valid(model)]) 170*dd3c26a0STobias Hieta 1716e2a64f2SRichard Smith def go(model): 1726e2a64f2SRichard Smith if not d.valid(model): 1736e2a64f2SRichard Smith return 1746e2a64f2SRichard Smith d.apply(model, n) 175*dd3c26a0STobias Hieta 1766e2a64f2SRichard Smith return go 1776e2a64f2SRichard Smith 178*dd3c26a0STobias Hieta 1796e2a64f2SRichard Smithdef add_import(model, options, module_name): 1806e2a64f2SRichard Smith def go(model): 1816e2a64f2SRichard Smith if module_name in model.modules: 1826e2a64f2SRichard Smith model.source += '#include "%s.h"\n' % module_name 1836e2a64f2SRichard Smith model.decls.update(model.modules[module_name][1]) 184*dd3c26a0STobias Hieta 1856e2a64f2SRichard Smith return go 1866e2a64f2SRichard Smith 187*dd3c26a0STobias Hieta 188*dd3c26a0STobias Hietasys.stdout.write("Finding bug: ") 1896e2a64f2SRichard Smithwhile True: 1906e2a64f2SRichard Smith if generate(): 1916e2a64f2SRichard Smith break 192*dd3c26a0STobias Hieta sys.stdout.write(".") 1936e2a64f2SRichard Smith sys.stdout.flush() 194