1 /** 2 Copyright: Copyright (c) 2017, Joakim Brännström. All rights reserved. 3 License: MPL-2 4 Author: Joakim Brännström (joakim.brannstrom@gmx.com) 5 6 This Source Code Form is subject to the terms of the Mozilla Public License, 7 v.2.0. If a copy of the MPL was not distributed with this file, You can obtain 8 one at http://mozilla.org/MPL/2.0/. 9 10 This file contains the C++ code generators. 11 */ 12 module dextool.plugin.fuzzer.backend.generate_cpp; 13 14 import logger = std.experimental.logger; 15 16 import dsrcgen.cpp : CppModule, E, Et; 17 import sumtype; 18 19 import cpptooling.data : CppRoot, CFunction, USRType, CxParam, CppVariable, CppNs, Language; 20 21 import dextool.plugin.fuzzer.type : Param, Symbol; 22 import dextool.plugin.fuzzer.backend.type; 23 24 @safe: 25 26 void generateMain(ref ImplData impl, CppModule m) { 27 immutable dextool_hpp = "dextool/dextool.hpp"; 28 immutable afl_integration_hpp = "dextool/afl_integration.hpp"; 29 immutable guide_data = "guide_data"; 30 immutable default_src = "stdin_src"; 31 32 m.include(dextool_hpp); 33 m.include(afl_integration_hpp); 34 m.sep(2); 35 36 with (m.namespace("dextool")) { 37 suppressIndent(1); 38 with (namespace("")) { 39 suppressIndent(1); 40 stmt(E("dextool::DefaultSource*") ~ E(default_src)); 41 } 42 sep(2); 43 44 with (func_body("DefaultSource&", "get_default_source")) { 45 return_("*" ~ default_src); 46 } 47 } 48 m.sep(2); 49 50 with (m.func_body("int", "main", "int argc", "char** argv")) { 51 return_(E("dextool::afl_main")("argc", "argv", "&dextool::" ~ default_src)); 52 } 53 } 54 55 private FuzzCase makeFuzzCase(IncludeT)(ulong index, IncludeT[] target_includes) { 56 import std.conv : to; 57 import dextool.type : Path; 58 59 auto r = FuzzCase(new CppModule); 60 r.testCaseId = index; 61 62 r.filename = Path("dextool_fuzz_case"); 63 r.includes = r.cpp.base; 64 r.includes.suppressIndent(1); 65 66 r.includes.include("dextool/dextool.hpp"); 67 r.includes.sep(2); 68 69 generateFuzzerTargetIncludes(target_includes, r.includes); 70 r.includes.sep(2); 71 72 r.body_ = r.cpp.suite( 73 "FUZZ_TEST_S(Generated, Test" ~ index.to!string ~ ", " ~ index.to!string ~ ")"); 74 75 return r; 76 } 77 78 void generateFuzzCases(IncludeT)(ref ImplData impl, IncludeT[] target_includes, ref GeneratedData gen) { 79 foreach (f; impl.root.funcRange) { 80 auto test_case_id = impl.symbolId[FullyQualifiedNameType(f.name)]; 81 auto fc = makeFuzzCase(test_case_id, target_includes); 82 generateFunctionFuzzTest(f, fc, impl.symbols); 83 gen.fuzzCases ~= fc; 84 } 85 } 86 87 private void generateFunctionFuzzTest(SymbolsT)(CFunction f, FuzzCase fc, SymbolsT symbols) { 88 import std.typecons : No; 89 import cpptooling.data; 90 import dextool.plugin.fuzzer.type : FullyQualifiedNameType, Param; 91 92 if (auto sym = FullyQualifiedNameType(f.name) in symbols) { 93 if (sym.hasFuzz) { 94 generateFuncUserFuzzer(f, sym, fc.body_, fc.includes); 95 } else if (sym.filter == Symbol.FilterKind.keep) { 96 Param[] params = sym.limits; 97 generateFuncParamFuzzer(f, params, fc.body_, fc.includes); 98 } 99 } else { 100 generateFuncParamFuzzer(f, Param[].init, fc.body_, fc.includes); 101 } 102 } 103 104 /// Generate the parameters with random values. 105 void generateFuncParamFuzzer(ParamsT)(CFunction f, ParamsT param_limits, 106 CppModule f_, CppModule extra_includes) { 107 import std.algorithm : joiner; 108 import std.conv : to, text; 109 import std.range : enumerate; 110 import std.typecons : Yes, No; 111 import cpptooling.data : getName, unpackParam, TypeKind, TypeKindAttr, toStringDecl; 112 113 struct Instantiate { 114 string name; 115 bool success; 116 } 117 118 Instantiate instantiateParamType(TypeT)(size_t idx, Param[] params, 119 TypeT type, ref string[] param_id, CppModule m) { 120 Instantiate rval; 121 rval.name = "param" ~ idx.to!string; 122 123 type.attr.isConst = No.isConst; 124 type.attr.isPtr = No.isPtr; 125 type.attr.isRef = No.isRef; 126 type.attr.isFuncPtr = No.isFuncPtr; 127 128 type.kind.info.match!((TypeKind.PointerInfo t) { 129 rval.success = false; 130 param_id ~= rval.name; 131 m.comment("Unable to fuzz a pointer. Use a custom fuzz function. Example:"); 132 m.comment(`<fuzz use="my_fuzz" include="my_include.hpp"/>`); 133 m.stmt(E(type.toStringDecl(rval.name)) = E(0)); 134 }, (_) { 135 rval.success = true; 136 param_id ~= rval.name; 137 m.stmt(type.toStringDecl(rval.name)); 138 }); 139 140 return rval; 141 } 142 143 void injectUserFuzzer(Param p, string varname, CppModule m) { 144 if (!p.hasFuzz) { 145 m.stmt(E("dextool::fuzz")(varname)); 146 return; 147 } 148 149 if (p.fuzz.include.length != 0) { 150 extra_includes.include(p.fuzz.include); 151 } 152 153 m.stmt(E(p.fuzz.use)(varname, p.fuzz.param)); 154 } 155 156 void injectCheck(Param p, string varname, CppModule m) { 157 if (!p.hasCheck) 158 return; 159 160 with (m.if_("!" ~ E(p.check)(p.condition, varname))) { 161 return_; 162 } 163 } 164 165 // generate the random values. 166 with (f_) { 167 string[] param_id; 168 string[] params; 169 170 foreach (pidx, p; f.paramRange.enumerate) { 171 auto utype = p.unpackParam; 172 if (utype.isVariadic) 173 continue; 174 auto type = utype.type; 175 176 // create the local variable for the parameters 177 auto ident = instantiateParamType(pidx, param_limits, type, param_id, f_); 178 179 if (!ident.success) 180 continue; 181 182 if (pidx >= param_limits.length) { 183 stmt(E("dextool::fuzz")(ident.name)); 184 } else { 185 Param param = param_limits[pidx]; 186 injectUserFuzzer(param, ident.name, f_); 187 injectCheck(param, ident.name, f_); 188 } 189 } 190 191 stmt(E(f.name)(param_id.joiner(", ").text)); 192 } 193 } 194 195 void generateFuzzerTargetIncludes(IncludeT)(IncludeT[] includes, CppModule m) { 196 foreach (incl; includes) { 197 auto incl_code = m; 198 if (incl.payload == Language.c) { 199 incl_code = m.suite(`extern "C"`); 200 incl_code.suppressIndent(1); 201 } 202 incl_code.include(cast(string) incl); 203 } 204 m.sep(2); 205 } 206 207 void generateFuncUserFuzzer(SymbolT)(CFunction f, SymbolT sym, CppModule f_, 208 CppModule extra_includes) { 209 import std.algorithm : map, joiner; 210 import std.range : iota; 211 import std.conv : to, text; 212 213 if (sym.hasInclude) { 214 extra_includes.include(sym.fuzz.include); 215 } 216 217 immutable user_ctx = "ctx"; 218 immutable prefix = "param"; 219 220 f_.stmt(E(sym.fuzz.use) ~ E(user_ctx)); 221 222 with (f_.if_("!" ~ user_ctx ~ ".is_valid")) { 223 return_; 224 } 225 f_.sep(2); 226 227 f_.stmt(E(f.name)(iota(f.paramRange.length) 228 .map!(a => user_ctx ~ "." ~ prefix ~ a.text).joiner(", ").text)); 229 }