brintos

brintos / llvm-project-archived public Read only

0
0
Text · 4.4 KiB · 49ac522 Raw
194 lines · python
1#!/usr/bin/env python2 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):18    def __init__(self, text, depends=[], provides=[], conflicts=[]):19        self.text = text20        self.depends = depends21        self.provides = provides22        self.conflicts = conflicts23 24    def valid(self, model):25        for i in self.depends:26            if i not in model.decls:27                return False28        for i in self.conflicts:29            if i in model.decls:30                return False31        return True32 33    def apply(self, model, name):34        for i in self.provides:35            model.decls[i] = True36        model.source += self.text % {"name": name}37 38 39decls = [40    Decl("struct X { int n; };\n", provides=["X"], conflicts=["X"]),41    Decl('static_assert(X{.n=1}.n == 1, "");\n', depends=["X"]),42    Decl("X %(name)s;\n", depends=["X"]),43]44 45 46class FS(object):47    def __init__(self):48        self.fs = {}49        self.prevfs = {}50 51    def write(self, path, contents):52        self.fs[path] = contents53 54    def done(self):55        for f, s in self.fs.items():56            if self.prevfs.get(f) != s:57                f = file(f, "w")58                f.write(s)59                f.close()60 61        for f in self.prevfs:62            if f not in self.fs:63                os.remove(f)64 65        self.prevfs, self.fs = self.fs, {}66 67 68fs = FS()69 70 71class CodeModel(object):72    def __init__(self):73        self.source = ""74        self.modules = {}75        self.decls = {}76        self.i = 077 78    def make_name(self):79        self.i += 180        return "n" + str(self.i)81 82    def fails(self):83        fs.write(84            "module.modulemap",85            "".join(86                'module %s { header "%s.h" export * }\n' % (m, m)87                for m in self.modules.keys()88            ),89        )90 91        for m, (s, _) in self.modules.items():92            fs.write("%s.h" % m, s)93 94        fs.write("main.cc", self.source)95        fs.done()96 97        return (98            subprocess.call(99                [clang, "-std=c++11", "-c", "-fmodules", "main.cc", "-o", "/dev/null"]100            )101            != 0102        )103 104 105def generate():106    model = CodeModel()107    m = []108 109    try:110        for d in mutations(model):111            d(model)112            m.append(d)113        if not model.fails():114            return115    except KeyboardInterrupt:116        print()117        return True118 119    sys.stdout.write("\nReducing:\n")120    sys.stdout.flush()121 122    try:123        while True:124            assert m, "got a failure with no steps; broken clang binary?"125            i = random.choice(list(range(len(m))))126            x = m[0:i] + m[i + 1 :]127            m2 = CodeModel()128            for d in x:129                d(m2)130            if m2.fails():131                m = x132                model = m2133            else:134                sys.stdout.write(".")135                sys.stdout.flush()136    except KeyboardInterrupt:137        # FIXME: Clean out output directory first.138        model.fails()139        return model140 141 142def choose(options):143    while True:144        i = int(random.uniform(0, len(options) + none_opts))145        if i >= len(options):146            break147        yield options[i]148 149 150def mutations(model):151    options = [create_module, add_top_level_decl]152    for opt in choose(options):153        yield opt(model, options)154 155 156def create_module(model, options):157    n = model.make_name()158 159    def go(model):160        model.modules[n] = (model.source, model.decls)161        (model.source, model.decls) = ("", {})162 163    options += [lambda model, options: add_import(model, options, n)]164    return go165 166 167def add_top_level_decl(model, options):168    n = model.make_name()169    d = random.choice([decl for decl in decls if decl.valid(model)])170 171    def go(model):172        if not d.valid(model):173            return174        d.apply(model, n)175 176    return go177 178 179def add_import(model, options, module_name):180    def go(model):181        if module_name in model.modules:182            model.source += '#include "%s.h"\n' % module_name183            model.decls.update(model.modules[module_name][1])184 185    return go186 187 188sys.stdout.write("Finding bug: ")189while True:190    if generate():191        break192    sys.stdout.write(".")193    sys.stdout.flush()194