xref: /aosp_15_r20/external/AFLplusplus/instrumentation/afl-gcc-common.h (revision 08b48e0b10e97b33e7b60c5b6e2243bd915777f2)
1 /* GCC plugin common infrastructure for AFL++ instrumentation passes.
2 
3    Copyright 2014-2019 Free Software Foundation, Inc
4    Copyright 2015, 2016 Google Inc. All rights reserved.
5    Copyright 2019-2024 AdaCore
6 
7    Written by Alexandre Oliva <[email protected]>, based on the AFL++
8    GCC plugin.
9 
10    This program is free software: you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation, either version 3 of the License, or
13    (at your option) any later version.
14 
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19 
20    You should have received a copy of the GNU General Public License
21    along with this program.  If not, see <http://www.gnu.org/licenses/>.
22 
23  */
24 
25 #include "../include/config.h"
26 #include "../include/debug.h"
27 
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <unistd.h>
31 
32 #ifdef likely
33   #undef likely
34 #endif
35 #ifdef unlikely
36   #undef unlikely
37 #endif
38 
39 #include <list>
40 #include <string>
41 #include <fstream>
42 
43 #include <algorithm>
44 #include <fnmatch.h>
45 
46 #include <gcc-plugin.h>
47 #include <plugin-version.h>
48 #include <toplev.h>
49 #include <tree-pass.h>
50 #include <context.h>
51 #include <tree.h>
52 #include <gimplify.h>
53 #include <basic-block.h>
54 #include <tree-ssa-alias.h>
55 #include <gimple-expr.h>
56 #include <gimple.h>
57 #include <gimple-iterator.h>
58 #include <stringpool.h>
59 #include <gimple-ssa.h>
60 #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) >= \
61     60200                                               /* >= version 6.2.0 */
62   #include <tree-vrp.h>
63 #endif
64 #include <tree-ssanames.h>
65 #include <tree-phinodes.h>
66 #include <ssa-iterators.h>
67 
68 #include <intl.h>
69 
70 namespace {
71 
72 struct afl_base_pass : gimple_opt_pass {
73 
afl_base_passafl_base_pass74   afl_base_pass(bool quiet, bool debug, struct pass_data const &pd)
75       : gimple_opt_pass(pd, g), be_quiet(quiet), debug(debug) {
76 
77     initInstrumentList();
78 
79   }
80 
81   /* Are we outputting to a non-terminal, or running with AFL_QUIET
82      set?  */
83   const bool be_quiet;
84 
85   /* Are we running with AFL_DEBUG set?  */
86   const bool debug;
87 
88 #define report_fatal_error(msg) BADF(msg)
89 
90   std::list<std::string> allowListFiles;
91   std::list<std::string> allowListFunctions;
92   std::list<std::string> denyListFiles;
93   std::list<std::string> denyListFunctions;
94 
95   /* Note: this ignore check is also called in isInInstrumentList() */
isIgnoreFunctionafl_base_pass96   bool isIgnoreFunction(function *F) {
97 
98     // Starting from "LLVMFuzzer" these are functions used in libfuzzer based
99     // fuzzing campaign installations, e.g. oss-fuzz
100 
101     static constexpr const char *ignoreList[] = {
102 
103         "asan.",
104         "llvm.",
105         "sancov.",
106         "__ubsan_",
107         "ign.",
108         "__afl_",
109         "_fini",
110         "__libc_csu",
111         "__asan",
112         "__msan",
113         "__cmplog",
114         "__sancov",
115         "msan.",
116         "LLVMFuzzerM",
117         "LLVMFuzzerC",
118         "LLVMFuzzerI",
119         "__decide_deferred",
120         "maybe_duplicate_stderr",
121         "discard_output",
122         "close_stdout",
123         "dup_and_close_stderr",
124         "maybe_close_fd_mask",
125         "ExecuteFilesOnyByOne"
126 
127     };
128 
129     const char *name = IDENTIFIER_POINTER(DECL_NAME(F->decl));
130     int         len = IDENTIFIER_LENGTH(DECL_NAME(F->decl));
131 
132     for (auto const &ignoreListFunc : ignoreList) {
133 
134       if (strncmp(name, ignoreListFunc, len) == 0) { return true; }
135 
136     }
137 
138     return false;
139 
140   }
141 
initInstrumentListafl_base_pass142   void initInstrumentList() {
143 
144     char *allowlist = getenv("AFL_GCC_ALLOWLIST");
145     if (!allowlist) allowlist = getenv("AFL_GCC_INSTRUMENT_FILE");
146     if (!allowlist) allowlist = getenv("AFL_GCC_WHITELIST");
147     if (!allowlist) allowlist = getenv("AFL_LLVM_ALLOWLIST");
148     if (!allowlist) allowlist = getenv("AFL_LLVM_INSTRUMENT_FILE");
149     if (!allowlist) allowlist = getenv("AFL_LLVM_WHITELIST");
150     char *denylist = getenv("AFL_GCC_DENYLIST");
151     if (!denylist) denylist = getenv("AFL_GCC_BLOCKLIST");
152     if (!denylist) denylist = getenv("AFL_LLVM_DENYLIST");
153     if (!denylist) denylist = getenv("AFL_LLVM_BLOCKLIST");
154 
155     if (allowlist && denylist)
156       FATAL(
157           "You can only specify either AFL_GCC_ALLOWLIST or AFL_GCC_DENYLIST "
158           "but not both!");
159 
160     if (allowlist) {
161 
162       std::string   line;
163       std::ifstream fileStream;
164       fileStream.open(allowlist);
165       if (!fileStream) report_fatal_error("Unable to open AFL_GCC_ALLOWLIST");
166       getline(fileStream, line);
167 
168       while (fileStream) {
169 
170         int         is_file = -1;
171         std::size_t npos;
172         std::string original_line = line;
173 
174         line.erase(std::remove_if(line.begin(), line.end(), ::isspace),
175                    line.end());
176 
177         // remove # and following
178         if ((npos = line.find("#")) != std::string::npos)
179           line = line.substr(0, npos);
180 
181         if (line.compare(0, 4, "fun:") == 0) {
182 
183           is_file = 0;
184           line = line.substr(4);
185 
186         } else if (line.compare(0, 9, "function:") == 0) {
187 
188           is_file = 0;
189           line = line.substr(9);
190 
191         } else if (line.compare(0, 4, "src:") == 0) {
192 
193           is_file = 1;
194           line = line.substr(4);
195 
196         } else if (line.compare(0, 7, "source:") == 0) {
197 
198           is_file = 1;
199           line = line.substr(7);
200 
201         }
202 
203         if (line.find(":") != std::string::npos) {
204 
205           FATAL("invalid line in AFL_GCC_ALLOWLIST: %s", original_line.c_str());
206 
207         }
208 
209         if (line.length() > 0) {
210 
211           // if the entry contains / or . it must be a file
212           if (is_file == -1)
213             if (line.find("/") != std::string::npos ||
214                 line.find(".") != std::string::npos)
215               is_file = 1;
216           // otherwise it is a function
217 
218           if (is_file == 1)
219             allowListFiles.push_back(line);
220           else
221             allowListFunctions.push_back(line);
222 
223         }
224 
225         getline(fileStream, line);
226 
227       }
228 
229       if (debug)
230         DEBUGF("loaded allowlist with %zu file and %zu function entries\n",
231                allowListFiles.size(), allowListFunctions.size());
232 
233     }
234 
235     if (denylist) {
236 
237       std::string   line;
238       std::ifstream fileStream;
239       fileStream.open(denylist);
240       if (!fileStream) report_fatal_error("Unable to open AFL_GCC_DENYLIST");
241       getline(fileStream, line);
242 
243       while (fileStream) {
244 
245         int         is_file = -1;
246         std::size_t npos;
247         std::string original_line = line;
248 
249         line.erase(std::remove_if(line.begin(), line.end(), ::isspace),
250                    line.end());
251 
252         // remove # and following
253         if ((npos = line.find("#")) != std::string::npos)
254           line = line.substr(0, npos);
255 
256         if (line.compare(0, 4, "fun:") == 0) {
257 
258           is_file = 0;
259           line = line.substr(4);
260 
261         } else if (line.compare(0, 9, "function:") == 0) {
262 
263           is_file = 0;
264           line = line.substr(9);
265 
266         } else if (line.compare(0, 4, "src:") == 0) {
267 
268           is_file = 1;
269           line = line.substr(4);
270 
271         } else if (line.compare(0, 7, "source:") == 0) {
272 
273           is_file = 1;
274           line = line.substr(7);
275 
276         }
277 
278         if (line.find(":") != std::string::npos) {
279 
280           FATAL("invalid line in AFL_GCC_DENYLIST: %s", original_line.c_str());
281 
282         }
283 
284         if (line.length() > 0) {
285 
286           // if the entry contains / or . it must be a file
287           if (is_file == -1)
288             if (line.find("/") != std::string::npos ||
289                 line.find(".") != std::string::npos)
290               is_file = 1;
291           // otherwise it is a function
292 
293           if (is_file == 1)
294             denyListFiles.push_back(line);
295           else
296             denyListFunctions.push_back(line);
297 
298         }
299 
300         getline(fileStream, line);
301 
302       }
303 
304       if (debug)
305         DEBUGF("loaded denylist with %zu file and %zu function entries\n",
306                denyListFiles.size(), denyListFunctions.size());
307 
308     }
309 
310   }
311 
312   /* Returns the source file name attached to the function declaration F. If
313      there is no source location information, returns an empty string.  */
getSourceNameafl_base_pass314   std::string getSourceName(function *F) {
315 
316     return DECL_SOURCE_FILE(F->decl) ? DECL_SOURCE_FILE(F->decl) : "";
317 
318   }
319 
isInInstrumentListafl_base_pass320   bool isInInstrumentList(function *F) {
321 
322     bool return_default = true;
323 
324     // is this a function with code? If it is external we don't instrument it
325     // anyway and it can't be in the instrument file list. Or if it is it is
326     // ignored.
327     if (isIgnoreFunction(F)) return false;
328 
329     if (!denyListFiles.empty() || !denyListFunctions.empty()) {
330 
331       if (!denyListFunctions.empty()) {
332 
333         std::string instFunction = IDENTIFIER_POINTER(DECL_NAME(F->decl));
334 
335         for (std::list<std::string>::iterator it = denyListFunctions.begin();
336              it != denyListFunctions.end(); ++it) {
337 
338           /* We don't check for filename equality here because
339            * filenames might actually be full paths. Instead we
340            * check that the actual filename ends in the filename
341            * specified in the list. We also allow UNIX-style pattern
342            * matching */
343 
344           if (instFunction.length() >= it->length()) {
345 
346             if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) {
347 
348               if (debug)
349                 DEBUGF(
350                     "Function %s is in the deny function list, not "
351                     "instrumenting ... \n",
352                     instFunction.c_str());
353               return false;
354 
355             }
356 
357           }
358 
359         }
360 
361       }
362 
363       if (!denyListFiles.empty()) {
364 
365         std::string source_file = getSourceName(F);
366 
367         if (!source_file.empty()) {
368 
369           for (std::list<std::string>::iterator it = denyListFiles.begin();
370                it != denyListFiles.end(); ++it) {
371 
372             /* We don't check for filename equality here because
373              * filenames might actually be full paths. Instead we
374              * check that the actual filename ends in the filename
375              * specified in the list. We also allow UNIX-style pattern
376              * matching */
377 
378             if (source_file.length() >= it->length()) {
379 
380               if (fnmatch(("*" + *it).c_str(), source_file.c_str(), 0) == 0) {
381 
382                 return false;
383 
384               }
385 
386             }
387 
388           }
389 
390         } else {
391 
392           // we could not find out the location. in this case we say it is not
393           // in the instrument file list
394           if (!be_quiet)
395             WARNF(
396                 "No debug information found for function %s, will be "
397                 "instrumented (recompile with -g -O[1-3]).",
398                 IDENTIFIER_POINTER(DECL_NAME(F->decl)));
399 
400         }
401 
402       }
403 
404     }
405 
406     // if we do not have a instrument file list return true
407     if (!allowListFiles.empty() || !allowListFunctions.empty()) {
408 
409       return_default = false;
410 
411       if (!allowListFunctions.empty()) {
412 
413         std::string instFunction = IDENTIFIER_POINTER(DECL_NAME(F->decl));
414 
415         for (std::list<std::string>::iterator it = allowListFunctions.begin();
416              it != allowListFunctions.end(); ++it) {
417 
418           /* We don't check for filename equality here because
419            * filenames might actually be full paths. Instead we
420            * check that the actual filename ends in the filename
421            * specified in the list. We also allow UNIX-style pattern
422            * matching */
423 
424           if (instFunction.length() >= it->length()) {
425 
426             if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) {
427 
428               if (debug)
429                 DEBUGF(
430                     "Function %s is in the allow function list, instrumenting "
431                     "... \n",
432                     instFunction.c_str());
433               return true;
434 
435             }
436 
437           }
438 
439         }
440 
441       }
442 
443       if (!allowListFiles.empty()) {
444 
445         std::string source_file = getSourceName(F);
446 
447         if (!source_file.empty()) {
448 
449           for (std::list<std::string>::iterator it = allowListFiles.begin();
450                it != allowListFiles.end(); ++it) {
451 
452             /* We don't check for filename equality here because
453              * filenames might actually be full paths. Instead we
454              * check that the actual filename ends in the filename
455              * specified in the list. We also allow UNIX-style pattern
456              * matching */
457 
458             if (source_file.length() >= it->length()) {
459 
460               if (fnmatch(("*" + *it).c_str(), source_file.c_str(), 0) == 0) {
461 
462                 if (debug)
463                   DEBUGF(
464                       "Function %s is in the allowlist (%s), instrumenting ... "
465                       "\n",
466                       IDENTIFIER_POINTER(DECL_NAME(F->decl)),
467                       source_file.c_str());
468                 return true;
469 
470               }
471 
472             }
473 
474           }
475 
476         } else {
477 
478           // we could not find out the location. In this case we say it is not
479           // in the instrument file list
480           if (!be_quiet)
481             WARNF(
482                 "No debug information found for function %s, will not be "
483                 "instrumented (recompile with -g -O[1-3]).",
484                 IDENTIFIER_POINTER(DECL_NAME(F->decl)));
485           return false;
486 
487         }
488 
489       }
490 
491     }
492 
493     return return_default;
494 
495   }
496 
497 };
498 
499 }  // namespace
500 
501 // compatibility for older gcc versions
502 #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) >= \
503     60200                                               /* >= version 6.2.0 */
504   #define gimple gimple *
505 #else
506   #define gimple gimple
507 #endif
508 
509