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 }