• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# llvm_mode persistent mode
2
3## 1) Introduction
4
5In persistent mode, AFL++ fuzzes a target multiple times in a single forked
6process, instead of forking a new process for each fuzz execution. This is the
7most effective way to fuzz, as the speed can easily be x10 or x20 times faster
8without any disadvantages. *All professional fuzzing uses this mode.*
9
10Persistent mode requires that the target can be called in one or more functions,
11and that it's state can be completely reset so that multiple calls can be
12performed without resource leaks, and that earlier runs will have no impact on
13future runs. An indicator for this is the `stability` value in the `afl-fuzz`
14UI. If this decreases to lower values in persistent mode compared to
15non-persistent mode, then the fuzz target keeps state.
16
17Examples can be found in [utils/persistent_mode](../utils/persistent_mode).
18
19## 2) TL;DR:
20
21Example `fuzz_target.c`:
22
23```c
24#include "what_you_need_for_your_target.h"
25
26__AFL_FUZZ_INIT();
27
28main() {
29
30  // anything else here, e.g. command line arguments, initialization, etc.
31
32#ifdef __AFL_HAVE_MANUAL_CONTROL
33  __AFL_INIT();
34#endif
35
36  unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF;  // must be after __AFL_INIT
37                                                 // and before __AFL_LOOP!
38
39  while (__AFL_LOOP(10000)) {
40
41    int len = __AFL_FUZZ_TESTCASE_LEN;  // don't use the macro directly in a
42                                        // call!
43
44    if (len < 8) continue;  // check for a required/useful minimum input length
45
46    /* Setup function call, e.g. struct target *tmp = libtarget_init() */
47    /* Call function to be fuzzed, e.g.: */
48    target_function(buf, len);
49    /* Reset state. e.g. libtarget_free(tmp) */
50
51  }
52
53  return 0;
54
55}
56```
57
58And then compile:
59
60```
61afl-clang-fast -o fuzz_target fuzz_target.c -lwhat_you_need_for_your_target
62```
63
64And that is it! The speed increase is usually x10 to x20.
65
66If you want to be able to compile the target without afl-clang-fast/lto, then
67add this just after the includes:
68
69```c
70#ifndef __AFL_FUZZ_TESTCASE_LEN
71  ssize_t fuzz_len;
72  #define __AFL_FUZZ_TESTCASE_LEN fuzz_len
73  unsigned char fuzz_buf[1024000];
74  #define __AFL_FUZZ_TESTCASE_BUF fuzz_buf
75  #define __AFL_FUZZ_INIT() void sync(void);
76  #define __AFL_LOOP(x) ((fuzz_len = read(0, fuzz_buf, sizeof(fuzz_buf))) > 0 ? 1 : 0)
77  #define __AFL_INIT() sync()
78#endif
79```
80
81## 3) Deferred initialization
82
83AFL++ tries to optimize performance by executing the targeted binary just once,
84stopping it just before `main()`, and then cloning this "main" process to get a
85steady supply of targets to fuzz.
86
87Although this approach eliminates much of the OS-, linker- and libc-level costs
88of executing the program, it does not always help with binaries that perform
89other time-consuming initialization steps - say, parsing a large config file
90before getting to the fuzzed data.
91
92In such cases, it's beneficial to initialize the forkserver a bit later, once
93most of the initialization work is already done, but before the binary attempts
94to read the fuzzed input and parse it; in some cases, this can offer a 10x+
95performance gain. You can implement delayed initialization in LLVM mode in a
96fairly simple way.
97
98First, find a suitable location in the code where the delayed cloning can take
99place. This needs to be done with *extreme* care to avoid breaking the binary.
100In particular, the program will probably malfunction if you select a location
101after:
102
103- The creation of any vital threads or child processes - since the forkserver
104  can't clone them easily.
105
106- The initialization of timers via `setitimer()` or equivalent calls.
107
108- The creation of temporary files, network sockets, offset-sensitive file
109  descriptors, and similar shared-state resources - but only provided that their
110  state meaningfully influences the behavior of the program later on.
111
112- Any access to the fuzzed input, including reading the metadata about its size.
113
114With the location selected, add this code in the appropriate spot:
115
116```c
117#ifdef __AFL_HAVE_MANUAL_CONTROL
118  __AFL_INIT();
119#endif
120```
121
122You don't need the #ifdef guards, but including them ensures that the program
123will keep working normally when compiled with a tool other than afl-clang-fast/
124afl-clang-lto/afl-gcc-fast.
125
126Finally, recompile the program with afl-clang-fast/afl-clang-lto/afl-gcc-fast
127(afl-gcc or afl-clang will *not* generate a deferred-initialization binary) -
128and you should be all set!
129
130## 4) Persistent mode
131
132Some libraries provide APIs that are stateless, or whose state can be reset in
133between processing different input files. When such a reset is performed, a
134single long-lived process can be reused to try out multiple test cases,
135eliminating the need for repeated `fork()` calls and the associated OS overhead.
136
137The basic structure of the program that does this would be:
138
139```c
140  while (__AFL_LOOP(1000)) {
141
142    /* Read input data. */
143    /* Call library code to be fuzzed. */
144    /* Reset state. */
145
146  }
147
148  /* Exit normally. */
149```
150
151The numerical value specified within the loop controls the maximum number of
152iterations before AFL++ will restart the process from scratch. This minimizes
153the impact of memory leaks and similar glitches; 1000 is a good starting point,
154and going much higher increases the likelihood of hiccups without giving you any
155real performance benefits.
156
157A more detailed template is shown in
158[utils/persistent_mode](../utils/persistent_mode). Similarly to the deferred
159initialization, the feature works only with afl-clang-fast; `#ifdef` guards can
160be used to suppress it when using other compilers.
161
162Note that as with the deferred initialization, the feature is easy to misuse; if
163you do not fully reset the critical state, you may end up with false positives
164or waste a whole lot of CPU power doing nothing useful at all. Be particularly
165wary of memory leaks and of the state of file descriptors.
166
167When running in this mode, the execution paths will inherently vary a bit
168depending on whether the input loop is being entered for the first time or
169executed again.
170
171## 5) Shared memory fuzzing
172
173You can speed up the fuzzing process even more by receiving the fuzzing data via
174shared memory instead of stdin or files. This is a further speed multiplier of
175about 2x.
176
177Setting this up is very easy:
178
179After the includes set the following macro:
180
181```c
182__AFL_FUZZ_INIT();
183```
184
185Directly at the start of main - or if you are using the deferred forkserver with
186`__AFL_INIT()`, then *after* `__AFL_INIT()`:
187
188```c
189  unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF;
190```
191
192Then as first line after the `__AFL_LOOP` while loop:
193
194```c
195  int len = __AFL_FUZZ_TESTCASE_LEN;
196```
197
198And that is all!