xref: /aosp_15_r20/external/AFLplusplus/instrumentation/afl-gcc-cmplog-pass.so.cc (revision 08b48e0b10e97b33e7b60c5b6e2243bd915777f2)
1 /* GCC plugin for cmplog instrumentation of code for AFL++.
2 
3    Copyright 2014-2019 Free Software Foundation, Inc
4    Copyright 2015, 2016 Google Inc. All rights reserved.
5    Copyright 2019-2020 AFLplusplus Project. All rights reserved.
6    Copyright 2019-2024 AdaCore
7 
8    Written by Alexandre Oliva <[email protected]>, based on the AFL++
9    LLVM CmpLog pass by Andrea Fioraldi <[email protected]>, and
10    on the AFL GCC pass.
11 
12    This program is free software: you can redistribute it and/or modify
13    it under the terms of the GNU General Public License as published by
14    the Free Software Foundation, either version 3 of the License, or
15    (at your option) any later version.
16 
17    This program is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20    GNU General Public License for more details.
21 
22    You should have received a copy of the GNU General Public License
23    along with this program.  If not, see <http://www.gnu.org/licenses/>.
24 
25  */
26 
27 #include "afl-gcc-common.h"
28 
29 /* This plugin, being under the same license as GCC, satisfies the
30    "GPL-compatible Software" definition in the GCC RUNTIME LIBRARY
31    EXCEPTION, so it can be part of an "Eligible" "Compilation
32    Process".  */
33 int plugin_is_GPL_compatible = 1;
34 
35 namespace {
36 
37 static const struct pass_data afl_cmplog_pass_data = {
38 
39     .type = GIMPLE_PASS,
40     .name = "aflcmplog",
41     .optinfo_flags = OPTGROUP_NONE,
42     .tv_id = TV_NONE,
43     .properties_required = 0,
44     .properties_provided = 0,
45     .properties_destroyed = 0,
46     .todo_flags_start = 0,
47     .todo_flags_finish = (TODO_update_ssa | TODO_cleanup_cfg | TODO_verify_il |
48                           TODO_rebuild_cgraph_edges),
49 
50 };
51 
52 struct afl_cmplog_pass : afl_base_pass {
53 
afl_cmplog_pass__anon0e48c2980111::afl_cmplog_pass54   afl_cmplog_pass(bool quiet)
55       : afl_base_pass(quiet, /*debug=*/false, afl_cmplog_pass_data),
56         t8u(),
57         cmplog_hooks() {
58 
59   }
60 
61   /* An unsigned 8-bit integral type.  */
62   tree t8u;
63 
64   /* Declarations for the various cmplog hook functions, allocated on demand..
65      [0] is for __cmplog_ins_hookN, that accepts non-power-of-2 sizes.
66      [n in 1..5] are for unsigned ints of 2^{n-1} bytes.  */
67   tree cmplog_hooks[6];
68 
cmplog_hook__anon0e48c2980111::afl_cmplog_pass69   tree cmplog_hook(unsigned i) {
70 
71     tree t, fnt;
72 
73     if (!t8u) {
74 
75       if (BITS_PER_UNIT == 8)
76         t8u = unsigned_char_type_node;
77       else
78         t8u = build_nonstandard_integer_type(8, 1);
79 
80     }
81 
82     if (i <= ARRAY_SIZE(cmplog_hooks) && cmplog_hooks[i])
83       return cmplog_hooks[i];
84 
85     switch (i) {
86 
87       case 0:
88 #ifdef uint128_type_node
89         t = uint128_type_node;
90 #else
91         t = build_nonstandard_integer_type(128, 1);
92 #endif
93         fnt =
94             build_function_type_list(void_type_node, t, t, t8u, t8u, NULL_TREE);
95         t = cmplog_hooks[0] = build_fn_decl("__cmplog_ins_hookN", fnt);
96         break;
97 
98       case 1:
99         t = t8u;
100         fnt = build_function_type_list(void_type_node, t, t, t8u, NULL_TREE);
101         t = cmplog_hooks[1] = build_fn_decl("__cmplog_ins_hook1", fnt);
102         break;
103 
104       case 2:
105         t = uint16_type_node;
106         fnt = build_function_type_list(void_type_node, t, t, t8u, NULL_TREE);
107         t = cmplog_hooks[2] = build_fn_decl("__cmplog_ins_hook2", fnt);
108         break;
109 
110       case 3:
111         t = uint32_type_node;
112         fnt = build_function_type_list(void_type_node, t, t, t8u, NULL_TREE);
113         t = cmplog_hooks[3] = build_fn_decl("__cmplog_ins_hook4", fnt);
114         break;
115 
116       case 4:
117         t = uint64_type_node;
118         fnt = build_function_type_list(void_type_node, t, t, t8u, NULL_TREE);
119         t = cmplog_hooks[4] = build_fn_decl("__cmplog_ins_hook8", fnt);
120         break;
121 
122       case 5:
123 #ifdef uint128_type_node
124         t = uint128_type_node;
125 #else
126         t = build_nonstandard_integer_type(128, 1);
127 #endif
128         fnt = build_function_type_list(void_type_node, t, t, t8u, NULL_TREE);
129         t = cmplog_hooks[5] = build_fn_decl("__cmplog_ins_hook16", fnt);
130         break;
131 
132       default:
133         gcc_unreachable();
134 
135     }
136 
137     /* Mark the newly-created decl as non-throwing, so that we can
138        insert call within basic blocks.  */
139     TREE_NOTHROW(t) = 1;
140 
141     return t;
142 
143   }
144 
145   /* Insert a cmplog hook call before GSI for a CODE compare between
146      LHS and RHS.  */
insert_cmplog_call__anon0e48c2980111::afl_cmplog_pass147   void insert_cmplog_call(gimple_stmt_iterator gsi, tree_code code, tree lhs,
148                           tree rhs) {
149 
150     gcc_checking_assert(TYPE_MAIN_VARIANT(TREE_TYPE(lhs)) ==
151                         TYPE_MAIN_VARIANT(TREE_TYPE(rhs)));
152 
153     tree fn;
154     bool pass_n = false;
155 
156     /* Obtain the compare operand size as a constant.  */
157     tree st = TREE_TYPE(lhs);
158     tree szt = TYPE_SIZE(st);
159 
160     if (!tree_fits_uhwi_p(szt)) return;
161 
162     unsigned HOST_WIDE_INT sz = tree_to_uhwi(szt);
163 
164     /* Round it up.  */
165     if (sz % 8) sz = (((sz - 1) / 8) + 1) * 8;
166 
167     /* Select the hook function to call, based on the size.  */
168     switch (sz) {
169 
170       default:
171         fn = cmplog_hook(0);
172         pass_n = true;
173         break;
174 
175       case 8:
176         fn = cmplog_hook(1);
177         break;
178 
179       case 16:
180         fn = cmplog_hook(2);
181         break;
182 
183       case 32:
184         fn = cmplog_hook(3);
185         break;
186 
187       case 64:
188         fn = cmplog_hook(4);
189         break;
190 
191       case 128:
192         fn = cmplog_hook(5);
193         break;
194 
195     }
196 
197     /* Set attr according to the compare operation.  */
198     unsigned char attr = 0;
199 
200     switch (code) {
201 
202       case UNORDERED_EXPR:
203       case ORDERED_EXPR:
204         /* ??? */
205         /* Fallthrough.  */
206       case NE_EXPR:
207       case LTGT_EXPR:
208         break;
209 
210       case EQ_EXPR:
211       case UNEQ_EXPR:
212         attr += 1;
213         break;
214 
215       case GT_EXPR:
216       case UNGT_EXPR:
217         attr += 2;
218         break;
219 
220       case GE_EXPR:
221       case UNGE_EXPR:
222         attr += 3;
223         break;
224 
225       case LT_EXPR:
226       case UNLT_EXPR:
227         attr += 4;
228         break;
229 
230       case LE_EXPR:
231       case UNLE_EXPR:
232         attr += 5;
233         break;
234 
235       default:
236         gcc_unreachable();
237 
238     }
239 
240     if (FLOAT_TYPE_P(TREE_TYPE(lhs))) {
241 
242       attr += 8;
243 
244       tree t = build_nonstandard_integer_type(sz, 1);
245 
246       tree   s = make_ssa_name(t);
247       gimple g = gimple_build_assign(s, VIEW_CONVERT_EXPR,
248                                      build1(VIEW_CONVERT_EXPR, t, lhs));
249       lhs = s;
250       gsi_insert_before(&gsi, g, GSI_SAME_STMT);
251 
252       s = make_ssa_name(t);
253       g = gimple_build_assign(s, VIEW_CONVERT_EXPR,
254                               build1(VIEW_CONVERT_EXPR, t, rhs));
255       rhs = s;
256       gsi_insert_before(&gsi, g, GSI_SAME_STMT);
257 
258     }
259 
260     /* Convert the operands to the hook arg type, if needed.  */
261     tree t = TREE_VALUE(TYPE_ARG_TYPES(TREE_TYPE(fn)));
262 
263     lhs = fold_convert_loc(UNKNOWN_LOCATION, t, lhs);
264     if (!is_gimple_val(lhs)) {
265 
266       tree   s = make_ssa_name(t);
267       gimple g = gimple_build_assign(s, lhs);
268       lhs = s;
269       gsi_insert_before(&gsi, g, GSI_SAME_STMT);
270 
271     }
272 
273     rhs = fold_convert_loc(UNKNOWN_LOCATION, t, rhs);
274     if (!is_gimple_val(rhs)) {
275 
276       tree   s = make_ssa_name(t);
277       gimple g = gimple_build_assign(s, rhs);
278       rhs = s;
279       gsi_insert_before(&gsi, g, GSI_SAME_STMT);
280 
281     }
282 
283     /* Insert the call.  */
284     tree   att = build_int_cst(t8u, attr);
285     gimple call;
286     if (pass_n)
287       call = gimple_build_call(fn, 4, lhs, rhs, att,
288                                build_int_cst(t8u, sz / 8 - 1));
289     else
290       call = gimple_build_call(fn, 3, lhs, rhs, att);
291 
292     gsi_insert_before(&gsi, call, GSI_SAME_STMT);
293 
294   }
295 
execute__anon0e48c2980111::afl_cmplog_pass296   virtual unsigned int execute(function *fn) {
297 
298     if (!isInInstrumentList(fn)) return 0;
299 
300     basic_block bb;
301     FOR_EACH_BB_FN(bb, fn) {
302 
303       /* A GIMPLE_COND or GIMPLE_SWITCH will always be the last stmt
304          in a BB.  */
305       gimple_stmt_iterator gsi = gsi_last_bb(bb);
306       if (gsi_end_p(gsi)) continue;
307 
308       gimple stmt = gsi_stmt(gsi);
309 
310       if (gimple_code(stmt) == GIMPLE_COND) {
311 
312         tree_code code = gimple_cond_code(stmt);
313         tree      lhs = gimple_cond_lhs(stmt);
314         tree      rhs = gimple_cond_rhs(stmt);
315 
316         insert_cmplog_call(gsi, code, lhs, rhs);
317 
318       } else if (gimple_code(stmt) == GIMPLE_SWITCH) {
319 
320         gswitch *sw = as_a<gswitch *>(stmt);
321         tree     lhs = gimple_switch_index(sw);
322 
323         for (int i = 0, e = gimple_switch_num_labels(sw); i < e; i++) {
324 
325           tree clx = gimple_switch_label(sw, i);
326           tree rhsl = CASE_LOW(clx);
327           /* Default case labels exprs don't have a CASE_LOW.  */
328           if (!rhsl) continue;
329           tree rhsh = CASE_HIGH(clx);
330           /* If there is a CASE_HIGH, issue range compares.  */
331           if (rhsh) {
332 
333             insert_cmplog_call(gsi, GE_EXPR, lhs, rhsl);
334             insert_cmplog_call(gsi, LE_EXPR, lhs, rhsh);
335 
336           }
337 
338           /* Otherwise, use a single equality compare.  */
339           else
340             insert_cmplog_call(gsi, EQ_EXPR, lhs, rhsl);
341 
342         }
343 
344       } else
345 
346         continue;
347 
348     }
349 
350     return 0;
351 
352   }
353 
354 };
355 
356 static struct plugin_info afl_cmplog_plugin = {
357 
358     .version = "20220420",
359     .help = G_("AFL gcc cmplog plugin\n\
360 \n\
361 Set AFL_QUIET in the environment to silence it.\n\
362 "),
363 
364 };
365 
366 }  // namespace
367 
368 /* This is the function GCC calls when loading a plugin.  Initialize
369    and register further callbacks.  */
plugin_init(struct plugin_name_args * info,struct plugin_gcc_version * version)370 int plugin_init(struct plugin_name_args   *info,
371                 struct plugin_gcc_version *version) {
372 
373   if (!plugin_default_version_check(version, &gcc_version))
374     FATAL(G_("GCC and plugin have incompatible versions, expected GCC %s, "
375              "is %s"),
376           gcc_version.basever, version->basever);
377 
378   /* Show a banner.  */
379   bool quiet = false;
380   if (isatty(2) && !getenv("AFL_QUIET"))
381     SAYF(cCYA "afl-gcc-cmplog-pass " cBRI VERSION cRST
382               " by <[email protected]>\n");
383   else
384     quiet = true;
385 
386   const char *name = info->base_name;
387   register_callback(name, PLUGIN_INFO, NULL, &afl_cmplog_plugin);
388 
389   afl_cmplog_pass          *aflp = new afl_cmplog_pass(quiet);
390   struct register_pass_info pass_info = {
391 
392       .pass = aflp,
393       .reference_pass_name = "ssa",
394       .ref_pass_instance_number = 1,
395       .pos_op = PASS_POS_INSERT_AFTER,
396 
397   };
398 
399   register_callback(name, PLUGIN_PASS_MANAGER_SETUP, NULL, &pass_info);
400 
401   return 0;
402 
403 }
404 
405