xref: /aosp_15_r20/external/AFLplusplus/instrumentation/README.lto.md (revision 08b48e0b10e97b33e7b60c5b6e2243bd915777f2)
1# afl-clang-lto - collision free instrumentation at link time
2
3## TL;DR:
4
5This version requires a LLVM 12 or newer.
6
71. Use afl-clang-lto/afl-clang-lto++ because the resulting binaries run
8   slightly faster and give better coverage.
9
102. You can use it together with COMPCOV, COMPLOG and the instrument file
11   listing features.
12
133. It only works with LLVM 12 or newer.
14
154. AUTODICTIONARY feature (see below)
16
175. If any problems arise, be sure to set `AR=llvm-ar RANLIB=llvm-ranlib AS=llvm-as`.
18   Some targets might need `LD=afl-clang-lto` and others `LD=afl-ld-lto`.
19
20## Introduction and problem description
21
22A big issue with how vanilla AFL worked was that the basic block IDs that are
23set during compilation are random - and hence naturally the larger the number
24of instrumented locations, the higher the number of edge collisions are in the
25map. This can result in not discovering new paths and therefore degrade the
26efficiency of the fuzzing process.
27
28*This issue is underestimated in the fuzzing community* With a 2^16 = 64kb
29standard map at already 256 instrumented blocks, there is on average one
30collision. On average, a target has 10.000 to 50.000 instrumented blocks, hence
31the real collisions are between 750-18.000!
32
33Note that PCGUARD (our own modified implementation and the SANCOV PCGUARD
34implementation from libfuzzer) also provides collision free coverage.
35It is a bit slower though and can a few targets with very early constructors.
36
37* We instrument at link time when we have all files pre-compiled.
38* To instrument at link time, we compile in LTO (link time optimization) mode.
39* Our compiler (afl-clang-lto/afl-clang-lto++) takes care of setting the correct
40  LTO options and runs our own afl-ld linker instead of the system linker.
41* The LLVM linker collects all LTO files to link and instruments them so that we
42  have non-colliding edge coverage.
43* We use a new (for afl) edge coverage - which is the same as in llvm
44  -fsanitize=coverage edge coverage mode. :)
45
46The result:
47
48* 10-25% speed gain compared to llvm_mode
49* guaranteed non-colliding edge coverage
50* The compile time, especially for binaries to an instrumented library, can be
51  much (and sometimes much much) longer.
52
53Example build output from a libtiff build:
54
55```
56libtool: link: afl-clang-lto -g -O2 -Wall -W -o thumbnail thumbnail.o  ../libtiff/.libs/libtiff.a ../port/.libs/libport.a -llzma -ljbig -ljpeg -lz -lm
57afl-clang-lto++2.63d by Marc "vanHauser" Heuse <[email protected]> in mode LTO
58afl-llvm-lto++2.63d by Marc "vanHauser" Heuse <[email protected]>
59AUTODICTIONARY: 11 strings found
60[+] Instrumented 12071 locations with no collisions (on average 1046 collisions would be in afl-gcc/afl-clang-fast) (non-hardened mode).
61```
62
63## Getting LLVM 12+
64
65### Installing llvm
66
67The best way to install LLVM is to follow [https://apt.llvm.org/](https://apt.llvm.org/)
68
69e.g. for LLVM 15:
70```
71wget https://apt.llvm.org/llvm.sh
72chmod +x llvm.sh
73sudo ./llvm.sh 15 all
74```
75
76LLVM 12 to 18 should be available in all current Linux repositories.
77
78## How to build afl-clang-lto
79
80That part is easy.
81Just set `LLVM_CONFIG` to the llvm-config-VERSION and build AFL++, e.g. for
82LLVM 15:
83
84```
85cd ~/AFLplusplus
86export LLVM_CONFIG=llvm-config-15
87make
88sudo make install
89```
90
91## How to use afl-clang-lto
92
93Just use afl-clang-lto like you did with afl-clang-fast or afl-gcc.
94
95Also, the instrument file listing (AFL_LLVM_ALLOWLIST/AFL_LLVM_DENYLIST ->
96[README.instrument_list.md](README.instrument_list.md)) and laf-intel/compcov
97(AFL_LLVM_LAF_* -> [README.laf-intel.md](README.laf-intel.md)) work.
98
99Example (note that you might need to add the version, e.g. `llvm-ar-15`:
100
101```
102CC=afl-clang-lto CXX=afl-clang-lto++ RANLIB=llvm-ranlib AR=llvm-ar AS=llvm-as ./configure
103make
104```
105
106NOTE: some targets also need to set the linker, try both `afl-clang-lto` and
107`afl-ld-lto` for `LD=` before `configure`.
108
109## Instrumenting shared libraries
110
111Note: this is highly discouraged! Try to compile to static libraries with
112afl-clang-lto instead of shared libraries!
113
114To make instrumented shared libraries work with afl-clang-lto, you have to do
115quite some extra steps.
116
117Every shared library you want to instrument has to be individually compiled. The
118environment variable `AFL_LLVM_LTO_DONTWRITEID=1` has to be set during
119compilation. Additionally, the environment variable `AFL_LLVM_LTO_STARTID` has
120to be set to the added edge count values of all previous compiled instrumented
121shared libraries for that target. E.g., for the first shared library this would
122be `AFL_LLVM_LTO_STARTID=0` and afl-clang-lto will then report how many edges
123have been instrumented (let's say it reported 1000 instrumented edges). The
124second shared library then has to be set to that value
125(`AFL_LLVM_LTO_STARTID=1000` in our example), for the third to all previous
126counts added, etc.
127
128The final program compilation step then may *not* have
129`AFL_LLVM_LTO_DONTWRITEID` set, and `AFL_LLVM_LTO_STARTID` must be set to all
130edge counts added of all shared libraries it will be linked to.
131
132This is quite some hands-on work, so better stay away from instrumenting shared
133libraries. :-)
134
135## AUTODICTIONARY feature
136
137While compiling, a dictionary based on string comparisons is automatically
138generated and put into the target binary. This dictionary is transferred to
139afl-fuzz on start. This improves coverage statistically by 5-10%. :)
140
141Note that if for any reason you do not want to use the autodictionary feature,
142then just set the environment variable `AFL_NO_AUTODICT` when starting afl-fuzz.
143
144## Fixed memory map
145
146To speed up fuzzing a little bit more, it is possible to set a fixed shared
147memory map. Recommended is the value 0x10000.
148
149In most cases, this will work without any problems. However, if a target uses
150early constructors, ifuncs, or a deferred forkserver, this can crash the target.
151
152Also, on unusual operating systems/processors/kernels or weird libraries the
153recommended 0x10000 address might not work, so then change the fixed address.
154
155To enable this feature, set `AFL_LLVM_MAP_ADDR` with the address.
156
157## Document edge IDs
158
159Setting `export AFL_LLVM_DOCUMENT_IDS=file` will document in a file which edge
160ID was given to which function. This helps to identify functions with variable
161bytes or which functions were touched by an input.
162
163## Solving difficult targets
164
165Some targets are difficult because the configure script does unusual stuff that
166is unexpected for afl. See the next section `Potential issues` for how to solve
167these.
168
169### Example: ffmpeg
170
171An example of a hard to solve target is ffmpeg. Here is how to successfully
172instrument it:
173
1741. Get and extract the current ffmpeg and change to its directory.
175
1762. Running configure with --cc=clang fails and various other items will fail
177   when compiling, so we have to trick configure:
178
179    ```
180    ./configure --enable-lto --disable-shared --disable-inline-asm
181    ```
182
1833. Now the configuration is done - and we edit the settings in
184   `./ffbuild/config.mak` (-: the original line, +: what to change it into):
185
186    ```
187    -CC=gcc
188    +CC=afl-clang-lto
189    -CXX=g++
190    +CXX=afl-clang-lto++
191    -AS=gcc
192    +AS=llvm-as
193    -LD=gcc
194    +LD=afl-clang-lto++
195    -DEPCC=gcc
196    +DEPCC=afl-clang-lto
197    -DEPAS=gcc
198    +DEPAS=afl-clang-lto++
199    -AR=ar
200    +AR=llvm-ar
201    -AR_CMD=ar
202    +AR_CMD=llvm-ar
203    -NM_CMD=nm -g
204    +NM_CMD=llvm-nm -g
205    -RANLIB=ranlib -D
206    +RANLIB=llvm-ranlib -D
207    ```
208
2094. Then type make, wait for a long time, and you are done. :)
210
211### Example: WebKit jsc
212
213Building jsc is difficult as the build script has bugs.
214
2151. Checkout Webkit:
216
217    ```
218    svn checkout https://svn.webkit.org/repository/webkit/trunk WebKit
219    cd WebKit
220    ```
221
2222. Fix the build environment:
223
224    ```
225    mkdir -p WebKitBuild/Release
226    cd WebKitBuild/Release
227    ln -s ../../../../../usr/bin/llvm-ar-12 llvm-ar-12
228    ln -s ../../../../../usr/bin/llvm-ranlib-12 llvm-ranlib-12
229    cd ../..
230    ```
231
2323. Build. :)
233
234    ```
235    Tools/Scripts/build-jsc --jsc-only --cli --cmakeargs="-DCMAKE_AR='llvm-ar-12' -DCMAKE_RANLIB='llvm-ranlib-12' -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -DCMAKE_CC_FLAGS='-O3 -lrt' -DCMAKE_CXX_FLAGS='-O3 -lrt' -DIMPORTED_LOCATION='/lib/x86_64-linux-gnu/' -DCMAKE_CC=afl-clang-lto -DCMAKE_CXX=afl-clang-lto++ -DENABLE_STATIC_JSC=ON"
236    ```
237
238## Potential issues
239
240### Compiling libraries fails
241
242If you see this message:
243
244```
245/bin/ld: libfoo.a: error adding symbols: archive has no index; run ranlib to add one
246```
247
248This is because usually gnu gcc ranlib is being called which cannot deal with
249clang LTO files. The solution is simple: when you `./configure`, you also have
250to set `RANLIB=llvm-ranlib` and `AR=llvm-ar`.
251
252Solution:
253
254```
255AR=llvm-ar RANLIB=llvm-ranlib CC=afl-clang-lto CXX=afl-clang-lto++ ./configure --disable-shared
256```
257
258And on some targets you have to set `AR=/RANLIB=` even for `make` as the
259configure script does not save it. Other targets ignore environment variables
260and need the parameters set via `./configure --cc=... --cxx= --ranlib= ...` etc.
261(I am looking at you ffmpeg!)
262
263If you see this message:
264
265```
266assembler command failed ...
267```
268
269Then try setting `llvm-as` for configure:
270
271```
272AS=llvm-as  ...
273```
274
275### Compiling programs still fail
276
277afl-clang-lto is still work in progress.
278
279Known issues:
280* Anything that LLVM 12+ cannot compile, afl-clang-lto cannot compile either -
281  obviously.
282* Anything that does not compile with LTO, afl-clang-lto cannot compile either -
283  obviously.
284
285Hence, if building a target with afl-clang-lto fails, try to build it with
286LLVM 12 and LTO enabled (`CC=clang-12`, `CXX=clang++-12`, `CFLAGS=-flto=full`,
287and `CXXFLAGS=-flto=full`).
288
289If this succeeds, then there is an issue with afl-clang-lto. Please report at
290[https://github.com/AFLplusplus/AFLplusplus/issues/226](https://github.com/AFLplusplus/AFLplusplus/issues/226).
291
292Even some targets where clang-12 fails can be built if the fail is just in
293`./configure`, see `Solving difficult targets` above.
294
295## History
296
297This was originally envisioned by hexcoder- in Summer 2019. However, we saw no
298way to create a pass that is run at link time - although there is a option for
299this in the PassManager: EP_FullLinkTimeOptimizationLast. ("Fun" info - nobody
300knows what this is doing. And the developer who implemented this didn't respond
301to emails.)
302
303In December then came the idea to implement this as a pass that is run via the
304LLVM "opt" program, which is performed via an own linker that afterwards calls
305the real linker. This was first implemented in January and work ... kinda. The
306LTO time instrumentation worked, however, "how" the basic blocks were
307instrumented was a problem, as reducing duplicates turned out to be very, very
308difficult with a program that has so many paths and therefore so many
309dependencies. A lot of strategies were implemented - and failed. And then sat
310solvers were tried, but with over 10.000 variables that turned out to be a
311dead-end too.
312
313The final idea to solve this came from domenukk who proposed to insert a block
314into an edge and then just use incremental counters ... and this worked! After
315some trials and errors to implement this vanhauser-thc found out that there is
316actually an LLVM function for this: SplitEdge() :-)
317
318Still more problems came up though as this only works without bugs from LLVM 9
319onwards, and with high optimization the link optimization ruins the instrumented
320control flow graph.
321
322This is all now fixed with LLVM 12+. The llvm's own linker is now able to load
323passes and this bypasses all problems we had.
324
325Happy end :)
326