1 /*
2 american fuzzy lop++ - LLVM LTO instrumentation pass
3 ----------------------------------------------------
4
5 Written by Marc Heuse <[email protected]>
6
7 Copyright 2019-2024 AFLplusplus Project. All rights reserved.
8
9 Licensed under the Apache License, Version 2.0 (the "License");
10 you may not use this file except in compliance with the License.
11 You may obtain a copy of the License at:
12
13 https://www.apache.org/licenses/LICENSE-2.0
14
15 This library is plugged into LLVM when invoking clang through afl-clang-lto.
16
17 */
18
19 #define AFL_LLVM_PASS
20
21 #include "config.h"
22 #include "debug.h"
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <string.h>
28 #include <sys/time.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <fcntl.h>
32 #include <ctype.h>
33
34 #include <list>
35 #include <string>
36 #include <fstream>
37 #include <set>
38
39 #include "llvm/Config/llvm-config.h"
40 #include "llvm/ADT/Statistic.h"
41 #include "llvm/IR/IRBuilder.h"
42 #if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */
43 #include "llvm/Passes/PassPlugin.h"
44 #include "llvm/Passes/PassBuilder.h"
45 #include "llvm/IR/PassManager.h"
46 #else
47 #include "llvm/IR/LegacyPassManager.h"
48 #endif
49 #include "llvm/IR/BasicBlock.h"
50 #include "llvm/IR/Module.h"
51 #include "llvm/IR/DebugInfo.h"
52 #include "llvm/IR/CFG.h"
53 #include "llvm/IR/Verifier.h"
54 #include "llvm/Support/Debug.h"
55 #include "llvm/Support/raw_ostream.h"
56 #if LLVM_VERSION_MAJOR < 17
57 #include "llvm/Transforms/IPO/PassManagerBuilder.h"
58 #endif
59 #include "llvm/Transforms/Utils/BasicBlockUtils.h"
60 #include "llvm/Analysis/LoopInfo.h"
61 #include "llvm/Analysis/ValueTracking.h"
62 #include "llvm/Pass.h"
63 #include "llvm/IR/Constants.h"
64
65 #include "afl-llvm-common.h"
66
67 #ifndef O_DSYNC
68 #define O_DSYNC O_SYNC
69 #endif
70
71 using namespace llvm;
72
73 namespace {
74
75 #if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */
76 class AFLdict2filePass : public PassInfoMixin<AFLdict2filePass> {
77
78 std::ofstream of;
79 void dict2file(u8 *, u32);
80
81 public:
AFLdict2filePass()82 AFLdict2filePass() {
83
84 #else
85
86 class AFLdict2filePass : public ModulePass {
87
88 std::ofstream of;
89 void dict2file(u8 *, u32);
90
91 public:
92 static char ID;
93
94 AFLdict2filePass() : ModulePass(ID) {
95
96 #endif
97
98 if (getenv("AFL_DEBUG")) debug = 1;
99
100 }
101
102 #if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */
103 PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM);
104 #else
105 bool runOnModule(Module &M) override;
106 #endif
107
108 };
109
110 } // namespace
111
112 #if LLVM_MAJOR >= 11
113 extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK
llvmGetPassPluginInfo()114 llvmGetPassPluginInfo() {
115
116 return {LLVM_PLUGIN_API_VERSION, "AFLdict2filePass", "v0.1",
117 /* lambda to insert our pass into the pass pipeline. */
118 [](PassBuilder &PB) {
119
120 #if LLVM_VERSION_MAJOR <= 13
121 using OptimizationLevel = typename PassBuilder::OptimizationLevel;
122 #endif
123 PB.registerOptimizerLastEPCallback(
124 [](ModulePassManager &MPM, OptimizationLevel OL) {
125
126 MPM.addPass(AFLdict2filePass());
127
128 });
129
130 }};
131
132 }
133
134 #else
135 char AFLdict2filePass::ID = 0;
136 #endif
137
dict2file(u8 * mem,u32 len)138 void AFLdict2filePass::dict2file(u8 *mem, u32 len) {
139
140 u32 i, j, binary = 0;
141 char line[MAX_AUTO_EXTRA * 8], tmp[8];
142
143 strcpy(line, "\"");
144 j = 1;
145 for (i = 0; i < len; i++) {
146
147 if (isprint(mem[i]) && mem[i] != '\\' && mem[i] != '"') {
148
149 line[j++] = mem[i];
150
151 } else {
152
153 if (i + 1 != len || mem[i] != 0 || binary || len == 4 || len == 8) {
154
155 line[j] = 0;
156 sprintf(tmp, "\\x%02x", (u8)mem[i]);
157 strcat(line, tmp);
158 j = strlen(line);
159
160 }
161
162 binary = 1;
163
164 }
165
166 }
167
168 line[j] = 0;
169 strcat(line, "\"\n");
170 of << line;
171 of.flush();
172
173 if (!be_quiet) fprintf(stderr, "Found dictionary token: %s", line);
174
175 }
176
177 #if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */
run(Module & M,ModuleAnalysisManager & MAM)178 PreservedAnalyses AFLdict2filePass::run(Module &M, ModuleAnalysisManager &MAM) {
179
180 #else
181 bool AFLdict2filePass::runOnModule(Module &M) {
182
183 #endif
184
185 DenseMap<Value *, std::string *> valueMap;
186 char *ptr;
187 int found = 0, handle_main = 1;
188
189 /* Show a banner */
190 setvbuf(stdout, NULL, _IONBF, 0);
191
192 if ((isatty(2) && !getenv("AFL_QUIET")) || debug) {
193
194 SAYF(cCYA "afl-llvm-dict2file" VERSION cRST
195 " by Marc \"vanHauser\" Heuse <[email protected]>\n");
196
197 } else {
198
199 be_quiet = 1;
200
201 }
202
203 if (getenv("AFL_LLVM_DICT2FILE_NO_MAIN")) { handle_main = 0; }
204
205 scanForDangerousFunctions(&M);
206
207 ptr = getenv("AFL_LLVM_DICT2FILE");
208
209 if (!ptr) {
210
211 #if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */
212 auto PA = PreservedAnalyses::all();
213 return PA;
214 #else
215 return true;
216 #endif
217
218 }
219
220 if (*ptr != '/')
221 FATAL("AFL_LLVM_DICT2FILE is not set to an absolute path: %s", ptr);
222
223 of.open(ptr, std::ofstream::out | std::ofstream::app);
224 if (!of.is_open()) PFATAL("Could not open/create %s.", ptr);
225
226 /* Instrument all the things! */
227
228 for (auto &F : M) {
229
230 if (!handle_main &&
231 (!F.getName().compare("main") || !F.getName().compare("_main"))) {
232
233 continue;
234
235 }
236
237 if (isIgnoreFunction(&F)) { continue; }
238 if (!isInInstrumentList(&F, MNAME) || !F.size()) { continue; }
239
240 /* Some implementation notes.
241 *
242 * We try to handle 3 cases:
243 * - memcmp("foo", arg, 3) <- literal string
244 * - static char globalvar[] = "foo";
245 * memcmp(globalvar, arg, 3) <- global variable
246 * - char localvar[] = "foo";
247 * memcmp(locallvar, arg, 3) <- local variable
248 *
249 * The local variable case is the hardest. We can only detect that
250 * case if there is no reassignment or change in the variable.
251 * And it might not work across llvm version.
252 * What we do is hooking the initializer function for local variables
253 * (llvm.memcpy.p0i8.p0i8.i64) and note the string and the assigned
254 * variable. And if that variable is then used in a compare function
255 * we use that noted string.
256 * This seems not to work for tokens that have a size <= 4 :-(
257 *
258 * - if the compared length is smaller than the string length we
259 * save the full string. This is likely better for fuzzing but
260 * might be wrong in a few cases depending on optimizers
261 *
262 * - not using StringRef because there is a bug in the llvm 11
263 * checkout I am using which sometimes points to wrong strings
264 *
265 * Over and out. Took me a full day. damn. mh/vh
266 */
267
268 for (auto &BB : F) {
269
270 for (auto &IN : BB) {
271
272 CallInst *callInst = nullptr;
273 CmpInst *cmpInst = nullptr;
274
275 if ((cmpInst = dyn_cast<CmpInst>(&IN))) {
276
277 Value *op = cmpInst->getOperand(1);
278 ConstantInt *ilen = dyn_cast<ConstantInt>(op);
279
280 /* We skip > 64 bit integers. why? first because their value is
281 difficult to obtain, and second because clang does not support
282 literals > 64 bit (as of llvm 12) */
283
284 if (ilen && ilen->uge(0xffffffffffffffff) == false) {
285
286 u64 val2 = 0, val = ilen->getZExtValue();
287 u32 len = 0;
288 if (val > 0x10000 && val < 0xffffffff) len = 4;
289 if (val > 0x100000001 && val < 0xffffffffffffffff) len = 8;
290
291 if (len) {
292
293 auto c = cmpInst->getPredicate();
294
295 switch (c) {
296
297 case CmpInst::FCMP_OGT: // fall through
298 case CmpInst::FCMP_OLE: // fall through
299 case CmpInst::ICMP_SLE: // fall through
300 case CmpInst::ICMP_SGT:
301
302 // signed comparison and it is a negative constant
303 if ((len == 4 && (val & 80000000)) ||
304 (len == 8 && (val & 8000000000000000))) {
305
306 if ((val & 0xffff) != 1) val2 = val - 1;
307 break;
308
309 }
310
311 // fall through
312
313 case CmpInst::FCMP_UGT: // fall through
314 case CmpInst::FCMP_ULE: // fall through
315 case CmpInst::ICMP_UGT: // fall through
316 case CmpInst::ICMP_ULE:
317 if ((val & 0xffff) != 0xfffe) val2 = val + 1;
318 break;
319
320 case CmpInst::FCMP_OLT: // fall through
321 case CmpInst::FCMP_OGE: // fall through
322 case CmpInst::ICMP_SLT: // fall through
323 case CmpInst::ICMP_SGE:
324
325 // signed comparison and it is a negative constant
326 if ((len == 4 && (val & 80000000)) ||
327 (len == 8 && (val & 8000000000000000))) {
328
329 if ((val & 0xffff) != 1) val2 = val - 1;
330 break;
331
332 }
333
334 // fall through
335
336 case CmpInst::FCMP_ULT: // fall through
337 case CmpInst::FCMP_UGE: // fall through
338 case CmpInst::ICMP_ULT: // fall through
339 case CmpInst::ICMP_UGE:
340 if ((val & 0xffff) != 1) val2 = val - 1;
341 break;
342
343 default:
344 val2 = 0;
345
346 }
347
348 dict2file((u8 *)&val, len);
349 found++;
350 if (val2) {
351
352 dict2file((u8 *)&val2, len);
353 found++;
354
355 }
356
357 }
358
359 }
360
361 }
362
363 if ((callInst = dyn_cast<CallInst>(&IN))) {
364
365 bool isStrcmp = true;
366 bool isMemcmp = true;
367 bool isStrncmp = true;
368 bool isStrcasecmp = true;
369 bool isStrncasecmp = true;
370 bool isIntMemcpy = true;
371 bool isStdString = true;
372 bool isStrstr = true;
373 size_t optLen = 0;
374
375 Function *Callee = callInst->getCalledFunction();
376 if (!Callee) continue;
377 if (callInst->getCallingConv() != llvm::CallingConv::C) continue;
378 std::string FuncName = Callee->getName().str();
379 isStrcmp &=
380 (!FuncName.compare("strcmp") || !FuncName.compare("xmlStrcmp") ||
381 !FuncName.compare("xmlStrEqual") ||
382 !FuncName.compare("g_strcmp0") ||
383 !FuncName.compare("curl_strequal") ||
384 !FuncName.compare("strcsequal"));
385 isMemcmp &=
386 (!FuncName.compare("memcmp") || !FuncName.compare("bcmp") ||
387 !FuncName.compare("CRYPTO_memcmp") ||
388 !FuncName.compare("OPENSSL_memcmp") ||
389 !FuncName.compare("memcmp_const_time") ||
390 !FuncName.compare("memcmpct"));
391 isStrncmp &= (!FuncName.compare("strncmp") ||
392 !FuncName.compare("xmlStrncmp") ||
393 !FuncName.compare("curl_strnequal"));
394 isStrcasecmp &= (!FuncName.compare("strcasecmp") ||
395 !FuncName.compare("stricmp") ||
396 !FuncName.compare("ap_cstr_casecmp") ||
397 !FuncName.compare("OPENSSL_strcasecmp") ||
398 !FuncName.compare("xmlStrcasecmp") ||
399 !FuncName.compare("g_strcasecmp") ||
400 !FuncName.compare("g_ascii_strcasecmp") ||
401 !FuncName.compare("Curl_strcasecompare") ||
402 !FuncName.compare("Curl_safe_strcasecompare") ||
403 !FuncName.compare("cmsstrcasecmp"));
404 isStrncasecmp &= (!FuncName.compare("strncasecmp") ||
405 !FuncName.compare("strnicmp") ||
406 !FuncName.compare("ap_cstr_casecmpn") ||
407 !FuncName.compare("OPENSSL_strncasecmp") ||
408 !FuncName.compare("xmlStrncasecmp") ||
409 !FuncName.compare("g_ascii_strncasecmp") ||
410 !FuncName.compare("Curl_strncasecompare") ||
411 !FuncName.compare("g_strncasecmp"));
412 isStrstr &= (!FuncName.compare("strstr") ||
413 !FuncName.compare("g_strstr_len") ||
414 !FuncName.compare("ap_strcasestr") ||
415 !FuncName.compare("xmlStrstr") ||
416 !FuncName.compare("xmlStrcasestr") ||
417 !FuncName.compare("g_str_has_prefix") ||
418 !FuncName.compare("g_str_has_suffix"));
419 isIntMemcpy &= !FuncName.compare("llvm.memcpy.p0i8.p0i8.i64");
420 isStdString &= ((FuncName.find("basic_string") != std::string::npos &&
421 FuncName.find("compare") != std::string::npos) ||
422 (FuncName.find("basic_string") != std::string::npos &&
423 FuncName.find("find") != std::string::npos));
424
425 if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp &&
426 !isStrncasecmp && !isIntMemcpy && !isStdString && !isStrstr)
427 continue;
428
429 /* Verify the strcmp/memcmp/strncmp/strcasecmp/strncasecmp function
430 * prototype */
431 FunctionType *FT = Callee->getFunctionType();
432
433 isStrstr &=
434 FT->getNumParams() == 2 &&
435 FT->getParamType(0) == FT->getParamType(1) &&
436 FT->getParamType(0) ==
437 IntegerType::getInt8Ty(M.getContext())->getPointerTo(0);
438 isStrcmp &=
439 FT->getNumParams() == 2 && FT->getReturnType()->isIntegerTy(32) &&
440 FT->getParamType(0) == FT->getParamType(1) &&
441 FT->getParamType(0) ==
442 IntegerType::getInt8Ty(M.getContext())->getPointerTo(0);
443 isStrcasecmp &=
444 FT->getNumParams() == 2 && FT->getReturnType()->isIntegerTy(32) &&
445 FT->getParamType(0) == FT->getParamType(1) &&
446 FT->getParamType(0) ==
447 IntegerType::getInt8Ty(M.getContext())->getPointerTo(0);
448 isMemcmp &= FT->getNumParams() == 3 &&
449 FT->getReturnType()->isIntegerTy(32) &&
450 FT->getParamType(0)->isPointerTy() &&
451 FT->getParamType(1)->isPointerTy() &&
452 FT->getParamType(2)->isIntegerTy();
453 isStrncmp &=
454 FT->getNumParams() == 3 && FT->getReturnType()->isIntegerTy(32) &&
455 FT->getParamType(0) == FT->getParamType(1) &&
456 FT->getParamType(0) ==
457 IntegerType::getInt8Ty(M.getContext())->getPointerTo(0) &&
458 FT->getParamType(2)->isIntegerTy();
459 isStrncasecmp &=
460 FT->getNumParams() == 3 && FT->getReturnType()->isIntegerTy(32) &&
461 FT->getParamType(0) == FT->getParamType(1) &&
462 FT->getParamType(0) ==
463 IntegerType::getInt8Ty(M.getContext())->getPointerTo(0) &&
464 FT->getParamType(2)->isIntegerTy();
465 isStdString &= FT->getNumParams() >= 2 &&
466 FT->getParamType(0)->isPointerTy() &&
467 FT->getParamType(1)->isPointerTy();
468
469 if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp &&
470 !isStrncasecmp && !isIntMemcpy && !isStdString && !isStrstr)
471 continue;
472
473 /* is a str{n,}{case,}cmp/memcmp, check if we have
474 * str{case,}cmp(x, "const") or str{case,}cmp("const", x)
475 * strn{case,}cmp(x, "const", ..) or strn{case,}cmp("const", x, ..)
476 * memcmp(x, "const", ..) or memcmp("const", x, ..) */
477 Value *Str1P = callInst->getArgOperand(0),
478 *Str2P = callInst->getArgOperand(1);
479 std::string Str1, Str2;
480 StringRef TmpStr;
481 bool HasStr1;
482 getConstantStringInfo(Str1P, TmpStr);
483
484 if (isStrstr || TmpStr.empty()) {
485
486 HasStr1 = false;
487
488 } else {
489
490 HasStr1 = true;
491 Str1 = TmpStr.str();
492
493 }
494
495 bool HasStr2;
496 getConstantStringInfo(Str2P, TmpStr);
497 if (TmpStr.empty()) {
498
499 HasStr2 = false;
500
501 } else {
502
503 HasStr2 = true;
504 Str2 = TmpStr.str();
505
506 }
507
508 if (debug)
509 fprintf(stderr, "F:%s %p(%s)->\"%s\"(%s) %p(%s)->\"%s\"(%s)\n",
510 FuncName.c_str(), (void *)Str1P,
511 Str1P->getName().str().c_str(), Str1.c_str(),
512 HasStr1 == true ? "true" : "false", (void *)Str2P,
513 Str2P->getName().str().c_str(), Str2.c_str(),
514 HasStr2 == true ? "true" : "false");
515
516 // we handle the 2nd parameter first because of llvm memcpy
517 if (!HasStr2) {
518
519 auto *Ptr = dyn_cast<ConstantExpr>(Str2P);
520 if (Ptr && Ptr->getOpcode() == Instruction::GetElementPtr) {
521
522 if (auto *Var = dyn_cast<GlobalVariable>(Ptr->getOperand(0))) {
523
524 if (Var->hasInitializer()) {
525
526 if (auto *Array =
527 dyn_cast<ConstantDataArray>(Var->getInitializer())) {
528
529 HasStr2 = true;
530 Str2 = Array->getRawDataValues().str();
531
532 }
533
534 }
535
536 }
537
538 }
539
540 }
541
542 // for the internal memcpy routine we only care for the second
543 // parameter and are not reporting anything.
544 if (isIntMemcpy == true) {
545
546 if (HasStr2 == true) {
547
548 Value *op2 = callInst->getArgOperand(2);
549 ConstantInt *ilen = dyn_cast<ConstantInt>(op2);
550 if (ilen) {
551
552 uint64_t literalLength = Str2.length();
553 uint64_t optLength = ilen->getZExtValue();
554 if (optLength > literalLength + 1) {
555
556 optLength = Str2.length() + 1;
557
558 }
559
560 if (literalLength + 1 == optLength) {
561
562 Str2.append("\0", 1); // add null byte
563
564 }
565
566 }
567
568 valueMap[Str1P] = new std::string(Str2);
569
570 if (debug) {
571
572 fprintf(stderr, "Saved: %s for %p\n", Str2.c_str(),
573 (void *)Str1P);
574
575 }
576
577 continue;
578
579 }
580
581 continue;
582
583 }
584
585 // Neither a literal nor a global variable?
586 // maybe it is a local variable that we saved
587 if (!HasStr2) {
588
589 std::string *strng = valueMap[Str2P];
590 if (strng && !strng->empty()) {
591
592 Str2 = *strng;
593 HasStr2 = true;
594 if (debug)
595 fprintf(stderr, "Filled2: %s for %p\n", strng->c_str(),
596 (void *)Str2P);
597
598 }
599
600 }
601
602 if (!HasStr1) {
603
604 auto Ptr = dyn_cast<ConstantExpr>(Str1P);
605
606 if (Ptr && Ptr->getOpcode() == Instruction::GetElementPtr) {
607
608 if (auto *Var = dyn_cast<GlobalVariable>(Ptr->getOperand(0))) {
609
610 if (Var->hasInitializer()) {
611
612 if (auto *Array =
613 dyn_cast<ConstantDataArray>(Var->getInitializer())) {
614
615 HasStr1 = true;
616 Str1 = Array->getRawDataValues().str();
617
618 }
619
620 }
621
622 }
623
624 }
625
626 }
627
628 // Neither a literal nor a global variable?
629 // maybe it is a local variable that we saved
630 if (!HasStr1) {
631
632 std::string *strng = valueMap[Str1P];
633 if (strng && !strng->empty()) {
634
635 Str1 = *strng;
636 HasStr1 = true;
637 if (debug)
638 fprintf(stderr, "Filled1: %s for %p\n", strng->c_str(),
639 (void *)Str1P);
640
641 }
642
643 }
644
645 /* handle cases of one string is const, one string is variable */
646 if (!(HasStr1 ^ HasStr2)) continue;
647
648 std::string thestring;
649
650 if (HasStr1)
651 thestring = Str1;
652 else
653 thestring = Str2;
654
655 optLen = thestring.length();
656
657 if (optLen < 2 || (optLen == 2 && !thestring[1])) { continue; }
658
659 if (isMemcmp || isStrncmp || isStrncasecmp) {
660
661 Value *op2 = callInst->getArgOperand(2);
662 ConstantInt *ilen = dyn_cast<ConstantInt>(op2);
663
664 if (ilen) {
665
666 uint64_t literalLength = optLen;
667 optLen = ilen->getZExtValue();
668 if (optLen > thestring.length() + 1) {
669
670 optLen = thestring.length() + 1;
671
672 }
673
674 if (optLen < 2) { continue; }
675 if (literalLength + 1 == optLen) { // add null byte
676
677 thestring.append("\0", 1);
678
679 }
680
681 }
682
683 }
684
685 // add null byte if this is a string compare function and a null
686 // was not already added
687 if (!isMemcmp) {
688
689 /*
690 if (addedNull == false && thestring[optLen - 1] != '\0')
691 {
692
693 thestring.append("\0", 1); // add null byte
694 optLen++;
695
696 }
697
698 */
699 if (!isStdString && thestring.find('\0', 0) != std::string::npos) {
700
701 // ensure we do not have garbage
702 size_t offset = thestring.find('\0', 0);
703 if (offset + 1 < optLen) optLen = offset + 1;
704 thestring = thestring.substr(0, optLen);
705
706 }
707
708 }
709
710 // we take the longer string, even if the compare was to a
711 // shorter part. Note that depending on the optimizer of the
712 // compiler this can be wrong, but it is more likely that this
713 // is helping the fuzzer
714 if (optLen != thestring.length()) optLen = thestring.length();
715 if (optLen > MAX_AUTO_EXTRA) optLen = MAX_AUTO_EXTRA;
716 if (optLen < 3) // too short? skip
717 continue;
718
719 ptr = (char *)thestring.c_str();
720
721 dict2file((u8 *)ptr, optLen);
722 found++;
723
724 }
725
726 }
727
728 }
729
730 }
731
732 of.close();
733
734 /* Say something nice. */
735
736 if (!be_quiet) {
737
738 if (!found)
739 OKF("No entries for a dictionary found.");
740 else
741 OKF("Wrote %d entries to the dictionary file.\n", found);
742
743 }
744
745 #if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */
746 auto PA = PreservedAnalyses::all();
747 return PA;
748 #else
749 return true;
750 #endif
751
752 }
753
754 #if LLVM_VERSION_MAJOR < 11 /* use old pass manager */
755 static void registerAFLdict2filePass(const PassManagerBuilder &,
756 legacy::PassManagerBase &PM) {
757
758 PM.add(new AFLdict2filePass());
759
760 }
761
762 static RegisterPass<AFLdict2filePass> X("afl-dict2file",
763 "AFL++ dict2file instrumentation pass",
764 false, false);
765
766 static RegisterStandardPasses RegisterAFLdict2filePass(
767 PassManagerBuilder::EP_OptimizerLast, registerAFLdict2filePass);
768
769 static RegisterStandardPasses RegisterAFLdict2filePass0(
770 PassManagerBuilder::EP_EnabledOnOptLevel0, registerAFLdict2filePass);
771
772 #endif
773
774