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