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 cpptooling.data : CppRoot, CFunction, USRType, CxParam, CppVariable, 17 CppNs, Language; 18 import dsrcgen.cpp : CppModule, E, Et; 19 20 import dextool.plugin.fuzzer.type : Param, Symbol; 21 import dextool.plugin.fuzzer.backend.type; 22 23 @safe: 24 25 void generateMain(ref ImplData impl, CppModule m) { 26 immutable dextool_hpp = "dextool/dextool.hpp"; 27 immutable afl_integration_hpp = "dextool/afl_integration.hpp"; 28 immutable guide_data = "guide_data"; 29 immutable default_src = "stdin_src"; 30 31 m.include(dextool_hpp); 32 m.include(afl_integration_hpp); 33 m.sep(2); 34 35 with (m.namespace("dextool")) { 36 suppressIndent(1); 37 with (namespace("")) { 38 suppressIndent(1); 39 stmt(E("dextool::DefaultSource*") ~ E(default_src)); 40 } 41 sep(2); 42 43 with (func_body("DefaultSource&", "get_default_source")) { 44 return_("*" ~ default_src); 45 } 46 } 47 m.sep(2); 48 49 with (m.func_body("int", "main", "int argc", "char** argv")) { 50 return_(E("dextool::afl_main")("argc", "argv", "&dextool::" ~ default_src)); 51 } 52 } 53 54 private FuzzCase makeFuzzCase(IncludeT)(ulong index, IncludeT[] target_includes) { 55 import std.conv : to; 56 import dextool.type : FileName; 57 58 auto r = FuzzCase(new CppModule); 59 r.testCaseId = index; 60 61 r.filename = FileName("dextool_fuzz_case"); 62 r.includes = r.cpp.base; 63 r.includes.suppressIndent(1); 64 65 r.includes.include("dextool/dextool.hpp"); 66 r.includes.sep(2); 67 68 generateFuzzerTargetIncludes(target_includes, r.includes); 69 r.includes.sep(2); 70 71 r.body_ = r.cpp.suite( 72 "FUZZ_TEST_S(Generated, Test" ~ index.to!string ~ ", " ~ index.to!string ~ ")"); 73 74 return r; 75 } 76 77 void generateFuzzCases(IncludeT)(ref ImplData impl, IncludeT[] target_includes, ref GeneratedData gen) { 78 foreach (f; impl.root.funcRange) { 79 auto test_case_id = impl.symbolId[FullyQualifiedNameType(f.name)]; 80 auto fc = makeFuzzCase(test_case_id, target_includes); 81 generateFunctionFuzzTest(f, fc, impl.symbols); 82 gen.fuzzCases ~= fc; 83 } 84 } 85 86 private void generateFunctionFuzzTest(SymbolsT)(CFunction f, FuzzCase fc, SymbolsT symbols) { 87 import std.typecons : No; 88 import cpptooling.data; 89 import dextool.plugin.fuzzer.type : FullyQualifiedNameType, Param; 90 91 if (auto sym = FullyQualifiedNameType(f.name) in symbols) { 92 if (sym.hasFuzz) { 93 generateFuncUserFuzzer(f, sym, fc.body_, fc.includes); 94 } else if (sym.filter == Symbol.FilterKind.keep) { 95 Param[] params = sym.limits; 96 generateFuncParamFuzzer(f, params, fc.body_, fc.includes); 97 } 98 } else { 99 generateFuncParamFuzzer(f, Param[].init, fc.body_, fc.includes); 100 } 101 } 102 103 /// Generate the parameters with random values. 104 void generateFuncParamFuzzer(ParamsT)(CFunction f, ParamsT param_limits, 105 CppModule f_, CppModule extra_includes) { 106 import std.algorithm : joiner; 107 import std.conv : to, text; 108 import std.range : enumerate; 109 import std.typecons : Yes, No; 110 import cpptooling.data : getName, unpackParam, TypeKind, TypeKindAttr, 111 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 if (type.kind.info.kind == TypeKind.Info.Kind.pointer) { 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 } else { 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 }