xref: /aosp_15_r20/external/AFLplusplus/instrumentation/afl-llvm-dict2file.so.cc (revision 08b48e0b10e97b33e7b60c5b6e2243bd915777f2)
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